@nestjs-kit/better-auth 0.0.0-stage → 2.0.0

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 (142) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +1078 -2
  3. package/dist/admin.cjs +88 -0
  4. package/dist/admin.cjs.map +1 -0
  5. package/dist/admin.d.cts +14 -0
  6. package/dist/admin.d.cts.map +1 -0
  7. package/dist/admin.d.mts +14 -0
  8. package/dist/admin.d.mts.map +1 -0
  9. package/dist/admin.mjs +85 -0
  10. package/dist/admin.mjs.map +1 -0
  11. package/dist/api-key.cjs +164 -0
  12. package/dist/api-key.cjs.map +1 -0
  13. package/dist/api-key.d.cts +42 -0
  14. package/dist/api-key.d.cts.map +1 -0
  15. package/dist/api-key.d.mts +42 -0
  16. package/dist/api-key.d.mts.map +1 -0
  17. package/dist/api-key.mjs +160 -0
  18. package/dist/api-key.mjs.map +1 -0
  19. package/dist/auth-contracts-Brh_Qko9.d.cts +1654 -0
  20. package/dist/auth-contracts-Brh_Qko9.d.cts.map +1 -0
  21. package/dist/auth-contracts-Cz7C4_Ch.d.mts +1654 -0
  22. package/dist/auth-contracts-Cz7C4_Ch.d.mts.map +1 -0
  23. package/dist/auth-decorators-B1mFnGJ6.mjs +284 -0
  24. package/dist/auth-decorators-B1mFnGJ6.mjs.map +1 -0
  25. package/dist/auth-decorators-Cu_nxWnF.cjs +421 -0
  26. package/dist/auth-decorators-Cu_nxWnF.cjs.map +1 -0
  27. package/dist/auth-errors-C3rbTV9p.mjs +301 -0
  28. package/dist/auth-errors-C3rbTV9p.mjs.map +1 -0
  29. package/dist/auth-errors-DmiKl0L_.cjs +378 -0
  30. package/dist/auth-errors-DmiKl0L_.cjs.map +1 -0
  31. package/dist/auth-guard-CLyhAEEj.cjs +136 -0
  32. package/dist/auth-guard-CLyhAEEj.cjs.map +1 -0
  33. package/dist/auth-guard-h9QB_l11.mjs +125 -0
  34. package/dist/auth-guard-h9QB_l11.mjs.map +1 -0
  35. package/dist/auth-module-BgcY2HpA.cjs +2809 -0
  36. package/dist/auth-module-BgcY2HpA.cjs.map +1 -0
  37. package/dist/auth-module-CtHx3gRU.mjs +2708 -0
  38. package/dist/auth-module-CtHx3gRU.mjs.map +1 -0
  39. package/dist/auth-scope-interceptor-BjxAL1-S.cjs +290 -0
  40. package/dist/auth-scope-interceptor-BjxAL1-S.cjs.map +1 -0
  41. package/dist/auth-scope-interceptor-DYPn3b8M.mjs +231 -0
  42. package/dist/auth-scope-interceptor-DYPn3b8M.mjs.map +1 -0
  43. package/dist/bridge-protocol-BDw8opAG.cjs +25 -0
  44. package/dist/bridge-protocol-BDw8opAG.cjs.map +1 -0
  45. package/dist/bridge-protocol-c66aEkr5.mjs +8 -0
  46. package/dist/bridge-protocol-c66aEkr5.mjs.map +1 -0
  47. package/dist/decorate-Cr4Q7bNE.cjs +319 -0
  48. package/dist/decorate-Cr4Q7bNE.cjs.map +1 -0
  49. package/dist/decorate-Dt0e9K6R.mjs +80 -0
  50. package/dist/decorate-Dt0e9K6R.mjs.map +1 -0
  51. package/dist/express.cjs +256 -0
  52. package/dist/express.cjs.map +1 -0
  53. package/dist/express.d.cts +37 -0
  54. package/dist/express.d.cts.map +1 -0
  55. package/dist/express.d.mts +37 -0
  56. package/dist/express.d.mts.map +1 -0
  57. package/dist/express.mjs +254 -0
  58. package/dist/express.mjs.map +1 -0
  59. package/dist/fastify.cjs +218 -0
  60. package/dist/fastify.cjs.map +1 -0
  61. package/dist/fastify.d.cts +28 -0
  62. package/dist/fastify.d.cts.map +1 -0
  63. package/dist/fastify.d.mts +28 -0
  64. package/dist/fastify.d.mts.map +1 -0
  65. package/dist/fastify.mjs +216 -0
  66. package/dist/fastify.mjs.map +1 -0
  67. package/dist/graphql.cjs +509 -0
  68. package/dist/graphql.cjs.map +1 -0
  69. package/dist/graphql.d.cts +38 -0
  70. package/dist/graphql.d.cts.map +1 -0
  71. package/dist/graphql.d.mts +38 -0
  72. package/dist/graphql.d.mts.map +1 -0
  73. package/dist/graphql.mjs +503 -0
  74. package/dist/graphql.mjs.map +1 -0
  75. package/dist/index.cjs +86 -0
  76. package/dist/index.d.cts +2 -0
  77. package/dist/index.d.mts +2 -0
  78. package/dist/index.mjs +8 -0
  79. package/dist/microservices.cjs +231 -0
  80. package/dist/microservices.cjs.map +1 -0
  81. package/dist/microservices.d.cts +39 -0
  82. package/dist/microservices.d.cts.map +1 -0
  83. package/dist/microservices.d.mts +39 -0
  84. package/dist/microservices.d.mts.map +1 -0
  85. package/dist/microservices.mjs +222 -0
  86. package/dist/microservices.mjs.map +1 -0
  87. package/dist/organization.cjs +191 -0
  88. package/dist/organization.cjs.map +1 -0
  89. package/dist/organization.d.cts +40 -0
  90. package/dist/organization.d.cts.map +1 -0
  91. package/dist/organization.d.mts +40 -0
  92. package/dist/organization.d.mts.map +1 -0
  93. package/dist/organization.mjs +177 -0
  94. package/dist/organization.mjs.map +1 -0
  95. package/dist/platform-CZl7TY8j.mjs +414 -0
  96. package/dist/platform-CZl7TY8j.mjs.map +1 -0
  97. package/dist/platform-YICqqYv6.cjs +473 -0
  98. package/dist/platform-YICqqYv6.cjs.map +1 -0
  99. package/dist/platform.cjs +10 -0
  100. package/dist/platform.d.cts +43 -0
  101. package/dist/platform.d.cts.map +1 -0
  102. package/dist/platform.d.mts +43 -0
  103. package/dist/platform.d.mts.map +1 -0
  104. package/dist/platform.mjs +1 -0
  105. package/dist/plugin.cjs +323 -0
  106. package/dist/plugin.cjs.map +1 -0
  107. package/dist/plugin.d.cts +24 -0
  108. package/dist/plugin.d.cts.map +1 -0
  109. package/dist/plugin.d.mts +24 -0
  110. package/dist/plugin.d.mts.map +1 -0
  111. package/dist/plugin.mjs +321 -0
  112. package/dist/plugin.mjs.map +1 -0
  113. package/dist/principal-readings-Chmt6rQV.cjs +208 -0
  114. package/dist/principal-readings-Chmt6rQV.cjs.map +1 -0
  115. package/dist/principal-readings-Ied2staw.mjs +191 -0
  116. package/dist/principal-readings-Ied2staw.mjs.map +1 -0
  117. package/dist/rolldown-runtime-VH7oDXx4.cjs +28 -0
  118. package/dist/testing/conformance.cjs +6214 -0
  119. package/dist/testing/conformance.cjs.map +1 -0
  120. package/dist/testing/conformance.d.cts +528 -0
  121. package/dist/testing/conformance.d.cts.map +1 -0
  122. package/dist/testing/conformance.d.mts +528 -0
  123. package/dist/testing/conformance.d.mts.map +1 -0
  124. package/dist/testing/conformance.mjs +6204 -0
  125. package/dist/testing/conformance.mjs.map +1 -0
  126. package/dist/testing.cjs +221 -0
  127. package/dist/testing.cjs.map +1 -0
  128. package/dist/testing.d.cts +64 -0
  129. package/dist/testing.d.cts.map +1 -0
  130. package/dist/testing.d.mts +64 -0
  131. package/dist/testing.d.mts.map +1 -0
  132. package/dist/testing.mjs +214 -0
  133. package/dist/testing.mjs.map +1 -0
  134. package/dist/websockets.cjs +396 -0
  135. package/dist/websockets.cjs.map +1 -0
  136. package/dist/websockets.d.cts +67 -0
  137. package/dist/websockets.d.cts.map +1 -0
  138. package/dist/websockets.d.mts +67 -0
  139. package/dist/websockets.d.mts.map +1 -0
  140. package/dist/websockets.mjs +383 -0
  141. package/dist/websockets.mjs.map +1 -0
  142. package/package.json +327 -3
@@ -0,0 +1,321 @@
1
+ import { APIError, createAuthMiddleware, isAPIError, setShouldSkipSessionRefresh } from "better-auth/api";
2
+ import { parseCookies } from "better-auth/cookies";
3
+ import { createDefu } from "defu";
4
+ //#region src/bridge-protocol.ts
5
+ const BRIDGE_HANDLE = Symbol.for("@nestjs-kit/better-auth:bridge");
6
+ //#endregion
7
+ //#region src/database-hook-dispatcher.ts
8
+ /**
9
+ * Construct all dispatchers once; bindings supply only the late-bound methods.
10
+ *
11
+ * The dispatchers take the SDK's own parameter types, so they satisfy `databaseHooks` whatever
12
+ * instance the program registers. Bound hooks declare `DatabaseHookData`, which adds the
13
+ * registered instance's plugin and additional fields (for example admin()'s `banned`) to the
14
+ * same rows; the dispatcher states that narrowing once when it forwards a payload.
15
+ */
16
+ function createDatabaseDispatchers(getBinding, countUnbound) {
17
+ function operation(target) {
18
+ const before = async (...[data, ctx]) => {
19
+ const binding = getBinding();
20
+ if (!binding) {
21
+ countUnbound();
22
+ return;
23
+ }
24
+ const hooks = binding.database[target].before;
25
+ let accumulated = {};
26
+ let changed = false;
27
+ for (const hook of hooks) {
28
+ const result = await hook(target.endsWith(".create") ? {
29
+ ...data,
30
+ ...accumulated
31
+ } : data, ctx);
32
+ if (result === false) return false;
33
+ if (!target.endsWith(".delete") && result && typeof result === "object" && "data" in result) {
34
+ accumulated = {
35
+ ...accumulated,
36
+ ...result.data
37
+ };
38
+ changed = true;
39
+ }
40
+ }
41
+ return changed ? { data: accumulated } : void 0;
42
+ };
43
+ const after = async (...[data, ctx]) => {
44
+ const binding = getBinding();
45
+ if (!binding) {
46
+ countUnbound();
47
+ return;
48
+ }
49
+ for (const hook of binding.database[target].after) await hook(data, ctx);
50
+ };
51
+ return {
52
+ before,
53
+ after
54
+ };
55
+ }
56
+ return {
57
+ user: {
58
+ create: operation("user.create"),
59
+ update: operation("user.update"),
60
+ delete: operation("user.delete")
61
+ },
62
+ session: {
63
+ create: operation("session.create"),
64
+ update: operation("session.update"),
65
+ delete: operation("session.delete")
66
+ },
67
+ account: {
68
+ create: operation("account.create"),
69
+ update: operation("account.update"),
70
+ delete: operation("account.delete")
71
+ },
72
+ verification: {
73
+ create: operation("verification.create"),
74
+ update: operation("verification.update"),
75
+ delete: operation("verification.delete")
76
+ }
77
+ };
78
+ }
79
+ //#endregion
80
+ //#region src/hook-dispatcher.ts
81
+ const API_ERROR_HEADERS = Symbol.for("better-call:api-error-headers");
82
+ const mergeContext = createDefu((object, key, value) => {
83
+ if (Array.isArray(object[key]) && Array.isArray(value)) {
84
+ object[key] = value;
85
+ return true;
86
+ }
87
+ });
88
+ function mergeHeaders(target, source) {
89
+ source?.forEach((value, key) => {
90
+ if (key === "set-cookie") target.append(key, value);
91
+ else target.set(key, value);
92
+ });
93
+ }
94
+ function errorHeaders(error) {
95
+ const attached = error[API_ERROR_HEADERS];
96
+ if (!attached && !error.headers) return;
97
+ const headers = new Headers(attached);
98
+ if (error.headers && error.headers !== attached) mergeHeaders(headers, new Headers(error.headers));
99
+ return headers;
100
+ }
101
+ /** Run each method through the SDK middleware so its cookie/header state is isolated. */
102
+ async function runBefore(binding, ctx, scope) {
103
+ let accumulated = {};
104
+ for (const hook of binding.before) {
105
+ let matched;
106
+ try {
107
+ matched = hook.matches(ctx, scope);
108
+ } catch (error) {
109
+ ctx.context.logger.error("An error occurred during Nest hook matcher execution:", error);
110
+ throw new APIError("INTERNAL_SERVER_ERROR", { message: "An error occurred during hook matcher execution. Check the logs for more details." });
111
+ }
112
+ if (!matched) continue;
113
+ let result;
114
+ try {
115
+ result = await createAuthMiddleware(hook.run)({
116
+ ...ctx,
117
+ returnHeaders: true
118
+ });
119
+ } catch (error) {
120
+ if (isAPIError(error)) ctx.responseHeaders = error[API_ERROR_HEADERS] ?? new Headers();
121
+ throw error;
122
+ }
123
+ ctx.context.responseHeaders ??= new Headers();
124
+ mergeHeaders(ctx.context.responseHeaders, result.headers);
125
+ const value = result.response;
126
+ if (value && typeof value === "object") {
127
+ if ("context" in value && typeof value.context === "object") {
128
+ const { headers, ...rest } = value.context;
129
+ if (headers instanceof Headers) {
130
+ const requestHeaders = accumulated.headers instanceof Headers ? accumulated.headers : new Headers();
131
+ headers.forEach((header, key) => {
132
+ requestHeaders.set(key, header);
133
+ });
134
+ accumulated.headers = requestHeaders;
135
+ }
136
+ accumulated = mergeContext(rest, accumulated);
137
+ } else return value;
138
+ }
139
+ }
140
+ return Object.keys(accumulated).length ? { context: accumulated } : void 0;
141
+ }
142
+ async function runAfter(binding, ctx, scope, relayMatcherError) {
143
+ for (const hook of binding.after) {
144
+ let matched;
145
+ try {
146
+ matched = hook.matches(ctx, scope);
147
+ } catch (error) {
148
+ if (!relayMatcherError) throw error;
149
+ relayMatcherError(error);
150
+ return;
151
+ }
152
+ if (!matched) continue;
153
+ let result;
154
+ try {
155
+ result = await createAuthMiddleware(hook.run)({
156
+ ...ctx,
157
+ returnHeaders: true
158
+ });
159
+ } catch (error) {
160
+ if (!isAPIError(error)) throw error;
161
+ result = {
162
+ response: error,
163
+ headers: errorHeaders(error)
164
+ };
165
+ }
166
+ if (result.headers) {
167
+ ctx.context.responseHeaders ??= new Headers();
168
+ mergeHeaders(ctx.context.responseHeaders, result.headers);
169
+ }
170
+ if (result.response !== void 0) ctx.context.returned = result.response;
171
+ }
172
+ return ctx.context.returned;
173
+ }
174
+ //#endregion
175
+ //#region src/plugin.ts
176
+ const NESTJS_PLUGIN_ID = "@nestjs-kit/better-auth";
177
+ const isRouter = (ctx) => "_flag" in ctx && ctx._flag === "router";
178
+ const separator = "\0";
179
+ function sessionToken(headers, ctx) {
180
+ return parseCookies(headers?.get("cookie") ?? "").get(ctx.context.authCookies.sessionToken.name) ?? "";
181
+ }
182
+ function credential(headers, ctx, extra) {
183
+ return [
184
+ sessionToken(headers, ctx),
185
+ headers?.get("authorization") ?? "",
186
+ ...extra.map((name) => headers?.get(name) ?? "")
187
+ ].join(separator);
188
+ }
189
+ function sameCredential(ctx, scope, extra) {
190
+ const call = credential(ctx.headers, ctx, extra);
191
+ return call === separator.repeat(extra.length + 1) || call === credential(scope.inbound?.(), ctx, extra);
192
+ }
193
+ /** Pure construction-time Better Auth plugin; the Nest kernel binds through a global symbol. */
194
+ function nestjs(options = {}) {
195
+ let binding = null;
196
+ let registration;
197
+ let unboundDispatches = 0;
198
+ const endpointResults = /* @__PURE__ */ new WeakSet();
199
+ const afterMatcherErrors = /* @__PURE__ */ new WeakMap();
200
+ const scope = () => binding?.current();
201
+ const clientIpHeader = options.clientIpHeader === false ? null : options.clientIpHeader ?? `x-nestjs-kit-better-auth-ip-${globalThis.crypto.randomUUID().replaceAll("-", "")}`;
202
+ const handle = {
203
+ protocol: 4,
204
+ clientIpHeader,
205
+ get state() {
206
+ return binding === null ? "unbound" : binding.state === "closed" ? "closed" : "bound";
207
+ },
208
+ get unboundDispatches() {
209
+ return unboundDispatches;
210
+ },
211
+ bind(next) {
212
+ if (binding && binding.instance !== next.instance) throw new Error(`PLUGIN_SHARED_BETWEEN_INSTANCES: '${binding.instance}' and '${next.instance}'`);
213
+ if (binding?.owner === next.owner && registration) return registration;
214
+ if (binding?.state === "bootstrapped") throw new Error(`INSTANCE_ALREADY_BOUND: bound to ${binding.owner.description}`);
215
+ let tookOverFrom;
216
+ if (binding?.state === "initialized") {
217
+ tookOverFrom = binding.owner.description;
218
+ binding.state = "displaced";
219
+ }
220
+ binding = next;
221
+ unboundDispatches = 0;
222
+ registration = {
223
+ tookOverFrom,
224
+ close: () => {
225
+ next.state = "closed";
226
+ }
227
+ };
228
+ return registration;
229
+ },
230
+ producedByEndpoint(value) {
231
+ return typeof value === "object" && value !== null && endpointResults.has(value);
232
+ }
233
+ };
234
+ return {
235
+ id: NESTJS_PLUGIN_ID,
236
+ [BRIDGE_HANDLE]: handle,
237
+ init: () => ({ options: {
238
+ databaseHooks: createDatabaseDispatchers(() => binding, () => {
239
+ unboundDispatches++;
240
+ }),
241
+ ...clientIpHeader ? { advanced: { ipAddress: { ipAddressHeaders: [clientIpHeader] } } } : {}
242
+ } }),
243
+ hooks: {
244
+ before: [
245
+ {
246
+ matcher: (ctx) => {
247
+ if (!binding) {
248
+ unboundDispatches++;
249
+ return false;
250
+ }
251
+ return !isRouter(ctx) && scope()?.cookies === null;
252
+ },
253
+ handler: createAuthMiddleware(async () => {
254
+ await setShouldSkipSessionRefresh(true);
255
+ })
256
+ },
257
+ {
258
+ matcher: (ctx) => {
259
+ if (!binding || isRouter(ctx)) return false;
260
+ const current = scope();
261
+ if (!current || current.internal) return false;
262
+ const check = current.checkCallerSession;
263
+ current.inbound?.();
264
+ const browser = current.browserHeaders?.();
265
+ const token = sessionToken(ctx.headers, ctx);
266
+ return !!check && token !== "" && token === sessionToken(browser, ctx);
267
+ },
268
+ handler: createAuthMiddleware(async (ctx) => {
269
+ scope()?.checkCallerSession?.(ctx.path, binding.instance);
270
+ })
271
+ },
272
+ {
273
+ matcher: () => binding !== null && binding.before.length > 0,
274
+ handler: createAuthMiddleware((ctx) => runBefore(binding, ctx, scope()))
275
+ }
276
+ ],
277
+ after: [
278
+ {
279
+ matcher: () => binding !== null && binding.after.length > 0,
280
+ handler: createAuthMiddleware((ctx) => runAfter(binding, ctx, scope(), (error) => {
281
+ afterMatcherErrors.set(ctx.context, error);
282
+ }))
283
+ },
284
+ {
285
+ matcher: (ctx) => {
286
+ if (afterMatcherErrors.has(ctx.context)) {
287
+ const error = afterMatcherErrors.get(ctx.context);
288
+ afterMatcherErrors.delete(ctx.context);
289
+ throw error;
290
+ }
291
+ return binding !== null && !isRouter(ctx);
292
+ },
293
+ handler: createAuthMiddleware(async (ctx) => {
294
+ const value = ctx.context.returned;
295
+ if (typeof value === "object" && value !== null) endpointResults.add(value);
296
+ })
297
+ },
298
+ {
299
+ matcher: (ctx) => {
300
+ if (isRouter(ctx)) return false;
301
+ const current = scope();
302
+ return !!current?.cookies && current.forward !== "none";
303
+ },
304
+ handler: createAuthMiddleware(async (ctx) => {
305
+ const current = scope();
306
+ if (current.forward === "same-credential" && !sameCredential(ctx, current, binding.credentialHeaders)) {
307
+ binding.onDropped(ctx.path);
308
+ return;
309
+ }
310
+ const cookies = ctx.context.responseHeaders?.getSetCookie() ?? [];
311
+ if (cookies.length) current.cookies.append(cookies);
312
+ })
313
+ }
314
+ ]
315
+ }
316
+ };
317
+ }
318
+ //#endregion
319
+ export { NESTJS_PLUGIN_ID, nestjs };
320
+
321
+ //# sourceMappingURL=plugin.mjs.map
@@ -0,0 +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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"}
@@ -0,0 +1,208 @@
1
+ const require_auth_errors = require("./auth-errors-DmiKl0L_.cjs");
2
+ let _nestjs_common = require("@nestjs/common");
3
+ //#region src/principal-readings.ts
4
+ const RESOLUTION = Symbol.for("@nestjs-kit/better-auth:resolution");
5
+ const TEST_STAMP = Symbol.for("@nestjs-kit/better-auth:test-stamp");
6
+ function object(value) {
7
+ return value !== null && (typeof value === "object" || typeof value === "function");
8
+ }
9
+ function records(target, create = false) {
10
+ let value = Reflect.get(target, RESOLUTION);
11
+ if (!value && create) {
12
+ value = {
13
+ own: /* @__PURE__ */ new Map(),
14
+ positions: /* @__PURE__ */ new Map()
15
+ };
16
+ Object.defineProperty(target, RESOLUTION, { value });
17
+ }
18
+ return value;
19
+ }
20
+ function sameElements(stamp, args) {
21
+ return stamp.elements.length === args.length && stamp.elements.every((value, index) => value === args[index]);
22
+ }
23
+ /**
24
+ * The stamp of this invocation: the one on the args array itself (WS, RPC and GraphQL guards and interceptors share
25
+ * that array), or one on its elements when the elements identify the invocation. That takes at least two object
26
+ * elements (an HTTP request and its response): a lone object, such as a WS socket beside a primitive payload, is shared
27
+ * by every message on its connection, so a stamp found only through it may belong to another invocation.
28
+ */
29
+ function matchingStamp(args) {
30
+ const own = Reflect.get(args, TEST_STAMP);
31
+ if (own && sameElements(own, args)) return own;
32
+ const objects = args.filter(object);
33
+ if (objects.length < 2) return;
34
+ for (const target of objects) {
35
+ const stamp = Reflect.get(target, TEST_STAMP);
36
+ if (stamp && sameElements(stamp, args)) return stamp;
37
+ }
38
+ }
39
+ function lineageFromArgs(args) {
40
+ const targets = [args, ...args.filter(object)];
41
+ for (const target of targets) {
42
+ const own = records(target)?.invocationLineage;
43
+ if (own) return own;
44
+ }
45
+ for (const target of targets) {
46
+ const carrier = records(target)?.lineage;
47
+ if (!carrier) continue;
48
+ for (const position of carrier.enclosing(args)) return {
49
+ ...carrier,
50
+ position
51
+ };
52
+ }
53
+ }
54
+ /** The intrinsic twin suppresses Nest's repeated log, while retaining the safe wire shape. */
55
+ var RepeatedReaderError = class extends _nestjs_common.IntrinsicException {
56
+ constructor(error) {
57
+ super(error.message);
58
+ Object.defineProperties(this, Object.getOwnPropertyDescriptors(error));
59
+ }
60
+ };
61
+ var PrincipalReadings = class {
62
+ scope;
63
+ constructor(scope) {
64
+ this.scope = scope;
65
+ }
66
+ record(call, reading) {
67
+ const record = records(call.invocation, true);
68
+ record.own.set(reading.instance, reading);
69
+ if (call.lineage) {
70
+ record.invocationLineage = call.lineage;
71
+ this.recordLineage(call.lineage, reading);
72
+ }
73
+ }
74
+ recordLineage(lineage, reading) {
75
+ const value = records(lineage.carrier, true);
76
+ value.positions.set(lineage.position, reading);
77
+ value.lineage = lineage;
78
+ }
79
+ enclosing(lineage, args) {
80
+ const positions = records(lineage.carrier)?.positions;
81
+ for (const position of lineage.enclosing(args)) {
82
+ const reading = positions?.get(position);
83
+ if (reading) return reading;
84
+ }
85
+ }
86
+ read(input) {
87
+ const { plan, args, call } = input;
88
+ const lineage = input.lineage ?? call?.lineage;
89
+ let resolvedInstance = plan?.instance;
90
+ try {
91
+ if (plan?.access === "public") return {
92
+ outcome: "no-identity",
93
+ instance: plan.instance
94
+ };
95
+ let reading;
96
+ let validation = lineage ?? lineageFromArgs(args);
97
+ if (plan?.access === "inherit") reading = lineage && this.enclosing(lineage, args);
98
+ else {
99
+ const targets = call ? [call.invocation] : [args, ...args.filter(object)];
100
+ for (const target of targets) {
101
+ const own = records(target)?.own;
102
+ reading = plan ? own?.get(plan.instance) : own?.size === 1 ? own.values().next().value : void 0;
103
+ if (reading) break;
104
+ }
105
+ if (!reading && !plan) for (const target of [args, ...args.filter(object)]) {
106
+ const candidate = records(target)?.lineage;
107
+ if (!candidate) continue;
108
+ reading = this.enclosing(candidate, args);
109
+ if (reading) {
110
+ validation = candidate;
111
+ break;
112
+ }
113
+ }
114
+ }
115
+ if (!reading) {
116
+ const stamp = matchingStamp(args);
117
+ const instance = plan?.instance ?? "default";
118
+ const result = stamp?.results.get(instance);
119
+ if (result) reading = {
120
+ ...result,
121
+ instance
122
+ };
123
+ }
124
+ if (!reading) throw require_auth_errors.BetterAuthConfigurationError.atRequest(input.beforeGuard && plan?.access !== "inherit" ? "PRINCIPAL_READ_BEFORE_GUARD" : "NO_AUTH_RESULT", "No authentication result: BetterAuthGuard did not run for this invocation", {
125
+ site: plan?.site ?? input.site,
126
+ hint: "Run BetterAuthGuard before reading a principal; test replacements must stampPrincipal()."
127
+ });
128
+ resolvedInstance = reading.instance;
129
+ if (reading.outcome === "authenticated") validation?.assertReadable?.(args);
130
+ return reading;
131
+ } catch (error) {
132
+ if (require_auth_errors.isConfigurationError(error)) throw this.deliver(error, input, resolvedInstance);
133
+ throw error;
134
+ }
135
+ }
136
+ current() {
137
+ const state = this.scope.current();
138
+ if (!state?.reading) throw require_auth_errors.BetterAuthConfigurationError.atRequest("NO_AUTH_SCOPE", "Principal readers require a handler scope");
139
+ return state.reading();
140
+ }
141
+ project(reading, spec, input) {
142
+ if (reading.outcome === "no-identity" || reading.outcome === "absent") return null;
143
+ if (reading.outcome === "rejected") throw reading.failure;
144
+ if (spec.kind && reading.principal.kind !== spec.kind) {
145
+ const error = require_auth_errors.BetterAuthConfigurationError.atRequest(spec.reason ?? "PRINCIPAL_KIND_REQUIRED", `This reader requires principal kind ${spec.kind}`, {
146
+ site: spec.site,
147
+ hint: "Use @CurrentPrincipal() or getPrincipal() and narrow p.kind."
148
+ });
149
+ throw this.deliver(error, input, reading.instance);
150
+ }
151
+ return spec.project(reading.principal);
152
+ }
153
+ deliver(error, input, instance) {
154
+ const state = this.scope.current();
155
+ const call = state?.call ?? input?.call;
156
+ const lineage = call?.lineage ?? input?.lineage ?? (input && lineageFromArgs(input.args));
157
+ const key = call?.key ?? lineage?.carrier ?? input?.args;
158
+ if (!key) return error;
159
+ return this.scope.surfaced(key, error, {
160
+ instance: instance ?? state?.plan?.instance ?? input?.plan?.instance ?? "default",
161
+ site: error.site ?? input?.site ?? input?.plan?.site ?? "principal reader",
162
+ lineage
163
+ }) ? new RepeatedReaderError(error) : error;
164
+ }
165
+ stamp(args, instance, result) {
166
+ stampResult(args, instance, result);
167
+ }
168
+ };
169
+ /** Test stamps are verified by array or element identity (see matchingStamp), so a stamp never leaks into another invocation. */
170
+ function stampResult(args, instance, result) {
171
+ const stamp = matchingStamp(args) ?? {
172
+ elements: [...args],
173
+ results: /* @__PURE__ */ new Map()
174
+ };
175
+ stamp.results.set(instance, result);
176
+ for (const target of [args, ...args]) if (object(target) && Object.isExtensible(target)) Object.defineProperty(target, TEST_STAMP, {
177
+ value: stamp,
178
+ configurable: true
179
+ });
180
+ }
181
+ function stampedReading(args, instance) {
182
+ const result = matchingStamp(args)?.results.get(instance);
183
+ return result ? {
184
+ ...result,
185
+ instance
186
+ } : void 0;
187
+ }
188
+ //#endregion
189
+ Object.defineProperty(exports, "PrincipalReadings", {
190
+ enumerable: true,
191
+ get: function() {
192
+ return PrincipalReadings;
193
+ }
194
+ });
195
+ Object.defineProperty(exports, "stampResult", {
196
+ enumerable: true,
197
+ get: function() {
198
+ return stampResult;
199
+ }
200
+ });
201
+ Object.defineProperty(exports, "stampedReading", {
202
+ enumerable: true,
203
+ get: function() {
204
+ return stampedReading;
205
+ }
206
+ });
207
+
208
+ //# sourceMappingURL=principal-readings-Chmt6rQV.cjs.map
@@ -0,0 +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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"}