@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.
Files changed (133) hide show
  1. package/README.md +3 -3
  2. package/dist/admin.cjs +3 -4
  3. package/dist/admin.d.cts +1 -3
  4. package/dist/admin.d.cts.map +1 -1
  5. package/dist/admin.d.mts +1 -3
  6. package/dist/admin.d.mts.map +1 -1
  7. package/dist/admin.mjs +3 -4
  8. package/dist/api-key.cjs +4 -5
  9. package/dist/api-key.d.cts +1 -3
  10. package/dist/api-key.d.cts.map +1 -1
  11. package/dist/api-key.d.mts +1 -3
  12. package/dist/api-key.d.mts.map +1 -1
  13. package/dist/api-key.mjs +4 -5
  14. package/dist/{auth-contracts-Brh_Qko9.d.cts → auth-contracts-CADGyI0O.d.mts} +3 -53
  15. package/dist/auth-contracts-CADGyI0O.d.mts.map +1 -0
  16. package/dist/{auth-contracts-Cz7C4_Ch.d.mts → auth-contracts-zvt8ysoZ.d.cts} +3 -53
  17. package/dist/auth-contracts-zvt8ysoZ.d.cts.map +1 -0
  18. package/dist/{auth-decorators-Cu_nxWnF.cjs → auth-decorators-CJKE9wlT.cjs} +8 -10
  19. package/dist/{auth-decorators-Cu_nxWnF.cjs.map → auth-decorators-CJKE9wlT.cjs.map} +1 -1
  20. package/dist/{auth-decorators-B1mFnGJ6.mjs → auth-decorators-CkNkLNpU.mjs} +8 -10
  21. package/dist/{auth-decorators-B1mFnGJ6.mjs.map → auth-decorators-CkNkLNpU.mjs.map} +1 -1
  22. package/dist/{auth-errors-C3rbTV9p.mjs → auth-errors-JjxGDVSt.mjs} +3 -5
  23. package/dist/{auth-errors-C3rbTV9p.mjs.map → auth-errors-JjxGDVSt.mjs.map} +1 -1
  24. package/dist/{auth-errors-DmiKl0L_.cjs → auth-errors-RW7hOVI-.cjs} +3 -5
  25. package/dist/{auth-errors-DmiKl0L_.cjs.map → auth-errors-RW7hOVI-.cjs.map} +1 -1
  26. package/dist/{auth-guard-h9QB_l11.mjs → auth-guard-Da92QEK5.mjs} +4 -5
  27. package/dist/{auth-guard-h9QB_l11.mjs.map → auth-guard-Da92QEK5.mjs.map} +1 -1
  28. package/dist/{auth-guard-CLyhAEEj.cjs → auth-guard-wapEh_M_.cjs} +4 -5
  29. package/dist/{auth-guard-CLyhAEEj.cjs.map → auth-guard-wapEh_M_.cjs.map} +1 -1
  30. package/dist/{auth-module-BgcY2HpA.cjs → auth-module-ab60-WJZ.cjs} +28 -47
  31. package/dist/auth-module-ab60-WJZ.cjs.map +1 -0
  32. package/dist/{auth-module-CtHx3gRU.mjs → auth-module-uy3BBWwR.mjs} +28 -47
  33. package/dist/auth-module-uy3BBWwR.mjs.map +1 -0
  34. package/dist/{auth-scope-interceptor-BjxAL1-S.cjs → auth-scope-interceptor-Bui20-Ps.cjs} +5 -7
  35. package/dist/{auth-scope-interceptor-BjxAL1-S.cjs.map → auth-scope-interceptor-Bui20-Ps.cjs.map} +1 -1
  36. package/dist/{auth-scope-interceptor-DYPn3b8M.mjs → auth-scope-interceptor-CvCQ52dV.mjs} +5 -7
  37. package/dist/{auth-scope-interceptor-DYPn3b8M.mjs.map → auth-scope-interceptor-CvCQ52dV.mjs.map} +1 -1
  38. package/dist/{bridge-protocol-c66aEkr5.mjs → bridge-protocol-CmRsff2P.mjs} +2 -3
  39. package/dist/{bridge-protocol-BDw8opAG.cjs.map → bridge-protocol-CmRsff2P.mjs.map} +1 -1
  40. package/dist/{bridge-protocol-BDw8opAG.cjs → bridge-protocol-DNFhIKdo.cjs} +2 -3
  41. package/dist/{bridge-protocol-c66aEkr5.mjs.map → bridge-protocol-DNFhIKdo.cjs.map} +1 -1
  42. package/dist/{decorate-Dt0e9K6R.mjs → decorate-bO-rBn-G.mjs} +5 -9
  43. package/dist/{decorate-Dt0e9K6R.mjs.map → decorate-bO-rBn-G.mjs.map} +1 -1
  44. package/dist/{decorate-Cr4Q7bNE.cjs → decorate-fMNf0caZ.cjs} +5 -9
  45. package/dist/{decorate-Cr4Q7bNE.cjs.map → decorate-fMNf0caZ.cjs.map} +1 -1
  46. package/dist/express.cjs +3 -4
  47. package/dist/express.d.cts +1 -3
  48. package/dist/express.d.cts.map +1 -1
  49. package/dist/express.d.mts +1 -3
  50. package/dist/express.d.mts.map +1 -1
  51. package/dist/express.mjs +3 -4
  52. package/dist/fastify.cjs +2 -3
  53. package/dist/fastify.d.cts +1 -3
  54. package/dist/fastify.d.cts.map +1 -1
  55. package/dist/fastify.d.mts +1 -3
  56. package/dist/fastify.d.mts.map +1 -1
  57. package/dist/fastify.mjs +2 -3
  58. package/dist/graphql.cjs +7 -9
  59. package/dist/graphql.cjs.map +1 -1
  60. package/dist/graphql.d.cts +1 -3
  61. package/dist/graphql.d.cts.map +1 -1
  62. package/dist/graphql.d.mts +1 -3
  63. package/dist/graphql.d.mts.map +1 -1
  64. package/dist/graphql.mjs +7 -9
  65. package/dist/graphql.mjs.map +1 -1
  66. package/dist/index.cjs +7 -7
  67. package/dist/index.d.cts +1 -1
  68. package/dist/index.d.mts +1 -1
  69. package/dist/index.mjs +7 -7
  70. package/dist/microservices.cjs +4 -6
  71. package/dist/microservices.cjs.map +1 -1
  72. package/dist/microservices.d.cts +1 -5
  73. package/dist/microservices.d.cts.map +1 -1
  74. package/dist/microservices.d.mts +1 -5
  75. package/dist/microservices.d.mts.map +1 -1
  76. package/dist/microservices.mjs +3 -5
  77. package/dist/microservices.mjs.map +1 -1
  78. package/dist/organization.cjs +3 -4
  79. package/dist/organization.d.cts +1 -3
  80. package/dist/organization.d.cts.map +1 -1
  81. package/dist/organization.d.mts +1 -3
  82. package/dist/organization.d.mts.map +1 -1
  83. package/dist/organization.mjs +3 -4
  84. package/dist/{platform-CZl7TY8j.mjs → platform-BKYVzAv1.mjs} +3 -5
  85. package/dist/{platform-CZl7TY8j.mjs.map → platform-BKYVzAv1.mjs.map} +1 -1
  86. package/dist/{platform-YICqqYv6.cjs → platform-C67dxSls.cjs} +3 -5
  87. package/dist/{platform-YICqqYv6.cjs.map → platform-C67dxSls.cjs.map} +1 -1
  88. package/dist/platform.cjs +1 -1
  89. package/dist/platform.d.cts +0 -2
  90. package/dist/platform.d.cts.map +1 -1
  91. package/dist/platform.d.mts +0 -2
  92. package/dist/platform.d.mts.map +1 -1
  93. package/dist/platform.mjs +1 -1
  94. package/dist/plugin.cjs +4 -8
  95. package/dist/plugin.cjs.map +1 -1
  96. package/dist/plugin.d.cts +0 -4
  97. package/dist/plugin.d.cts.map +1 -1
  98. package/dist/plugin.d.mts +0 -4
  99. package/dist/plugin.d.mts.map +1 -1
  100. package/dist/plugin.mjs +4 -8
  101. package/dist/plugin.mjs.map +1 -1
  102. package/dist/{principal-readings-Ied2staw.mjs → principal-readings-BuXTU0-2.mjs} +3 -4
  103. package/dist/{principal-readings-Ied2staw.mjs.map → principal-readings-BuXTU0-2.mjs.map} +1 -1
  104. package/dist/{principal-readings-Chmt6rQV.cjs → principal-readings-JLNT58k0.cjs} +3 -4
  105. package/dist/{principal-readings-Chmt6rQV.cjs.map → principal-readings-JLNT58k0.cjs.map} +1 -1
  106. package/dist/{rolldown-runtime-VH7oDXx4.cjs → rolldown-runtime-CmQyd0Gu.cjs} +1 -2
  107. package/dist/testing/conformance.cjs +24 -31
  108. package/dist/testing/conformance.cjs.map +1 -1
  109. package/dist/testing/conformance.d.cts +4 -14
  110. package/dist/testing/conformance.d.cts.map +1 -1
  111. package/dist/testing/conformance.d.mts +4 -14
  112. package/dist/testing/conformance.d.mts.map +1 -1
  113. package/dist/testing/conformance.mjs +23 -30
  114. package/dist/testing/conformance.mjs.map +1 -1
  115. package/dist/testing.cjs +6 -7
  116. package/dist/testing.d.cts +1 -3
  117. package/dist/testing.d.cts.map +1 -1
  118. package/dist/testing.d.mts +1 -3
  119. package/dist/testing.d.mts.map +1 -1
  120. package/dist/testing.mjs +6 -7
  121. package/dist/websockets.cjs +7 -10
  122. package/dist/websockets.cjs.map +1 -1
  123. package/dist/websockets.d.cts +1 -7
  124. package/dist/websockets.d.cts.map +1 -1
  125. package/dist/websockets.d.mts +1 -7
  126. package/dist/websockets.d.mts.map +1 -1
  127. package/dist/websockets.mjs +7 -10
  128. package/dist/websockets.mjs.map +1 -1
  129. package/package.json +24 -24
  130. package/dist/auth-contracts-Brh_Qko9.d.cts.map +0 -1
  131. package/dist/auth-contracts-Cz7C4_Ch.d.mts.map +0 -1
  132. package/dist/auth-module-BgcY2HpA.cjs.map +0 -1
  133. package/dist/auth-module-CtHx3gRU.mjs.map +0 -1
@@ -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
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.cts","names":[],"sources":["../src/auth-contracts.ts","../src/plugin.ts"],"mappings":";;;;;UAuXiB;;;;;;;EAOf;;;;qBC7WW;;wBA0CG,OACd,UAAS,sBACR;EAAqB,WAAW"}
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
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.mts","names":[],"sources":["../src/auth-contracts.ts","../src/plugin.ts"],"mappings":";;;;;UAuXiB;;;;;;;EAOf;;;;qBC7WW;;wBA0CG,OACd,UAAS,sBACR;EAAqB,WAAW"}
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
- //#region src/bridge-protocol.ts
4
+ // src/bridge-protocol.ts
5
5
  const BRIDGE_HANDLE = Symbol.for("@nestjs-kit/better-auth:bridge");
6
- //#endregion
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
- //#endregion
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
- //#endregion
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
@@ -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-C3rbTV9p.mjs";
1
+ import { c as isConfigurationError, n as BetterAuthConfigurationError } from "./auth-errors-JjxGDVSt.mjs";
2
2
  import { IntrinsicException } from "@nestjs/common";
3
- //#region src/principal-readings.ts
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-Ied2staw.mjs.map
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-DmiKl0L_.cjs");
1
+ const require_auth_errors = require("./auth-errors-RW7hOVI-.cjs");
2
2
  let _nestjs_common = require("@nestjs/common");
3
- //#region src/principal-readings.ts
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-Chmt6rQV.cjs.map
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
- //#region \0rolldown/runtime.js
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() {