Merge branch 'canary' into feat/yandex-social-provider

This commit is contained in:
Vladislav Popovič
2025-12-29 20:15:43 +03:00
committed by GitHub
9 changed files with 169 additions and 96 deletions

View File

@@ -274,7 +274,13 @@ export function CommunityPluginsTable() {
});
return (
<div className="w-full space-y-4">
<div className="w-full">
<div className="flex items-center justify-between text-xs text-muted-foreground">
<p>
Showing {table.getRowModel().rows.length} of {communityPlugins.length}{" "}
plugins
</p>
</div>
<div className="relative">
<Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
<input
@@ -285,8 +291,7 @@ export function CommunityPluginsTable() {
className="w-full rounded-lg border bg-background pl-10 pr-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
/>
</div>
<div className="rounded-lg border">
<div className="rounded-lg">
<div className="overflow-x-auto">
<table className="w-full">
<thead>
@@ -339,13 +344,6 @@ export function CommunityPluginsTable() {
</table>
</div>
</div>
<div className="flex items-center justify-between text-sm text-muted-foreground">
<div>
Showing {table.getRowModel().rows.length} of {communityPlugins.length}{" "}
plugins
</div>
</div>
</div>
);
}

View File

@@ -1968,7 +1968,7 @@ C0.7,239.6,62.1,0.5,62.2,0.4c0,0,54,13.8,119.9,30.8S302.1,62,302.2,62c0.2,0,0.2,
href: "/docs/plugins/jwt",
},
{
title: "3rd party",
title: "Payments",
group: true,
href: "",
icon: () => <LucideAArrowDown className="w-4 h-4" />,
@@ -2105,26 +2105,6 @@ C0.7,239.6,62.1,0.5,62.2,0.4c0,0,54,13.8,119.9,30.8S302.1,62,302.2,62c0.2,0,0.2,
</svg>
),
},
{
title: "Dub",
href: "/docs/plugins/dub",
icon: () => (
<svg
width="1.2em"
height="1.2em"
viewBox="0 0 65 64"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M32.5 64C50.1731 64 64.5 49.6731 64.5 32C64.5 20.1555 58.0648 9.81393 48.5 4.28099V31.9999V47.9998H40.5V45.8594C38.1466 47.2207 35.4143 47.9999 32.5 47.9999C23.6634 47.9999 16.5 40.8364 16.5 31.9999C16.5 23.1633 23.6634 15.9999 32.5 15.9999C35.4143 15.9999 38.1466 16.779 40.5 18.1404V1.00812C37.943 0.350018 35.2624 0 32.5 0C14.8269 0 0.500038 14.3269 0.500038 32C0.500038 49.6731 14.8269 64 32.5 64Z"
fill="currentColor"
/>
</svg>
),
},
{
title: "Commet",
href: "/docs/plugins/commet",
@@ -2147,6 +2127,32 @@ C0.7,239.6,62.1,0.5,62.2,0.4c0,0,54,13.8,119.9,30.8S302.1,62,302.2,62c0.2,0,0.2,
</svg>
),
},
{
title: "Others",
group: true,
icon: () => null,
href: "",
},
{
title: "Dub",
href: "/docs/plugins/dub",
icon: () => (
<svg
width="1.2em"
height="1.2em"
viewBox="0 0 65 64"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M32.5 64C50.1731 64 64.5 49.6731 64.5 32C64.5 20.1555 58.0648 9.81393 48.5 4.28099V31.9999V47.9998H40.5V45.8594C38.1466 47.2207 35.4143 47.9999 32.5 47.9999C23.6634 47.9999 16.5 40.8364 16.5 31.9999C16.5 23.1633 23.6634 15.9999 32.5 15.9999C35.4143 15.9999 38.1466 16.779 40.5 18.1404V1.00812C37.943 0.350018 35.2624 0 32.5 0C14.8269 0 0.500038 14.3269 0.500038 32C0.500038 49.6731 14.8269 64 32.5 64Z"
fill="currentColor"
/>
</svg>
),
},
{
title: "Community Plugins",
href: "/docs/plugins/community-plugins",

View File

@@ -5,10 +5,12 @@ description: A list of recommended community plugins.
import { CommunityPluginsTable } from "@/components/community-plugins-table";
This page showcases a list of recommended community made plugins.
This page showcases a list of recommended community made plugins. We encourage you to create custom plugins and maybe get added to the list!
We encourage you to create custom plugins and maybe get added to the list!
## Create Your Own Plugin
To create your own custom plugin, get started by reading our [plugins documentation](/docs/concepts/plugins). And if you want to share your plugin with the community, please open a pull request to add it to this list.
## Browse Community Plugins
<CommunityPluginsTable />

View File

@@ -12,8 +12,8 @@
"scripts:sync-orama": "node ./scripts/sync-orama.ts"
},
"dependencies": {
"@ai-sdk/openai-compatible": "^1.0.20",
"@ai-sdk/react": "^2.0.64",
"@ai-sdk/openai-compatible": "^2.0.1",
"@ai-sdk/react": "^3.0.3",
"@better-auth/utils": "catalog:",
"@better-fetch/fetch": "catalog:",
"@hookform/resolvers": "^5.2.1",
@@ -50,7 +50,7 @@
"@tanstack/react-table": "^8.21.3",
"@vercel/analytics": "^1.5.0",
"@vercel/og": "^0.8.5",
"ai": "^5.0.64",
"ai": "^6.0.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "1.1.1",

View File

@@ -0,0 +1,65 @@
import { describe, expect, it, vi } from "vitest";
import { createDynamicPathProxy } from "./proxy";
describe("createDynamicPathProxy", () => {
it("should avoid duplicate signal processing", async () => {
const client = vi.fn(async (path, options) => {
if (options?.onSuccess) {
await options.onSuccess({} as any);
}
return {
data: {},
error: null,
};
});
const knownPathMethods = {
"/test": "POST",
} as const;
const signalSet = vi.fn();
const signalGet = vi.fn(() => false);
const atoms = {
testSignal: {
get: signalGet,
set: signalSet,
subscribe: () => () => {},
listen: () => () => {},
off: () => {},
value: false,
} as any,
};
const atomListeners = [
{
matcher: (path: string) => path === "/test",
signal: "testSignal",
},
{
matcher: (path: string) => path === "/test",
signal: "testSignal",
},
];
const proxy = createDynamicPathProxy(
{
test: {},
},
client as any,
knownPathMethods,
atoms,
atomListeners,
);
vi.useFakeTimers();
await (proxy.test as any)();
expect(client).toHaveBeenCalled();
vi.runAllTimers();
expect(signalSet).toHaveBeenCalledTimes(1);
vi.useRealTimers();
});
});

View File

@@ -1,5 +1,6 @@
import type {
BetterAuthClientPlugin,
ClientAtomListener,
ClientFetchOption,
} from "@better-auth/core";
import type { BetterFetch } from "@better-fetch/fetch";
@@ -100,9 +101,15 @@ export function createDynamicPathProxy<T extends Record<string, any>>(
*/
const matches = atomListeners.filter((s) => s.matcher(routePath));
if (!matches.length) return;
const visited = new Set<ClientAtomListener["signal"]>();
for (const match of matches) {
const signal = atoms[match.signal as any];
if (!signal) return;
if (visited.has(match.signal)) {
continue;
}
visited.add(match.signal);
/**
* To avoid race conditions we set the signal in a setTimeout
*/

View File

@@ -172,7 +172,7 @@ describe("session-refresh", () => {
vi.useRealTimers();
});
it("should allow refetch on focus when session is null even within rate limit", async () => {
it("should rate limit refetch on focus when session is null", async () => {
const sessionAtom: SessionAtom = atom({
data: {
user: { id: "1", email: "test@test.com" },
@@ -223,15 +223,15 @@ describe("session-refresh", () => {
// Immediately trigger another focus event (within rate limit window)
manager.triggerRefetch({ event: "visibilitychange" });
// Signal should change because session is null (rate limit bypassed)
expect(signalChangeCount).toBeGreaterThan(signalCountAfterFirstFocus);
// Signal should NOT change because rate limit should apply even when session is null
expect(signalChangeCount).toBe(signalCountAfterFirstFocus);
unsubscribeSignal();
manager.cleanup();
vi.useRealTimers();
});
it("should allow refetch on focus when session is undefined even within rate limit", async () => {
it("should rate limit refetch on focus when session is undefined", async () => {
const sessionAtom: SessionAtom = atom({
data: {
user: { id: "1", email: "test@test.com" },
@@ -282,8 +282,8 @@ describe("session-refresh", () => {
// Immediately trigger another focus event (within rate limit window)
manager.triggerRefetch({ event: "visibilitychange" });
// Signal should change because session is undefined (rate limit bypassed)
expect(signalChangeCount).toBeGreaterThan(signalCountAfterFirstFocus);
// Signal should NOT change because rate limit should apply even when session is undefined
expect(signalChangeCount).toBe(signalCountAfterFirstFocus);
unsubscribeSignal();
manager.cleanup();

View File

@@ -100,13 +100,10 @@ export function createSessionRefreshManager(opts: SessionRefreshOptions) {
// Rate limit: don't refetch on focus if a session request was made recently
if (event?.event === "visibilitychange") {
const timeSinceLastRequest = now() - state.lastSessionRequest;
if (
timeSinceLastRequest < FOCUS_REFETCH_RATE_LIMIT_SECONDS &&
currentSession?.data !== null &&
currentSession?.data !== undefined
) {
if (timeSinceLastRequest < FOCUS_REFETCH_RATE_LIMIT_SECONDS) {
return;
}
state.lastSessionRequest = now();
}
if (
@@ -114,9 +111,6 @@ export function createSessionRefreshManager(opts: SessionRefreshOptions) {
currentSession?.data === undefined ||
event?.event === "visibilitychange"
) {
if (event?.event === "visibilitychange") {
state.lastSessionRequest = now();
}
state.lastSync = now();
sessionSignal.set(!sessionSignal.get());
}

93
pnpm-lock.yaml generated
View File

@@ -598,11 +598,11 @@ importers:
docs:
dependencies:
'@ai-sdk/openai-compatible':
specifier: ^1.0.20
version: 1.0.20(zod@4.1.13)
specifier: ^2.0.1
version: 2.0.1(zod@4.1.13)
'@ai-sdk/react':
specifier: ^2.0.64
version: 2.0.64(react@19.2.3)(zod@4.1.13)
specifier: ^3.0.3
version: 3.0.3(react@19.2.3)(zod@4.1.13)
'@better-auth/utils':
specifier: 'catalog:'
version: 0.3.0
@@ -712,8 +712,8 @@ importers:
specifier: ^0.8.5
version: 0.8.5
ai:
specifier: ^5.0.64
version: 5.0.64(zod@4.1.13)
specifier: ^6.0.3
version: 6.0.3(zod@4.1.13)
class-variance-authority:
specifier: ^0.7.1
version: 0.7.1
@@ -1575,37 +1575,33 @@ packages:
graphql:
optional: true
'@ai-sdk/gateway@1.0.35':
resolution: {integrity: sha512-cdsXbeRRMi6QxbZscin69Asx2fi0d2TmmPngcPFUMpZbchGEBiJYVNvIfiALKFKXEq0l/w0xGNV3E13vroaleA==}
'@ai-sdk/gateway@3.0.2':
resolution: {integrity: sha512-giJEg9ob45htbu3iautK+2kvplY2JnTj7ir4wZzYSQWvqGatWfBBfDuNCU5wSJt9BCGjymM5ZS9ziD42JGCZBw==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.25.76 || ^4.1.8
'@ai-sdk/openai-compatible@1.0.20':
resolution: {integrity: sha512-UIg7dj79wYsmHFyk8snF9q2qhbQZMI0XiUNhilMg/4htPqYF0z6Q5GSRMUtYEo7yN14a8g0KeVOawE6+H4VYcg==}
'@ai-sdk/openai-compatible@2.0.1':
resolution: {integrity: sha512-6tfF6YAREdECPHqQ4ydZFQyY6AiwRc+MHZPbwx2HGsLBTPQXtImLO+iew/pB9XjD2G8s4un6PSNYK//Y+jWDcQ==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.25.76 || ^4.1.8
'@ai-sdk/provider-utils@3.0.11':
resolution: {integrity: sha512-4hgHj89VqyOHzGaV85TkcgvO8WjecVF35TOUVg+C56vnzpWSgdIZu/ZWZNdZ6BTrv8y0N1toBWW7XcWiRRicLg==}
'@ai-sdk/provider-utils@4.0.1':
resolution: {integrity: sha512-de2v8gH9zj47tRI38oSxhQIewmNc+OZjYIOOaMoVWKL65ERSav2PYYZHPSPCrfOeLMkv+Dyh8Y0QGwkO29wMWQ==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.25.76 || ^4.1.8
'@ai-sdk/provider@2.0.0':
resolution: {integrity: sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==}
'@ai-sdk/provider@3.0.0':
resolution: {integrity: sha512-m9ka3ptkPQbaHHZHqDXDF9C9B5/Mav0KTdky1k2HZ3/nrW2t1AgObxIVPyGDWQNS9FXT/FS6PIoSjpcP/No8rQ==}
engines: {node: '>=18'}
'@ai-sdk/react@2.0.64':
resolution: {integrity: sha512-o5scrAU+V3MJRD2HoX/pKUYZHITTTaNIM35fFH8n7mdCFovNUI1DiAHYtw8IMM+a5GM92jPvfejKRKaHHs/LVQ==}
'@ai-sdk/react@3.0.3':
resolution: {integrity: sha512-mLIgQuBdIX9gxCYQN3Pv/J8ARoFreIKYr/TVQtI+FwEzejuGFimTyhDln7UIBfrnm3Mpn1xENIWZfDfCRF7wkw==}
engines: {node: '>=18'}
peerDependencies:
react: ^18 || ^19 || ^19.0.0-rc
zod: ^3.25.76 || ^4.1.8
peerDependenciesMeta:
zod:
optional: true
react: ^18 || ~19.0.1 || ~19.1.2 || ^19.2.1
'@algolia/abtesting@1.2.0':
resolution: {integrity: sha512-Z6Liq7US5CpdHExZLfPMBPxQHHUObV587kGvCLniLr1UTx0fGFIeGNWd005WIqQXqEda9GyAi7T2e7DUupVv0g==}
@@ -6258,6 +6254,9 @@ packages:
'@standard-schema/spec@1.0.0':
resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==}
'@standard-schema/spec@1.1.0':
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
'@standard-schema/utils@0.3.0':
resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==}
@@ -6910,8 +6909,8 @@ packages:
resolution: {integrity: sha512-fHqnxfBYcwkamlEgcIzaZqL8IHT09hR7FZL7UdMTdGJyoaBzM/dY6ulO5Swi4ig30FrBJI9I2C+GLV9sb9vexA==}
engines: {node: '>=16'}
'@vercel/oidc@3.0.2':
resolution: {integrity: sha512-JekxQ0RApo4gS4un/iMGsIL1/k4KUBe3HmnGcDvzHuFBdQdudEJgTqcsJC7y6Ul4Yw5CeykgvQbX2XeEJd0+DA==}
'@vercel/oidc@3.0.5':
resolution: {integrity: sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw==}
engines: {node: '>= 20'}
'@vinxi/listhen@1.5.6':
@@ -7099,8 +7098,8 @@ packages:
resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
engines: {node: '>=8'}
ai@5.0.64:
resolution: {integrity: sha512-a7H1z2Xz6NQdgx+FIdDlkenoPYBbxbmJSbRfnOFnYS1S1XraiHT8M85hLvz8d8zlxVtSSjiP+c4EjqwtAe72cg==}
ai@6.0.3:
resolution: {integrity: sha512-OOo+/C+sEyscoLnbY3w42vjQDICioVNyS+F+ogwq6O5RJL/vgWGuiLzFwuP7oHTeni/MkmX8tIge48GTdaV7QQ==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.25.76 || ^4.1.8
@@ -14455,39 +14454,39 @@ snapshots:
optionalDependencies:
graphql: 16.12.0
'@ai-sdk/gateway@1.0.35(zod@4.1.13)':
'@ai-sdk/gateway@3.0.2(zod@4.1.13)':
dependencies:
'@ai-sdk/provider': 2.0.0
'@ai-sdk/provider-utils': 3.0.11(zod@4.1.13)
'@vercel/oidc': 3.0.2
'@ai-sdk/provider': 3.0.0
'@ai-sdk/provider-utils': 4.0.1(zod@4.1.13)
'@vercel/oidc': 3.0.5
zod: 4.1.13
'@ai-sdk/openai-compatible@1.0.20(zod@4.1.13)':
'@ai-sdk/openai-compatible@2.0.1(zod@4.1.13)':
dependencies:
'@ai-sdk/provider': 2.0.0
'@ai-sdk/provider-utils': 3.0.11(zod@4.1.13)
'@ai-sdk/provider': 3.0.0
'@ai-sdk/provider-utils': 4.0.1(zod@4.1.13)
zod: 4.1.13
'@ai-sdk/provider-utils@3.0.11(zod@4.1.13)':
'@ai-sdk/provider-utils@4.0.1(zod@4.1.13)':
dependencies:
'@ai-sdk/provider': 2.0.0
'@standard-schema/spec': 1.0.0
'@ai-sdk/provider': 3.0.0
'@standard-schema/spec': 1.1.0
eventsource-parser: 3.0.6
zod: 4.1.13
'@ai-sdk/provider@2.0.0':
'@ai-sdk/provider@3.0.0':
dependencies:
json-schema: 0.4.0
'@ai-sdk/react@2.0.64(react@19.2.3)(zod@4.1.13)':
'@ai-sdk/react@3.0.3(react@19.2.3)(zod@4.1.13)':
dependencies:
'@ai-sdk/provider-utils': 3.0.11(zod@4.1.13)
ai: 5.0.64(zod@4.1.13)
'@ai-sdk/provider-utils': 4.0.1(zod@4.1.13)
ai: 6.0.3(zod@4.1.13)
react: 19.2.3
swr: 2.3.6(react@19.2.3)
throttleit: 2.1.0
optionalDependencies:
zod: 4.1.13
transitivePeerDependencies:
- zod
'@algolia/abtesting@1.2.0':
dependencies:
@@ -19744,6 +19743,8 @@ snapshots:
'@standard-schema/spec@1.0.0': {}
'@standard-schema/spec@1.1.0': {}
'@standard-schema/utils@0.3.0': {}
'@sveltejs/acorn-typescript@1.0.7(acorn@8.15.0)':
@@ -20528,7 +20529,7 @@ snapshots:
'@resvg/resvg-wasm': 2.4.0
satori: 0.16.0
'@vercel/oidc@3.0.2': {}
'@vercel/oidc@3.0.5': {}
'@vinxi/listhen@1.5.6':
dependencies:
@@ -20788,11 +20789,11 @@ snapshots:
clean-stack: 2.2.0
indent-string: 4.0.0
ai@5.0.64(zod@4.1.13):
ai@6.0.3(zod@4.1.13):
dependencies:
'@ai-sdk/gateway': 1.0.35(zod@4.1.13)
'@ai-sdk/provider': 2.0.0
'@ai-sdk/provider-utils': 3.0.11(zod@4.1.13)
'@ai-sdk/gateway': 3.0.2(zod@4.1.13)
'@ai-sdk/provider': 3.0.0
'@ai-sdk/provider-utils': 4.0.1(zod@4.1.13)
'@opentelemetry/api': 1.9.0
zod: 4.1.13