@nestjs-kit/better-auth 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/admin.cjs +3 -4
- package/dist/admin.d.cts +1 -3
- package/dist/admin.d.cts.map +1 -1
- package/dist/admin.d.mts +1 -3
- package/dist/admin.d.mts.map +1 -1
- package/dist/admin.mjs +3 -4
- package/dist/api-key.cjs +4 -5
- package/dist/api-key.d.cts +1 -3
- package/dist/api-key.d.cts.map +1 -1
- package/dist/api-key.d.mts +1 -3
- package/dist/api-key.d.mts.map +1 -1
- package/dist/api-key.mjs +4 -5
- package/dist/{auth-contracts-Brh_Qko9.d.cts → auth-contracts-CADGyI0O.d.mts} +3 -53
- package/dist/auth-contracts-CADGyI0O.d.mts.map +1 -0
- package/dist/{auth-contracts-Cz7C4_Ch.d.mts → auth-contracts-zvt8ysoZ.d.cts} +3 -53
- package/dist/auth-contracts-zvt8ysoZ.d.cts.map +1 -0
- package/dist/{auth-decorators-Cu_nxWnF.cjs → auth-decorators-CJKE9wlT.cjs} +8 -10
- package/dist/{auth-decorators-Cu_nxWnF.cjs.map → auth-decorators-CJKE9wlT.cjs.map} +1 -1
- package/dist/{auth-decorators-B1mFnGJ6.mjs → auth-decorators-CkNkLNpU.mjs} +8 -10
- package/dist/{auth-decorators-B1mFnGJ6.mjs.map → auth-decorators-CkNkLNpU.mjs.map} +1 -1
- package/dist/{auth-errors-C3rbTV9p.mjs → auth-errors-JjxGDVSt.mjs} +3 -5
- package/dist/{auth-errors-C3rbTV9p.mjs.map → auth-errors-JjxGDVSt.mjs.map} +1 -1
- package/dist/{auth-errors-DmiKl0L_.cjs → auth-errors-RW7hOVI-.cjs} +3 -5
- package/dist/{auth-errors-DmiKl0L_.cjs.map → auth-errors-RW7hOVI-.cjs.map} +1 -1
- package/dist/{auth-guard-h9QB_l11.mjs → auth-guard-Da92QEK5.mjs} +4 -5
- package/dist/{auth-guard-h9QB_l11.mjs.map → auth-guard-Da92QEK5.mjs.map} +1 -1
- package/dist/{auth-guard-CLyhAEEj.cjs → auth-guard-wapEh_M_.cjs} +4 -5
- package/dist/{auth-guard-CLyhAEEj.cjs.map → auth-guard-wapEh_M_.cjs.map} +1 -1
- package/dist/{auth-module-BgcY2HpA.cjs → auth-module-ab60-WJZ.cjs} +28 -47
- package/dist/auth-module-ab60-WJZ.cjs.map +1 -0
- package/dist/{auth-module-CtHx3gRU.mjs → auth-module-uy3BBWwR.mjs} +28 -47
- package/dist/auth-module-uy3BBWwR.mjs.map +1 -0
- package/dist/{auth-scope-interceptor-BjxAL1-S.cjs → auth-scope-interceptor-Bui20-Ps.cjs} +5 -7
- package/dist/{auth-scope-interceptor-BjxAL1-S.cjs.map → auth-scope-interceptor-Bui20-Ps.cjs.map} +1 -1
- package/dist/{auth-scope-interceptor-DYPn3b8M.mjs → auth-scope-interceptor-CvCQ52dV.mjs} +5 -7
- package/dist/{auth-scope-interceptor-DYPn3b8M.mjs.map → auth-scope-interceptor-CvCQ52dV.mjs.map} +1 -1
- package/dist/{bridge-protocol-c66aEkr5.mjs → bridge-protocol-CmRsff2P.mjs} +2 -3
- package/dist/{bridge-protocol-BDw8opAG.cjs.map → bridge-protocol-CmRsff2P.mjs.map} +1 -1
- package/dist/{bridge-protocol-BDw8opAG.cjs → bridge-protocol-DNFhIKdo.cjs} +2 -3
- package/dist/{bridge-protocol-c66aEkr5.mjs.map → bridge-protocol-DNFhIKdo.cjs.map} +1 -1
- package/dist/{decorate-Dt0e9K6R.mjs → decorate-bO-rBn-G.mjs} +5 -9
- package/dist/{decorate-Dt0e9K6R.mjs.map → decorate-bO-rBn-G.mjs.map} +1 -1
- package/dist/{decorate-Cr4Q7bNE.cjs → decorate-fMNf0caZ.cjs} +5 -9
- package/dist/{decorate-Cr4Q7bNE.cjs.map → decorate-fMNf0caZ.cjs.map} +1 -1
- package/dist/express.cjs +3 -4
- package/dist/express.d.cts +1 -3
- package/dist/express.d.cts.map +1 -1
- package/dist/express.d.mts +1 -3
- package/dist/express.d.mts.map +1 -1
- package/dist/express.mjs +3 -4
- package/dist/fastify.cjs +2 -3
- package/dist/fastify.d.cts +1 -3
- package/dist/fastify.d.cts.map +1 -1
- package/dist/fastify.d.mts +1 -3
- package/dist/fastify.d.mts.map +1 -1
- package/dist/fastify.mjs +2 -3
- package/dist/graphql.cjs +7 -9
- package/dist/graphql.cjs.map +1 -1
- package/dist/graphql.d.cts +1 -3
- package/dist/graphql.d.cts.map +1 -1
- package/dist/graphql.d.mts +1 -3
- package/dist/graphql.d.mts.map +1 -1
- package/dist/graphql.mjs +7 -9
- package/dist/graphql.mjs.map +1 -1
- package/dist/index.cjs +7 -7
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +7 -7
- package/dist/microservices.cjs +4 -6
- package/dist/microservices.cjs.map +1 -1
- package/dist/microservices.d.cts +1 -5
- package/dist/microservices.d.cts.map +1 -1
- package/dist/microservices.d.mts +1 -5
- package/dist/microservices.d.mts.map +1 -1
- package/dist/microservices.mjs +3 -5
- package/dist/microservices.mjs.map +1 -1
- package/dist/organization.cjs +3 -4
- package/dist/organization.d.cts +1 -3
- package/dist/organization.d.cts.map +1 -1
- package/dist/organization.d.mts +1 -3
- package/dist/organization.d.mts.map +1 -1
- package/dist/organization.mjs +3 -4
- package/dist/{platform-CZl7TY8j.mjs → platform-BKYVzAv1.mjs} +3 -5
- package/dist/{platform-CZl7TY8j.mjs.map → platform-BKYVzAv1.mjs.map} +1 -1
- package/dist/{platform-YICqqYv6.cjs → platform-C67dxSls.cjs} +3 -5
- package/dist/{platform-YICqqYv6.cjs.map → platform-C67dxSls.cjs.map} +1 -1
- package/dist/platform.cjs +1 -1
- package/dist/platform.d.cts +0 -2
- package/dist/platform.d.cts.map +1 -1
- package/dist/platform.d.mts +0 -2
- package/dist/platform.d.mts.map +1 -1
- package/dist/platform.mjs +1 -1
- package/dist/plugin.cjs +4 -8
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +0 -4
- package/dist/plugin.d.cts.map +1 -1
- package/dist/plugin.d.mts +0 -4
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +4 -8
- package/dist/plugin.mjs.map +1 -1
- package/dist/{principal-readings-Ied2staw.mjs → principal-readings-BuXTU0-2.mjs} +3 -4
- package/dist/{principal-readings-Ied2staw.mjs.map → principal-readings-BuXTU0-2.mjs.map} +1 -1
- package/dist/{principal-readings-Chmt6rQV.cjs → principal-readings-JLNT58k0.cjs} +3 -4
- package/dist/{principal-readings-Chmt6rQV.cjs.map → principal-readings-JLNT58k0.cjs.map} +1 -1
- package/dist/{rolldown-runtime-VH7oDXx4.cjs → rolldown-runtime-CmQyd0Gu.cjs} +1 -2
- package/dist/testing/conformance.cjs +24 -31
- package/dist/testing/conformance.cjs.map +1 -1
- package/dist/testing/conformance.d.cts +4 -14
- package/dist/testing/conformance.d.cts.map +1 -1
- package/dist/testing/conformance.d.mts +4 -14
- package/dist/testing/conformance.d.mts.map +1 -1
- package/dist/testing/conformance.mjs +23 -30
- package/dist/testing/conformance.mjs.map +1 -1
- package/dist/testing.cjs +6 -7
- package/dist/testing.d.cts +1 -3
- package/dist/testing.d.cts.map +1 -1
- package/dist/testing.d.mts +1 -3
- package/dist/testing.d.mts.map +1 -1
- package/dist/testing.mjs +6 -7
- package/dist/websockets.cjs +7 -10
- package/dist/websockets.cjs.map +1 -1
- package/dist/websockets.d.cts +1 -7
- package/dist/websockets.d.cts.map +1 -1
- package/dist/websockets.d.mts +1 -7
- package/dist/websockets.d.mts.map +1 -1
- package/dist/websockets.mjs +7 -10
- package/dist/websockets.mjs.map +1 -1
- package/package.json +24 -24
- package/dist/auth-contracts-Brh_Qko9.d.cts.map +0 -1
- package/dist/auth-contracts-Cz7C4_Ch.d.mts.map +0 -1
- package/dist/auth-module-BgcY2HpA.cjs.map +0 -1
- package/dist/auth-module-CtHx3gRU.mjs.map +0 -1
package/dist/plugin.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.cjs","names":["createDefu","APIError","createAuthMiddleware","isAPIError","parseCookies","createAuthMiddleware","setShouldSkipSessionRefresh"],"sources":["../src/bridge-protocol.ts","../src/database-hook-dispatcher.ts","../src/hook-dispatcher.ts","../src/plugin.ts"],"sourcesContent":["import type { HookEndpointContext } from \"better-auth\";\nimport type { CookieSink, DatabaseHookTarget } from \"./auth-contracts.js\";\nimport type { DatabaseHookMethod } from \"./auth-types.js\";\n\nexport const BRIDGE_HANDLE = Symbol.for(\"@nestjs-kit/better-auth:bridge\");\nexport const EXTENSION_DEFINITION = Symbol.for(\n \"@nestjs-kit/better-auth:extension\",\n);\nexport const UPGRADE_REQUEST = Symbol.for(\n \"@nestjs-kit/better-auth:upgrade-request\",\n);\n\nexport interface BridgeHandle {\n readonly protocol: 4; // checked by core (B04); v5: binding state and instance, the caller-session check\n readonly clientIpHeader: string | null; // single source of truth for the header name (§6.8)\n /** 'unbound' until the first bind(); 'bound' while an application's hooks are registered; 'closed' after its shutdown, hooks still registered. */\n readonly state: \"unbound\" | \"bound\" | \"closed\";\n /** Endpoint dispatches and database-hook dispatches (writes) seen before the first binding, reset by every bind() (B26). */\n readonly unboundDispatches: number;\n /**\n * Registers the application's hooks and returns close(), which the kernel calls in onApplicationShutdown: a closed binding keeps\n * dispatching until another application binds (§10.6). A second bind() from the same owner for the same instance is a no-op that\n * returns the existing registration (NestMicroservice.init() runs lifecycle hooks twice); from the same owner for another instance it\n * throws PLUGIN_SHARED_BETWEEN_INSTANCES (B03). Throws INSTANCE_ALREADY_BOUND if another bootstrapped application is bound; takes over\n * a never-bootstrapped one (reported, B06) and a closed one (silently). A never-bootstrapped binding it takes over becomes\n * 'displaced': an application whose init() was still running when another bound fails in onApplicationBootstrap (B06).\n */\n bind(binding: BridgeBinding): { close(): void; tookOverFrom?: string };\n /** true when an endpoint returned `value` (an object) in a non-router dispatch observed while bound; false for before-hook short-circuits (LEAD-V37). */\n producedByEndpoint(value: unknown): boolean;\n}\nexport interface BridgeBinding {\n /** The application: compared by identity for the same-owner no-op; `description` goes into the B06 message. */\n readonly owner: { readonly description: string };\n /** The instance this plugin object serves; one plugin object serves one instance (B03). */\n readonly instance: string;\n /**\n * 'initialized' at bind; the kernel sets 'bootstrapped' in onApplicationBootstrap; close() sets 'closed'; bind() of another\n * application sets 'displaced' on an 'initialized' binding it takes over.\n */\n state: \"initialized\" | \"bootstrapped\" | \"closed\" | \"displaced\";\n /** View of the kernel's RequestScope; the plugin never owns an AsyncLocalStorage. [C] */\n current(): ScopeView | undefined;\n /** Credential header names declared by the instance's principal sources (credential matching, §7.5). */\n readonly credentialHeaders: readonly string[];\n readonly before: readonly CompiledHook[]; // sorted by order, then discovery order\n readonly after: readonly CompiledHook[];\n readonly database: Readonly<{\n [E in DatabaseHookTarget]: {\n readonly before: readonly DatabaseHookMethod<E, \"before\">[];\n readonly after: readonly DatabaseHookMethod<E, \"after\">[];\n };\n }>;\n /** Debug log when the bridge drops a direct call's cookies because its credential is foreign. */\n onDropped(path: string): void;\n}\nexport interface ScopeView {\n /** Cookie-capable forwarding handlers have already passed the enforcing form check on every method/operation (§7.10). */\n readonly cookies: CookieSink | null;\n readonly forward: \"none\" | \"same-credential\" | \"any\";\n readonly inbound: (() => Headers) | undefined;\n readonly internal: boolean;\n /**\n * Present in EVERY handler scope whose transport exposes a browser leg, unless the plan's origin check is 'off' — enforcing or not,\n * so safe methods and GraphQL queries are covered too (§7.5). Returns when this leg has a passing origin verdict for `instance`, or\n * the single kernel disable predicate (§7.10 item 4) holds; a guard run without a passing verdict is insufficient. Otherwise throws the kernel's\n * BetterAuthConfigurationError PUBLIC_HANDLER_USED_CALLER_SESSION.\n */\n readonly checkCallerSession?: (path: string, instance: string) => void;\n /** The browser leg's OWN headers (TransportCall.browser.headers()), before any transport credential mapping replaced them; undefined\n * when the transport exposes no browser leg. Entry 2 recognizes an ambiently carried session cookie with it. */\n readonly browserHeaders: (() => Headers) | undefined;\n}\nexport interface CompiledHook {\n /**\n * Evaluate the user predicate once and let it throw. The dispatcher logs and\n * converts before-hook matcher failures to APIError 500; after-hook matcher\n * failures cross the next fixed SDK matcher boundary unchanged, so the SDK\n * cannot recover them as handler APIErrors. Never swallow a failure as a\n * non-match or evaluate the predicate again in run().\n */\n matches(ctx: HookEndpointContext, scope: ScopeView | undefined): boolean;\n run(ctx: HookEndpointContext): Promise<unknown>;\n}\n","import type { BetterAuthOptions } from \"better-auth\";\nimport type { DatabaseHookTarget } from \"./auth-contracts.js\";\nimport type {\n DatabaseHookData,\n DatabaseHookResult,\n DbHookFn,\n} from \"./auth-types.js\";\nimport type { BridgeBinding } from \"./bridge-protocol.js\";\n\n/**\n * Construct all dispatchers once; bindings supply only the late-bound methods.\n *\n * The dispatchers take the SDK's own parameter types, so they satisfy `databaseHooks` whatever\n * instance the program registers. Bound hooks declare `DatabaseHookData`, which adds the\n * registered instance's plugin and additional fields (for example admin()'s `banned`) to the\n * same rows; the dispatcher states that narrowing once when it forwards a payload.\n */\nexport function createDatabaseDispatchers(\n getBinding: () => BridgeBinding | null,\n countUnbound: () => void,\n): NonNullable<BetterAuthOptions[\"databaseHooks\"]> {\n function operation<E extends DatabaseHookTarget>(target: E) {\n const before = async (\n ...[data, ctx]: Parameters<DbHookFn<E, \"before\">>\n ): Promise<DatabaseHookResult<E, \"before\">> => {\n const binding = getBinding();\n if (!binding) {\n countUnbound();\n return undefined as DatabaseHookResult<E, \"before\">;\n }\n const hooks = binding.database[target].before;\n let accumulated: Record<string, unknown> = {};\n let changed = false;\n for (const hook of hooks) {\n const result = await hook(\n (target.endsWith(\".create\")\n ? { ...data, ...accumulated }\n : data) as DatabaseHookData<E, \"before\">,\n ctx,\n );\n if (result === false) {\n return false as DatabaseHookResult<E, \"before\">;\n }\n if (\n !target.endsWith(\".delete\") &&\n result &&\n typeof result === \"object\" &&\n \"data\" in result\n ) {\n accumulated = { ...accumulated, ...result.data };\n changed = true;\n }\n }\n // A generic target cannot express the runtime delete narrowing. Delete\n // never reaches this branch; the public binding keeps its void/false type.\n return (\n changed ? { data: accumulated } : undefined\n ) as DatabaseHookResult<E, \"before\">;\n };\n const after = async (\n ...[data, ctx]: Parameters<DbHookFn<E, \"after\">>\n ): Promise<void> => {\n const binding = getBinding();\n if (!binding) {\n countUnbound();\n return;\n }\n for (const hook of binding.database[target].after) {\n await hook(data as DatabaseHookData<E, \"after\">, ctx);\n }\n };\n return { before, after };\n }\n return {\n user: {\n create: operation(\"user.create\"),\n update: operation(\"user.update\"),\n delete: operation(\"user.delete\"),\n },\n session: {\n create: operation(\"session.create\"),\n update: operation(\"session.update\"),\n delete: operation(\"session.delete\"),\n },\n account: {\n create: operation(\"account.create\"),\n update: operation(\"account.update\"),\n delete: operation(\"account.delete\"),\n },\n verification: {\n create: operation(\"verification.create\"),\n update: operation(\"verification.update\"),\n delete: operation(\"verification.delete\"),\n },\n };\n}\n","import type { HookEndpointContext } from \"better-auth\";\nimport { APIError, createAuthMiddleware, isAPIError } from \"better-auth/api\";\nimport { createDefu } from \"defu\";\nimport type { BridgeBinding, ScopeView } from \"./bridge-protocol.js\";\n\nconst API_ERROR_HEADERS = Symbol.for(\"better-call:api-error-headers\");\nconst mergeContext = createDefu((object, key, value) => {\n if (Array.isArray(object[key]) && Array.isArray(value)) {\n object[key] = value;\n return true;\n }\n});\n\nfunction mergeHeaders(\n target: Headers,\n source: Headers | null | undefined,\n): void {\n source?.forEach((value, key) => {\n if (key === \"set-cookie\") {\n target.append(key, value);\n } else {\n target.set(key, value);\n }\n });\n}\n\nfunction errorHeaders(error: APIError): Headers | undefined {\n const attached = (error as unknown as Record<symbol, Headers | undefined>)[\n API_ERROR_HEADERS\n ];\n if (!attached && !error.headers) {\n return undefined;\n }\n const headers = new Headers(attached);\n if (error.headers && error.headers !== attached) {\n mergeHeaders(headers, new Headers(error.headers));\n }\n return headers;\n}\n\n/** Run each method through the SDK middleware so its cookie/header state is isolated. */\nexport async function runBefore(\n binding: BridgeBinding,\n ctx: HookEndpointContext,\n scope: ScopeView | undefined,\n): Promise<unknown> {\n let accumulated: Record<string, unknown> = {};\n for (const hook of binding.before) {\n let matched: boolean;\n try {\n matched = hook.matches(ctx, scope);\n } catch (error) {\n ctx.context.logger.error(\n \"An error occurred during Nest hook matcher execution:\",\n error,\n );\n throw new APIError(\"INTERNAL_SERVER_ERROR\", {\n message:\n \"An error occurred during hook matcher execution. Check the logs for more details.\",\n });\n }\n if (!matched) {\n continue;\n }\n let result: { response: unknown; headers: Headers };\n try {\n result = (await createAuthMiddleware(hook.run)({\n ...ctx,\n returnHeaders: true,\n })) as { response: unknown; headers: Headers };\n } catch (error) {\n if (isAPIError(error)) {\n // The enclosing SDK middleware reattaches its own header object. Keep\n // the throwing method's headers, including non-enumerable cookie state.\n ctx.responseHeaders =\n (error as unknown as Record<symbol, Headers | undefined>)[\n API_ERROR_HEADERS\n ] ?? new Headers();\n }\n throw error;\n }\n ctx.context.responseHeaders ??= new Headers();\n mergeHeaders(ctx.context.responseHeaders, result.headers);\n const value = result.response;\n if (value && typeof value === \"object\") {\n if (\"context\" in value && typeof value.context === \"object\") {\n const { headers, ...rest } = value.context as Record<string, unknown>;\n if (headers instanceof Headers) {\n const requestHeaders =\n accumulated.headers instanceof Headers\n ? accumulated.headers\n : new Headers();\n headers.forEach((header, key) => {\n requestHeaders.set(key, header);\n });\n accumulated.headers = requestHeaders;\n }\n accumulated = mergeContext(rest, accumulated);\n } else {\n return value;\n }\n }\n }\n return Object.keys(accumulated).length ? { context: accumulated } : undefined;\n}\n\nexport async function runAfter(\n binding: BridgeBinding,\n ctx: HookEndpointContext,\n scope: ScopeView | undefined,\n relayMatcherError?: (error: unknown) => void,\n): Promise<unknown> {\n for (const hook of binding.after) {\n let matched: boolean;\n try {\n matched = hook.matches(ctx, scope);\n } catch (error) {\n if (!relayMatcherError) {\n throw error;\n }\n // A surrounding SDK handler catches APIErrors as recoverable results.\n // Stop here and rethrow from the next fixed SDK matcher instead.\n relayMatcherError(error);\n return undefined;\n }\n if (!matched) {\n continue;\n }\n let result: { response: unknown; headers?: Headers | null };\n try {\n result = (await createAuthMiddleware(hook.run)({\n ...ctx,\n returnHeaders: true,\n })) as { response: unknown; headers: Headers };\n } catch (error) {\n if (!isAPIError(error)) {\n throw error;\n }\n result = { response: error, headers: errorHeaders(error) };\n }\n if (result.headers) {\n ctx.context.responseHeaders ??= new Headers();\n mergeHeaders(ctx.context.responseHeaders, result.headers);\n }\n if (result.response !== undefined) {\n ctx.context.returned = result.response;\n }\n }\n return ctx.context.returned;\n}\n","import type { BetterAuthPlugin, HookEndpointContext } from \"better-auth\";\nimport {\n createAuthMiddleware,\n setShouldSkipSessionRefresh,\n} from \"better-auth/api\";\nimport { parseCookies } from \"better-auth/cookies\";\nimport type { NestjsPluginOptions } from \"./auth-contracts.js\";\nimport {\n BRIDGE_HANDLE,\n type BridgeBinding,\n type BridgeHandle,\n type ScopeView,\n} from \"./bridge-protocol.js\";\nimport { createDatabaseDispatchers } from \"./database-hook-dispatcher.js\";\nimport { runAfter, runBefore } from \"./hook-dispatcher.js\";\n\nexport type { NestjsPluginOptions } from \"./auth-contracts.js\";\nexport const NESTJS_PLUGIN_ID = \"@nestjs-kit/better-auth\";\n\nconst isRouter = (ctx: object): boolean =>\n \"_flag\" in ctx && ctx._flag === \"router\";\nconst separator = \"\\u0000\";\n\nfunction sessionToken(\n headers: Headers | undefined,\n ctx: HookEndpointContext,\n): string {\n return (\n parseCookies(headers?.get(\"cookie\") ?? \"\").get(\n ctx.context.authCookies.sessionToken.name,\n ) ?? \"\"\n );\n}\n\nfunction credential(\n headers: Headers | undefined,\n ctx: HookEndpointContext,\n extra: readonly string[],\n): string {\n return [\n sessionToken(headers, ctx),\n headers?.get(\"authorization\") ?? \"\",\n ...extra.map((name) => headers?.get(name) ?? \"\"),\n ].join(separator);\n}\n\nfunction sameCredential(\n ctx: HookEndpointContext,\n scope: ScopeView,\n extra: readonly string[],\n): boolean {\n const call = credential(ctx.headers, ctx, extra);\n return (\n call === separator.repeat(extra.length + 1) ||\n call === credential(scope.inbound?.(), ctx, extra)\n );\n}\n\n/** Pure construction-time Better Auth plugin; the Nest kernel binds through a global symbol. */\nexport function nestjs(\n options: NestjsPluginOptions = {},\n): BetterAuthPlugin & { id: typeof NESTJS_PLUGIN_ID } {\n let binding: BridgeBinding | null = null;\n let registration: ReturnType<BridgeHandle[\"bind\"]> | undefined;\n let unboundDispatches = 0;\n const endpointResults = new WeakSet<object>();\n const afterMatcherErrors = new WeakMap<object, unknown>();\n const scope = () => binding?.current();\n const clientIpHeader =\n options.clientIpHeader === false\n ? null\n : (options.clientIpHeader ??\n `x-nestjs-kit-better-auth-ip-${globalThis.crypto.randomUUID().replaceAll(\"-\", \"\")}`);\n const handle: BridgeHandle = {\n protocol: 4,\n clientIpHeader,\n get state() {\n return binding === null\n ? \"unbound\"\n : binding.state === \"closed\"\n ? \"closed\"\n : \"bound\";\n },\n get unboundDispatches() {\n return unboundDispatches;\n },\n bind(next) {\n if (binding && binding.instance !== next.instance) {\n throw new Error(\n `PLUGIN_SHARED_BETWEEN_INSTANCES: '${binding.instance}' and '${next.instance}'`,\n );\n }\n if (binding?.owner === next.owner && registration) {\n return registration;\n }\n if (binding?.state === \"bootstrapped\") {\n throw new Error(\n `INSTANCE_ALREADY_BOUND: bound to ${binding.owner.description}`,\n );\n }\n let tookOverFrom: string | undefined;\n if (binding?.state === \"initialized\") {\n tookOverFrom = binding.owner.description;\n // Its application may still be initializing: its bootstrap must fail rather than run without hooks.\n binding.state = \"displaced\";\n }\n binding = next;\n unboundDispatches = 0;\n registration = {\n tookOverFrom,\n close: () => {\n next.state = \"closed\";\n },\n };\n return registration;\n },\n producedByEndpoint(value) {\n return (\n typeof value === \"object\" &&\n value !== null &&\n endpointResults.has(value)\n );\n },\n };\n const plugin = {\n id: NESTJS_PLUGIN_ID,\n [BRIDGE_HANDLE]: handle,\n init: () => ({\n options: {\n databaseHooks: createDatabaseDispatchers(\n () => binding,\n () => {\n unboundDispatches++;\n },\n ),\n ...(clientIpHeader\n ? { advanced: { ipAddress: { ipAddressHeaders: [clientIpHeader] } } }\n : {}),\n },\n }),\n hooks: {\n before: [\n {\n matcher: (ctx) => {\n if (!binding) {\n unboundDispatches++;\n return false;\n }\n return !isRouter(ctx) && scope()?.cookies === null;\n },\n handler: createAuthMiddleware(async () => {\n await setShouldSkipSessionRefresh(true);\n }),\n },\n {\n matcher: (ctx) => {\n if (!binding || isRouter(ctx)) {\n return false;\n }\n const current = scope();\n if (!current || current.internal) {\n return false;\n }\n // Access capabilities before testing credentials: extraction errors\n // must fail even a credential-free direct endpoint call.\n const check = current.checkCallerSession;\n current.inbound?.();\n const browser = current.browserHeaders?.();\n const token = sessionToken(ctx.headers, ctx);\n // Only the browser leg's own cookie is ambient. A session cookie that a\n // transport mapped in-band (graphql-ws connectionParams, socket\n // credentials) cannot ride a cross-site request.\n return (\n !!check && token !== \"\" && token === sessionToken(browser, ctx)\n );\n },\n handler: createAuthMiddleware(async (ctx) => {\n scope()?.checkCallerSession?.(ctx.path, binding!.instance);\n }),\n },\n {\n matcher: () => binding !== null && binding.before.length > 0,\n handler: createAuthMiddleware((ctx) =>\n runBefore(binding!, ctx, scope()),\n ),\n },\n ],\n after: [\n {\n matcher: () => binding !== null && binding.after.length > 0,\n handler: createAuthMiddleware((ctx) =>\n runAfter(binding!, ctx, scope(), (error) => {\n afterMatcherErrors.set(ctx.context, error);\n }),\n ),\n },\n {\n matcher: (ctx) => {\n // Each endpoint dispatch owns this shared context, including nested\n // calls. Rethrow outside the SDK's recoverable handler-error catch.\n if (afterMatcherErrors.has(ctx.context)) {\n const error = afterMatcherErrors.get(ctx.context);\n afterMatcherErrors.delete(ctx.context);\n throw error;\n }\n return binding !== null && !isRouter(ctx);\n },\n handler: createAuthMiddleware(async (ctx) => {\n const value = ctx.context.returned;\n if (typeof value === \"object\" && value !== null) {\n endpointResults.add(value);\n }\n }),\n },\n {\n matcher: (ctx) => {\n if (isRouter(ctx)) {\n return false;\n }\n const current = scope();\n return !!current?.cookies && current.forward !== \"none\";\n },\n handler: createAuthMiddleware(async (ctx) => {\n const current = scope()!;\n if (\n current.forward === \"same-credential\" &&\n !sameCredential(ctx, current, binding!.credentialHeaders)\n ) {\n binding!.onDropped(ctx.path);\n return;\n }\n const cookies = ctx.context.responseHeaders?.getSetCookie() ?? [];\n if (cookies.length) {\n current.cookies!.append(cookies);\n }\n }),\n },\n ],\n },\n } satisfies BetterAuthPlugin & { [BRIDGE_HANDLE]: BridgeHandle };\n return plugin;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin.cjs","names":["createDefu","APIError","createAuthMiddleware","isAPIError","parseCookies","createAuthMiddleware","setShouldSkipSessionRefresh"],"sources":["../src/bridge-protocol.ts","../src/database-hook-dispatcher.ts","../src/hook-dispatcher.ts","../src/plugin.ts"],"sourcesContent":["import type { HookEndpointContext } from \"better-auth\";\nimport type { CookieSink, DatabaseHookTarget } from \"./auth-contracts.js\";\nimport type { DatabaseHookMethod } from \"./auth-types.js\";\n\nexport const BRIDGE_HANDLE = Symbol.for(\"@nestjs-kit/better-auth:bridge\");\nexport const EXTENSION_DEFINITION = Symbol.for(\n \"@nestjs-kit/better-auth:extension\",\n);\nexport const UPGRADE_REQUEST = Symbol.for(\n \"@nestjs-kit/better-auth:upgrade-request\",\n);\n\nexport interface BridgeHandle {\n readonly protocol: 4; // checked by core (B04); v5: binding state and instance, the caller-session check\n readonly clientIpHeader: string | null; // single source of truth for the header name (§6.8)\n /** 'unbound' until the first bind(); 'bound' while an application's hooks are registered; 'closed' after its shutdown, hooks still registered. */\n readonly state: \"unbound\" | \"bound\" | \"closed\";\n /** Endpoint dispatches and database-hook dispatches (writes) seen before the first binding, reset by every bind() (B26). */\n readonly unboundDispatches: number;\n /**\n * Registers the application's hooks and returns close(), which the kernel calls in onApplicationShutdown: a closed binding keeps\n * dispatching until another application binds (§10.6). A second bind() from the same owner for the same instance is a no-op that\n * returns the existing registration (NestMicroservice.init() runs lifecycle hooks twice); from the same owner for another instance it\n * throws PLUGIN_SHARED_BETWEEN_INSTANCES (B03). Throws INSTANCE_ALREADY_BOUND if another bootstrapped application is bound; takes over\n * a never-bootstrapped one (reported, B06) and a closed one (silently). A never-bootstrapped binding it takes over becomes\n * 'displaced': an application whose init() was still running when another bound fails in onApplicationBootstrap (B06).\n */\n bind(binding: BridgeBinding): { close(): void; tookOverFrom?: string };\n /** true when an endpoint returned `value` (an object) in a non-router dispatch observed while bound; false for before-hook short-circuits (LEAD-V37). */\n producedByEndpoint(value: unknown): boolean;\n}\nexport interface BridgeBinding {\n /** The application: compared by identity for the same-owner no-op; `description` goes into the B06 message. */\n readonly owner: { readonly description: string };\n /** The instance this plugin object serves; one plugin object serves one instance (B03). */\n readonly instance: string;\n /**\n * 'initialized' at bind; the kernel sets 'bootstrapped' in onApplicationBootstrap; close() sets 'closed'; bind() of another\n * application sets 'displaced' on an 'initialized' binding it takes over.\n */\n state: \"initialized\" | \"bootstrapped\" | \"closed\" | \"displaced\";\n /** View of the kernel's RequestScope; the plugin never owns an AsyncLocalStorage. [C] */\n current(): ScopeView | undefined;\n /** Credential header names declared by the instance's principal sources (credential matching, §7.5). */\n readonly credentialHeaders: readonly string[];\n readonly before: readonly CompiledHook[]; // sorted by order, then discovery order\n readonly after: readonly CompiledHook[];\n readonly database: Readonly<{\n [E in DatabaseHookTarget]: {\n readonly before: readonly DatabaseHookMethod<E, \"before\">[];\n readonly after: readonly DatabaseHookMethod<E, \"after\">[];\n };\n }>;\n /** Debug log when the bridge drops a direct call's cookies because its credential is foreign. */\n onDropped(path: string): void;\n}\nexport interface ScopeView {\n /** Cookie-capable forwarding handlers have already passed the enforcing form check on every method/operation (§7.10). */\n readonly cookies: CookieSink | null;\n readonly forward: \"none\" | \"same-credential\" | \"any\";\n readonly inbound: (() => Headers) | undefined;\n readonly internal: boolean;\n /**\n * Present in EVERY handler scope whose transport exposes a browser leg, unless the plan's origin check is 'off' — enforcing or not,\n * so safe methods and GraphQL queries are covered too (§7.5). Returns when this leg has a passing origin verdict for `instance`, or\n * the single kernel disable predicate (§7.10 item 4) holds; a guard run without a passing verdict is insufficient. Otherwise throws the kernel's\n * BetterAuthConfigurationError PUBLIC_HANDLER_USED_CALLER_SESSION.\n */\n readonly checkCallerSession?: (path: string, instance: string) => void;\n /** The browser leg's OWN headers (TransportCall.browser.headers()), before any transport credential mapping replaced them; undefined\n * when the transport exposes no browser leg. Entry 2 recognizes an ambiently carried session cookie with it. */\n readonly browserHeaders: (() => Headers) | undefined;\n}\nexport interface CompiledHook {\n /**\n * Evaluate the user predicate once and let it throw. The dispatcher logs and\n * converts before-hook matcher failures to APIError 500; after-hook matcher\n * failures cross the next fixed SDK matcher boundary unchanged, so the SDK\n * cannot recover them as handler APIErrors. Never swallow a failure as a\n * non-match or evaluate the predicate again in run().\n */\n matches(ctx: HookEndpointContext, scope: ScopeView | undefined): boolean;\n run(ctx: HookEndpointContext): Promise<unknown>;\n}\n","import type { BetterAuthOptions } from \"better-auth\";\nimport type { DatabaseHookTarget } from \"./auth-contracts.js\";\nimport type {\n DatabaseHookData,\n DatabaseHookResult,\n DbHookFn,\n} from \"./auth-types.js\";\nimport type { BridgeBinding } from \"./bridge-protocol.js\";\n\n/**\n * Construct all dispatchers once; bindings supply only the late-bound methods.\n *\n * The dispatchers take the SDK's own parameter types, so they satisfy `databaseHooks` whatever\n * instance the program registers. Bound hooks declare `DatabaseHookData`, which adds the\n * registered instance's plugin and additional fields (for example admin()'s `banned`) to the\n * same rows; the dispatcher states that narrowing once when it forwards a payload.\n */\nexport function createDatabaseDispatchers(\n getBinding: () => BridgeBinding | null,\n countUnbound: () => void,\n): NonNullable<BetterAuthOptions[\"databaseHooks\"]> {\n function operation<E extends DatabaseHookTarget>(target: E) {\n const before = async (\n ...[data, ctx]: Parameters<DbHookFn<E, \"before\">>\n ): Promise<DatabaseHookResult<E, \"before\">> => {\n const binding = getBinding();\n if (!binding) {\n countUnbound();\n return undefined as DatabaseHookResult<E, \"before\">;\n }\n const hooks = binding.database[target].before;\n let accumulated: Record<string, unknown> = {};\n let changed = false;\n for (const hook of hooks) {\n const result = await hook(\n (target.endsWith(\".create\")\n ? { ...data, ...accumulated }\n : data) as DatabaseHookData<E, \"before\">,\n ctx,\n );\n if (result === false) {\n return false as DatabaseHookResult<E, \"before\">;\n }\n if (\n !target.endsWith(\".delete\") &&\n result &&\n typeof result === \"object\" &&\n \"data\" in result\n ) {\n accumulated = { ...accumulated, ...result.data };\n changed = true;\n }\n }\n // A generic target cannot express the runtime delete narrowing. Delete\n // never reaches this branch; the public binding keeps its void/false type.\n return (\n changed ? { data: accumulated } : undefined\n ) as DatabaseHookResult<E, \"before\">;\n };\n const after = async (\n ...[data, ctx]: Parameters<DbHookFn<E, \"after\">>\n ): Promise<void> => {\n const binding = getBinding();\n if (!binding) {\n countUnbound();\n return;\n }\n for (const hook of binding.database[target].after) {\n await hook(data as DatabaseHookData<E, \"after\">, ctx);\n }\n };\n return { before, after };\n }\n return {\n user: {\n create: operation(\"user.create\"),\n update: operation(\"user.update\"),\n delete: operation(\"user.delete\"),\n },\n session: {\n create: operation(\"session.create\"),\n update: operation(\"session.update\"),\n delete: operation(\"session.delete\"),\n },\n account: {\n create: operation(\"account.create\"),\n update: operation(\"account.update\"),\n delete: operation(\"account.delete\"),\n },\n verification: {\n create: operation(\"verification.create\"),\n update: operation(\"verification.update\"),\n delete: operation(\"verification.delete\"),\n },\n };\n}\n","import type { HookEndpointContext } from \"better-auth\";\nimport { APIError, createAuthMiddleware, isAPIError } from \"better-auth/api\";\nimport { createDefu } from \"defu\";\nimport type { BridgeBinding, ScopeView } from \"./bridge-protocol.js\";\n\nconst API_ERROR_HEADERS = Symbol.for(\"better-call:api-error-headers\");\nconst mergeContext = createDefu((object, key, value) => {\n if (Array.isArray(object[key]) && Array.isArray(value)) {\n object[key] = value;\n return true;\n }\n});\n\nfunction mergeHeaders(\n target: Headers,\n source: Headers | null | undefined,\n): void {\n source?.forEach((value, key) => {\n if (key === \"set-cookie\") {\n target.append(key, value);\n } else {\n target.set(key, value);\n }\n });\n}\n\nfunction errorHeaders(error: APIError): Headers | undefined {\n const attached = (error as unknown as Record<symbol, Headers | undefined>)[\n API_ERROR_HEADERS\n ];\n if (!attached && !error.headers) {\n return undefined;\n }\n const headers = new Headers(attached);\n if (error.headers && error.headers !== attached) {\n mergeHeaders(headers, new Headers(error.headers));\n }\n return headers;\n}\n\n/** Run each method through the SDK middleware so its cookie/header state is isolated. */\nexport async function runBefore(\n binding: BridgeBinding,\n ctx: HookEndpointContext,\n scope: ScopeView | undefined,\n): Promise<unknown> {\n let accumulated: Record<string, unknown> = {};\n for (const hook of binding.before) {\n let matched: boolean;\n try {\n matched = hook.matches(ctx, scope);\n } catch (error) {\n ctx.context.logger.error(\n \"An error occurred during Nest hook matcher execution:\",\n error,\n );\n throw new APIError(\"INTERNAL_SERVER_ERROR\", {\n message:\n \"An error occurred during hook matcher execution. Check the logs for more details.\",\n });\n }\n if (!matched) {\n continue;\n }\n let result: { response: unknown; headers: Headers };\n try {\n result = (await createAuthMiddleware(hook.run)({\n ...ctx,\n returnHeaders: true,\n })) as { response: unknown; headers: Headers };\n } catch (error) {\n if (isAPIError(error)) {\n // The enclosing SDK middleware reattaches its own header object. Keep\n // the throwing method's headers, including non-enumerable cookie state.\n ctx.responseHeaders =\n (error as unknown as Record<symbol, Headers | undefined>)[\n API_ERROR_HEADERS\n ] ?? new Headers();\n }\n throw error;\n }\n ctx.context.responseHeaders ??= new Headers();\n mergeHeaders(ctx.context.responseHeaders, result.headers);\n const value = result.response;\n if (value && typeof value === \"object\") {\n if (\"context\" in value && typeof value.context === \"object\") {\n const { headers, ...rest } = value.context as Record<string, unknown>;\n if (headers instanceof Headers) {\n const requestHeaders =\n accumulated.headers instanceof Headers\n ? accumulated.headers\n : new Headers();\n headers.forEach((header, key) => {\n requestHeaders.set(key, header);\n });\n accumulated.headers = requestHeaders;\n }\n accumulated = mergeContext(rest, accumulated);\n } else {\n return value;\n }\n }\n }\n return Object.keys(accumulated).length ? { context: accumulated } : undefined;\n}\n\nexport async function runAfter(\n binding: BridgeBinding,\n ctx: HookEndpointContext,\n scope: ScopeView | undefined,\n relayMatcherError?: (error: unknown) => void,\n): Promise<unknown> {\n for (const hook of binding.after) {\n let matched: boolean;\n try {\n matched = hook.matches(ctx, scope);\n } catch (error) {\n if (!relayMatcherError) {\n throw error;\n }\n // A surrounding SDK handler catches APIErrors as recoverable results.\n // Stop here and rethrow from the next fixed SDK matcher instead.\n relayMatcherError(error);\n return undefined;\n }\n if (!matched) {\n continue;\n }\n let result: { response: unknown; headers?: Headers | null };\n try {\n result = (await createAuthMiddleware(hook.run)({\n ...ctx,\n returnHeaders: true,\n })) as { response: unknown; headers: Headers };\n } catch (error) {\n if (!isAPIError(error)) {\n throw error;\n }\n result = { response: error, headers: errorHeaders(error) };\n }\n if (result.headers) {\n ctx.context.responseHeaders ??= new Headers();\n mergeHeaders(ctx.context.responseHeaders, result.headers);\n }\n if (result.response !== undefined) {\n ctx.context.returned = result.response;\n }\n }\n return ctx.context.returned;\n}\n","import type { BetterAuthPlugin, HookEndpointContext } from \"better-auth\";\nimport {\n createAuthMiddleware,\n setShouldSkipSessionRefresh,\n} from \"better-auth/api\";\nimport { parseCookies } from \"better-auth/cookies\";\nimport type { NestjsPluginOptions } from \"./auth-contracts.js\";\nimport {\n BRIDGE_HANDLE,\n type BridgeBinding,\n type BridgeHandle,\n type ScopeView,\n} from \"./bridge-protocol.js\";\nimport { createDatabaseDispatchers } from \"./database-hook-dispatcher.js\";\nimport { runAfter, runBefore } from \"./hook-dispatcher.js\";\n\nexport type { NestjsPluginOptions } from \"./auth-contracts.js\";\nexport const NESTJS_PLUGIN_ID = \"@nestjs-kit/better-auth\";\n\nconst isRouter = (ctx: object): boolean =>\n \"_flag\" in ctx && ctx._flag === \"router\";\nconst separator = \"\\u0000\";\n\nfunction sessionToken(\n headers: Headers | undefined,\n ctx: HookEndpointContext,\n): string {\n return (\n parseCookies(headers?.get(\"cookie\") ?? \"\").get(\n ctx.context.authCookies.sessionToken.name,\n ) ?? \"\"\n );\n}\n\nfunction credential(\n headers: Headers | undefined,\n ctx: HookEndpointContext,\n extra: readonly string[],\n): string {\n return [\n sessionToken(headers, ctx),\n headers?.get(\"authorization\") ?? \"\",\n ...extra.map((name) => headers?.get(name) ?? \"\"),\n ].join(separator);\n}\n\nfunction sameCredential(\n ctx: HookEndpointContext,\n scope: ScopeView,\n extra: readonly string[],\n): boolean {\n const call = credential(ctx.headers, ctx, extra);\n return (\n call === separator.repeat(extra.length + 1) ||\n call === credential(scope.inbound?.(), ctx, extra)\n );\n}\n\n/** Pure construction-time Better Auth plugin; the Nest kernel binds through a global symbol. */\nexport function nestjs(\n options: NestjsPluginOptions = {},\n): BetterAuthPlugin & { id: typeof NESTJS_PLUGIN_ID } {\n let binding: BridgeBinding | null = null;\n let registration: ReturnType<BridgeHandle[\"bind\"]> | undefined;\n let unboundDispatches = 0;\n const endpointResults = new WeakSet<object>();\n const afterMatcherErrors = new WeakMap<object, unknown>();\n const scope = () => binding?.current();\n const clientIpHeader =\n options.clientIpHeader === false\n ? null\n : (options.clientIpHeader ??\n `x-nestjs-kit-better-auth-ip-${globalThis.crypto.randomUUID().replaceAll(\"-\", \"\")}`);\n const handle: BridgeHandle = {\n protocol: 4,\n clientIpHeader,\n get state() {\n return binding === null\n ? \"unbound\"\n : binding.state === \"closed\"\n ? \"closed\"\n : \"bound\";\n },\n get unboundDispatches() {\n return unboundDispatches;\n },\n bind(next) {\n if (binding && binding.instance !== next.instance) {\n throw new Error(\n `PLUGIN_SHARED_BETWEEN_INSTANCES: '${binding.instance}' and '${next.instance}'`,\n );\n }\n if (binding?.owner === next.owner && registration) {\n return registration;\n }\n if (binding?.state === \"bootstrapped\") {\n throw new Error(\n `INSTANCE_ALREADY_BOUND: bound to ${binding.owner.description}`,\n );\n }\n let tookOverFrom: string | undefined;\n if (binding?.state === \"initialized\") {\n tookOverFrom = binding.owner.description;\n // Its application may still be initializing: its bootstrap must fail rather than run without hooks.\n binding.state = \"displaced\";\n }\n binding = next;\n unboundDispatches = 0;\n registration = {\n tookOverFrom,\n close: () => {\n next.state = \"closed\";\n },\n };\n return registration;\n },\n producedByEndpoint(value) {\n return (\n typeof value === \"object\" &&\n value !== null &&\n endpointResults.has(value)\n );\n },\n };\n const plugin = {\n id: NESTJS_PLUGIN_ID,\n [BRIDGE_HANDLE]: handle,\n init: () => ({\n options: {\n databaseHooks: createDatabaseDispatchers(\n () => binding,\n () => {\n unboundDispatches++;\n },\n ),\n ...(clientIpHeader\n ? { advanced: { ipAddress: { ipAddressHeaders: [clientIpHeader] } } }\n : {}),\n },\n }),\n hooks: {\n before: [\n {\n matcher: (ctx) => {\n if (!binding) {\n unboundDispatches++;\n return false;\n }\n return !isRouter(ctx) && scope()?.cookies === null;\n },\n handler: createAuthMiddleware(async () => {\n await setShouldSkipSessionRefresh(true);\n }),\n },\n {\n matcher: (ctx) => {\n if (!binding || isRouter(ctx)) {\n return false;\n }\n const current = scope();\n if (!current || current.internal) {\n return false;\n }\n // Access capabilities before testing credentials: extraction errors\n // must fail even a credential-free direct endpoint call.\n const check = current.checkCallerSession;\n current.inbound?.();\n const browser = current.browserHeaders?.();\n const token = sessionToken(ctx.headers, ctx);\n // Only the browser leg's own cookie is ambient. A session cookie that a\n // transport mapped in-band (graphql-ws connectionParams, socket\n // credentials) cannot ride a cross-site request.\n return (\n !!check && token !== \"\" && token === sessionToken(browser, ctx)\n );\n },\n handler: createAuthMiddleware(async (ctx) => {\n scope()?.checkCallerSession?.(ctx.path, binding!.instance);\n }),\n },\n {\n matcher: () => binding !== null && binding.before.length > 0,\n handler: createAuthMiddleware((ctx) =>\n runBefore(binding!, ctx, scope()),\n ),\n },\n ],\n after: [\n {\n matcher: () => binding !== null && binding.after.length > 0,\n handler: createAuthMiddleware((ctx) =>\n runAfter(binding!, ctx, scope(), (error) => {\n afterMatcherErrors.set(ctx.context, error);\n }),\n ),\n },\n {\n matcher: (ctx) => {\n // Each endpoint dispatch owns this shared context, including nested\n // calls. Rethrow outside the SDK's recoverable handler-error catch.\n if (afterMatcherErrors.has(ctx.context)) {\n const error = afterMatcherErrors.get(ctx.context);\n afterMatcherErrors.delete(ctx.context);\n throw error;\n }\n return binding !== null && !isRouter(ctx);\n },\n handler: createAuthMiddleware(async (ctx) => {\n const value = ctx.context.returned;\n if (typeof value === \"object\" && value !== null) {\n endpointResults.add(value);\n }\n }),\n },\n {\n matcher: (ctx) => {\n if (isRouter(ctx)) {\n return false;\n }\n const current = scope();\n return !!current?.cookies && current.forward !== \"none\";\n },\n handler: createAuthMiddleware(async (ctx) => {\n const current = scope()!;\n if (\n current.forward === \"same-credential\" &&\n !sameCredential(ctx, current, binding!.credentialHeaders)\n ) {\n binding!.onDropped(ctx.path);\n return;\n }\n const cookies = ctx.context.responseHeaders?.getSetCookie() ?? [];\n if (cookies.length) {\n current.cookies!.append(cookies);\n }\n }),\n },\n ],\n },\n } satisfies BetterAuthPlugin & { [BRIDGE_HANDLE]: BridgeHandle };\n return plugin;\n}\n"],"mappings":"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"}
|
package/dist/plugin.d.cts
CHANGED
|
@@ -2,7 +2,6 @@ import { BetterAuthPlugin } from "better-auth";
|
|
|
2
2
|
import "@nestjs/common";
|
|
3
3
|
import "@nestjs/core";
|
|
4
4
|
import "rxjs";
|
|
5
|
-
//#region src/auth-contracts.d.ts
|
|
6
5
|
interface NestjsPluginOptions {
|
|
7
6
|
/**
|
|
8
7
|
* Header better-auth reads the client IP from, contributed to advanced.ipAddress.ipAddressHeaders as a default
|
|
@@ -12,13 +11,10 @@ interface NestjsPluginOptions {
|
|
|
12
11
|
*/
|
|
13
12
|
clientIpHeader?: string | false;
|
|
14
13
|
}
|
|
15
|
-
//#endregion
|
|
16
|
-
//#region src/plugin.d.ts
|
|
17
14
|
export declare const NESTJS_PLUGIN_ID = "@nestjs-kit/better-auth";
|
|
18
15
|
/** Pure construction-time Better Auth plugin; the Nest kernel binds through a global symbol. */
|
|
19
16
|
export declare function nestjs(options?: NestjsPluginOptions): BetterAuthPlugin & {
|
|
20
17
|
id: typeof NESTJS_PLUGIN_ID;
|
|
21
18
|
};
|
|
22
|
-
//#endregion
|
|
23
19
|
export type { NestjsPluginOptions };
|
|
24
20
|
//# sourceMappingURL=plugin.d.cts.map
|
package/dist/plugin.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.cts","names":[],"sources":["../src/auth-contracts.ts","../src/plugin.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugin.d.cts","names":[],"sources":["../src/auth-contracts.ts","../src/plugin.ts"],"mappings":";;;;UAyXiB;;;;;;;EAOf;;qBC/WW;;wBA0CG,OACd,UAAS,sBACR;EAAqB,WAAW"}
|
package/dist/plugin.d.mts
CHANGED
|
@@ -2,7 +2,6 @@ import { BetterAuthPlugin } from "better-auth";
|
|
|
2
2
|
import "@nestjs/common";
|
|
3
3
|
import "@nestjs/core";
|
|
4
4
|
import "rxjs";
|
|
5
|
-
//#region src/auth-contracts.d.ts
|
|
6
5
|
interface NestjsPluginOptions {
|
|
7
6
|
/**
|
|
8
7
|
* Header better-auth reads the client IP from, contributed to advanced.ipAddress.ipAddressHeaders as a default
|
|
@@ -12,13 +11,10 @@ interface NestjsPluginOptions {
|
|
|
12
11
|
*/
|
|
13
12
|
clientIpHeader?: string | false;
|
|
14
13
|
}
|
|
15
|
-
//#endregion
|
|
16
|
-
//#region src/plugin.d.ts
|
|
17
14
|
export declare const NESTJS_PLUGIN_ID = "@nestjs-kit/better-auth";
|
|
18
15
|
/** Pure construction-time Better Auth plugin; the Nest kernel binds through a global symbol. */
|
|
19
16
|
export declare function nestjs(options?: NestjsPluginOptions): BetterAuthPlugin & {
|
|
20
17
|
id: typeof NESTJS_PLUGIN_ID;
|
|
21
18
|
};
|
|
22
|
-
//#endregion
|
|
23
19
|
export type { NestjsPluginOptions };
|
|
24
20
|
//# sourceMappingURL=plugin.d.mts.map
|
package/dist/plugin.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.mts","names":[],"sources":["../src/auth-contracts.ts","../src/plugin.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugin.d.mts","names":[],"sources":["../src/auth-contracts.ts","../src/plugin.ts"],"mappings":";;;;UAyXiB;;;;;;;EAOf;;qBC/WW;;wBA0CG,OACd,UAAS,sBACR;EAAqB,WAAW"}
|
package/dist/plugin.mjs
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { APIError, createAuthMiddleware, isAPIError, setShouldSkipSessionRefresh } from "better-auth/api";
|
|
2
2
|
import { parseCookies } from "better-auth/cookies";
|
|
3
3
|
import { createDefu } from "defu";
|
|
4
|
-
|
|
4
|
+
// src/bridge-protocol.ts
|
|
5
5
|
const BRIDGE_HANDLE = Symbol.for("@nestjs-kit/better-auth:bridge");
|
|
6
|
-
|
|
7
|
-
//#region src/database-hook-dispatcher.ts
|
|
6
|
+
// src/database-hook-dispatcher.ts
|
|
8
7
|
/**
|
|
9
8
|
* Construct all dispatchers once; bindings supply only the late-bound methods.
|
|
10
9
|
*
|
|
@@ -76,8 +75,7 @@ function createDatabaseDispatchers(getBinding, countUnbound) {
|
|
|
76
75
|
}
|
|
77
76
|
};
|
|
78
77
|
}
|
|
79
|
-
|
|
80
|
-
//#region src/hook-dispatcher.ts
|
|
78
|
+
// src/hook-dispatcher.ts
|
|
81
79
|
const API_ERROR_HEADERS = Symbol.for("better-call:api-error-headers");
|
|
82
80
|
const mergeContext = createDefu((object, key, value) => {
|
|
83
81
|
if (Array.isArray(object[key]) && Array.isArray(value)) {
|
|
@@ -171,8 +169,7 @@ async function runAfter(binding, ctx, scope, relayMatcherError) {
|
|
|
171
169
|
}
|
|
172
170
|
return ctx.context.returned;
|
|
173
171
|
}
|
|
174
|
-
|
|
175
|
-
//#region src/plugin.ts
|
|
172
|
+
// src/plugin.ts
|
|
176
173
|
const NESTJS_PLUGIN_ID = "@nestjs-kit/better-auth";
|
|
177
174
|
const isRouter = (ctx) => "_flag" in ctx && ctx._flag === "router";
|
|
178
175
|
const separator = "\0";
|
|
@@ -315,7 +312,6 @@ function nestjs(options = {}) {
|
|
|
315
312
|
}
|
|
316
313
|
};
|
|
317
314
|
}
|
|
318
|
-
//#endregion
|
|
319
315
|
export { NESTJS_PLUGIN_ID, nestjs };
|
|
320
316
|
|
|
321
317
|
//# sourceMappingURL=plugin.mjs.map
|
package/dist/plugin.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.mjs","names":[],"sources":["../src/bridge-protocol.ts","../src/database-hook-dispatcher.ts","../src/hook-dispatcher.ts","../src/plugin.ts"],"sourcesContent":["import type { HookEndpointContext } from \"better-auth\";\nimport type { CookieSink, DatabaseHookTarget } from \"./auth-contracts.js\";\nimport type { DatabaseHookMethod } from \"./auth-types.js\";\n\nexport const BRIDGE_HANDLE = Symbol.for(\"@nestjs-kit/better-auth:bridge\");\nexport const EXTENSION_DEFINITION = Symbol.for(\n \"@nestjs-kit/better-auth:extension\",\n);\nexport const UPGRADE_REQUEST = Symbol.for(\n \"@nestjs-kit/better-auth:upgrade-request\",\n);\n\nexport interface BridgeHandle {\n readonly protocol: 4; // checked by core (B04); v5: binding state and instance, the caller-session check\n readonly clientIpHeader: string | null; // single source of truth for the header name (§6.8)\n /** 'unbound' until the first bind(); 'bound' while an application's hooks are registered; 'closed' after its shutdown, hooks still registered. */\n readonly state: \"unbound\" | \"bound\" | \"closed\";\n /** Endpoint dispatches and database-hook dispatches (writes) seen before the first binding, reset by every bind() (B26). */\n readonly unboundDispatches: number;\n /**\n * Registers the application's hooks and returns close(), which the kernel calls in onApplicationShutdown: a closed binding keeps\n * dispatching until another application binds (§10.6). A second bind() from the same owner for the same instance is a no-op that\n * returns the existing registration (NestMicroservice.init() runs lifecycle hooks twice); from the same owner for another instance it\n * throws PLUGIN_SHARED_BETWEEN_INSTANCES (B03). Throws INSTANCE_ALREADY_BOUND if another bootstrapped application is bound; takes over\n * a never-bootstrapped one (reported, B06) and a closed one (silently). A never-bootstrapped binding it takes over becomes\n * 'displaced': an application whose init() was still running when another bound fails in onApplicationBootstrap (B06).\n */\n bind(binding: BridgeBinding): { close(): void; tookOverFrom?: string };\n /** true when an endpoint returned `value` (an object) in a non-router dispatch observed while bound; false for before-hook short-circuits (LEAD-V37). */\n producedByEndpoint(value: unknown): boolean;\n}\nexport interface BridgeBinding {\n /** The application: compared by identity for the same-owner no-op; `description` goes into the B06 message. */\n readonly owner: { readonly description: string };\n /** The instance this plugin object serves; one plugin object serves one instance (B03). */\n readonly instance: string;\n /**\n * 'initialized' at bind; the kernel sets 'bootstrapped' in onApplicationBootstrap; close() sets 'closed'; bind() of another\n * application sets 'displaced' on an 'initialized' binding it takes over.\n */\n state: \"initialized\" | \"bootstrapped\" | \"closed\" | \"displaced\";\n /** View of the kernel's RequestScope; the plugin never owns an AsyncLocalStorage. [C] */\n current(): ScopeView | undefined;\n /** Credential header names declared by the instance's principal sources (credential matching, §7.5). */\n readonly credentialHeaders: readonly string[];\n readonly before: readonly CompiledHook[]; // sorted by order, then discovery order\n readonly after: readonly CompiledHook[];\n readonly database: Readonly<{\n [E in DatabaseHookTarget]: {\n readonly before: readonly DatabaseHookMethod<E, \"before\">[];\n readonly after: readonly DatabaseHookMethod<E, \"after\">[];\n };\n }>;\n /** Debug log when the bridge drops a direct call's cookies because its credential is foreign. */\n onDropped(path: string): void;\n}\nexport interface ScopeView {\n /** Cookie-capable forwarding handlers have already passed the enforcing form check on every method/operation (§7.10). */\n readonly cookies: CookieSink | null;\n readonly forward: \"none\" | \"same-credential\" | \"any\";\n readonly inbound: (() => Headers) | undefined;\n readonly internal: boolean;\n /**\n * Present in EVERY handler scope whose transport exposes a browser leg, unless the plan's origin check is 'off' — enforcing or not,\n * so safe methods and GraphQL queries are covered too (§7.5). Returns when this leg has a passing origin verdict for `instance`, or\n * the single kernel disable predicate (§7.10 item 4) holds; a guard run without a passing verdict is insufficient. Otherwise throws the kernel's\n * BetterAuthConfigurationError PUBLIC_HANDLER_USED_CALLER_SESSION.\n */\n readonly checkCallerSession?: (path: string, instance: string) => void;\n /** The browser leg's OWN headers (TransportCall.browser.headers()), before any transport credential mapping replaced them; undefined\n * when the transport exposes no browser leg. Entry 2 recognizes an ambiently carried session cookie with it. */\n readonly browserHeaders: (() => Headers) | undefined;\n}\nexport interface CompiledHook {\n /**\n * Evaluate the user predicate once and let it throw. The dispatcher logs and\n * converts before-hook matcher failures to APIError 500; after-hook matcher\n * failures cross the next fixed SDK matcher boundary unchanged, so the SDK\n * cannot recover them as handler APIErrors. Never swallow a failure as a\n * non-match or evaluate the predicate again in run().\n */\n matches(ctx: HookEndpointContext, scope: ScopeView | undefined): boolean;\n run(ctx: HookEndpointContext): Promise<unknown>;\n}\n","import type { BetterAuthOptions } from \"better-auth\";\nimport type { DatabaseHookTarget } from \"./auth-contracts.js\";\nimport type {\n DatabaseHookData,\n DatabaseHookResult,\n DbHookFn,\n} from \"./auth-types.js\";\nimport type { BridgeBinding } from \"./bridge-protocol.js\";\n\n/**\n * Construct all dispatchers once; bindings supply only the late-bound methods.\n *\n * The dispatchers take the SDK's own parameter types, so they satisfy `databaseHooks` whatever\n * instance the program registers. Bound hooks declare `DatabaseHookData`, which adds the\n * registered instance's plugin and additional fields (for example admin()'s `banned`) to the\n * same rows; the dispatcher states that narrowing once when it forwards a payload.\n */\nexport function createDatabaseDispatchers(\n getBinding: () => BridgeBinding | null,\n countUnbound: () => void,\n): NonNullable<BetterAuthOptions[\"databaseHooks\"]> {\n function operation<E extends DatabaseHookTarget>(target: E) {\n const before = async (\n ...[data, ctx]: Parameters<DbHookFn<E, \"before\">>\n ): Promise<DatabaseHookResult<E, \"before\">> => {\n const binding = getBinding();\n if (!binding) {\n countUnbound();\n return undefined as DatabaseHookResult<E, \"before\">;\n }\n const hooks = binding.database[target].before;\n let accumulated: Record<string, unknown> = {};\n let changed = false;\n for (const hook of hooks) {\n const result = await hook(\n (target.endsWith(\".create\")\n ? { ...data, ...accumulated }\n : data) as DatabaseHookData<E, \"before\">,\n ctx,\n );\n if (result === false) {\n return false as DatabaseHookResult<E, \"before\">;\n }\n if (\n !target.endsWith(\".delete\") &&\n result &&\n typeof result === \"object\" &&\n \"data\" in result\n ) {\n accumulated = { ...accumulated, ...result.data };\n changed = true;\n }\n }\n // A generic target cannot express the runtime delete narrowing. Delete\n // never reaches this branch; the public binding keeps its void/false type.\n return (\n changed ? { data: accumulated } : undefined\n ) as DatabaseHookResult<E, \"before\">;\n };\n const after = async (\n ...[data, ctx]: Parameters<DbHookFn<E, \"after\">>\n ): Promise<void> => {\n const binding = getBinding();\n if (!binding) {\n countUnbound();\n return;\n }\n for (const hook of binding.database[target].after) {\n await hook(data as DatabaseHookData<E, \"after\">, ctx);\n }\n };\n return { before, after };\n }\n return {\n user: {\n create: operation(\"user.create\"),\n update: operation(\"user.update\"),\n delete: operation(\"user.delete\"),\n },\n session: {\n create: operation(\"session.create\"),\n update: operation(\"session.update\"),\n delete: operation(\"session.delete\"),\n },\n account: {\n create: operation(\"account.create\"),\n update: operation(\"account.update\"),\n delete: operation(\"account.delete\"),\n },\n verification: {\n create: operation(\"verification.create\"),\n update: operation(\"verification.update\"),\n delete: operation(\"verification.delete\"),\n },\n };\n}\n","import type { HookEndpointContext } from \"better-auth\";\nimport { APIError, createAuthMiddleware, isAPIError } from \"better-auth/api\";\nimport { createDefu } from \"defu\";\nimport type { BridgeBinding, ScopeView } from \"./bridge-protocol.js\";\n\nconst API_ERROR_HEADERS = Symbol.for(\"better-call:api-error-headers\");\nconst mergeContext = createDefu((object, key, value) => {\n if (Array.isArray(object[key]) && Array.isArray(value)) {\n object[key] = value;\n return true;\n }\n});\n\nfunction mergeHeaders(\n target: Headers,\n source: Headers | null | undefined,\n): void {\n source?.forEach((value, key) => {\n if (key === \"set-cookie\") {\n target.append(key, value);\n } else {\n target.set(key, value);\n }\n });\n}\n\nfunction errorHeaders(error: APIError): Headers | undefined {\n const attached = (error as unknown as Record<symbol, Headers | undefined>)[\n API_ERROR_HEADERS\n ];\n if (!attached && !error.headers) {\n return undefined;\n }\n const headers = new Headers(attached);\n if (error.headers && error.headers !== attached) {\n mergeHeaders(headers, new Headers(error.headers));\n }\n return headers;\n}\n\n/** Run each method through the SDK middleware so its cookie/header state is isolated. */\nexport async function runBefore(\n binding: BridgeBinding,\n ctx: HookEndpointContext,\n scope: ScopeView | undefined,\n): Promise<unknown> {\n let accumulated: Record<string, unknown> = {};\n for (const hook of binding.before) {\n let matched: boolean;\n try {\n matched = hook.matches(ctx, scope);\n } catch (error) {\n ctx.context.logger.error(\n \"An error occurred during Nest hook matcher execution:\",\n error,\n );\n throw new APIError(\"INTERNAL_SERVER_ERROR\", {\n message:\n \"An error occurred during hook matcher execution. Check the logs for more details.\",\n });\n }\n if (!matched) {\n continue;\n }\n let result: { response: unknown; headers: Headers };\n try {\n result = (await createAuthMiddleware(hook.run)({\n ...ctx,\n returnHeaders: true,\n })) as { response: unknown; headers: Headers };\n } catch (error) {\n if (isAPIError(error)) {\n // The enclosing SDK middleware reattaches its own header object. Keep\n // the throwing method's headers, including non-enumerable cookie state.\n ctx.responseHeaders =\n (error as unknown as Record<symbol, Headers | undefined>)[\n API_ERROR_HEADERS\n ] ?? new Headers();\n }\n throw error;\n }\n ctx.context.responseHeaders ??= new Headers();\n mergeHeaders(ctx.context.responseHeaders, result.headers);\n const value = result.response;\n if (value && typeof value === \"object\") {\n if (\"context\" in value && typeof value.context === \"object\") {\n const { headers, ...rest } = value.context as Record<string, unknown>;\n if (headers instanceof Headers) {\n const requestHeaders =\n accumulated.headers instanceof Headers\n ? accumulated.headers\n : new Headers();\n headers.forEach((header, key) => {\n requestHeaders.set(key, header);\n });\n accumulated.headers = requestHeaders;\n }\n accumulated = mergeContext(rest, accumulated);\n } else {\n return value;\n }\n }\n }\n return Object.keys(accumulated).length ? { context: accumulated } : undefined;\n}\n\nexport async function runAfter(\n binding: BridgeBinding,\n ctx: HookEndpointContext,\n scope: ScopeView | undefined,\n relayMatcherError?: (error: unknown) => void,\n): Promise<unknown> {\n for (const hook of binding.after) {\n let matched: boolean;\n try {\n matched = hook.matches(ctx, scope);\n } catch (error) {\n if (!relayMatcherError) {\n throw error;\n }\n // A surrounding SDK handler catches APIErrors as recoverable results.\n // Stop here and rethrow from the next fixed SDK matcher instead.\n relayMatcherError(error);\n return undefined;\n }\n if (!matched) {\n continue;\n }\n let result: { response: unknown; headers?: Headers | null };\n try {\n result = (await createAuthMiddleware(hook.run)({\n ...ctx,\n returnHeaders: true,\n })) as { response: unknown; headers: Headers };\n } catch (error) {\n if (!isAPIError(error)) {\n throw error;\n }\n result = { response: error, headers: errorHeaders(error) };\n }\n if (result.headers) {\n ctx.context.responseHeaders ??= new Headers();\n mergeHeaders(ctx.context.responseHeaders, result.headers);\n }\n if (result.response !== undefined) {\n ctx.context.returned = result.response;\n }\n }\n return ctx.context.returned;\n}\n","import type { BetterAuthPlugin, HookEndpointContext } from \"better-auth\";\nimport {\n createAuthMiddleware,\n setShouldSkipSessionRefresh,\n} from \"better-auth/api\";\nimport { parseCookies } from \"better-auth/cookies\";\nimport type { NestjsPluginOptions } from \"./auth-contracts.js\";\nimport {\n BRIDGE_HANDLE,\n type BridgeBinding,\n type BridgeHandle,\n type ScopeView,\n} from \"./bridge-protocol.js\";\nimport { createDatabaseDispatchers } from \"./database-hook-dispatcher.js\";\nimport { runAfter, runBefore } from \"./hook-dispatcher.js\";\n\nexport type { NestjsPluginOptions } from \"./auth-contracts.js\";\nexport const NESTJS_PLUGIN_ID = \"@nestjs-kit/better-auth\";\n\nconst isRouter = (ctx: object): boolean =>\n \"_flag\" in ctx && ctx._flag === \"router\";\nconst separator = \"\\u0000\";\n\nfunction sessionToken(\n headers: Headers | undefined,\n ctx: HookEndpointContext,\n): string {\n return (\n parseCookies(headers?.get(\"cookie\") ?? \"\").get(\n ctx.context.authCookies.sessionToken.name,\n ) ?? \"\"\n );\n}\n\nfunction credential(\n headers: Headers | undefined,\n ctx: HookEndpointContext,\n extra: readonly string[],\n): string {\n return [\n sessionToken(headers, ctx),\n headers?.get(\"authorization\") ?? \"\",\n ...extra.map((name) => headers?.get(name) ?? \"\"),\n ].join(separator);\n}\n\nfunction sameCredential(\n ctx: HookEndpointContext,\n scope: ScopeView,\n extra: readonly string[],\n): boolean {\n const call = credential(ctx.headers, ctx, extra);\n return (\n call === separator.repeat(extra.length + 1) ||\n call === credential(scope.inbound?.(), ctx, extra)\n );\n}\n\n/** Pure construction-time Better Auth plugin; the Nest kernel binds through a global symbol. */\nexport function nestjs(\n options: NestjsPluginOptions = {},\n): BetterAuthPlugin & { id: typeof NESTJS_PLUGIN_ID } {\n let binding: BridgeBinding | null = null;\n let registration: ReturnType<BridgeHandle[\"bind\"]> | undefined;\n let unboundDispatches = 0;\n const endpointResults = new WeakSet<object>();\n const afterMatcherErrors = new WeakMap<object, unknown>();\n const scope = () => binding?.current();\n const clientIpHeader =\n options.clientIpHeader === false\n ? null\n : (options.clientIpHeader ??\n `x-nestjs-kit-better-auth-ip-${globalThis.crypto.randomUUID().replaceAll(\"-\", \"\")}`);\n const handle: BridgeHandle = {\n protocol: 4,\n clientIpHeader,\n get state() {\n return binding === null\n ? \"unbound\"\n : binding.state === \"closed\"\n ? \"closed\"\n : \"bound\";\n },\n get unboundDispatches() {\n return unboundDispatches;\n },\n bind(next) {\n if (binding && binding.instance !== next.instance) {\n throw new Error(\n `PLUGIN_SHARED_BETWEEN_INSTANCES: '${binding.instance}' and '${next.instance}'`,\n );\n }\n if (binding?.owner === next.owner && registration) {\n return registration;\n }\n if (binding?.state === \"bootstrapped\") {\n throw new Error(\n `INSTANCE_ALREADY_BOUND: bound to ${binding.owner.description}`,\n );\n }\n let tookOverFrom: string | undefined;\n if (binding?.state === \"initialized\") {\n tookOverFrom = binding.owner.description;\n // Its application may still be initializing: its bootstrap must fail rather than run without hooks.\n binding.state = \"displaced\";\n }\n binding = next;\n unboundDispatches = 0;\n registration = {\n tookOverFrom,\n close: () => {\n next.state = \"closed\";\n },\n };\n return registration;\n },\n producedByEndpoint(value) {\n return (\n typeof value === \"object\" &&\n value !== null &&\n endpointResults.has(value)\n );\n },\n };\n const plugin = {\n id: NESTJS_PLUGIN_ID,\n [BRIDGE_HANDLE]: handle,\n init: () => ({\n options: {\n databaseHooks: createDatabaseDispatchers(\n () => binding,\n () => {\n unboundDispatches++;\n },\n ),\n ...(clientIpHeader\n ? { advanced: { ipAddress: { ipAddressHeaders: [clientIpHeader] } } }\n : {}),\n },\n }),\n hooks: {\n before: [\n {\n matcher: (ctx) => {\n if (!binding) {\n unboundDispatches++;\n return false;\n }\n return !isRouter(ctx) && scope()?.cookies === null;\n },\n handler: createAuthMiddleware(async () => {\n await setShouldSkipSessionRefresh(true);\n }),\n },\n {\n matcher: (ctx) => {\n if (!binding || isRouter(ctx)) {\n return false;\n }\n const current = scope();\n if (!current || current.internal) {\n return false;\n }\n // Access capabilities before testing credentials: extraction errors\n // must fail even a credential-free direct endpoint call.\n const check = current.checkCallerSession;\n current.inbound?.();\n const browser = current.browserHeaders?.();\n const token = sessionToken(ctx.headers, ctx);\n // Only the browser leg's own cookie is ambient. A session cookie that a\n // transport mapped in-band (graphql-ws connectionParams, socket\n // credentials) cannot ride a cross-site request.\n return (\n !!check && token !== \"\" && token === sessionToken(browser, ctx)\n );\n },\n handler: createAuthMiddleware(async (ctx) => {\n scope()?.checkCallerSession?.(ctx.path, binding!.instance);\n }),\n },\n {\n matcher: () => binding !== null && binding.before.length > 0,\n handler: createAuthMiddleware((ctx) =>\n runBefore(binding!, ctx, scope()),\n ),\n },\n ],\n after: [\n {\n matcher: () => binding !== null && binding.after.length > 0,\n handler: createAuthMiddleware((ctx) =>\n runAfter(binding!, ctx, scope(), (error) => {\n afterMatcherErrors.set(ctx.context, error);\n }),\n ),\n },\n {\n matcher: (ctx) => {\n // Each endpoint dispatch owns this shared context, including nested\n // calls. Rethrow outside the SDK's recoverable handler-error catch.\n if (afterMatcherErrors.has(ctx.context)) {\n const error = afterMatcherErrors.get(ctx.context);\n afterMatcherErrors.delete(ctx.context);\n throw error;\n }\n return binding !== null && !isRouter(ctx);\n },\n handler: createAuthMiddleware(async (ctx) => {\n const value = ctx.context.returned;\n if (typeof value === \"object\" && value !== null) {\n endpointResults.add(value);\n }\n }),\n },\n {\n matcher: (ctx) => {\n if (isRouter(ctx)) {\n return false;\n }\n const current = scope();\n return !!current?.cookies && current.forward !== \"none\";\n },\n handler: createAuthMiddleware(async (ctx) => {\n const current = scope()!;\n if (\n current.forward === \"same-credential\" &&\n !sameCredential(ctx, current, binding!.credentialHeaders)\n ) {\n binding!.onDropped(ctx.path);\n return;\n }\n const cookies = ctx.context.responseHeaders?.getSetCookie() ?? [];\n if (cookies.length) {\n current.cookies!.append(cookies);\n }\n }),\n },\n ],\n },\n } satisfies BetterAuthPlugin & { [BRIDGE_HANDLE]: BridgeHandle };\n return plugin;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin.mjs","names":[],"sources":["../src/bridge-protocol.ts","../src/database-hook-dispatcher.ts","../src/hook-dispatcher.ts","../src/plugin.ts"],"sourcesContent":["import type { HookEndpointContext } from \"better-auth\";\nimport type { CookieSink, DatabaseHookTarget } from \"./auth-contracts.js\";\nimport type { DatabaseHookMethod } from \"./auth-types.js\";\n\nexport const BRIDGE_HANDLE = Symbol.for(\"@nestjs-kit/better-auth:bridge\");\nexport const EXTENSION_DEFINITION = Symbol.for(\n \"@nestjs-kit/better-auth:extension\",\n);\nexport const UPGRADE_REQUEST = Symbol.for(\n \"@nestjs-kit/better-auth:upgrade-request\",\n);\n\nexport interface BridgeHandle {\n readonly protocol: 4; // checked by core (B04); v5: binding state and instance, the caller-session check\n readonly clientIpHeader: string | null; // single source of truth for the header name (§6.8)\n /** 'unbound' until the first bind(); 'bound' while an application's hooks are registered; 'closed' after its shutdown, hooks still registered. */\n readonly state: \"unbound\" | \"bound\" | \"closed\";\n /** Endpoint dispatches and database-hook dispatches (writes) seen before the first binding, reset by every bind() (B26). */\n readonly unboundDispatches: number;\n /**\n * Registers the application's hooks and returns close(), which the kernel calls in onApplicationShutdown: a closed binding keeps\n * dispatching until another application binds (§10.6). A second bind() from the same owner for the same instance is a no-op that\n * returns the existing registration (NestMicroservice.init() runs lifecycle hooks twice); from the same owner for another instance it\n * throws PLUGIN_SHARED_BETWEEN_INSTANCES (B03). Throws INSTANCE_ALREADY_BOUND if another bootstrapped application is bound; takes over\n * a never-bootstrapped one (reported, B06) and a closed one (silently). A never-bootstrapped binding it takes over becomes\n * 'displaced': an application whose init() was still running when another bound fails in onApplicationBootstrap (B06).\n */\n bind(binding: BridgeBinding): { close(): void; tookOverFrom?: string };\n /** true when an endpoint returned `value` (an object) in a non-router dispatch observed while bound; false for before-hook short-circuits (LEAD-V37). */\n producedByEndpoint(value: unknown): boolean;\n}\nexport interface BridgeBinding {\n /** The application: compared by identity for the same-owner no-op; `description` goes into the B06 message. */\n readonly owner: { readonly description: string };\n /** The instance this plugin object serves; one plugin object serves one instance (B03). */\n readonly instance: string;\n /**\n * 'initialized' at bind; the kernel sets 'bootstrapped' in onApplicationBootstrap; close() sets 'closed'; bind() of another\n * application sets 'displaced' on an 'initialized' binding it takes over.\n */\n state: \"initialized\" | \"bootstrapped\" | \"closed\" | \"displaced\";\n /** View of the kernel's RequestScope; the plugin never owns an AsyncLocalStorage. [C] */\n current(): ScopeView | undefined;\n /** Credential header names declared by the instance's principal sources (credential matching, §7.5). */\n readonly credentialHeaders: readonly string[];\n readonly before: readonly CompiledHook[]; // sorted by order, then discovery order\n readonly after: readonly CompiledHook[];\n readonly database: Readonly<{\n [E in DatabaseHookTarget]: {\n readonly before: readonly DatabaseHookMethod<E, \"before\">[];\n readonly after: readonly DatabaseHookMethod<E, \"after\">[];\n };\n }>;\n /** Debug log when the bridge drops a direct call's cookies because its credential is foreign. */\n onDropped(path: string): void;\n}\nexport interface ScopeView {\n /** Cookie-capable forwarding handlers have already passed the enforcing form check on every method/operation (§7.10). */\n readonly cookies: CookieSink | null;\n readonly forward: \"none\" | \"same-credential\" | \"any\";\n readonly inbound: (() => Headers) | undefined;\n readonly internal: boolean;\n /**\n * Present in EVERY handler scope whose transport exposes a browser leg, unless the plan's origin check is 'off' — enforcing or not,\n * so safe methods and GraphQL queries are covered too (§7.5). Returns when this leg has a passing origin verdict for `instance`, or\n * the single kernel disable predicate (§7.10 item 4) holds; a guard run without a passing verdict is insufficient. Otherwise throws the kernel's\n * BetterAuthConfigurationError PUBLIC_HANDLER_USED_CALLER_SESSION.\n */\n readonly checkCallerSession?: (path: string, instance: string) => void;\n /** The browser leg's OWN headers (TransportCall.browser.headers()), before any transport credential mapping replaced them; undefined\n * when the transport exposes no browser leg. Entry 2 recognizes an ambiently carried session cookie with it. */\n readonly browserHeaders: (() => Headers) | undefined;\n}\nexport interface CompiledHook {\n /**\n * Evaluate the user predicate once and let it throw. The dispatcher logs and\n * converts before-hook matcher failures to APIError 500; after-hook matcher\n * failures cross the next fixed SDK matcher boundary unchanged, so the SDK\n * cannot recover them as handler APIErrors. Never swallow a failure as a\n * non-match or evaluate the predicate again in run().\n */\n matches(ctx: HookEndpointContext, scope: ScopeView | undefined): boolean;\n run(ctx: HookEndpointContext): Promise<unknown>;\n}\n","import type { BetterAuthOptions } from \"better-auth\";\nimport type { DatabaseHookTarget } from \"./auth-contracts.js\";\nimport type {\n DatabaseHookData,\n DatabaseHookResult,\n DbHookFn,\n} from \"./auth-types.js\";\nimport type { BridgeBinding } from \"./bridge-protocol.js\";\n\n/**\n * Construct all dispatchers once; bindings supply only the late-bound methods.\n *\n * The dispatchers take the SDK's own parameter types, so they satisfy `databaseHooks` whatever\n * instance the program registers. Bound hooks declare `DatabaseHookData`, which adds the\n * registered instance's plugin and additional fields (for example admin()'s `banned`) to the\n * same rows; the dispatcher states that narrowing once when it forwards a payload.\n */\nexport function createDatabaseDispatchers(\n getBinding: () => BridgeBinding | null,\n countUnbound: () => void,\n): NonNullable<BetterAuthOptions[\"databaseHooks\"]> {\n function operation<E extends DatabaseHookTarget>(target: E) {\n const before = async (\n ...[data, ctx]: Parameters<DbHookFn<E, \"before\">>\n ): Promise<DatabaseHookResult<E, \"before\">> => {\n const binding = getBinding();\n if (!binding) {\n countUnbound();\n return undefined as DatabaseHookResult<E, \"before\">;\n }\n const hooks = binding.database[target].before;\n let accumulated: Record<string, unknown> = {};\n let changed = false;\n for (const hook of hooks) {\n const result = await hook(\n (target.endsWith(\".create\")\n ? { ...data, ...accumulated }\n : data) as DatabaseHookData<E, \"before\">,\n ctx,\n );\n if (result === false) {\n return false as DatabaseHookResult<E, \"before\">;\n }\n if (\n !target.endsWith(\".delete\") &&\n result &&\n typeof result === \"object\" &&\n \"data\" in result\n ) {\n accumulated = { ...accumulated, ...result.data };\n changed = true;\n }\n }\n // A generic target cannot express the runtime delete narrowing. Delete\n // never reaches this branch; the public binding keeps its void/false type.\n return (\n changed ? { data: accumulated } : undefined\n ) as DatabaseHookResult<E, \"before\">;\n };\n const after = async (\n ...[data, ctx]: Parameters<DbHookFn<E, \"after\">>\n ): Promise<void> => {\n const binding = getBinding();\n if (!binding) {\n countUnbound();\n return;\n }\n for (const hook of binding.database[target].after) {\n await hook(data as DatabaseHookData<E, \"after\">, ctx);\n }\n };\n return { before, after };\n }\n return {\n user: {\n create: operation(\"user.create\"),\n update: operation(\"user.update\"),\n delete: operation(\"user.delete\"),\n },\n session: {\n create: operation(\"session.create\"),\n update: operation(\"session.update\"),\n delete: operation(\"session.delete\"),\n },\n account: {\n create: operation(\"account.create\"),\n update: operation(\"account.update\"),\n delete: operation(\"account.delete\"),\n },\n verification: {\n create: operation(\"verification.create\"),\n update: operation(\"verification.update\"),\n delete: operation(\"verification.delete\"),\n },\n };\n}\n","import type { HookEndpointContext } from \"better-auth\";\nimport { APIError, createAuthMiddleware, isAPIError } from \"better-auth/api\";\nimport { createDefu } from \"defu\";\nimport type { BridgeBinding, ScopeView } from \"./bridge-protocol.js\";\n\nconst API_ERROR_HEADERS = Symbol.for(\"better-call:api-error-headers\");\nconst mergeContext = createDefu((object, key, value) => {\n if (Array.isArray(object[key]) && Array.isArray(value)) {\n object[key] = value;\n return true;\n }\n});\n\nfunction mergeHeaders(\n target: Headers,\n source: Headers | null | undefined,\n): void {\n source?.forEach((value, key) => {\n if (key === \"set-cookie\") {\n target.append(key, value);\n } else {\n target.set(key, value);\n }\n });\n}\n\nfunction errorHeaders(error: APIError): Headers | undefined {\n const attached = (error as unknown as Record<symbol, Headers | undefined>)[\n API_ERROR_HEADERS\n ];\n if (!attached && !error.headers) {\n return undefined;\n }\n const headers = new Headers(attached);\n if (error.headers && error.headers !== attached) {\n mergeHeaders(headers, new Headers(error.headers));\n }\n return headers;\n}\n\n/** Run each method through the SDK middleware so its cookie/header state is isolated. */\nexport async function runBefore(\n binding: BridgeBinding,\n ctx: HookEndpointContext,\n scope: ScopeView | undefined,\n): Promise<unknown> {\n let accumulated: Record<string, unknown> = {};\n for (const hook of binding.before) {\n let matched: boolean;\n try {\n matched = hook.matches(ctx, scope);\n } catch (error) {\n ctx.context.logger.error(\n \"An error occurred during Nest hook matcher execution:\",\n error,\n );\n throw new APIError(\"INTERNAL_SERVER_ERROR\", {\n message:\n \"An error occurred during hook matcher execution. Check the logs for more details.\",\n });\n }\n if (!matched) {\n continue;\n }\n let result: { response: unknown; headers: Headers };\n try {\n result = (await createAuthMiddleware(hook.run)({\n ...ctx,\n returnHeaders: true,\n })) as { response: unknown; headers: Headers };\n } catch (error) {\n if (isAPIError(error)) {\n // The enclosing SDK middleware reattaches its own header object. Keep\n // the throwing method's headers, including non-enumerable cookie state.\n ctx.responseHeaders =\n (error as unknown as Record<symbol, Headers | undefined>)[\n API_ERROR_HEADERS\n ] ?? new Headers();\n }\n throw error;\n }\n ctx.context.responseHeaders ??= new Headers();\n mergeHeaders(ctx.context.responseHeaders, result.headers);\n const value = result.response;\n if (value && typeof value === \"object\") {\n if (\"context\" in value && typeof value.context === \"object\") {\n const { headers, ...rest } = value.context as Record<string, unknown>;\n if (headers instanceof Headers) {\n const requestHeaders =\n accumulated.headers instanceof Headers\n ? accumulated.headers\n : new Headers();\n headers.forEach((header, key) => {\n requestHeaders.set(key, header);\n });\n accumulated.headers = requestHeaders;\n }\n accumulated = mergeContext(rest, accumulated);\n } else {\n return value;\n }\n }\n }\n return Object.keys(accumulated).length ? { context: accumulated } : undefined;\n}\n\nexport async function runAfter(\n binding: BridgeBinding,\n ctx: HookEndpointContext,\n scope: ScopeView | undefined,\n relayMatcherError?: (error: unknown) => void,\n): Promise<unknown> {\n for (const hook of binding.after) {\n let matched: boolean;\n try {\n matched = hook.matches(ctx, scope);\n } catch (error) {\n if (!relayMatcherError) {\n throw error;\n }\n // A surrounding SDK handler catches APIErrors as recoverable results.\n // Stop here and rethrow from the next fixed SDK matcher instead.\n relayMatcherError(error);\n return undefined;\n }\n if (!matched) {\n continue;\n }\n let result: { response: unknown; headers?: Headers | null };\n try {\n result = (await createAuthMiddleware(hook.run)({\n ...ctx,\n returnHeaders: true,\n })) as { response: unknown; headers: Headers };\n } catch (error) {\n if (!isAPIError(error)) {\n throw error;\n }\n result = { response: error, headers: errorHeaders(error) };\n }\n if (result.headers) {\n ctx.context.responseHeaders ??= new Headers();\n mergeHeaders(ctx.context.responseHeaders, result.headers);\n }\n if (result.response !== undefined) {\n ctx.context.returned = result.response;\n }\n }\n return ctx.context.returned;\n}\n","import type { BetterAuthPlugin, HookEndpointContext } from \"better-auth\";\nimport {\n createAuthMiddleware,\n setShouldSkipSessionRefresh,\n} from \"better-auth/api\";\nimport { parseCookies } from \"better-auth/cookies\";\nimport type { NestjsPluginOptions } from \"./auth-contracts.js\";\nimport {\n BRIDGE_HANDLE,\n type BridgeBinding,\n type BridgeHandle,\n type ScopeView,\n} from \"./bridge-protocol.js\";\nimport { createDatabaseDispatchers } from \"./database-hook-dispatcher.js\";\nimport { runAfter, runBefore } from \"./hook-dispatcher.js\";\n\nexport type { NestjsPluginOptions } from \"./auth-contracts.js\";\nexport const NESTJS_PLUGIN_ID = \"@nestjs-kit/better-auth\";\n\nconst isRouter = (ctx: object): boolean =>\n \"_flag\" in ctx && ctx._flag === \"router\";\nconst separator = \"\\u0000\";\n\nfunction sessionToken(\n headers: Headers | undefined,\n ctx: HookEndpointContext,\n): string {\n return (\n parseCookies(headers?.get(\"cookie\") ?? \"\").get(\n ctx.context.authCookies.sessionToken.name,\n ) ?? \"\"\n );\n}\n\nfunction credential(\n headers: Headers | undefined,\n ctx: HookEndpointContext,\n extra: readonly string[],\n): string {\n return [\n sessionToken(headers, ctx),\n headers?.get(\"authorization\") ?? \"\",\n ...extra.map((name) => headers?.get(name) ?? \"\"),\n ].join(separator);\n}\n\nfunction sameCredential(\n ctx: HookEndpointContext,\n scope: ScopeView,\n extra: readonly string[],\n): boolean {\n const call = credential(ctx.headers, ctx, extra);\n return (\n call === separator.repeat(extra.length + 1) ||\n call === credential(scope.inbound?.(), ctx, extra)\n );\n}\n\n/** Pure construction-time Better Auth plugin; the Nest kernel binds through a global symbol. */\nexport function nestjs(\n options: NestjsPluginOptions = {},\n): BetterAuthPlugin & { id: typeof NESTJS_PLUGIN_ID } {\n let binding: BridgeBinding | null = null;\n let registration: ReturnType<BridgeHandle[\"bind\"]> | undefined;\n let unboundDispatches = 0;\n const endpointResults = new WeakSet<object>();\n const afterMatcherErrors = new WeakMap<object, unknown>();\n const scope = () => binding?.current();\n const clientIpHeader =\n options.clientIpHeader === false\n ? null\n : (options.clientIpHeader ??\n `x-nestjs-kit-better-auth-ip-${globalThis.crypto.randomUUID().replaceAll(\"-\", \"\")}`);\n const handle: BridgeHandle = {\n protocol: 4,\n clientIpHeader,\n get state() {\n return binding === null\n ? \"unbound\"\n : binding.state === \"closed\"\n ? \"closed\"\n : \"bound\";\n },\n get unboundDispatches() {\n return unboundDispatches;\n },\n bind(next) {\n if (binding && binding.instance !== next.instance) {\n throw new Error(\n `PLUGIN_SHARED_BETWEEN_INSTANCES: '${binding.instance}' and '${next.instance}'`,\n );\n }\n if (binding?.owner === next.owner && registration) {\n return registration;\n }\n if (binding?.state === \"bootstrapped\") {\n throw new Error(\n `INSTANCE_ALREADY_BOUND: bound to ${binding.owner.description}`,\n );\n }\n let tookOverFrom: string | undefined;\n if (binding?.state === \"initialized\") {\n tookOverFrom = binding.owner.description;\n // Its application may still be initializing: its bootstrap must fail rather than run without hooks.\n binding.state = \"displaced\";\n }\n binding = next;\n unboundDispatches = 0;\n registration = {\n tookOverFrom,\n close: () => {\n next.state = \"closed\";\n },\n };\n return registration;\n },\n producedByEndpoint(value) {\n return (\n typeof value === \"object\" &&\n value !== null &&\n endpointResults.has(value)\n );\n },\n };\n const plugin = {\n id: NESTJS_PLUGIN_ID,\n [BRIDGE_HANDLE]: handle,\n init: () => ({\n options: {\n databaseHooks: createDatabaseDispatchers(\n () => binding,\n () => {\n unboundDispatches++;\n },\n ),\n ...(clientIpHeader\n ? { advanced: { ipAddress: { ipAddressHeaders: [clientIpHeader] } } }\n : {}),\n },\n }),\n hooks: {\n before: [\n {\n matcher: (ctx) => {\n if (!binding) {\n unboundDispatches++;\n return false;\n }\n return !isRouter(ctx) && scope()?.cookies === null;\n },\n handler: createAuthMiddleware(async () => {\n await setShouldSkipSessionRefresh(true);\n }),\n },\n {\n matcher: (ctx) => {\n if (!binding || isRouter(ctx)) {\n return false;\n }\n const current = scope();\n if (!current || current.internal) {\n return false;\n }\n // Access capabilities before testing credentials: extraction errors\n // must fail even a credential-free direct endpoint call.\n const check = current.checkCallerSession;\n current.inbound?.();\n const browser = current.browserHeaders?.();\n const token = sessionToken(ctx.headers, ctx);\n // Only the browser leg's own cookie is ambient. A session cookie that a\n // transport mapped in-band (graphql-ws connectionParams, socket\n // credentials) cannot ride a cross-site request.\n return (\n !!check && token !== \"\" && token === sessionToken(browser, ctx)\n );\n },\n handler: createAuthMiddleware(async (ctx) => {\n scope()?.checkCallerSession?.(ctx.path, binding!.instance);\n }),\n },\n {\n matcher: () => binding !== null && binding.before.length > 0,\n handler: createAuthMiddleware((ctx) =>\n runBefore(binding!, ctx, scope()),\n ),\n },\n ],\n after: [\n {\n matcher: () => binding !== null && binding.after.length > 0,\n handler: createAuthMiddleware((ctx) =>\n runAfter(binding!, ctx, scope(), (error) => {\n afterMatcherErrors.set(ctx.context, error);\n }),\n ),\n },\n {\n matcher: (ctx) => {\n // Each endpoint dispatch owns this shared context, including nested\n // calls. Rethrow outside the SDK's recoverable handler-error catch.\n if (afterMatcherErrors.has(ctx.context)) {\n const error = afterMatcherErrors.get(ctx.context);\n afterMatcherErrors.delete(ctx.context);\n throw error;\n }\n return binding !== null && !isRouter(ctx);\n },\n handler: createAuthMiddleware(async (ctx) => {\n const value = ctx.context.returned;\n if (typeof value === \"object\" && value !== null) {\n endpointResults.add(value);\n }\n }),\n },\n {\n matcher: (ctx) => {\n if (isRouter(ctx)) {\n return false;\n }\n const current = scope();\n return !!current?.cookies && current.forward !== \"none\";\n },\n handler: createAuthMiddleware(async (ctx) => {\n const current = scope()!;\n if (\n current.forward === \"same-credential\" &&\n !sameCredential(ctx, current, binding!.credentialHeaders)\n ) {\n binding!.onDropped(ctx.path);\n return;\n }\n const cookies = ctx.context.responseHeaders?.getSetCookie() ?? [];\n if (cookies.length) {\n current.cookies!.append(cookies);\n }\n }),\n },\n ],\n },\n } satisfies BetterAuthPlugin & { [BRIDGE_HANDLE]: BridgeHandle };\n return plugin;\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { c as isConfigurationError, n as BetterAuthConfigurationError } from "./auth-errors-
|
|
1
|
+
import { c as isConfigurationError, n as BetterAuthConfigurationError } from "./auth-errors-JjxGDVSt.mjs";
|
|
2
2
|
import { IntrinsicException } from "@nestjs/common";
|
|
3
|
-
|
|
3
|
+
// src/principal-readings.ts
|
|
4
4
|
const RESOLUTION = Symbol.for("@nestjs-kit/better-auth:resolution");
|
|
5
5
|
const TEST_STAMP = Symbol.for("@nestjs-kit/better-auth:test-stamp");
|
|
6
6
|
function object(value) {
|
|
@@ -185,7 +185,6 @@ function stampedReading(args, instance) {
|
|
|
185
185
|
instance
|
|
186
186
|
} : void 0;
|
|
187
187
|
}
|
|
188
|
-
//#endregion
|
|
189
188
|
export { stampResult as n, stampedReading as r, PrincipalReadings as t };
|
|
190
189
|
|
|
191
|
-
//# sourceMappingURL=principal-readings-
|
|
190
|
+
//# sourceMappingURL=principal-readings-BuXTU0-2.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"principal-readings-Ied2staw.mjs","names":[],"sources":["../src/principal-readings.ts"],"sourcesContent":["import { IntrinsicException } from \"@nestjs/common\";\nimport type {\n InvocationLineage,\n PrincipalReading,\n PrincipalResult,\n RoutePlan,\n TransportCall,\n} from \"./auth-contracts.js\";\nimport {\n BetterAuthConfigurationError,\n isConfigurationError,\n} from \"./auth-errors.js\";\nimport type { AuthPrincipal } from \"./auth-types.js\";\nimport { RequestScope } from \"./request-scope.js\";\n\nconst RESOLUTION = Symbol.for(\"@nestjs-kit/better-auth:resolution\");\nconst TEST_STAMP = Symbol.for(\"@nestjs-kit/better-auth:test-stamp\");\ninterface Records {\n own: Map<string, PrincipalReading>;\n positions: Map<string, PrincipalReading>;\n lineage?: InvocationLineage;\n invocationLineage?: InvocationLineage;\n}\ninterface TestStamp {\n elements: readonly unknown[];\n results: Map<string, PrincipalResult>;\n}\nexport interface ReadingInput {\n readonly args: readonly unknown[];\n readonly call?: TransportCall;\n readonly lineage?: InvocationLineage;\n readonly plan?: Pick<RoutePlan, \"access\" | \"instance\" | \"site\">;\n readonly beforeGuard?: boolean;\n readonly site?: string;\n}\n\nfunction object(value: unknown): value is object {\n return (\n value !== null && (typeof value === \"object\" || typeof value === \"function\")\n );\n}\nfunction records(target: object, create = false): Records | undefined {\n let value = Reflect.get(target, RESOLUTION) as Records | undefined;\n if (!value && create) {\n value = { own: new Map(), positions: new Map() };\n Object.defineProperty(target, RESOLUTION, { value });\n }\n return value;\n}\nfunction sameElements(stamp: TestStamp, args: readonly unknown[]): boolean {\n return (\n stamp.elements.length === args.length &&\n stamp.elements.every((value, index) => value === args[index])\n );\n}\n\n/**\n * The stamp of this invocation: the one on the args array itself (WS, RPC and GraphQL guards and interceptors share\n * that array), or one on its elements when the elements identify the invocation. That takes at least two object\n * elements (an HTTP request and its response): a lone object, such as a WS socket beside a primitive payload, is shared\n * by every message on its connection, so a stamp found only through it may belong to another invocation.\n */\nfunction matchingStamp(args: readonly unknown[]): TestStamp | undefined {\n const own = Reflect.get(args, TEST_STAMP) as TestStamp | undefined;\n if (own && sameElements(own, args)) {\n return own;\n }\n const objects = args.filter(object);\n if (objects.length < 2) {\n return undefined;\n }\n for (const target of objects) {\n const stamp = Reflect.get(target, TEST_STAMP) as TestStamp | undefined;\n if (stamp && sameElements(stamp, args)) {\n return stamp;\n }\n }\n return undefined;\n}\n\nfunction lineageFromArgs(\n args: readonly unknown[],\n): InvocationLineage | undefined {\n const targets = [args, ...args.filter(object)];\n for (const target of targets) {\n const own = records(target)?.invocationLineage;\n if (own) {\n return own;\n }\n }\n for (const target of targets) {\n const carrier = records(target)?.lineage;\n if (!carrier) {\n continue;\n }\n // The recorded callback is structural, but its recorded position may belong\n // to a sibling operation in the same batched GraphQL carrier.\n for (const position of carrier.enclosing(args)) {\n return { ...carrier, position };\n }\n }\n return undefined;\n}\n\n/** The intrinsic twin suppresses Nest's repeated log, while retaining the safe wire shape. */\nexport class RepeatedReaderError extends IntrinsicException {\n constructor(error: BetterAuthConfigurationError) {\n super(error.message);\n Object.defineProperties(this, Object.getOwnPropertyDescriptors(error));\n }\n}\n\nexport class PrincipalReadings {\n constructor(private readonly scope: RequestScope) {}\n\n record(\n call: Pick<TransportCall, \"invocation\" | \"lineage\">,\n reading: PrincipalReading,\n ): void {\n const record = records(call.invocation, true)!;\n record.own.set(reading.instance, reading);\n if (call.lineage) {\n record.invocationLineage = call.lineage;\n this.recordLineage(call.lineage, reading);\n }\n }\n\n recordLineage(lineage: InvocationLineage, reading: PrincipalReading): void {\n const value = records(lineage.carrier, true)!;\n value.positions.set(lineage.position, reading);\n value.lineage = lineage;\n }\n\n enclosing(\n lineage: InvocationLineage,\n args: readonly unknown[],\n ): PrincipalReading | undefined {\n const positions = records(lineage.carrier)?.positions;\n for (const position of lineage.enclosing(args)) {\n const reading = positions?.get(position);\n if (reading) {\n return reading;\n }\n }\n return undefined;\n }\n\n read(input: ReadingInput): PrincipalReading {\n const { plan, args, call } = input;\n const lineage = input.lineage ?? call?.lineage;\n let resolvedInstance = plan?.instance;\n try {\n if (plan?.access === \"public\") {\n return { outcome: \"no-identity\", instance: plan.instance };\n }\n let reading: PrincipalReading | undefined;\n let validation = lineage ?? lineageFromArgs(args);\n if (plan?.access === \"inherit\") {\n reading = lineage && this.enclosing(lineage, args);\n } else {\n const targets = call\n ? [call.invocation]\n : [args, ...args.filter(object)];\n for (const target of targets) {\n const own = records(target)?.own;\n reading = plan\n ? own?.get(plan.instance)\n : own?.size === 1\n ? own.values().next().value\n : undefined;\n if (reading) {\n break;\n }\n }\n if (!reading && !plan) {\n for (const target of [args, ...args.filter(object)]) {\n const candidate = records(target)?.lineage;\n if (!candidate) {\n continue;\n }\n reading = this.enclosing(candidate, args);\n if (reading) {\n validation = candidate;\n break;\n }\n }\n }\n }\n if (!reading) {\n const stamp = matchingStamp(args);\n const instance = plan?.instance ?? \"default\";\n const result = stamp?.results.get(instance);\n if (result) {\n reading = { ...result, instance };\n }\n }\n if (!reading) {\n throw BetterAuthConfigurationError.atRequest(\n input.beforeGuard && plan?.access !== \"inherit\"\n ? \"PRINCIPAL_READ_BEFORE_GUARD\"\n : \"NO_AUTH_RESULT\",\n \"No authentication result: BetterAuthGuard did not run for this invocation\",\n {\n site: plan?.site ?? input.site,\n hint: \"Run BetterAuthGuard before reading a principal; test replacements must stampPrincipal().\",\n },\n );\n }\n resolvedInstance = reading.instance;\n if (reading.outcome === \"authenticated\") {\n validation?.assertReadable?.(args);\n }\n return reading;\n } catch (error) {\n if (isConfigurationError(error)) {\n throw this.deliver(error, input, resolvedInstance);\n }\n throw error;\n }\n }\n\n current(): PrincipalReading {\n const state = this.scope.current();\n if (!state?.reading) {\n throw BetterAuthConfigurationError.atRequest(\n \"NO_AUTH_SCOPE\",\n \"Principal readers require a handler scope\",\n );\n }\n return state.reading();\n }\n\n project<T>(\n reading: PrincipalReading,\n spec: {\n kind?: string;\n reason?: string;\n site: string;\n project: (principal: AuthPrincipal) => T;\n },\n input?: ReadingInput,\n ): T | null {\n if (reading.outcome === \"no-identity\" || reading.outcome === \"absent\") {\n return null;\n }\n if (reading.outcome === \"rejected\") {\n throw reading.failure;\n }\n if (spec.kind && reading.principal.kind !== spec.kind) {\n const error = BetterAuthConfigurationError.atRequest(\n spec.reason ?? \"PRINCIPAL_KIND_REQUIRED\",\n `This reader requires principal kind ${spec.kind}`,\n {\n site: spec.site,\n hint: \"Use @CurrentPrincipal() or getPrincipal() and narrow p.kind.\",\n },\n );\n throw this.deliver(error, input, reading.instance);\n }\n return spec.project(reading.principal);\n }\n\n deliver(\n error: BetterAuthConfigurationError,\n input?: ReadingInput,\n instance?: string,\n ): BetterAuthConfigurationError | RepeatedReaderError {\n const state = this.scope.current();\n const call = state?.call ?? input?.call;\n const lineage =\n call?.lineage ?? input?.lineage ?? (input && lineageFromArgs(input.args));\n const key = call?.key ?? lineage?.carrier ?? input?.args;\n if (!key) {\n return error;\n }\n const repeated = this.scope.surfaced(key, error, {\n instance:\n instance ?? state?.plan?.instance ?? input?.plan?.instance ?? \"default\",\n site:\n error.site ?? input?.site ?? input?.plan?.site ?? \"principal reader\",\n lineage,\n });\n return repeated ? new RepeatedReaderError(error) : error;\n }\n\n stamp(\n args: readonly unknown[],\n instance: string,\n result: PrincipalResult,\n ): void {\n stampResult(args, instance, result);\n }\n}\n\n/** Test stamps are verified by array or element identity (see matchingStamp), so a stamp never leaks into another invocation. */\nexport function stampResult(\n args: readonly unknown[],\n instance: string,\n result: PrincipalResult,\n): void {\n const stamp = matchingStamp(args) ?? {\n elements: [...args],\n results: new Map(),\n };\n stamp.results.set(instance, result);\n for (const target of [args, ...args]) {\n if (object(target) && Object.isExtensible(target)) {\n Object.defineProperty(target, TEST_STAMP, {\n value: stamp,\n configurable: true,\n });\n }\n }\n}\n\nexport function stampedReading(\n args: readonly unknown[],\n instance: string,\n): PrincipalReading | undefined {\n const result = matchingStamp(args)?.results.get(instance);\n return result ? { ...result, instance } : undefined;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"principal-readings-BuXTU0-2.mjs","names":[],"sources":["../src/principal-readings.ts"],"sourcesContent":["import { IntrinsicException } from \"@nestjs/common\";\nimport type {\n InvocationLineage,\n PrincipalReading,\n PrincipalResult,\n RoutePlan,\n TransportCall,\n} from \"./auth-contracts.js\";\nimport {\n BetterAuthConfigurationError,\n isConfigurationError,\n} from \"./auth-errors.js\";\nimport type { AuthPrincipal } from \"./auth-types.js\";\nimport { RequestScope } from \"./request-scope.js\";\n\nconst RESOLUTION = Symbol.for(\"@nestjs-kit/better-auth:resolution\");\nconst TEST_STAMP = Symbol.for(\"@nestjs-kit/better-auth:test-stamp\");\ninterface Records {\n own: Map<string, PrincipalReading>;\n positions: Map<string, PrincipalReading>;\n lineage?: InvocationLineage;\n invocationLineage?: InvocationLineage;\n}\ninterface TestStamp {\n elements: readonly unknown[];\n results: Map<string, PrincipalResult>;\n}\nexport interface ReadingInput {\n readonly args: readonly unknown[];\n readonly call?: TransportCall;\n readonly lineage?: InvocationLineage;\n readonly plan?: Pick<RoutePlan, \"access\" | \"instance\" | \"site\">;\n readonly beforeGuard?: boolean;\n readonly site?: string;\n}\n\nfunction object(value: unknown): value is object {\n return (\n value !== null && (typeof value === \"object\" || typeof value === \"function\")\n );\n}\nfunction records(target: object, create = false): Records | undefined {\n let value = Reflect.get(target, RESOLUTION) as Records | undefined;\n if (!value && create) {\n value = { own: new Map(), positions: new Map() };\n Object.defineProperty(target, RESOLUTION, { value });\n }\n return value;\n}\nfunction sameElements(stamp: TestStamp, args: readonly unknown[]): boolean {\n return (\n stamp.elements.length === args.length &&\n stamp.elements.every((value, index) => value === args[index])\n );\n}\n\n/**\n * The stamp of this invocation: the one on the args array itself (WS, RPC and GraphQL guards and interceptors share\n * that array), or one on its elements when the elements identify the invocation. That takes at least two object\n * elements (an HTTP request and its response): a lone object, such as a WS socket beside a primitive payload, is shared\n * by every message on its connection, so a stamp found only through it may belong to another invocation.\n */\nfunction matchingStamp(args: readonly unknown[]): TestStamp | undefined {\n const own = Reflect.get(args, TEST_STAMP) as TestStamp | undefined;\n if (own && sameElements(own, args)) {\n return own;\n }\n const objects = args.filter(object);\n if (objects.length < 2) {\n return undefined;\n }\n for (const target of objects) {\n const stamp = Reflect.get(target, TEST_STAMP) as TestStamp | undefined;\n if (stamp && sameElements(stamp, args)) {\n return stamp;\n }\n }\n return undefined;\n}\n\nfunction lineageFromArgs(\n args: readonly unknown[],\n): InvocationLineage | undefined {\n const targets = [args, ...args.filter(object)];\n for (const target of targets) {\n const own = records(target)?.invocationLineage;\n if (own) {\n return own;\n }\n }\n for (const target of targets) {\n const carrier = records(target)?.lineage;\n if (!carrier) {\n continue;\n }\n // The recorded callback is structural, but its recorded position may belong\n // to a sibling operation in the same batched GraphQL carrier.\n for (const position of carrier.enclosing(args)) {\n return { ...carrier, position };\n }\n }\n return undefined;\n}\n\n/** The intrinsic twin suppresses Nest's repeated log, while retaining the safe wire shape. */\nexport class RepeatedReaderError extends IntrinsicException {\n constructor(error: BetterAuthConfigurationError) {\n super(error.message);\n Object.defineProperties(this, Object.getOwnPropertyDescriptors(error));\n }\n}\n\nexport class PrincipalReadings {\n constructor(private readonly scope: RequestScope) {}\n\n record(\n call: Pick<TransportCall, \"invocation\" | \"lineage\">,\n reading: PrincipalReading,\n ): void {\n const record = records(call.invocation, true)!;\n record.own.set(reading.instance, reading);\n if (call.lineage) {\n record.invocationLineage = call.lineage;\n this.recordLineage(call.lineage, reading);\n }\n }\n\n recordLineage(lineage: InvocationLineage, reading: PrincipalReading): void {\n const value = records(lineage.carrier, true)!;\n value.positions.set(lineage.position, reading);\n value.lineage = lineage;\n }\n\n enclosing(\n lineage: InvocationLineage,\n args: readonly unknown[],\n ): PrincipalReading | undefined {\n const positions = records(lineage.carrier)?.positions;\n for (const position of lineage.enclosing(args)) {\n const reading = positions?.get(position);\n if (reading) {\n return reading;\n }\n }\n return undefined;\n }\n\n read(input: ReadingInput): PrincipalReading {\n const { plan, args, call } = input;\n const lineage = input.lineage ?? call?.lineage;\n let resolvedInstance = plan?.instance;\n try {\n if (plan?.access === \"public\") {\n return { outcome: \"no-identity\", instance: plan.instance };\n }\n let reading: PrincipalReading | undefined;\n let validation = lineage ?? lineageFromArgs(args);\n if (plan?.access === \"inherit\") {\n reading = lineage && this.enclosing(lineage, args);\n } else {\n const targets = call\n ? [call.invocation]\n : [args, ...args.filter(object)];\n for (const target of targets) {\n const own = records(target)?.own;\n reading = plan\n ? own?.get(plan.instance)\n : own?.size === 1\n ? own.values().next().value\n : undefined;\n if (reading) {\n break;\n }\n }\n if (!reading && !plan) {\n for (const target of [args, ...args.filter(object)]) {\n const candidate = records(target)?.lineage;\n if (!candidate) {\n continue;\n }\n reading = this.enclosing(candidate, args);\n if (reading) {\n validation = candidate;\n break;\n }\n }\n }\n }\n if (!reading) {\n const stamp = matchingStamp(args);\n const instance = plan?.instance ?? \"default\";\n const result = stamp?.results.get(instance);\n if (result) {\n reading = { ...result, instance };\n }\n }\n if (!reading) {\n throw BetterAuthConfigurationError.atRequest(\n input.beforeGuard && plan?.access !== \"inherit\"\n ? \"PRINCIPAL_READ_BEFORE_GUARD\"\n : \"NO_AUTH_RESULT\",\n \"No authentication result: BetterAuthGuard did not run for this invocation\",\n {\n site: plan?.site ?? input.site,\n hint: \"Run BetterAuthGuard before reading a principal; test replacements must stampPrincipal().\",\n },\n );\n }\n resolvedInstance = reading.instance;\n if (reading.outcome === \"authenticated\") {\n validation?.assertReadable?.(args);\n }\n return reading;\n } catch (error) {\n if (isConfigurationError(error)) {\n throw this.deliver(error, input, resolvedInstance);\n }\n throw error;\n }\n }\n\n current(): PrincipalReading {\n const state = this.scope.current();\n if (!state?.reading) {\n throw BetterAuthConfigurationError.atRequest(\n \"NO_AUTH_SCOPE\",\n \"Principal readers require a handler scope\",\n );\n }\n return state.reading();\n }\n\n project<T>(\n reading: PrincipalReading,\n spec: {\n kind?: string;\n reason?: string;\n site: string;\n project: (principal: AuthPrincipal) => T;\n },\n input?: ReadingInput,\n ): T | null {\n if (reading.outcome === \"no-identity\" || reading.outcome === \"absent\") {\n return null;\n }\n if (reading.outcome === \"rejected\") {\n throw reading.failure;\n }\n if (spec.kind && reading.principal.kind !== spec.kind) {\n const error = BetterAuthConfigurationError.atRequest(\n spec.reason ?? \"PRINCIPAL_KIND_REQUIRED\",\n `This reader requires principal kind ${spec.kind}`,\n {\n site: spec.site,\n hint: \"Use @CurrentPrincipal() or getPrincipal() and narrow p.kind.\",\n },\n );\n throw this.deliver(error, input, reading.instance);\n }\n return spec.project(reading.principal);\n }\n\n deliver(\n error: BetterAuthConfigurationError,\n input?: ReadingInput,\n instance?: string,\n ): BetterAuthConfigurationError | RepeatedReaderError {\n const state = this.scope.current();\n const call = state?.call ?? input?.call;\n const lineage =\n call?.lineage ?? input?.lineage ?? (input && lineageFromArgs(input.args));\n const key = call?.key ?? lineage?.carrier ?? input?.args;\n if (!key) {\n return error;\n }\n const repeated = this.scope.surfaced(key, error, {\n instance:\n instance ?? state?.plan?.instance ?? input?.plan?.instance ?? \"default\",\n site:\n error.site ?? input?.site ?? input?.plan?.site ?? \"principal reader\",\n lineage,\n });\n return repeated ? new RepeatedReaderError(error) : error;\n }\n\n stamp(\n args: readonly unknown[],\n instance: string,\n result: PrincipalResult,\n ): void {\n stampResult(args, instance, result);\n }\n}\n\n/** Test stamps are verified by array or element identity (see matchingStamp), so a stamp never leaks into another invocation. */\nexport function stampResult(\n args: readonly unknown[],\n instance: string,\n result: PrincipalResult,\n): void {\n const stamp = matchingStamp(args) ?? {\n elements: [...args],\n results: new Map(),\n };\n stamp.results.set(instance, result);\n for (const target of [args, ...args]) {\n if (object(target) && Object.isExtensible(target)) {\n Object.defineProperty(target, TEST_STAMP, {\n value: stamp,\n configurable: true,\n });\n }\n }\n}\n\nexport function stampedReading(\n args: readonly unknown[],\n instance: string,\n): PrincipalReading | undefined {\n const result = matchingStamp(args)?.results.get(instance);\n return result ? { ...result, instance } : undefined;\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
const require_auth_errors = require("./auth-errors-
|
|
1
|
+
const require_auth_errors = require("./auth-errors-RW7hOVI-.cjs");
|
|
2
2
|
let _nestjs_common = require("@nestjs/common");
|
|
3
|
-
|
|
3
|
+
// src/principal-readings.ts
|
|
4
4
|
const RESOLUTION = Symbol.for("@nestjs-kit/better-auth:resolution");
|
|
5
5
|
const TEST_STAMP = Symbol.for("@nestjs-kit/better-auth:test-stamp");
|
|
6
6
|
function object(value) {
|
|
@@ -185,7 +185,6 @@ function stampedReading(args, instance) {
|
|
|
185
185
|
instance
|
|
186
186
|
} : void 0;
|
|
187
187
|
}
|
|
188
|
-
//#endregion
|
|
189
188
|
Object.defineProperty(exports, "PrincipalReadings", {
|
|
190
189
|
enumerable: true,
|
|
191
190
|
get: function() {
|
|
@@ -205,4 +204,4 @@ Object.defineProperty(exports, "stampedReading", {
|
|
|
205
204
|
}
|
|
206
205
|
});
|
|
207
206
|
|
|
208
|
-
//# sourceMappingURL=principal-readings-
|
|
207
|
+
//# sourceMappingURL=principal-readings-JLNT58k0.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"principal-readings-Chmt6rQV.cjs","names":["IntrinsicException","BetterAuthConfigurationError","isConfigurationError"],"sources":["../src/principal-readings.ts"],"sourcesContent":["import { IntrinsicException } from \"@nestjs/common\";\nimport type {\n InvocationLineage,\n PrincipalReading,\n PrincipalResult,\n RoutePlan,\n TransportCall,\n} from \"./auth-contracts.js\";\nimport {\n BetterAuthConfigurationError,\n isConfigurationError,\n} from \"./auth-errors.js\";\nimport type { AuthPrincipal } from \"./auth-types.js\";\nimport { RequestScope } from \"./request-scope.js\";\n\nconst RESOLUTION = Symbol.for(\"@nestjs-kit/better-auth:resolution\");\nconst TEST_STAMP = Symbol.for(\"@nestjs-kit/better-auth:test-stamp\");\ninterface Records {\n own: Map<string, PrincipalReading>;\n positions: Map<string, PrincipalReading>;\n lineage?: InvocationLineage;\n invocationLineage?: InvocationLineage;\n}\ninterface TestStamp {\n elements: readonly unknown[];\n results: Map<string, PrincipalResult>;\n}\nexport interface ReadingInput {\n readonly args: readonly unknown[];\n readonly call?: TransportCall;\n readonly lineage?: InvocationLineage;\n readonly plan?: Pick<RoutePlan, \"access\" | \"instance\" | \"site\">;\n readonly beforeGuard?: boolean;\n readonly site?: string;\n}\n\nfunction object(value: unknown): value is object {\n return (\n value !== null && (typeof value === \"object\" || typeof value === \"function\")\n );\n}\nfunction records(target: object, create = false): Records | undefined {\n let value = Reflect.get(target, RESOLUTION) as Records | undefined;\n if (!value && create) {\n value = { own: new Map(), positions: new Map() };\n Object.defineProperty(target, RESOLUTION, { value });\n }\n return value;\n}\nfunction sameElements(stamp: TestStamp, args: readonly unknown[]): boolean {\n return (\n stamp.elements.length === args.length &&\n stamp.elements.every((value, index) => value === args[index])\n );\n}\n\n/**\n * The stamp of this invocation: the one on the args array itself (WS, RPC and GraphQL guards and interceptors share\n * that array), or one on its elements when the elements identify the invocation. That takes at least two object\n * elements (an HTTP request and its response): a lone object, such as a WS socket beside a primitive payload, is shared\n * by every message on its connection, so a stamp found only through it may belong to another invocation.\n */\nfunction matchingStamp(args: readonly unknown[]): TestStamp | undefined {\n const own = Reflect.get(args, TEST_STAMP) as TestStamp | undefined;\n if (own && sameElements(own, args)) {\n return own;\n }\n const objects = args.filter(object);\n if (objects.length < 2) {\n return undefined;\n }\n for (const target of objects) {\n const stamp = Reflect.get(target, TEST_STAMP) as TestStamp | undefined;\n if (stamp && sameElements(stamp, args)) {\n return stamp;\n }\n }\n return undefined;\n}\n\nfunction lineageFromArgs(\n args: readonly unknown[],\n): InvocationLineage | undefined {\n const targets = [args, ...args.filter(object)];\n for (const target of targets) {\n const own = records(target)?.invocationLineage;\n if (own) {\n return own;\n }\n }\n for (const target of targets) {\n const carrier = records(target)?.lineage;\n if (!carrier) {\n continue;\n }\n // The recorded callback is structural, but its recorded position may belong\n // to a sibling operation in the same batched GraphQL carrier.\n for (const position of carrier.enclosing(args)) {\n return { ...carrier, position };\n }\n }\n return undefined;\n}\n\n/** The intrinsic twin suppresses Nest's repeated log, while retaining the safe wire shape. */\nexport class RepeatedReaderError extends IntrinsicException {\n constructor(error: BetterAuthConfigurationError) {\n super(error.message);\n Object.defineProperties(this, Object.getOwnPropertyDescriptors(error));\n }\n}\n\nexport class PrincipalReadings {\n constructor(private readonly scope: RequestScope) {}\n\n record(\n call: Pick<TransportCall, \"invocation\" | \"lineage\">,\n reading: PrincipalReading,\n ): void {\n const record = records(call.invocation, true)!;\n record.own.set(reading.instance, reading);\n if (call.lineage) {\n record.invocationLineage = call.lineage;\n this.recordLineage(call.lineage, reading);\n }\n }\n\n recordLineage(lineage: InvocationLineage, reading: PrincipalReading): void {\n const value = records(lineage.carrier, true)!;\n value.positions.set(lineage.position, reading);\n value.lineage = lineage;\n }\n\n enclosing(\n lineage: InvocationLineage,\n args: readonly unknown[],\n ): PrincipalReading | undefined {\n const positions = records(lineage.carrier)?.positions;\n for (const position of lineage.enclosing(args)) {\n const reading = positions?.get(position);\n if (reading) {\n return reading;\n }\n }\n return undefined;\n }\n\n read(input: ReadingInput): PrincipalReading {\n const { plan, args, call } = input;\n const lineage = input.lineage ?? call?.lineage;\n let resolvedInstance = plan?.instance;\n try {\n if (plan?.access === \"public\") {\n return { outcome: \"no-identity\", instance: plan.instance };\n }\n let reading: PrincipalReading | undefined;\n let validation = lineage ?? lineageFromArgs(args);\n if (plan?.access === \"inherit\") {\n reading = lineage && this.enclosing(lineage, args);\n } else {\n const targets = call\n ? [call.invocation]\n : [args, ...args.filter(object)];\n for (const target of targets) {\n const own = records(target)?.own;\n reading = plan\n ? own?.get(plan.instance)\n : own?.size === 1\n ? own.values().next().value\n : undefined;\n if (reading) {\n break;\n }\n }\n if (!reading && !plan) {\n for (const target of [args, ...args.filter(object)]) {\n const candidate = records(target)?.lineage;\n if (!candidate) {\n continue;\n }\n reading = this.enclosing(candidate, args);\n if (reading) {\n validation = candidate;\n break;\n }\n }\n }\n }\n if (!reading) {\n const stamp = matchingStamp(args);\n const instance = plan?.instance ?? \"default\";\n const result = stamp?.results.get(instance);\n if (result) {\n reading = { ...result, instance };\n }\n }\n if (!reading) {\n throw BetterAuthConfigurationError.atRequest(\n input.beforeGuard && plan?.access !== \"inherit\"\n ? \"PRINCIPAL_READ_BEFORE_GUARD\"\n : \"NO_AUTH_RESULT\",\n \"No authentication result: BetterAuthGuard did not run for this invocation\",\n {\n site: plan?.site ?? input.site,\n hint: \"Run BetterAuthGuard before reading a principal; test replacements must stampPrincipal().\",\n },\n );\n }\n resolvedInstance = reading.instance;\n if (reading.outcome === \"authenticated\") {\n validation?.assertReadable?.(args);\n }\n return reading;\n } catch (error) {\n if (isConfigurationError(error)) {\n throw this.deliver(error, input, resolvedInstance);\n }\n throw error;\n }\n }\n\n current(): PrincipalReading {\n const state = this.scope.current();\n if (!state?.reading) {\n throw BetterAuthConfigurationError.atRequest(\n \"NO_AUTH_SCOPE\",\n \"Principal readers require a handler scope\",\n );\n }\n return state.reading();\n }\n\n project<T>(\n reading: PrincipalReading,\n spec: {\n kind?: string;\n reason?: string;\n site: string;\n project: (principal: AuthPrincipal) => T;\n },\n input?: ReadingInput,\n ): T | null {\n if (reading.outcome === \"no-identity\" || reading.outcome === \"absent\") {\n return null;\n }\n if (reading.outcome === \"rejected\") {\n throw reading.failure;\n }\n if (spec.kind && reading.principal.kind !== spec.kind) {\n const error = BetterAuthConfigurationError.atRequest(\n spec.reason ?? \"PRINCIPAL_KIND_REQUIRED\",\n `This reader requires principal kind ${spec.kind}`,\n {\n site: spec.site,\n hint: \"Use @CurrentPrincipal() or getPrincipal() and narrow p.kind.\",\n },\n );\n throw this.deliver(error, input, reading.instance);\n }\n return spec.project(reading.principal);\n }\n\n deliver(\n error: BetterAuthConfigurationError,\n input?: ReadingInput,\n instance?: string,\n ): BetterAuthConfigurationError | RepeatedReaderError {\n const state = this.scope.current();\n const call = state?.call ?? input?.call;\n const lineage =\n call?.lineage ?? input?.lineage ?? (input && lineageFromArgs(input.args));\n const key = call?.key ?? lineage?.carrier ?? input?.args;\n if (!key) {\n return error;\n }\n const repeated = this.scope.surfaced(key, error, {\n instance:\n instance ?? state?.plan?.instance ?? input?.plan?.instance ?? \"default\",\n site:\n error.site ?? input?.site ?? input?.plan?.site ?? \"principal reader\",\n lineage,\n });\n return repeated ? new RepeatedReaderError(error) : error;\n }\n\n stamp(\n args: readonly unknown[],\n instance: string,\n result: PrincipalResult,\n ): void {\n stampResult(args, instance, result);\n }\n}\n\n/** Test stamps are verified by array or element identity (see matchingStamp), so a stamp never leaks into another invocation. */\nexport function stampResult(\n args: readonly unknown[],\n instance: string,\n result: PrincipalResult,\n): void {\n const stamp = matchingStamp(args) ?? {\n elements: [...args],\n results: new Map(),\n };\n stamp.results.set(instance, result);\n for (const target of [args, ...args]) {\n if (object(target) && Object.isExtensible(target)) {\n Object.defineProperty(target, TEST_STAMP, {\n value: stamp,\n configurable: true,\n });\n }\n }\n}\n\nexport function stampedReading(\n args: readonly unknown[],\n instance: string,\n): PrincipalReading | undefined {\n const result = matchingStamp(args)?.results.get(instance);\n return result ? { ...result, instance } : undefined;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"principal-readings-JLNT58k0.cjs","names":["IntrinsicException","BetterAuthConfigurationError","isConfigurationError"],"sources":["../src/principal-readings.ts"],"sourcesContent":["import { IntrinsicException } from \"@nestjs/common\";\nimport type {\n InvocationLineage,\n PrincipalReading,\n PrincipalResult,\n RoutePlan,\n TransportCall,\n} from \"./auth-contracts.js\";\nimport {\n BetterAuthConfigurationError,\n isConfigurationError,\n} from \"./auth-errors.js\";\nimport type { AuthPrincipal } from \"./auth-types.js\";\nimport { RequestScope } from \"./request-scope.js\";\n\nconst RESOLUTION = Symbol.for(\"@nestjs-kit/better-auth:resolution\");\nconst TEST_STAMP = Symbol.for(\"@nestjs-kit/better-auth:test-stamp\");\ninterface Records {\n own: Map<string, PrincipalReading>;\n positions: Map<string, PrincipalReading>;\n lineage?: InvocationLineage;\n invocationLineage?: InvocationLineage;\n}\ninterface TestStamp {\n elements: readonly unknown[];\n results: Map<string, PrincipalResult>;\n}\nexport interface ReadingInput {\n readonly args: readonly unknown[];\n readonly call?: TransportCall;\n readonly lineage?: InvocationLineage;\n readonly plan?: Pick<RoutePlan, \"access\" | \"instance\" | \"site\">;\n readonly beforeGuard?: boolean;\n readonly site?: string;\n}\n\nfunction object(value: unknown): value is object {\n return (\n value !== null && (typeof value === \"object\" || typeof value === \"function\")\n );\n}\nfunction records(target: object, create = false): Records | undefined {\n let value = Reflect.get(target, RESOLUTION) as Records | undefined;\n if (!value && create) {\n value = { own: new Map(), positions: new Map() };\n Object.defineProperty(target, RESOLUTION, { value });\n }\n return value;\n}\nfunction sameElements(stamp: TestStamp, args: readonly unknown[]): boolean {\n return (\n stamp.elements.length === args.length &&\n stamp.elements.every((value, index) => value === args[index])\n );\n}\n\n/**\n * The stamp of this invocation: the one on the args array itself (WS, RPC and GraphQL guards and interceptors share\n * that array), or one on its elements when the elements identify the invocation. That takes at least two object\n * elements (an HTTP request and its response): a lone object, such as a WS socket beside a primitive payload, is shared\n * by every message on its connection, so a stamp found only through it may belong to another invocation.\n */\nfunction matchingStamp(args: readonly unknown[]): TestStamp | undefined {\n const own = Reflect.get(args, TEST_STAMP) as TestStamp | undefined;\n if (own && sameElements(own, args)) {\n return own;\n }\n const objects = args.filter(object);\n if (objects.length < 2) {\n return undefined;\n }\n for (const target of objects) {\n const stamp = Reflect.get(target, TEST_STAMP) as TestStamp | undefined;\n if (stamp && sameElements(stamp, args)) {\n return stamp;\n }\n }\n return undefined;\n}\n\nfunction lineageFromArgs(\n args: readonly unknown[],\n): InvocationLineage | undefined {\n const targets = [args, ...args.filter(object)];\n for (const target of targets) {\n const own = records(target)?.invocationLineage;\n if (own) {\n return own;\n }\n }\n for (const target of targets) {\n const carrier = records(target)?.lineage;\n if (!carrier) {\n continue;\n }\n // The recorded callback is structural, but its recorded position may belong\n // to a sibling operation in the same batched GraphQL carrier.\n for (const position of carrier.enclosing(args)) {\n return { ...carrier, position };\n }\n }\n return undefined;\n}\n\n/** The intrinsic twin suppresses Nest's repeated log, while retaining the safe wire shape. */\nexport class RepeatedReaderError extends IntrinsicException {\n constructor(error: BetterAuthConfigurationError) {\n super(error.message);\n Object.defineProperties(this, Object.getOwnPropertyDescriptors(error));\n }\n}\n\nexport class PrincipalReadings {\n constructor(private readonly scope: RequestScope) {}\n\n record(\n call: Pick<TransportCall, \"invocation\" | \"lineage\">,\n reading: PrincipalReading,\n ): void {\n const record = records(call.invocation, true)!;\n record.own.set(reading.instance, reading);\n if (call.lineage) {\n record.invocationLineage = call.lineage;\n this.recordLineage(call.lineage, reading);\n }\n }\n\n recordLineage(lineage: InvocationLineage, reading: PrincipalReading): void {\n const value = records(lineage.carrier, true)!;\n value.positions.set(lineage.position, reading);\n value.lineage = lineage;\n }\n\n enclosing(\n lineage: InvocationLineage,\n args: readonly unknown[],\n ): PrincipalReading | undefined {\n const positions = records(lineage.carrier)?.positions;\n for (const position of lineage.enclosing(args)) {\n const reading = positions?.get(position);\n if (reading) {\n return reading;\n }\n }\n return undefined;\n }\n\n read(input: ReadingInput): PrincipalReading {\n const { plan, args, call } = input;\n const lineage = input.lineage ?? call?.lineage;\n let resolvedInstance = plan?.instance;\n try {\n if (plan?.access === \"public\") {\n return { outcome: \"no-identity\", instance: plan.instance };\n }\n let reading: PrincipalReading | undefined;\n let validation = lineage ?? lineageFromArgs(args);\n if (plan?.access === \"inherit\") {\n reading = lineage && this.enclosing(lineage, args);\n } else {\n const targets = call\n ? [call.invocation]\n : [args, ...args.filter(object)];\n for (const target of targets) {\n const own = records(target)?.own;\n reading = plan\n ? own?.get(plan.instance)\n : own?.size === 1\n ? own.values().next().value\n : undefined;\n if (reading) {\n break;\n }\n }\n if (!reading && !plan) {\n for (const target of [args, ...args.filter(object)]) {\n const candidate = records(target)?.lineage;\n if (!candidate) {\n continue;\n }\n reading = this.enclosing(candidate, args);\n if (reading) {\n validation = candidate;\n break;\n }\n }\n }\n }\n if (!reading) {\n const stamp = matchingStamp(args);\n const instance = plan?.instance ?? \"default\";\n const result = stamp?.results.get(instance);\n if (result) {\n reading = { ...result, instance };\n }\n }\n if (!reading) {\n throw BetterAuthConfigurationError.atRequest(\n input.beforeGuard && plan?.access !== \"inherit\"\n ? \"PRINCIPAL_READ_BEFORE_GUARD\"\n : \"NO_AUTH_RESULT\",\n \"No authentication result: BetterAuthGuard did not run for this invocation\",\n {\n site: plan?.site ?? input.site,\n hint: \"Run BetterAuthGuard before reading a principal; test replacements must stampPrincipal().\",\n },\n );\n }\n resolvedInstance = reading.instance;\n if (reading.outcome === \"authenticated\") {\n validation?.assertReadable?.(args);\n }\n return reading;\n } catch (error) {\n if (isConfigurationError(error)) {\n throw this.deliver(error, input, resolvedInstance);\n }\n throw error;\n }\n }\n\n current(): PrincipalReading {\n const state = this.scope.current();\n if (!state?.reading) {\n throw BetterAuthConfigurationError.atRequest(\n \"NO_AUTH_SCOPE\",\n \"Principal readers require a handler scope\",\n );\n }\n return state.reading();\n }\n\n project<T>(\n reading: PrincipalReading,\n spec: {\n kind?: string;\n reason?: string;\n site: string;\n project: (principal: AuthPrincipal) => T;\n },\n input?: ReadingInput,\n ): T | null {\n if (reading.outcome === \"no-identity\" || reading.outcome === \"absent\") {\n return null;\n }\n if (reading.outcome === \"rejected\") {\n throw reading.failure;\n }\n if (spec.kind && reading.principal.kind !== spec.kind) {\n const error = BetterAuthConfigurationError.atRequest(\n spec.reason ?? \"PRINCIPAL_KIND_REQUIRED\",\n `This reader requires principal kind ${spec.kind}`,\n {\n site: spec.site,\n hint: \"Use @CurrentPrincipal() or getPrincipal() and narrow p.kind.\",\n },\n );\n throw this.deliver(error, input, reading.instance);\n }\n return spec.project(reading.principal);\n }\n\n deliver(\n error: BetterAuthConfigurationError,\n input?: ReadingInput,\n instance?: string,\n ): BetterAuthConfigurationError | RepeatedReaderError {\n const state = this.scope.current();\n const call = state?.call ?? input?.call;\n const lineage =\n call?.lineage ?? input?.lineage ?? (input && lineageFromArgs(input.args));\n const key = call?.key ?? lineage?.carrier ?? input?.args;\n if (!key) {\n return error;\n }\n const repeated = this.scope.surfaced(key, error, {\n instance:\n instance ?? state?.plan?.instance ?? input?.plan?.instance ?? \"default\",\n site:\n error.site ?? input?.site ?? input?.plan?.site ?? \"principal reader\",\n lineage,\n });\n return repeated ? new RepeatedReaderError(error) : error;\n }\n\n stamp(\n args: readonly unknown[],\n instance: string,\n result: PrincipalResult,\n ): void {\n stampResult(args, instance, result);\n }\n}\n\n/** Test stamps are verified by array or element identity (see matchingStamp), so a stamp never leaks into another invocation. */\nexport function stampResult(\n args: readonly unknown[],\n instance: string,\n result: PrincipalResult,\n): void {\n const stamp = matchingStamp(args) ?? {\n elements: [...args],\n results: new Map(),\n };\n stamp.results.set(instance, result);\n for (const target of [args, ...args]) {\n if (object(target) && Object.isExtensible(target)) {\n Object.defineProperty(target, TEST_STAMP, {\n value: stamp,\n configurable: true,\n });\n }\n }\n}\n\nexport function stampedReading(\n args: readonly unknown[],\n instance: string,\n): PrincipalReading | undefined {\n const result = matchingStamp(args)?.results.get(instance);\n return result ? { ...result, instance } : undefined;\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// \0rolldown/runtime.js
|
|
2
2
|
var __create = Object.create;
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -19,7 +19,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
19
19
|
value: mod,
|
|
20
20
|
enumerable: true
|
|
21
21
|
}) : target, mod));
|
|
22
|
-
//#endregion
|
|
23
22
|
Object.defineProperty(exports, "__toESM", {
|
|
24
23
|
enumerable: true,
|
|
25
24
|
get: function() {
|