@nestjs-kit/better-auth 0.0.0-stage → 2.1.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,2708 @@
1
+ import { a as getAPIErrorHeaders, c as isConfigurationError, f as ErrorRedactor, i as createInfrastructureError, l as isInfrastructureError, n as BetterAuthConfigurationError, s as isAuthFailure, t as AuthFailures } from "./auth-errors-C3rbTV9p.mjs";
2
+ import { A as SKIP_ORIGIN_CHECK_METADATA, B as getPrincipalSourcesToken, C as PRINCIPAL_RESOLVER, D as ROUTE_PLANNER, E as REQUIREMENTS_METADATA, F as getBetterAuthHandleToken, I as getBetterAuthInstanceToken, L as getBetterAuthOptionsToken, M as TRANSPORT_REGISTRY, N as USE_BETTER_AUTH_METADATA, O as SCOPE_CORE, R as getBetterAuthServiceToken, S as PRINCIPAL_READINGS, T as REQUEST_SCOPE, _ as MOUNT_COORDINATOR, a as ACCESS_METADATA, b as POLICY_RESOLVER, c as AUTH_INSTANCE_METADATA, d as FRESHNESS_METADATA, f as GUARD_CORE, g as INVOCATION_VALUES, h as INVOCATION_PARAMS_METADATA, i as ACCEPT_PRINCIPALS_METADATA, j as TRANSPORTS, k as SKIP_DEFAULT_REQUIREMENTS_METADATA, m as INSTANCE_REGISTRY, n as __decorateParam, o as AMBIGUOUS_INVOCATION_VALUE, r as __decorateMetadata, s as AUTH_ENHANCER, t as __decorate, u as FORWARD_COOKIES_METADATA, v as PLATFORMS, x as PRINCIPAL_PARAMS_METADATA, y as POLICY_INVOKER, z as getExtensionToken } from "./decorate-Dt0e9K6R.mjs";
3
+ import { a as absent, c as normalizeForRequest, i as MemoKeys, l as rejected, n as ScopeCore, o as authenticated, r as ChainPrincipalResolver, t as BetterAuthScopeInterceptor, u as requestHeaders } from "./auth-scope-interceptor-DYPn3b8M.mjs";
4
+ import { n as GuardCore, t as BetterAuthGuard } from "./auth-guard-h9QB_l11.mjs";
5
+ import { n as EXTENSION_DEFINITION, t as BRIDGE_HANDLE } from "./bridge-protocol-c66aEkr5.mjs";
6
+ import { C as memoKey, S as RequestScope, b as readHookMetadata, u as Require, y as definePrincipalParam } from "./auth-decorators-B1mFnGJ6.mjs";
7
+ import { t as PrincipalReadings } from "./principal-readings-Ied2staw.mjs";
8
+ import { i as readBoundedBody } from "./platform-CZl7TY8j.mjs";
9
+ import { isAPIError } from "better-auth/api";
10
+ import { Controller, ForbiddenException, Get, Global, HttpException, Inject, Logger, Module, RequestMethod, UnauthorizedException, UseGuards, UseInterceptors, VERSION_NEUTRAL, VersioningType } from "@nestjs/common";
11
+ import { APP_GUARD, APP_INTERCEPTOR, ApplicationConfig, DiscoveryModule, DiscoveryService, HttpAdapterHost, MetadataScanner, ModuleRef, ModulesContainer, Reflector } from "@nestjs/core";
12
+ //#region src/route-planner.ts
13
+ function classes(target) {
14
+ const result = [];
15
+ for (let current = target; current && current !== Function.prototype; current = Object.getPrototypeOf(current)) result.push(current);
16
+ return result;
17
+ }
18
+ function own(key, target) {
19
+ return Reflect.getOwnMetadata(key, target);
20
+ }
21
+ function closest(key, targets) {
22
+ for (const target of targets) {
23
+ const value = own(key, target);
24
+ if (value !== void 0) return value;
25
+ }
26
+ }
27
+ function methodName(target, handler) {
28
+ for (let prototype = target.prototype; prototype && prototype !== Object.prototype; prototype = Object.getPrototypeOf(prototype)) for (const name of Object.getOwnPropertyNames(prototype)) if (name !== "constructor" && Object.getOwnPropertyDescriptor(prototype, name)?.value === handler) return name;
29
+ throw new BetterAuthConfigurationError("UNKNOWN_HANDLER", `Cannot locate handler on ${target.name}`);
30
+ }
31
+ /** An empty allOf() would allow every caller and an empty anyOf() every denial; neither is a requirement. */
32
+ function rejectEmptyGroups(expression, site, origin) {
33
+ if (!("anyOf" in expression) && !("allOf" in expression)) return;
34
+ const [name, children] = "anyOf" in expression ? ["anyOf", expression.anyOf] : ["allOf", expression.allOf];
35
+ if (!children.length) throw new BetterAuthConfigurationError("EMPTY_REQUIREMENT_GROUP", `${site} has an empty ${name}() requirement group (declared by ${origin})`, "Give the group at least one requirement, or omit the requirement when a configured list is empty.");
36
+ for (const child of children) rejectEmptyGroups(child, site, origin);
37
+ }
38
+ var RoutePlanner = class {
39
+ instances;
40
+ policies;
41
+ transports;
42
+ cache = /* @__PURE__ */ new WeakMap();
43
+ constructor(instances, policies, transports) {
44
+ this.instances = instances;
45
+ this.policies = policies;
46
+ this.transports = transports;
47
+ }
48
+ prepare(target, method) {
49
+ const handler = Reflect.get(target.prototype, method);
50
+ if (typeof handler !== "function") throw new BetterAuthConfigurationError("UNKNOWN_HANDLER", `${target.name}.${method} is not a handler`);
51
+ const chain = classes(target);
52
+ const nested = this.transports.defaultAccessFor(target, method) === "inherit";
53
+ const levels = nested ? [handler] : [handler, ...chain];
54
+ const site = `${target.name}.${method}`;
55
+ for (const level of levels) {
56
+ const access = own(ACCESS_METADATA, level);
57
+ if ((access === "public" || access === "optional") && (own(REQUIREMENTS_METADATA, level)?.length ?? 0) > 0) throw new BetterAuthConfigurationError("CONTRADICTORY_ACCESS", `${site} combines anonymous access and requirements at the same level`);
58
+ }
59
+ const instance = closest(AUTH_INSTANCE_METADATA, [handler, ...chain]) ?? "default";
60
+ const entry = this.instances.get(instance);
61
+ let access = nested ? "inherit" : entry.options.defaultAccess === "public" ? "public" : "required";
62
+ for (const level of levels) {
63
+ const declared = own(ACCESS_METADATA, level);
64
+ if (declared) {
65
+ access = declared;
66
+ break;
67
+ }
68
+ if ((own(REQUIREMENTS_METADATA, level)?.length ?? 0) > 0 || nested && own(ACCEPT_PRINCIPALS_METADATA, level) !== void 0) {
69
+ access = "required";
70
+ break;
71
+ }
72
+ }
73
+ const skips = closest(SKIP_DEFAULT_REQUIREMENTS_METADATA, levels) === true;
74
+ const declaredRequirements = access === "required" ? [
75
+ ...(skips ? [] : entry.options.defaultRequirements ?? []).map((expression) => ({
76
+ expression,
77
+ origin: "defaultRequirements"
78
+ })),
79
+ ...nested ? [] : [...chain].reverse().flatMap((level) => (own(REQUIREMENTS_METADATA, level) ?? []).map((expression) => ({
80
+ expression,
81
+ origin: `${level.name} (class)`
82
+ }))),
83
+ ...(own(REQUIREMENTS_METADATA, handler) ?? []).map((expression) => ({
84
+ expression,
85
+ origin: `${site} (method)`
86
+ }))
87
+ ] : [];
88
+ const requirements = declaredRequirements.map((item) => item.expression);
89
+ for (const { expression, origin } of declaredRequirements) rejectEmptyGroups(expression, site, origin);
90
+ return {
91
+ handler,
92
+ chain,
93
+ levels,
94
+ nested,
95
+ site,
96
+ instance,
97
+ entry,
98
+ access,
99
+ skips,
100
+ requirements,
101
+ declaredRequirements
102
+ };
103
+ }
104
+ requirementsOf(target, method) {
105
+ return Object.freeze(this.prepare(target, method).requirements);
106
+ }
107
+ forContext(context) {
108
+ try {
109
+ return this.plan(context.getClass(), methodName(context.getClass(), context.getHandler()));
110
+ } catch (error) {
111
+ if (isConfigurationError(error) && error.phase !== "request") throw BetterAuthConfigurationError.atRequest(error.code, error.detail, {
112
+ site: `${context.getClass().name}.${context.getHandler().name}`,
113
+ hint: error.hint
114
+ });
115
+ throw error;
116
+ }
117
+ }
118
+ plan(target, method) {
119
+ const handler = Reflect.get(target.prototype, method);
120
+ let cache = this.cache.get(target);
121
+ const previous = cache?.get(handler);
122
+ if (previous) return previous;
123
+ const p = this.prepare(target, method);
124
+ const { entry, levels, chain } = p;
125
+ const allKinds = new Set(entry.sources.flatMap((source) => [...source.kinds]));
126
+ const accepts = new Set(closest(ACCEPT_PRINCIPALS_METADATA, levels) ?? entry.sources.filter((source) => source.acceptance === "default").flatMap((source) => [...source.kinds]));
127
+ let fresh = closest(FRESHNESS_METADATA, levels) === true || entry.options.session !== false && entry.options.session?.freshness === "authoritative";
128
+ const admitted = (expression) => {
129
+ if ("anyOf" in expression) return new Set(expression.anyOf.flatMap((child) => [...admitted(child)]));
130
+ if ("allOf" in expression) return intersection(expression.allOf.map(admitted), allKinds);
131
+ const policy = this.policies.resolve(expression.policy);
132
+ const kinds = expression.principals ?? policy.requires?.principals;
133
+ if (expression.freshIdentity ?? policy.requires?.freshIdentity) fresh = true;
134
+ if (kinds) for (const kind of kinds) accepts.add(kind);
135
+ return new Set(kinds ?? entry.sources.filter((source) => !source.delegates).flatMap((source) => [...source.kinds]));
136
+ };
137
+ const permitted = p.requirements.map(admitted);
138
+ for (const kind of accepts) if (!allKinds.has(kind)) throw new BetterAuthConfigurationError("UNKNOWN_PRINCIPAL_KIND", `${p.site} accepts unavailable principal kind ${kind}`);
139
+ const parameters = own(PRINCIPAL_PARAMS_METADATA, handler) ?? [];
140
+ if (p.access === "public" && parameters.length) throw new BetterAuthConfigurationError("PUBLIC_READS_PRINCIPAL", `${p.site} reads a principal on a public handler`, "Use OptionalAuth");
141
+ const principalParams = parameters.flatMap((param) => param.kind ? [{
142
+ kind: param.kind,
143
+ reason: param.reason
144
+ }] : []);
145
+ if (p.access !== "inherit") {
146
+ for (const param of principalParams) if ([...accepts].some((kind) => kind !== param.kind)) throw new BetterAuthConfigurationError("PRINCIPAL_PARAM_CONFLICT", `${p.site} admits a kind rejected by its principal parameter`, "Use CurrentPrincipal and narrow kind");
147
+ }
148
+ if (p.access === "required" && !intersection([accepts, ...permitted], allKinds).size) {
149
+ const describe = (expression) => {
150
+ if ("anyOf" in expression) return `anyOf(${expression.anyOf.map(describe).join(", ")})`;
151
+ if ("allOf" in expression) return `allOf(${expression.allOf.map(describe).join(", ")})`;
152
+ return expression.label ?? this.policies.resolve(expression.policy).id;
153
+ };
154
+ const declarations = p.declaredRequirements.map(({ expression, origin }) => `${origin}: ${describe(expression)}`);
155
+ throw new BetterAuthConfigurationError("UNSATISFIABLE_PRINCIPAL_KINDS", `${p.site} has no eligible principal kind satisfying its requirements. Accepted kinds: ${[...accepts].join(", ") || "none"}.${declarations.length ? ` Requirements: ${declarations.join("; ")}.` : ""}`, p.declaredRequirements.some((item) => item.origin === "defaultRequirements") ? "Review the conflicting requirements; use @SkipDefaultRequirements() to explicitly opt this handler out of instance defaults." : "Review accepted kinds and requirement restrictions; configure a default source or explicitly accept a registered principal kind.");
156
+ }
157
+ const forwardDirectCalls = closest(FORWARD_COOKIES_METADATA, [handler, ...chain]) ?? entry.options.cookies?.forwardDirectCalls ?? false;
158
+ const originCheck = closest(SKIP_ORIGIN_CHECK_METADATA, [handler, ...chain]) || entry.options.originCheck?.mode === "off" ? "off" : forwardDirectCalls ? "form" : "cookie";
159
+ const declares = forwardDirectCalls || levels.some((level) => {
160
+ const access = own(ACCESS_METADATA, level);
161
+ return access === "required" || access === "optional" || [
162
+ ACCEPT_PRINCIPALS_METADATA,
163
+ REQUIREMENTS_METADATA,
164
+ PRINCIPAL_PARAMS_METADATA,
165
+ INVOCATION_PARAMS_METADATA
166
+ ].some((key) => own(key, level) !== void 0);
167
+ });
168
+ const plan = Object.freeze({
169
+ instance: p.instance,
170
+ access: p.access,
171
+ nested: p.nested,
172
+ site: p.site,
173
+ requirements: Object.freeze(p.requirements),
174
+ skipsDefaultRequirements: p.access === "required" && p.skips,
175
+ freshness: fresh ? "authoritative" : "default",
176
+ accepts,
177
+ principalParams: Object.freeze(principalParams),
178
+ originCheck,
179
+ sourceSet: JSON.stringify(entry.sources.flatMap((source, index) => source.kinds.some((kind) => accepts.has(kind)) ? [index] : [])),
180
+ forwardDirectCalls,
181
+ declares
182
+ });
183
+ if (!cache) {
184
+ cache = /* @__PURE__ */ new WeakMap();
185
+ this.cache.set(target, cache);
186
+ }
187
+ cache.set(handler, plan);
188
+ return plan;
189
+ }
190
+ };
191
+ function intersection(sets, universe) {
192
+ return new Set([...universe].filter((kind) => sets.every((set) => set.has(kind))));
193
+ }
194
+ //#endregion
195
+ //#region src/authorization-evaluator.ts
196
+ const allow = () => ({ effect: "allow" });
197
+ const deny = (input) => ({
198
+ effect: "deny",
199
+ ...input
200
+ });
201
+ function definePolicy(policy) {
202
+ return (params, options) => ({
203
+ policy,
204
+ params,
205
+ ...options
206
+ });
207
+ }
208
+ var DefaultPolicyInvoker = class {
209
+ async invoke(policy, params, context) {
210
+ return policy.evaluate(params, context);
211
+ }
212
+ };
213
+ var AuthorizationEvaluator = class {
214
+ policies;
215
+ invoker;
216
+ resolver;
217
+ scope;
218
+ keys = new MemoKeys();
219
+ constructor(policies, invoker, resolver, scope) {
220
+ this.policies = policies;
221
+ this.invoker = invoker;
222
+ this.resolver = resolver;
223
+ this.scope = scope;
224
+ }
225
+ async evaluate(plan, principal, call, entry, execution, transport) {
226
+ if (!plan.requirements.length) return allow();
227
+ const headers = requestHeaders(call, entry);
228
+ const context = {
229
+ principal,
230
+ instance: entry.name,
231
+ transport,
232
+ headers,
233
+ cookies: call.cookies,
234
+ request: call.request,
235
+ param: (name) => call.param(name),
236
+ handler: {
237
+ class: execution.getClass(),
238
+ method: methodName(execution.getClass(), execution.getHandler())
239
+ },
240
+ auth: entry.handle,
241
+ execution,
242
+ memo: (key, fn) => this.scope.memoPolicyIo(call.key, entry.name, this.keys.key(call.key, "policy-io", key), fn, entry.options.limits?.maxAuthorizationCallsPerRequest)
243
+ };
244
+ const publish = (runs) => {
245
+ const ambiguous = /* @__PURE__ */ new Set();
246
+ for (const [index, run] of runs.entries()) for (const other of runs.slice(index + 1)) if ([...run].some(([slot, value]) => other.has(slot) && !Object.is(other.get(slot), value))) for (const slot of [...run.keys(), ...other.keys()]) ambiguous.add(slot);
247
+ const merged = /* @__PURE__ */ new Map();
248
+ for (const run of runs) for (const [slot, value] of run) merged.set(slot, ambiguous.has(slot) ? AMBIGUOUS_INVOCATION_VALUE : value);
249
+ if (!merged.size) return;
250
+ let instances = Reflect.get(call.invocation, INVOCATION_VALUES);
251
+ if (!instances) {
252
+ instances = /* @__PURE__ */ new Map();
253
+ Object.defineProperty(call.invocation, INVOCATION_VALUES, { value: instances });
254
+ }
255
+ let values = instances.get(entry.name);
256
+ if (!values) {
257
+ values = /* @__PURE__ */ new Map();
258
+ instances.set(entry.name, values);
259
+ }
260
+ for (const [slot, value] of merged) {
261
+ this.scope.stateFor(call.invocation).values.set(this.scope.valueKey(entry.name, slot), value);
262
+ values.set(slot, value);
263
+ }
264
+ };
265
+ const withoutValues = (decision) => ({
266
+ decision,
267
+ published: []
268
+ });
269
+ const evaluate = (expression) => this.scope.memoDecision(call.invocation, entry.name, this.keys.key(call.invocation, "decision", expression), async () => {
270
+ if ("anyOf" in expression || "allOf" in expression) {
271
+ const disjunction = "anyOf" in expression;
272
+ const children = "anyOf" in expression ? expression.anyOf : expression.allOf;
273
+ const denials = [];
274
+ if (!children.length) return withoutValues(deny({ reason: "NO_REQUIREMENT_SATISFIED" }));
275
+ const published = [];
276
+ for (const child of children) {
277
+ const outcome = await evaluate(child);
278
+ const decision = outcome.decision;
279
+ if (decision.effect === "allow" && disjunction) return outcome;
280
+ if (decision.effect === "deny") {
281
+ if (!disjunction) return withoutValues(decision);
282
+ denials.push(decision);
283
+ } else published.push(...outcome.published);
284
+ }
285
+ if (!disjunction) return {
286
+ decision: allow(),
287
+ published
288
+ };
289
+ return withoutValues(deny({
290
+ reason: denials[0]?.reason ?? "NO_REQUIREMENT_SATISFIED",
291
+ status: denials.some((d) => d.status === 401) ? 401 : 403,
292
+ message: children.map((child) => "policy" in child ? child.label ?? this.policies.resolve(child.policy).id : "requirement group").join(" or ")
293
+ }));
294
+ }
295
+ const policy = this.policies.resolve(expression.policy);
296
+ const kinds = expression.principals ?? policy.requires?.principals;
297
+ if (kinds && !kinds.includes(principal.kind) || !kinds && principal.delegation) return withoutValues(deny({ reason: "PRINCIPAL_NOT_SUPPORTED" }));
298
+ const values = /* @__PURE__ */ new Map();
299
+ const leaf = {
300
+ ...context,
301
+ provide: (slot, value) => {
302
+ values.set(slot, value);
303
+ }
304
+ };
305
+ try {
306
+ const decision = await entry.handle.run({
307
+ cookies: call.cookies,
308
+ forward: "same-credential",
309
+ inbound: () => headers,
310
+ internal: true
311
+ }, () => this.invoker.invoke(policy, expression.params, leaf));
312
+ return decision.effect === "allow" && values.size ? {
313
+ decision,
314
+ published: [values]
315
+ } : withoutValues(decision);
316
+ } catch (error) {
317
+ const normalized = normalizeForRequest(error, "policy", plan.site, entry, headers);
318
+ if (isAuthFailure(normalized)) {
319
+ if (normalized.status === 429) throw normalized;
320
+ return withoutValues(deny({
321
+ status: normalized.status,
322
+ reason: normalized.reason ?? normalized.code,
323
+ message: normalized.message,
324
+ challenge: normalized.challenge
325
+ }));
326
+ }
327
+ const source = entry.sources.find((item) => item.id === principal.source && item.sessionBacked);
328
+ if (source) await this.scope.memoPolicyIo(call.key, entry.name, JSON.stringify(["reclassify", source.id]), async () => {
329
+ const result = await this.resolver.resolve(call, {
330
+ auth: entry.handle,
331
+ freshness: "authoritative",
332
+ accepts: plan.accepts,
333
+ sourceSet: plan.sourceSet,
334
+ reclassify: { sourceId: source.id }
335
+ });
336
+ if (result.outcome === "authenticated") throw createInfrastructureError(normalized.sessionLoss, {
337
+ headers,
338
+ credentialHeaders: entry.credentialHeaders,
339
+ exposeRawCause: entry.options.errors?.exposeRawCause
340
+ });
341
+ return result;
342
+ }, entry.options.limits?.maxAuthorizationCallsPerRequest);
343
+ return withoutValues(deny({
344
+ status: 401,
345
+ reason: normalized.reason ?? "UNAUTHENTICATED"
346
+ }));
347
+ }
348
+ });
349
+ const published = [];
350
+ for (const expression of plan.requirements) {
351
+ const outcome = await evaluate(expression);
352
+ if (outcome.decision.effect === "deny") return outcome.decision;
353
+ published.push(...outcome.published);
354
+ }
355
+ publish(published);
356
+ return allow();
357
+ }
358
+ };
359
+ //#endregion
360
+ //#region src/trusted-origins.ts
361
+ /** Auth route path exceptions never exempt application operations. */
362
+ function originChecksDisabled(context) {
363
+ return context.skipCSRFCheck || context.skipOriginCheck === true && context.options.advanced?.disableCSRFCheck === void 0;
364
+ }
365
+ function isDynamicBaseURL(value) {
366
+ return typeof value === "object" && value !== null && Array.isArray(value.allowedHosts);
367
+ }
368
+ /**
369
+ * A subset of better-auth's loopback classification: every host accepted here is loopback there, so an unusual spelling
370
+ * better-auth also accepts only makes the kernel stricter, never more lenient.
371
+ */
372
+ function isLoopbackHost(host) {
373
+ let name = host.trim().toLowerCase();
374
+ if (name.startsWith("[")) return name.slice(1, name.indexOf("]")) === "::1";
375
+ const colon = name.indexOf(":");
376
+ if (colon !== -1 && name.indexOf(":", colon + 1) === -1) name = name.slice(0, colon);
377
+ name = name.replace(/\.+$/, "");
378
+ if (name === "localhost" || name.endsWith(".localhost") || name === "::1") return true;
379
+ const octets = /^127\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(name);
380
+ return !!octets && octets.slice(1).every((octet) => Number(octet) <= 255);
381
+ }
382
+ /** The origins better-auth's getTrustedOrigins() adds before the trustedOrigins option. */
383
+ function baseOrigins(context) {
384
+ const configured = context.options.baseURL;
385
+ if (isDynamicBaseURL(configured)) {
386
+ const origins = [];
387
+ const protocol = configured.protocol;
388
+ for (const host of configured.allowedHosts) {
389
+ if (host.includes("://")) {
390
+ origins.push(host);
391
+ continue;
392
+ }
393
+ if (!protocol || protocol === "https" || protocol === "auto") origins.push(`https://${host}`);
394
+ if (protocol === "http" || protocol === "auto" || isLoopbackHost(host)) origins.push(`http://${host}`);
395
+ }
396
+ if (configured.fallback) try {
397
+ origins.push(new URL(configured.fallback).origin);
398
+ } catch {}
399
+ return origins;
400
+ }
401
+ try {
402
+ return context.baseURL ? [new URL(context.baseURL).origin] : [];
403
+ } catch {
404
+ return [];
405
+ }
406
+ }
407
+ function environmentOrigins() {
408
+ const value = globalThis.process?.env?.BETTER_AUTH_TRUSTED_ORIGINS;
409
+ return value ? value.split(",") : [];
410
+ }
411
+ var TrustedOrigins = class {
412
+ context;
413
+ constructor(context) {
414
+ this.context = context;
415
+ }
416
+ /**
417
+ * The router's per-request list (better-auth getTrustedOrigins(options, request)). An array option is already part of
418
+ * the init-time list; a function option is evaluated with the request only, because the init-time list also holds its
419
+ * no-request result, which the router never trusts.
420
+ */
421
+ async list(request) {
422
+ const context = await this.context();
423
+ const configured = context.options.trustedOrigins;
424
+ const dynamic = typeof configured === "function";
425
+ const contributed = dynamic ? await configured(request) : configured;
426
+ const origins = new Set(dynamic ? baseOrigins(context) : context.trustedOrigins);
427
+ if (Array.isArray(contributed)) {
428
+ for (const origin of contributed) if (typeof origin === "string" && origin) origins.add(origin);
429
+ }
430
+ if (dynamic) {
431
+ for (const origin of environmentOrigins()) if (origin) origins.add(origin);
432
+ }
433
+ if (request && !context.options.baseURL && !context.baseURL) origins.add(new URL(request.url).origin);
434
+ return [...origins];
435
+ }
436
+ async isTrusted(origin, request) {
437
+ const [context, trustedOrigins] = await Promise.all([this.context(), this.list(request)]);
438
+ return context.isTrustedOrigin.call({ trustedOrigins }, origin, { allowRelativePaths: false });
439
+ }
440
+ };
441
+ //#endregion
442
+ //#region src/bridge-client.ts
443
+ /** Preserve capabilities lazily; spreading a ScopeView can extract credentials. */
444
+ function derivedScope(scope, init) {
445
+ const outer = scope.current();
446
+ return {
447
+ view: {
448
+ get cookies() {
449
+ return init.cookies;
450
+ },
451
+ get forward() {
452
+ return init.forward ?? "same-credential";
453
+ },
454
+ get inbound() {
455
+ return init.inbound;
456
+ },
457
+ get internal() {
458
+ return init.internal ?? false;
459
+ },
460
+ get browserHeaders() {
461
+ return outer?.view.browserHeaders;
462
+ },
463
+ get checkCallerSession() {
464
+ return outer?.view.checkCallerSession;
465
+ }
466
+ },
467
+ get call() {
468
+ return outer?.call;
469
+ },
470
+ get plan() {
471
+ return outer?.plan;
472
+ },
473
+ get reading() {
474
+ return outer?.reading;
475
+ }
476
+ };
477
+ }
478
+ function createAuthHandle(name, instance, scope, ready) {
479
+ const context = () => instance.$context;
480
+ const trusted = new TrustedOrigins(context);
481
+ return {
482
+ name,
483
+ instance,
484
+ get api() {
485
+ return instance.api;
486
+ },
487
+ context,
488
+ async hasPlugin(id) {
489
+ return (await context()).hasPlugin(id);
490
+ },
491
+ run(init, fn) {
492
+ return scope.run(derivedScope(scope, init), fn);
493
+ },
494
+ checkOrigin(browser, mode) {
495
+ return ready().origin.check(browser, mode);
496
+ },
497
+ producedByEndpoint(value) {
498
+ return ready().bridge.producedByEndpoint(value);
499
+ },
500
+ isTrustedOrigin(origin, request) {
501
+ return trusted.isTrusted(origin, request);
502
+ }
503
+ };
504
+ }
505
+ var BridgeClient = class {
506
+ #registrations = [];
507
+ bind(entry, binding) {
508
+ const result = entry.bridge.bind(binding);
509
+ this.#registrations.push({
510
+ binding,
511
+ close: result.close
512
+ });
513
+ return result;
514
+ }
515
+ bootstrap() {
516
+ const displaced = this.#registrations.filter(({ binding }) => binding.state === "displaced");
517
+ if (displaced.length) throw new BetterAuthConfigurationError("INSTANCE_ALREADY_BOUND", `Another application bound ${displaced.map(({ binding }) => `'${binding.instance}'`).join(", ")} while this application initialized.`, "Initialize applications that share a Better Auth instance one after another, and close one before the next binds.");
518
+ for (const { binding } of this.#registrations) binding.state = "bootstrapped";
519
+ }
520
+ close() {
521
+ for (const registration of this.#registrations) registration.close();
522
+ this.#registrations.length = 0;
523
+ }
524
+ };
525
+ //#endregion
526
+ //#region src/origin-check.ts
527
+ const ORIGIN_PROOF = Symbol.for("@nestjs-kit/better-auth:origin-proof");
528
+ const ORIGIN_CALCULATION_FAILURES = Symbol.for("@nestjs-kit/better-auth:origin-calculation-failures");
529
+ function diagnosticOrigin(headers) {
530
+ const origin = headers.get("origin");
531
+ const referer = headers.get("referer");
532
+ let value = origin || referer || "(missing)";
533
+ if (!origin && referer) try {
534
+ value = new URL(referer).origin;
535
+ } catch {
536
+ value = "(invalid referer)";
537
+ }
538
+ return value.replace(/[\u0000-\u001f\u007f]/g, "").slice(0, 100);
539
+ }
540
+ /** Bounded, per-application diagnostics; the next denial rolls the window without timers. */
541
+ var OriginDiagnostics = class {
542
+ logger;
543
+ #openedAt;
544
+ #pairs = /* @__PURE__ */ new Set();
545
+ #further = /* @__PURE__ */ new Map();
546
+ #origins = /* @__PURE__ */ new Map();
547
+ constructor(logger) {
548
+ this.logger = logger;
549
+ }
550
+ record(instance, failure, headers, trustedCount) {
551
+ const now = Date.now();
552
+ if (this.#openedAt !== void 0 && now - this.#openedAt >= 36e5) {
553
+ this.logger.warn(`Origin check window closed: further denials ${JSON.stringify([...this.#further])}; frequent origins ${JSON.stringify([...this.#origins].sort((a, b) => b[1] - a[1]).slice(0, 5))}`);
554
+ this.#pairs.clear();
555
+ this.#further.clear();
556
+ this.#origins.clear();
557
+ this.#openedAt = void 0;
558
+ }
559
+ this.#openedAt ??= now;
560
+ const origin = diagnosticOrigin(headers);
561
+ const reason = failure.reason ?? failure.code;
562
+ const pair = JSON.stringify([reason, origin]);
563
+ const message = `Origin check ${instance}: ${reason}, origin ${origin}, trusted origins ${trustedCount}. Add the origin to trustedOrigins or send Origin from non-browser clients.`;
564
+ if (!this.#pairs.has(pair) && this.#pairs.size < 100) {
565
+ this.#pairs.add(pair);
566
+ this.logger.warn(message);
567
+ } else this.#further.set(reason, (this.#further.get(reason) ?? 0) + 1);
568
+ if (this.#origins.has(origin) || this.#origins.size < 100) this.#origins.set(origin, (this.#origins.get(origin) ?? 0) + 1);
569
+ this.logger.debug(message);
570
+ }
571
+ advisoryFailure(instance, headers, trustedCount) {
572
+ this.record(instance, AuthFailures.forbidden("ORIGIN_CHECK_UNAVAILABLE"), headers, trustedCount);
573
+ }
574
+ };
575
+ var OriginCheck = class {
576
+ scope;
577
+ init;
578
+ #trusted;
579
+ #trustedCounts = /* @__PURE__ */ new WeakMap();
580
+ constructor(scope, init) {
581
+ this.scope = scope;
582
+ this.init = init;
583
+ this.#trusted = new TrustedOrigins(async () => init.context);
584
+ }
585
+ check(browser, mode) {
586
+ const origins = this.scope.stateFor(browser.key).origins;
587
+ const key = memoKey(this.init.instance, mode === "cookie" && !browser.enforce ? "cookie-advisory" : mode);
588
+ const existing = origins.get(key);
589
+ if (existing) return existing;
590
+ let headers;
591
+ let calculationFailed = false;
592
+ const promise = Promise.resolve().then(async () => {
593
+ if (this.init.options?.mode === "off" || originChecksDisabled(this.init.context)) return null;
594
+ headers = browser.headers();
595
+ const failure = await this.verdict(browser, headers, mode, () => {
596
+ calculationFailed = true;
597
+ });
598
+ if (failure) this.init.diagnostics?.record(this.init.instance, failure, headers, this.#trustedCounts.get(browser.key) ?? this.init.context.trustedOrigins.length);
599
+ else if (mode === "form" || headers.has("cookie")) this.scope.stateFor(browser.key).values.set(this.scope.valueKey(this.init.instance, ORIGIN_PROOF), true);
600
+ return failure;
601
+ }).catch((error) => {
602
+ if (origins.get(key) === promise) origins.delete(key);
603
+ if (isConfigurationError(error)) throw error;
604
+ const failure = isInfrastructureError(error) ? error : createInfrastructureError(error, {
605
+ headers,
606
+ credentialHeaders: this.init.credentialHeaders,
607
+ exposeRawCause: this.init.exposeRawCause
608
+ });
609
+ if (calculationFailed && headers) this.calculationFailures(browser).set(promise, headers);
610
+ throw failure;
611
+ });
612
+ origins.set(key, promise);
613
+ return promise;
614
+ }
615
+ async advisory(browser) {
616
+ const verdict = this.check(browser, "cookie");
617
+ try {
618
+ await verdict;
619
+ } catch (error) {
620
+ const headers = this.calculationFailures(browser).get(verdict);
621
+ if (!isInfrastructureError(error) || !headers) throw error;
622
+ this.init.diagnostics?.advisoryFailure(this.init.instance, headers, this.init.context.trustedOrigins.length);
623
+ }
624
+ }
625
+ assertCallerSession(browser, path, instance) {
626
+ if (instance === this.init.instance && (this.init.options?.mode === "off" || originChecksDisabled(this.init.context) || this.scope.stateFor(browser.key).values.get(this.scope.valueKey(this.init.instance, ORIGIN_PROOF)) === true)) return;
627
+ throw BetterAuthConfigurationError.atRequest("PUBLIC_HANDLER_USED_CALLER_SESSION", "A direct caller-session call requires a passing origin verdict for this browser leg and instance", {
628
+ site: path,
629
+ hint: "Guard the handler and pass its origin check before making this call."
630
+ });
631
+ }
632
+ calculationFailures(browser) {
633
+ const values = this.scope.stateFor(browser.key).values;
634
+ const key = this.scope.valueKey(this.init.instance, ORIGIN_CALCULATION_FAILURES);
635
+ let failures = values.get(key);
636
+ if (!failures) {
637
+ failures = /* @__PURE__ */ new WeakMap();
638
+ values.set(key, failures);
639
+ }
640
+ return failures;
641
+ }
642
+ async verdict(browser, headers, mode, onCalculationFailure) {
643
+ if (!headers.has("cookie")) {
644
+ if (mode === "cookie") return null;
645
+ const site = headers.get("sec-fetch-site");
646
+ const navigation = headers.get("sec-fetch-mode");
647
+ const metadata = [
648
+ site,
649
+ navigation,
650
+ headers.get("sec-fetch-dest")
651
+ ].some((value) => Boolean(value?.trim()));
652
+ if (metadata && site === "cross-site" && navigation === "navigate") return AuthFailures.forbidden("CROSS_SITE_NAVIGATION_LOGIN_BLOCKED");
653
+ if (!metadata && !headers.get("origin") && !headers.get("referer")) return null;
654
+ }
655
+ const origin = headers.get("origin");
656
+ const referer = headers.get("referer");
657
+ const value = headers.get("sec-fetch-site") === "same-origin" && (origin === "null" || mode === "cookie" && !browser.enforce && !origin && !referer) ? new URL(browser.url).origin : origin || referer;
658
+ if (!value || value === "null") {
659
+ if (this.init.options?.missingOrigin === "allow-non-browser" && !headers.has("origin") && !headers.has("referer") && !headers.has("sec-fetch-site")) return null;
660
+ return AuthFailures.forbidden("MISSING_OR_NULL_ORIGIN");
661
+ }
662
+ const request = new Request(browser.url, { headers });
663
+ const trustedOrigins = await this.#trusted.list(request).catch((error) => {
664
+ onCalculationFailure();
665
+ throw error;
666
+ });
667
+ this.#trustedCounts.set(browser.key, trustedOrigins.length);
668
+ return this.init.context.isTrustedOrigin.call({ trustedOrigins }, value, { allowRelativePaths: false }) ? null : AuthFailures.forbidden("INVALID_ORIGIN");
669
+ }
670
+ };
671
+ //#endregion
672
+ //#region src/instance-registry.ts
673
+ /** Nest considers transient providers static; auth collaborators require singleton trees. */
674
+ function isSingletonDependencyTree(root) {
675
+ const pending = [root];
676
+ const seen = /* @__PURE__ */ new Set();
677
+ while (pending.length) {
678
+ const wrapper = pending.pop();
679
+ if (seen.has(wrapper)) continue;
680
+ seen.add(wrapper);
681
+ if (wrapper.isTransient || !wrapper.isDependencyTreeStatic()) return false;
682
+ for (const dependency of wrapper.getCtorMetadata() ?? []) if (dependency) pending.push(dependency);
683
+ for (const property of wrapper.getPropertiesMetadata() ?? []) pending.push(property.wrapper);
684
+ for (const enhancer of wrapper.getEnhancersMetadata() ?? []) pending.push(enhancer);
685
+ }
686
+ return true;
687
+ }
688
+ function configurationIssue(error, code, detail) {
689
+ if (isConfigurationError(error)) return error;
690
+ const issue = new BetterAuthConfigurationError(code, detail);
691
+ Object.defineProperty(issue, "cause", { value: error });
692
+ return issue;
693
+ }
694
+ let InstanceRegistry = class InstanceRegistry {
695
+ scope;
696
+ #registrations = [];
697
+ #entries = /* @__PURE__ */ new Map();
698
+ logger = new Logger("BetterAuth");
699
+ diagnostics = new OriginDiagnostics(this.logger);
700
+ state = "new";
701
+ adapter = null;
702
+ owner = { description: "Nest application" };
703
+ constructor(scope) {
704
+ this.scope = scope;
705
+ }
706
+ register(options, staticOptions, sources, appOptions) {
707
+ const name = staticOptions.name || "default";
708
+ const registration = {
709
+ name,
710
+ options,
711
+ staticOptions,
712
+ sources,
713
+ appOptions,
714
+ instance: options.auth,
715
+ handle: createAuthHandle(name, options.auth, this.scope, () => this.get(name))
716
+ };
717
+ this.#registrations.push(registration);
718
+ return registration;
719
+ }
720
+ registrations() {
721
+ return [...this.#registrations];
722
+ }
723
+ get(name) {
724
+ const entry = this.#entries.get(name);
725
+ if (!entry) throw new BetterAuthConfigurationError("UNKNOWN_INSTANCE", `Auth instance '${name}' is unknown or not initialized.`, "Register the instance once with BetterAuthModule.forRoot().");
726
+ return entry;
727
+ }
728
+ list() {
729
+ return [...this.#entries.values()];
730
+ }
731
+ begin(adapter) {
732
+ if (this.state !== "new") {
733
+ if (this.adapter !== adapter) throw new BetterAuthConfigurationError("LATE_HTTP_ADAPTER", "Another application changed the active HTTP adapter.", "Close the first application before creating another.");
734
+ return false;
735
+ }
736
+ this.adapter = adapter;
737
+ this.state = "initialized";
738
+ this.owner = { description: `Nest application ${globalThis.crypto.randomUUID()}` };
739
+ return true;
740
+ }
741
+ async initialize(issues) {
742
+ this.#entries.clear();
743
+ const seen = /* @__PURE__ */ new Set();
744
+ const bridgeOwners = /* @__PURE__ */ new Map();
745
+ const shared = /* @__PURE__ */ new Set();
746
+ let globals = 0;
747
+ for (const registration of this.#registrations) {
748
+ const { name, instance, options, staticOptions, appOptions, sources, handle } = registration;
749
+ if (seen.has(name)) issues.push(new BetterAuthConfigurationError("DUPLICATE_INSTANCE", `Instance '${name}' was registered more than once.`, "Import BetterAuthModule.forRoot() once per instance."));
750
+ seen.add(name);
751
+ if (staticOptions.globalGuard ?? name === "default") globals++;
752
+ if (name !== "default" && (appOptions.platforms !== void 0 || appOptions.transports !== void 0)) issues.push(new BetterAuthConfigurationError("APP_EXTENSIONS_ON_NAMED_INSTANCE", `Instance '${name}' declares app-wide extensions.`, "Declare platforms and transports on the default instance."));
753
+ if (!instance || typeof instance.handler !== "function" || typeof instance.api?.getSession !== "function" || typeof instance.$context?.then !== "function") {
754
+ issues.push(new BetterAuthConfigurationError("NOT_AN_AUTH_INSTANCE", `Instance '${name}' must be the object returned by betterAuth().`));
755
+ continue;
756
+ }
757
+ let context;
758
+ try {
759
+ context = await instance.$context;
760
+ } catch (error) {
761
+ issues.push(configurationIssue(error, "AUTH_CONTEXT_FAILED", `Instance '${name}' failed to initialize its auth context.`));
762
+ continue;
763
+ }
764
+ const plugin = context.getPlugin("@nestjs-kit/better-auth");
765
+ const bridge = plugin && typeof plugin === "object" ? Reflect.get(plugin, BRIDGE_HANDLE) : void 0;
766
+ if (!bridge) {
767
+ issues.push(new BetterAuthConfigurationError("PLUGIN_MISSING", `Instance '${name}' has no nestjs() bridge.`, "Add nestjs() as the LAST betterAuth plugins entry."));
768
+ continue;
769
+ }
770
+ if (bridge.protocol !== 4 || typeof bridge.bind !== "function") {
771
+ issues.push(new BetterAuthConfigurationError("PLUGIN_PROTOCOL_MISMATCH", `Instance '${name}' has bridge protocol ${bridge.protocol}; expected 4.`, "Align the module and plugin package versions."));
772
+ continue;
773
+ }
774
+ const previous = bridgeOwners.get(bridge);
775
+ if (previous !== void 0) {
776
+ shared.add(name);
777
+ shared.add(previous);
778
+ issues.push(new BetterAuthConfigurationError("PLUGIN_SHARED_BETWEEN_INSTANCES", `'${previous}' and '${name}' share one plugin object.`, "Call nestjs() separately inside every betterAuth() construction."));
779
+ }
780
+ bridgeOwners.set(bridge, name);
781
+ const plugins = context.options.plugins ?? [];
782
+ const index = plugins.findIndex((value) => value.id === "@nestjs-kit/better-auth");
783
+ if (index >= 0 && plugins.slice(index + 1).some((value) => value.hooks?.after?.length)) this.logger.warn(`W_PLUGIN_NOT_LAST: '${name}': put nestjs() after plugins with after hooks.`);
784
+ const credentialHeaders = [...new Set(sources.flatMap((source) => source.credentialHeaders ?? []).map((value) => value.toLowerCase()))];
785
+ this.#entries.set(name, {
786
+ name,
787
+ instance,
788
+ options,
789
+ staticOptions,
790
+ sources,
791
+ handle,
792
+ context,
793
+ bridge,
794
+ credentialHeaders,
795
+ origin: new OriginCheck(this.scope, {
796
+ instance: name,
797
+ context,
798
+ options: options.originCheck,
799
+ diagnostics: this.diagnostics,
800
+ credentialHeaders,
801
+ exposeRawCause: options.errors?.exposeRawCause
802
+ })
803
+ });
804
+ }
805
+ for (const name of shared) this.#entries.delete(name);
806
+ if (globals > 1) issues.push(new BetterAuthConfigurationError("DUPLICATE_GLOBAL_GUARD", "More than one registration enables the global guard.", "Enable globalGuard on only one instance."));
807
+ if (!seen.has("default")) issues.push(new BetterAuthConfigurationError("NO_DEFAULT_INSTANCE", "Named registrations require one default instance.", "Add BetterAuthModule.forRoot({ auth })."));
808
+ }
809
+ reset() {
810
+ this.state = "new";
811
+ this.#entries.clear();
812
+ }
813
+ };
814
+ InstanceRegistry = __decorate([__decorateParam(0, Inject(REQUEST_SCOPE)), __decorateMetadata("design:paramtypes", [Object])], InstanceRegistry);
815
+ //#endregion
816
+ //#region src/route-paths.ts
817
+ function addLeadingSlash(path) {
818
+ if (typeof path !== "string" || path === "") return "";
819
+ return path.startsWith("/") || path.startsWith("{/") ? path : `/${path}`;
820
+ }
821
+ function stripEndSlash(path) {
822
+ return path.endsWith("/") ? path.slice(0, -1) : path;
823
+ }
824
+ function versionPrefix(options) {
825
+ if (options.type === VersioningType.URI) {
826
+ if (options.prefix === false) return "";
827
+ if (options.prefix !== void 0) return options.prefix;
828
+ }
829
+ return "v";
830
+ }
831
+ function append(paths, fragment) {
832
+ return fragment ? paths.map((path) => stripEndSlash(path) + addLeadingSlash(fragment)) : [...paths];
833
+ }
834
+ function withoutVersionPrefix(path, version, options) {
835
+ if (typeof version !== "string") {
836
+ let stripped = path;
837
+ for (const value of version) if (typeof value === "string") stripped = withoutVersionPrefix(stripped, value, options);
838
+ return stripped;
839
+ }
840
+ const prefix = `/${versionPrefix(options)}${version}`;
841
+ return path.startsWith(prefix) ? path.replace(prefix, "") : path;
842
+ }
843
+ /**
844
+ * Composes the paths Nest registers for a handler path, with the rules of Nest's router: URI
845
+ * version segments, then the module, controller and handler paths, then the global prefix
846
+ * unless an exclusion matches the method and the unversioned path. `@nestjs/core` does not
847
+ * export its `RoutePathFactory`, so a test compares this function with that class.
848
+ */
849
+ function composeRoutePaths(input, requestMethod, exclusions) {
850
+ const version = input.methodVersion || input.controllerVersion;
851
+ const options = input.versioningOptions;
852
+ let paths = [""];
853
+ if (version && options?.type === VersioningType.URI) {
854
+ const prefix = versionPrefix(options);
855
+ paths = (typeof version === "object" ? version : [version]).map((value) => value === VERSION_NEUTRAL ? "" : `/${prefix}${value}`);
856
+ }
857
+ paths = append(paths, input.modulePath);
858
+ paths = append(paths, input.ctrlPath);
859
+ paths = append(paths, input.methodPath);
860
+ const globalPrefix = input.globalPrefix;
861
+ if (globalPrefix) {
862
+ const excluded = (path) => {
863
+ if (requestMethod === void 0 || !Array.isArray(exclusions)) return false;
864
+ const candidate = addLeadingSlash(version && version !== VERSION_NEUTRAL && options?.type === VersioningType.URI ? withoutVersionPrefix(path, version, options) : path);
865
+ return exclusions.some((route) => (route.requestMethod === RequestMethod.ALL || route.requestMethod === -1 || route.requestMethod === requestMethod) && route.pathRegex.exec(candidate) !== null);
866
+ };
867
+ paths = paths.map((path) => excluded(path) ? path : stripEndSlash(globalPrefix) + path);
868
+ }
869
+ return paths.map((path) => addLeadingSlash(path || "/")).map((path) => path === "/" ? path : stripEndSlash(path));
870
+ }
871
+ //#endregion
872
+ //#region src/boot-validator.ts
873
+ function requirementLeaves(expressions) {
874
+ return expressions.flatMap((expression) => "anyOf" in expression ? requirementLeaves(expression.anyOf) : "allOf" in expression ? requirementLeaves(expression.allOf) : [expression]);
875
+ }
876
+ function productionMode() {
877
+ return ![
878
+ "development",
879
+ "dev",
880
+ "test"
881
+ ].includes(process.env.NODE_ENV ?? "");
882
+ }
883
+ function ancestors(target) {
884
+ const result = [];
885
+ for (let current = target; typeof current === "function" && current !== Function.prototype; current = Object.getPrototypeOf(current)) result.push(current);
886
+ return result;
887
+ }
888
+ function domainsOverlap(a, b) {
889
+ if (!a || !b) return true;
890
+ const left = a.replace(/^\./, "").toLowerCase();
891
+ const right = b.replace(/^\./, "").toLowerCase();
892
+ return left === right || left.endsWith(`.${right}`) || right.endsWith(`.${left}`);
893
+ }
894
+ function pathsOverlap(a = "/", b = "/") {
895
+ return a === b || a.startsWith(b.endsWith("/") ? b : `${b}/`) || b.startsWith(a.endsWith("/") ? a : `${a}/`);
896
+ }
897
+ function routeMayWarnAboutMount(path, basePath) {
898
+ const route = path.toLowerCase().replace(/(?<!\/)\/+$/, "") || "/";
899
+ const base = basePath.toLowerCase().replace(/(?<!\/)\/+$/, "") || "/";
900
+ if (base === "/") return route.startsWith("/");
901
+ if (route === base || route.startsWith(`${base}/`)) return true;
902
+ const marker = route.search(/[:*{]/);
903
+ if (marker === -1) return false;
904
+ const literal = route.slice(0, marker);
905
+ return base.startsWith(literal) || literal.startsWith(`${base}/`);
906
+ }
907
+ let BootValidator = class BootValidator {
908
+ discovery;
909
+ scanner;
910
+ reflector;
911
+ moduleRef;
912
+ appConfig;
913
+ instances;
914
+ planner;
915
+ policies;
916
+ transports;
917
+ mounts;
918
+ logger = new Logger("BetterAuth");
919
+ #plans = [];
920
+ #claims = [];
921
+ #globalGuard = false;
922
+ #globalScope = false;
923
+ constructor(discovery, scanner, reflector, moduleRef, appConfig, instances, planner, policies, transports, mounts) {
924
+ this.discovery = discovery;
925
+ this.scanner = scanner;
926
+ this.reflector = reflector;
927
+ this.moduleRef = moduleRef;
928
+ this.appConfig = appConfig;
929
+ this.instances = instances;
930
+ this.planner = planner;
931
+ this.policies = policies;
932
+ this.transports = transports;
933
+ this.mounts = mounts;
934
+ }
935
+ warn(code, message) {
936
+ this.logger.warn(`${code}: ${message}`);
937
+ }
938
+ sites() {
939
+ const sites = [];
940
+ const seen = /* @__PURE__ */ new Map();
941
+ for (const wrapper of [...this.discovery.getControllers(), ...this.discovery.getProviders()]) {
942
+ if (wrapper.isAlias || !wrapper.instance || typeof wrapper.instance !== "object" && typeof wrapper.instance !== "function") continue;
943
+ const prototype = Object.getPrototypeOf(wrapper.instance);
944
+ const target = prototype?.constructor;
945
+ if (!prototype || !target || target === Object || target === Function) continue;
946
+ const methods = seen.get(target) ?? /* @__PURE__ */ new Set();
947
+ seen.set(target, methods);
948
+ for (const method of this.scanner.getAllMethodNames(prototype)) {
949
+ if (methods.has(method)) continue;
950
+ methods.add(method);
951
+ const handler = Reflect.get(prototype, method);
952
+ if (typeof handler === "function") sites.push({
953
+ target,
954
+ method,
955
+ handler
956
+ });
957
+ }
958
+ }
959
+ return sites;
960
+ }
961
+ singletonChecks(issues, references) {
962
+ const refs = new Set(references.map((value) => value.policy));
963
+ for (const wrapper of this.discovery.getProviders()) if (((typeof wrapper.token === "symbol" ? Symbol.keyFor(wrapper.token) : void 0)?.startsWith("@nestjs-kit/better-auth:ext:") || refs.has(wrapper.token) || refs.has(wrapper.metatype)) && !isSingletonDependencyTree(wrapper)) issues.push(new BetterAuthConfigurationError("NON_SINGLETON_EXTENSION", `Extension/policy '${String(wrapper.name)}' has a non-static dependency tree.`, "Use singleton extensions and singleton dependencies."));
964
+ }
965
+ canaries() {
966
+ let Probe = class Probe {
967
+ run() {}
968
+ };
969
+ __decorate([
970
+ Get("handler"),
971
+ __decorateMetadata("design:type", Function),
972
+ __decorateMetadata("design:paramtypes", []),
973
+ __decorateMetadata("design:returntype", void 0)
974
+ ], Probe.prototype, "run", null);
975
+ Probe = __decorate([Controller("coverage-probe")], Probe);
976
+ if (Reflect.getOwnMetadata("path", Probe) !== "coverage-probe" || Reflect.getOwnMetadata("path", Probe.prototype.run) !== "handler" || Reflect.getOwnMetadata("method", Probe.prototype.run) !== 0) throw new BetterAuthConfigurationError("NEST_METADATA_KEY_CHANGED", "Boot route checks cannot read Nest path/method metadata.");
977
+ UseGuards(BetterAuthGuard)(Probe);
978
+ UseInterceptors(BetterAuthScopeInterceptor)(Probe);
979
+ if (!Reflect.getOwnMetadata("__guards__", Probe)?.includes(BetterAuthGuard) || !Reflect.getOwnMetadata("__interceptors__", Probe)?.includes(BetterAuthScopeInterceptor)) throw new BetterAuthConfigurationError("NEST_METADATA_KEY_CHANGED", "Boot coverage cannot read Nest '__guards__' / '__interceptors__' metadata.", "Use a compatible Nest version and report the metadata change.");
980
+ }
981
+ enhancer(value, kind) {
982
+ const token = kind === "guard" ? BetterAuthGuard : BetterAuthScopeInterceptor;
983
+ let actual;
984
+ try {
985
+ actual = this.moduleRef.get(token, { strict: false });
986
+ } catch {
987
+ actual = void 0;
988
+ }
989
+ if (value === token || actual !== void 0 && value === actual) return true;
990
+ if (value && (typeof value === "object" || typeof value === "function")) {
991
+ const brand = Reflect.get(value, AUTH_ENHANCER) ?? (typeof value === "function" ? Reflect.get(value.prototype ?? {}, AUTH_ENHANCER) : Reflect.get(Object.getPrototypeOf(value) ?? {}, AUTH_ENHANCER));
992
+ return brand === kind || kind === "scope" && brand === "interceptor" || typeof brand === "object" && brand !== null && Reflect.get(brand, kind) === true;
993
+ }
994
+ return false;
995
+ }
996
+ local(site, kind) {
997
+ const handler = site.method ? Reflect.get(site.target.prototype ?? {}, site.method) : void 0;
998
+ const targets = [...ancestors(site.target), ...handler ? [handler] : []];
999
+ if (targets.some((target) => Reflect.getOwnMetadata(USE_BETTER_AUTH_METADATA, target) === true)) return true;
1000
+ const key = kind === "guard" ? "__guards__" : "__interceptors__";
1001
+ return targets.some((target) => (Reflect.getOwnMetadata(key, target) ?? []).some((value) => this.enhancer(value, kind)));
1002
+ }
1003
+ trigger(target) {
1004
+ const access = Reflect.getOwnMetadata(ACCESS_METADATA, target);
1005
+ if (access === "required" || access === "optional" || access === "authenticated") return true;
1006
+ return [
1007
+ ACCEPT_PRINCIPALS_METADATA,
1008
+ REQUIREMENTS_METADATA,
1009
+ PRINCIPAL_PARAMS_METADATA,
1010
+ INVOCATION_PARAMS_METADATA,
1011
+ FORWARD_COOKIES_METADATA,
1012
+ FRESHNESS_METADATA,
1013
+ AUTH_INSTANCE_METADATA,
1014
+ USE_BETTER_AUTH_METADATA
1015
+ ].some((key) => Reflect.hasOwnMetadata(key, target));
1016
+ }
1017
+ coverage(sites, issues) {
1018
+ this.#globalGuard = [...this.appConfig.getGlobalGuards(), ...this.appConfig.getGlobalRequestGuards().map((wrapper) => wrapper.instance)].some((value) => this.enhancer(value, "guard"));
1019
+ this.#globalScope = [...this.appConfig.getGlobalInterceptors(), ...this.appConfig.getGlobalRequestInterceptors().map((wrapper) => wrapper.instance)].some((value) => this.enhancer(value, "scope"));
1020
+ for (const claim of this.#claims) {
1021
+ let plan;
1022
+ try {
1023
+ plan = claim.method ? this.planner.plan(claim.target, claim.method) : void 0;
1024
+ } catch {
1025
+ continue;
1026
+ }
1027
+ const classEntry = this.instances.list().find((entry) => entry.name === (Reflect.getMetadata(AUTH_INSTANCE_METADATA, claim.target) ?? "default"));
1028
+ const classForm = (Reflect.getMetadata(FORWARD_COOKIES_METADATA, claim.target) ?? classEntry?.options.cookies?.forwardDirectCalls ?? false) && Reflect.getMetadata(SKIP_ORIGIN_CHECK_METADATA, claim.target) !== true && classEntry?.options.originCheck?.mode !== "off";
1029
+ const accessCovered = plan ? (plan.access === "public" || plan.access === "inherit") && plan.originCheck !== "form" : Reflect.getMetadata(ACCESS_METADATA, claim.target) === "public" && !classForm;
1030
+ const localGuard = this.local(claim, "guard");
1031
+ const localScope = this.local(claim, "scope");
1032
+ const covered = accessCovered || claim.reach === "global" && (this.#globalGuard || localGuard) || claim.reach === "explicit" && localGuard && localScope;
1033
+ const declares = plan?.declares ?? ancestors(claim.target).some((target) => this.trigger(target));
1034
+ if (!covered && (claim.options.everyHandler || declares) && claim.options.coverage !== "off") {
1035
+ const message = `${claim.target.name}${claim.method ? `.${claim.method}` : ""} is not covered by the required auth enhancers.`;
1036
+ if (claim.options.coverage === "warn") this.warn(claim.options.code, `${message} ${claim.options.hint}`);
1037
+ else issues.push(new BetterAuthConfigurationError(claim.options.code, message, claim.options.hint));
1038
+ }
1039
+ }
1040
+ for (const site of sites) {
1041
+ const classClaims = this.#claims.filter((claim) => claim.target === site.target);
1042
+ if (classClaims.some((claim) => claim.method === void 0 || claim.method === site.method)) continue;
1043
+ if (this.trigger(site.handler) || !classClaims.length && ancestors(site.target).some((target) => this.trigger(target))) issues.push(new BetterAuthConfigurationError("UNGUARDED_AUTH_METADATA", `${site.target.name}.${site.method} declares auth metadata but no transport claims it. Registered transports: ${this.transports.list().map((value) => value.id).join(", ")}.`, "Register a transport that claims this handler."));
1044
+ }
1045
+ if (!this.#globalScope) {
1046
+ const affected = this.#claims.flatMap((claim) => {
1047
+ if (this.local(claim, "scope")) return [];
1048
+ const plan = this.#plans.find((site) => site.target === claim.target && site.method === claim.method)?.plan;
1049
+ if (plan) return plan.declares ? [{
1050
+ claim,
1051
+ forwarding: plan.forwardDirectCalls
1052
+ }] : [];
1053
+ if (claim.method !== void 0) return [];
1054
+ const entry = this.instances.list().find((value) => value.name === (Reflect.getMetadata(AUTH_INSTANCE_METADATA, claim.target) ?? "default"));
1055
+ const forwarding = Reflect.getMetadata(FORWARD_COOKIES_METADATA, claim.target) ?? entry?.options.cookies?.forwardDirectCalls ?? false;
1056
+ return forwarding || ancestors(claim.target).some((target) => this.trigger(target)) ? [{
1057
+ claim,
1058
+ forwarding: Boolean(forwarding)
1059
+ }] : [];
1060
+ });
1061
+ const seen = /* @__PURE__ */ new Map();
1062
+ const uncovered = affected.sort((left, right) => Number(right.forwarding) - Number(left.forwarding)).filter(({ claim }) => {
1063
+ const methods = seen.get(claim.target) ?? /* @__PURE__ */ new Set();
1064
+ seen.set(claim.target, methods);
1065
+ if (methods.has(claim.method)) return false;
1066
+ methods.add(claim.method);
1067
+ return true;
1068
+ }).slice(0, 20).map(({ claim }) => `${claim.target.name}.${claim.method ?? "*"}`);
1069
+ this.warn("W_NO_GLOBAL_SCOPE", `BetterAuthScopeInterceptor is not global; unscoped direct auth.api calls have no origin proof, refresh suppression or cookie bridge. Enable globalScope or apply @UseBetterAuth(). ${uncovered.join(", ")}`);
1070
+ }
1071
+ }
1072
+ entryChecks(entry, policies, issues) {
1073
+ const { name, context, options, sources, bridge } = entry;
1074
+ for (const unit of [...sources, ...policies]) for (const plugin of unit.requires?.plugins ?? []) if (!context.hasPlugin(plugin)) issues.push(new BetterAuthConfigurationError("PLUGIN_PREREQUISITE", `'${name}': '${unit.id}' requires the '${plugin}' plugin.`, `Add the '${plugin}' Better Auth plugin.`));
1075
+ const base = context.options.baseURL;
1076
+ const dynamic = !!base && typeof base === "object" && "allowedHosts" in base;
1077
+ if (dynamic && !("fallback" in base && base.fallback)) {
1078
+ const hostless = [
1079
+ ...sources,
1080
+ ...policies,
1081
+ ...this.transports.list()
1082
+ ].filter((unit) => unit.requires?.hostlessCalls);
1083
+ if (hostless.length) issues.push(new BetterAuthConfigurationError("DYNAMIC_BASE_URL_WITHOUT_FALLBACK", `'${name}': ${hostless.map((unit) => unit.id).join(", ")} make hostless calls.`, "Add baseURL.fallback to the dynamic allowedHosts configuration."));
1084
+ else this.warn("W_DYNAMIC_HOST", `'${name}': dynamic baseURL has no fallback; rejected hosts may make auth.handler throw.`);
1085
+ }
1086
+ const production = productionMode();
1087
+ if (production && !context.baseURL && !dynamic) {
1088
+ if (options.http?.allowRequestDerivedBaseURL) this.warn("W_REQUEST_DERIVED_BASE_URL", `'${name}': requests choose the origin of token links and trusted origins.`);
1089
+ else issues.push(new BetterAuthConfigurationError("UNSAFE_BASE_URL", `'${name}' has no safe baseURL in production.`, "Set baseURL / BETTER_AUTH_URL, or dynamic baseURL.allowedHosts."));
1090
+ }
1091
+ if (production && context.secret === "better-auth-secret-12345678901234567890") issues.push(new BetterAuthConfigurationError("DEFAULT_SECRET", `'${name}' uses Better Auth's public default signing secret.`, "Set BETTER_AUTH_SECRET or betterAuth({ secret })."));
1092
+ if (context.skipCSRFCheck || context.skipOriginCheck === true) {
1093
+ const explicit = context.options.advanced;
1094
+ const cause = `disableCSRFCheck=${String(explicit?.disableCSRFCheck)}, disableOriginCheck=${String(explicit?.disableOriginCheck)}; unset flags use Better Auth's import-time NODE_ENV/TEST defaults`;
1095
+ const exception = context.skipOriginCheck === true && explicit?.disableCSRFCheck === false ? "; disableCSRFCheck:false does not re-enable SDK origin checks; app-route checks remain on" : "";
1096
+ this.warn("W_ORIGIN_CHECK", `'${name}': resolved skipCSRFCheck=${context.skipCSRFCheck}, skipOriginCheck=${String(context.skipOriginCheck)} (${cause})${exception}.`);
1097
+ }
1098
+ if (Array.isArray(context.skipOriginCheck) && context.skipOriginCheck.length) this.logger.log(`I_AUTH_ORIGIN_PATH_SKIPS: '${name}': ${context.skipOriginCheck.join(", ")}; app-route checks are unchanged.`);
1099
+ if (production && options.originCheck?.mode === "off") this.warn("W_ORIGIN_CHECK", `'${name}': application origin checks are disabled.`);
1100
+ if (name === "default" && options.cookies?.forwardDirectCalls) this.warn("W_FORWARD_DIRECT_CALLS", "The default instance forwards same-credential direct-call cookies app-wide; every handler requires form-CSRF enforcement.");
1101
+ const proxy = this.mounts.adapter && this.mounts.platform?.proxyTrust?.(this.mounts.adapter);
1102
+ const ip = context.options.advanced?.ipAddress;
1103
+ const role = context.rateLimit.enabled ? "rate-limit buckets and session IP" : "session IP only (rate limiter disabled)";
1104
+ if (production && bridge.clientIpHeader && ip?.ipAddressHeaders?.[0] !== bridge.clientIpHeader) this.warn("W_CLIENT_IP", `'${name}': the bridge IP header is not first in effective ipAddressHeaders (${role}).`);
1105
+ if (production && !bridge.clientIpHeader && !ip?.ipAddressHeaders?.length && !ip?.trustedProxies?.length) this.warn("W_CLIENT_IP", `'${name}': no bridge IP header or SDK IP configuration (${role}).`);
1106
+ if (production && bridge.clientIpHeader && ip?.trustedProxies?.length && proxy && proxy.mode === "none") this.warn("W_CLIENT_IP", `'${name}': SDK trustedProxies rechecks the socket IP without platform proxy trust (${role}); configure platform trust or set clientIpHeader:false.`);
1107
+ if (production && proxy && proxy.mode === "all") this.warn("W_PROXY_TRUST_ALL", `'${name}': the platform trusts every proxy; clients can choose ${role}.`);
1108
+ if (production && !context.rateLimit.enabled && context.options.rateLimit?.enabled === void 0) {
1109
+ const contradiction = process.env.NODE_ENV === "production" ? " while process.env.NODE_ENV is production; Better Auth imported before that value existed" : "";
1110
+ this.warn("W_RATE_LIMIT_DISABLED", `'${name}': better-auth resolved rateLimit.enabled=false${contradiction}. Set NODE_ENV=production before auth.ts is imported, rateLimit.enabled:true, or explicitly disable it when an edge proxy limits traffic.`);
1111
+ }
1112
+ }
1113
+ cookies(issues) {
1114
+ const entries = this.instances.list();
1115
+ for (let i = 0; i < entries.length; i++) for (const right of entries.slice(i + 1)) {
1116
+ const left = entries[i];
1117
+ for (const a of Object.values(left.context.authCookies)) for (const b of Object.values(right.context.authCookies)) if (a.name === b.name && domainsOverlap(a.attributes.domain, b.attributes.domain) && pathsOverlap(a.attributes.path, b.attributes.path)) issues.push(new BetterAuthConfigurationError("COOKIE_NAME_COLLISION", `'${left.name}' and '${right.name}' both use cookie '${a.name}' on overlapping scopes.`, "Set advanced.cookiePrefix on one instance."));
1118
+ }
1119
+ }
1120
+ async validate(issues) {
1121
+ this.#plans.length = 0;
1122
+ this.#claims.length = 0;
1123
+ const sites = this.sites();
1124
+ const candidates = /* @__PURE__ */ new Map();
1125
+ const prepared = /* @__PURE__ */ new Set();
1126
+ const compiled = /* @__PURE__ */ new Map();
1127
+ const failures = /* @__PURE__ */ new Map();
1128
+ const reported = /* @__PURE__ */ new Set();
1129
+ const references = [];
1130
+ const report = (error, detail) => {
1131
+ if (!reported.has(error)) {
1132
+ reported.add(error);
1133
+ issues.push(configurationIssue(error, "UNRESOLVED_POLICY", detail));
1134
+ }
1135
+ };
1136
+ const candidate = (target, method) => {
1137
+ let methods = candidates.get(target);
1138
+ if (!methods) {
1139
+ methods = /* @__PURE__ */ new Map();
1140
+ candidates.set(target, methods);
1141
+ }
1142
+ const existing = methods.get(method);
1143
+ if (existing) return existing;
1144
+ const handler = Reflect.get(target.prototype ?? {}, method);
1145
+ if (typeof handler !== "function") throw new BetterAuthConfigurationError("UNKNOWN_HANDLER", `${target.name}.${method} is not a handler.`);
1146
+ const site = {
1147
+ target,
1148
+ method,
1149
+ handler
1150
+ };
1151
+ methods.set(method, site);
1152
+ return site;
1153
+ };
1154
+ const prepare = (site) => {
1155
+ if (failures.has(site)) throw failures.get(site);
1156
+ if (prepared.has(site)) return;
1157
+ try {
1158
+ const leaves = requirementLeaves(this.planner.requirementsOf(site.target, site.method));
1159
+ references.push(...leaves);
1160
+ for (const requirement of leaves) this.policies.resolve(requirement.policy);
1161
+ prepared.add(site);
1162
+ } catch (error) {
1163
+ failures.set(site, error);
1164
+ throw error;
1165
+ }
1166
+ };
1167
+ const planOf = (target, method) => {
1168
+ const site = candidate(target, method);
1169
+ prepare(site);
1170
+ const cached = compiled.get(site);
1171
+ if (cached) return cached;
1172
+ try {
1173
+ const plan = this.planner.plan(site.target, site.method);
1174
+ compiled.set(site, plan);
1175
+ this.#plans.push({
1176
+ ...site,
1177
+ plan
1178
+ });
1179
+ return plan;
1180
+ } catch (error) {
1181
+ failures.set(site, error);
1182
+ throw error;
1183
+ }
1184
+ };
1185
+ for (const site of sites) if (this.trigger(site.handler) || [
1186
+ ACCESS_METADATA,
1187
+ FRESHNESS_METADATA,
1188
+ SKIP_ORIGIN_CHECK_METADATA,
1189
+ SKIP_DEFAULT_REQUIREMENTS_METADATA
1190
+ ].some((key) => Reflect.hasOwnMetadata(key, site.handler))) {
1191
+ const selected = candidate(site.target, site.method);
1192
+ try {
1193
+ prepare(selected);
1194
+ } catch (error) {
1195
+ report(error, `Cannot prepare ${site.target.name}.${site.method}.`);
1196
+ }
1197
+ }
1198
+ let canariesPassed = false;
1199
+ try {
1200
+ this.canaries();
1201
+ canariesPassed = true;
1202
+ } catch (error) {
1203
+ issues.push(configurationIssue(error, "NEST_METADATA_KEY_CHANGED", "Coverage metadata canaries failed."));
1204
+ }
1205
+ if (canariesPassed) for (const transport of this.transports.list()) try {
1206
+ await transport.validate?.({
1207
+ discovery: this.discovery,
1208
+ reflector: this.reflector,
1209
+ moduleRef: this.moduleRef,
1210
+ hasHttpAdapter: this.mounts.adapter !== null,
1211
+ planOf,
1212
+ claim: (target, method, reach, options) => {
1213
+ this.#claims.push({
1214
+ transport,
1215
+ target,
1216
+ method,
1217
+ reach,
1218
+ options
1219
+ });
1220
+ if (method !== void 0) candidate(target, method);
1221
+ },
1222
+ logger: this.logger
1223
+ });
1224
+ } catch (error) {
1225
+ if (!reported.has(error)) {
1226
+ reported.add(error);
1227
+ issues.push(configurationIssue(error, "TRANSPORT_VALIDATION_FAILED", `Transport '${transport.id}' failed validation.`));
1228
+ }
1229
+ }
1230
+ for (const methods of candidates.values()) for (const site of methods.values()) try {
1231
+ prepare(site);
1232
+ } catch (error) {
1233
+ report(error, `Cannot prepare ${site.target.name}.${site.method}.`);
1234
+ }
1235
+ this.singletonChecks(issues, references);
1236
+ for (const methods of candidates.values()) for (const site of methods.values()) try {
1237
+ planOf(site.target, site.method);
1238
+ } catch (error) {
1239
+ report(error, `Cannot compile ${site.target.name}.${site.method}.`);
1240
+ }
1241
+ for (const site of this.#plans) try {
1242
+ const entry = this.instances.get(site.plan.instance);
1243
+ for (const requirement of requirementLeaves(site.plan.requirements)) await this.policies.resolve(requirement.policy).validate?.(requirement.params, {
1244
+ auth: entry.handle,
1245
+ context: entry.context,
1246
+ site: site.plan.site
1247
+ });
1248
+ } catch (error) {
1249
+ report(error, `Cannot validate ${site.target.name}.${site.method}.`);
1250
+ }
1251
+ if (canariesPassed) {
1252
+ this.coverage(sites, issues);
1253
+ const applicationRoutes = [];
1254
+ const globalPrefix = this.appConfig.getGlobalPrefix();
1255
+ const globalPrefixExclusions = this.appConfig.getGlobalPrefixOptions().exclude;
1256
+ const versioningOptions = this.appConfig.getVersioning();
1257
+ const modules = this.moduleRef.get(ModulesContainer, { strict: false });
1258
+ for (const wrapper of this.discovery.getControllers()) {
1259
+ const target = wrapper.metatype;
1260
+ if (!target?.prototype) continue;
1261
+ const moduleType = wrapper.host?.metatype;
1262
+ const modulePath = moduleType ? Reflect.getMetadata(`__module_path__${modules.applicationId}`, moduleType) ?? Reflect.getMetadata("__module_path__", moduleType) : void 0;
1263
+ const host = Reflect.getMetadata("host", target);
1264
+ const controllerVersion = versioningOptions ? Reflect.getMetadata("__version__", target) ?? versioningOptions.defaultVersion : void 0;
1265
+ const rawPrefixes = Reflect.getMetadata("path", target);
1266
+ const prefixes = Array.isArray(rawPrefixes) ? rawPrefixes : [rawPrefixes ?? ""];
1267
+ for (const method of this.scanner.getAllMethodNames(target.prototype)) {
1268
+ const handler = Reflect.get(target.prototype, method);
1269
+ const rawPaths = Reflect.getMetadata("path", handler);
1270
+ const requestMethod = Reflect.getMetadata("method", handler);
1271
+ if (rawPaths === void 0 || requestMethod === void 0) continue;
1272
+ const methodVersion = Reflect.getMetadata("__version__", handler);
1273
+ const effectiveVersion = methodVersion ?? controllerVersion;
1274
+ const conditions = [];
1275
+ if (host !== void 0) conditions.push("host");
1276
+ if (versioningOptions && versioningOptions.type !== VersioningType.URI && effectiveVersion !== void 0 && effectiveVersion !== VERSION_NEUTRAL) conditions.push("version");
1277
+ for (const prefix of prefixes) for (const path of Array.isArray(rawPaths) ? rawPaths : [rawPaths]) {
1278
+ const resolvedPaths = composeRoutePaths({
1279
+ ctrlPath: prefix,
1280
+ methodPath: path,
1281
+ modulePath,
1282
+ globalPrefix,
1283
+ controllerVersion,
1284
+ methodVersion,
1285
+ versioningOptions
1286
+ }, requestMethod, globalPrefixExclusions).map((route) => this.mounts.normalizeApplicationRoute(route));
1287
+ const source = `${target.name}.${method}`;
1288
+ applicationRoutes.push(Object.freeze({
1289
+ method: RequestMethod[requestMethod] ?? String(requestMethod),
1290
+ paths: Object.freeze([...resolvedPaths]),
1291
+ conditions: Object.freeze([...conditions]),
1292
+ source
1293
+ }));
1294
+ for (const route of resolvedPaths) {
1295
+ const binding = this.mounts.bindings().find((candidate) => routeMayWarnAboutMount(route, candidate.basePath));
1296
+ if (!binding) continue;
1297
+ this.warn("W_ROUTE_SHADOWS_AUTH", `${source} at '${route}' may shadow auth mount '${binding.basePath}'.`);
1298
+ }
1299
+ }
1300
+ }
1301
+ }
1302
+ this.mounts.registerApplicationRoutes(Object.freeze(applicationRoutes), issues);
1303
+ }
1304
+ this.cookies(issues);
1305
+ for (const entry of this.instances.list()) {
1306
+ const plans = this.#plans.filter((site) => site.plan.instance === entry.name);
1307
+ const policies = [...new Set(plans.flatMap((site) => requirementLeaves(site.plan.requirements).map((requirement) => this.policies.resolve(requirement.policy))))];
1308
+ this.entryChecks(entry, policies, issues);
1309
+ const handlers = plans.map((site) => {
1310
+ const claims = this.#claims.filter((claim) => claim.target === site.target && (claim.method === void 0 || claim.method === site.method));
1311
+ return {
1312
+ plan: site.plan,
1313
+ transports: [...new Set(claims.map((claim) => claim.transport.id))],
1314
+ inputs: [...new Set(claims.flatMap((claim) => claim.options.inputs ?? []))]
1315
+ };
1316
+ });
1317
+ const advice = {
1318
+ instance: entry.name,
1319
+ auth: entry.handle,
1320
+ context: entry.context,
1321
+ production: productionMode(),
1322
+ hasHttpAdapter: this.mounts.adapter !== null,
1323
+ originCheck: {
1324
+ mode: entry.options.originCheck?.mode ?? "cookie",
1325
+ missingOrigin: entry.options.originCheck?.missingOrigin ?? "reject"
1326
+ },
1327
+ handlers,
1328
+ transports: this.transports.list().map((transport) => ({ id: transport.id })),
1329
+ sources: entry.sources,
1330
+ policies
1331
+ };
1332
+ for (const unit of [
1333
+ ...entry.sources,
1334
+ ...policies,
1335
+ ...this.transports.list(),
1336
+ ...this.mounts.platform ? [this.mounts.platform] : []
1337
+ ]) try {
1338
+ for (const item of await unit.advise?.(advice) ?? []) {
1339
+ const line = `${item.code}: '${entry.name}' ${unit.id}: ${item.message}${item.hint ? ` ${item.hint}` : ""}`;
1340
+ if (item.level === "warn") this.logger.warn(line);
1341
+ else this.logger.log(line);
1342
+ }
1343
+ } catch (error) {
1344
+ issues.push(configurationIssue(error, "EXTENSION_ADVICE_FAILED", `Advice from '${unit.id}' failed.`));
1345
+ }
1346
+ }
1347
+ }
1348
+ summary(hooks) {
1349
+ for (const entry of this.instances.list()) {
1350
+ if (entry.options.logSummary === false) continue;
1351
+ const mount = this.mounts.binding(entry.name);
1352
+ const plans = this.#plans.filter((site) => site.plan.instance === entry.name).map((site) => site.plan);
1353
+ const rate = entry.context.rateLimit.enabled ? "on" : entry.context.options.rateLimit?.enabled === false ? "off (rateLimit.enabled:false)" : process.env.NODE_ENV === "production" ? "off (Better Auth saw no NODE_ENV=production at import time; see W_RATE_LIMIT_DISABLED)" : `off (NODE_ENV ${process.env.NODE_ENV ?? "unset"}; Better Auth enables it for NODE_ENV=production)`;
1354
+ const table = hooks.get(entry.name);
1355
+ const hookSummary = table ? `${table.before.length} before, ${table.after.length} after, ${Object.values(table.database).reduce((count, value) => count + value.before.length + value.after.length, 0)} database` : "none";
1356
+ const proxy = this.mounts.adapter ? this.mounts.platform?.proxyTrust?.(this.mounts.adapter) : void 0;
1357
+ const defaults = `access: ${entry.options.defaultAccess ?? "authenticated"}; origin: ${entry.options.originCheck?.mode ?? "cookie"}/${entry.options.originCheck?.missingOrigin ?? "reject"}; direct-call cookies: ${entry.options.cookies?.forwardDirectCalls ? "same-credential" : "opt-in"}`;
1358
+ const guardOrder = this.appConfig.getGlobalGuards().map((guard) => guard.constructor.name).join(" → ");
1359
+ this.logger.log(`'${entry.name}': ${mount ? `mounted at ${mount.basePath} (${mount.bodyLimit} bytes)` : "not mounted"}; platform: ${this.mounts.platform?.id ?? "none"} (trust proxy: ${proxy?.mode ?? "unknown"}${proxy?.detail ? ` ${proxy.detail}` : ""}); outside global prefix '${this.appConfig.getGlobalPrefix()}'; hooks: ${hookSummary}; defaults: ${defaults}; transports: ${this.transports.list().map((unit) => unit.id).join(", ")}; principals: ${entry.sources.map((unit) => `${unit.id} (${unit.acceptance ?? "explicit"})`).join(" → ")}; handlers: ${plans.length} (${plans.filter((plan) => plan.access === "public").length} public, ${plans.filter((plan) => plan.access === "optional").length} optional, ${plans.filter((plan) => plan.access === "inherit").length} inherit, ${plans.filter((plan) => plan.skipsDefaultRequirements).length} skip defaults); environment: ${productionMode() ? "production" : "development"} (NODE_ENV ${process.env.NODE_ENV ?? "unset"}); rate limit: ${rate}; scope: ${this.#globalScope ? "global" : "off — see W_NO_GLOBAL_SCOPE"}; global guards: ${guardOrder}`);
1360
+ }
1361
+ }
1362
+ };
1363
+ BootValidator = __decorate([
1364
+ __decorateParam(0, Inject(DiscoveryService)),
1365
+ __decorateParam(1, Inject(MetadataScanner)),
1366
+ __decorateParam(2, Inject(Reflector)),
1367
+ __decorateParam(3, Inject(ModuleRef)),
1368
+ __decorateParam(4, Inject(ApplicationConfig)),
1369
+ __decorateParam(5, Inject(INSTANCE_REGISTRY)),
1370
+ __decorateParam(6, Inject(ROUTE_PLANNER)),
1371
+ __decorateParam(7, Inject(POLICY_RESOLVER)),
1372
+ __decorateParam(8, Inject(TRANSPORT_REGISTRY)),
1373
+ __decorateParam(9, Inject(MOUNT_COORDINATOR))
1374
+ ], BootValidator);
1375
+ //#endregion
1376
+ //#region src/hook-binder.ts
1377
+ const DATABASE_TARGETS = [
1378
+ "user",
1379
+ "session",
1380
+ "account",
1381
+ "verification"
1382
+ ].flatMap((model) => [
1383
+ "create",
1384
+ "update",
1385
+ "delete"
1386
+ ].map((operation) => `${model}.${operation}`));
1387
+ function emptyHookTables() {
1388
+ return {
1389
+ before: [],
1390
+ after: [],
1391
+ database: Object.fromEntries(DATABASE_TARGETS.map((target) => [target, {
1392
+ before: [],
1393
+ after: []
1394
+ }]))
1395
+ };
1396
+ }
1397
+ function distance$1(a, b) {
1398
+ let row = Array.from({ length: b.length + 1 }, (_, i) => i);
1399
+ for (let i = 1; i <= a.length; i++) {
1400
+ const next = [i];
1401
+ for (let j = 1; j <= b.length; j++) next[j] = Math.min(next[j - 1] + 1, row[j] + 1, row[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
1402
+ row = next;
1403
+ }
1404
+ return row[b.length];
1405
+ }
1406
+ let HookBinder = class HookBinder {
1407
+ discovery;
1408
+ scanner;
1409
+ logger = new Logger("BetterAuth");
1410
+ constructor(discovery, scanner) {
1411
+ this.discovery = discovery;
1412
+ this.scanner = scanner;
1413
+ }
1414
+ discover(registry, issues) {
1415
+ const endpoint = /* @__PURE__ */ new Map();
1416
+ const database = /* @__PURE__ */ new Map();
1417
+ const registrations = new Set(registry.registrations().map((item) => item.name));
1418
+ const discovered = /* @__PURE__ */ new Map();
1419
+ for (const wrapper of this.discovery.getProviders()) {
1420
+ if (wrapper.isAlias || !wrapper.instance || typeof wrapper.instance !== "object" && typeof wrapper.instance !== "function") continue;
1421
+ const instance = wrapper.instance;
1422
+ if (!Object.getPrototypeOf(instance)) continue;
1423
+ const methods = new Set(this.scanner.getAllMethodNames(instance));
1424
+ for (let current = instance; current && current !== Object.prototype; current = Object.getPrototypeOf(current)) for (const key of Object.getOwnPropertySymbols(current)) if (typeof Object.getOwnPropertyDescriptor(current, key)?.value === "function") methods.add(key);
1425
+ let found = false;
1426
+ for (const method of methods) {
1427
+ const handler = Reflect.get(instance, method);
1428
+ if (typeof handler !== "function") continue;
1429
+ const metadata = readHookMetadata(handler);
1430
+ if (!metadata.hooks.length && !metadata.databaseHooks.length) continue;
1431
+ found = true;
1432
+ if (!isSingletonDependencyTree(wrapper)) {
1433
+ issues.push(new BetterAuthConfigurationError("NON_SINGLETON_EXTENSION", `Hook provider '${wrapper.name}' is not a static singleton.`, "Remove request/transient dependencies from hook providers."));
1434
+ continue;
1435
+ }
1436
+ for (const declaration of metadata.hooks) {
1437
+ const name = declaration.options.instance ?? "default";
1438
+ if (!registrations.has(name)) {
1439
+ issues.push(new BetterAuthConfigurationError("UNKNOWN_INSTANCE", `Hook '${wrapper.name}.${String(method)}' names unknown instance '${name}'.`));
1440
+ continue;
1441
+ }
1442
+ const entry = registry.list().find((value) => value.name === name);
1443
+ if (!entry) continue;
1444
+ const paths = Object.values(entry.instance.api).flatMap((api) => typeof api === "function" && typeof Reflect.get(api, "path") === "string" ? [Reflect.get(api, "path")] : []);
1445
+ const { match, options } = declaration;
1446
+ const patterns = typeof match === "string" ? [match] : Array.isArray(match) ? match : [];
1447
+ for (const pattern of patterns) if (!paths.includes(pattern)) {
1448
+ const closest = [...paths].sort((a, b) => distance$1(a, pattern) - distance$1(b, pattern))[0];
1449
+ issues.push(new BetterAuthConfigurationError("UNKNOWN_HOOK_PATH", `Hook '${wrapper.name}.${String(method)}' has unknown path '${pattern}'.`, closest ? `Did you mean '${closest}'?` : "Use an endpoint path exposed by this auth instance."));
1450
+ }
1451
+ const hook = {
1452
+ matches(ctx, scope) {
1453
+ const router = Reflect.get(ctx, "_flag") === "router";
1454
+ if (options.calls === "http" && !router || options.calls === "server" && router || options.skipInternal && scope?.internal) return false;
1455
+ if (match === void 0) return true;
1456
+ if (typeof match === "function") return match(ctx);
1457
+ return typeof match === "string" ? ctx.path === match : match.includes(ctx.path ?? "");
1458
+ },
1459
+ async run(ctx) {
1460
+ return handler.call(instance, ctx);
1461
+ }
1462
+ };
1463
+ const list = endpoint.get(name) ?? [];
1464
+ list.push({
1465
+ phase: declaration.phase,
1466
+ order: options.order ?? 0,
1467
+ hook
1468
+ });
1469
+ endpoint.set(name, list);
1470
+ }
1471
+ for (const declaration of metadata.databaseHooks) {
1472
+ const name = declaration.options.instance ?? "default";
1473
+ if (!registrations.has(name)) {
1474
+ issues.push(new BetterAuthConfigurationError("UNKNOWN_INSTANCE", `Database hook '${wrapper.name}.${String(method)}' names unknown instance '${name}'.`));
1475
+ continue;
1476
+ }
1477
+ const list = database.get(name) ?? [];
1478
+ list.push({
1479
+ phase: declaration.phase,
1480
+ target: declaration.target,
1481
+ order: declaration.options.order ?? 0,
1482
+ hook: handler.bind(instance)
1483
+ });
1484
+ database.set(name, list);
1485
+ }
1486
+ }
1487
+ if (found) {
1488
+ const owner = wrapper.host?.name ?? "unknown module";
1489
+ const previous = discovered.get(wrapper.metatype);
1490
+ if (previous) this.logger.warn(`W_DUPLICATE_HOOK_PROVIDER: '${wrapper.name}' is provided in '${previous}' and '${owner}'; both registrations run.`);
1491
+ discovered.set(wrapper.metatype, owner);
1492
+ }
1493
+ }
1494
+ const tables = /* @__PURE__ */ new Map();
1495
+ for (const name of registrations) {
1496
+ const hooks = (endpoint.get(name) ?? []).sort((a, b) => a.order - b.order);
1497
+ const db = (database.get(name) ?? []).sort((a, b) => a.order - b.order);
1498
+ tables.set(name, {
1499
+ before: hooks.filter((value) => value.phase === "before").map((value) => value.hook),
1500
+ after: hooks.filter((value) => value.phase === "after").map((value) => value.hook),
1501
+ database: Object.fromEntries(DATABASE_TARGETS.map((target) => [target, {
1502
+ before: db.filter((value) => value.target === target && value.phase === "before").map((value) => value.hook),
1503
+ after: db.filter((value) => value.target === target && value.phase === "after").map((value) => value.hook)
1504
+ }]))
1505
+ });
1506
+ }
1507
+ return tables;
1508
+ }
1509
+ };
1510
+ HookBinder = __decorate([
1511
+ __decorateParam(0, Inject(DiscoveryService)),
1512
+ __decorateParam(1, Inject(MetadataScanner)),
1513
+ __decorateMetadata("design:paramtypes", [typeof DiscoveryService === "undefined" ? Object : DiscoveryService, typeof MetadataScanner === "undefined" ? Object : MetadataScanner])
1514
+ ], HookBinder);
1515
+ //#endregion
1516
+ //#region src/auth-exchange.ts
1517
+ const HOP_HEADERS = [
1518
+ "connection",
1519
+ "keep-alive",
1520
+ "proxy-connection",
1521
+ "transfer-encoding",
1522
+ "te",
1523
+ "trailer",
1524
+ "upgrade",
1525
+ "proxy-authenticate",
1526
+ "proxy-authorization"
1527
+ ];
1528
+ function sdkAPIError(error) {
1529
+ try {
1530
+ return isAPIError(error);
1531
+ } catch {
1532
+ return false;
1533
+ }
1534
+ }
1535
+ function production() {
1536
+ return ![
1537
+ "development",
1538
+ "dev",
1539
+ "test"
1540
+ ].includes(process.env.NODE_ENV ?? "");
1541
+ }
1542
+ function distance(left, right) {
1543
+ let previous = Array.from({ length: right.length + 1 }, (_, index) => index);
1544
+ for (let row = 0; row < left.length; row++) {
1545
+ const current = [row + 1];
1546
+ for (let column = 0; column < right.length; column++) current.push(Math.min(current[column] + 1, previous[column + 1] + 1, previous[column] + Number(left[row] !== right[column])));
1547
+ previous = current;
1548
+ }
1549
+ return previous[right.length];
1550
+ }
1551
+ /** App-owned exchange; all platform request and response operations stay outside core. */
1552
+ var AuthExchange = class {
1553
+ scope;
1554
+ #warnedProxy = false;
1555
+ constructor(scope) {
1556
+ this.scope = scope;
1557
+ }
1558
+ create(entry, init) {
1559
+ const paths = [...new Set(Object.values(entry.instance.api).flatMap((endpoint) => {
1560
+ const path = Reflect.get(endpoint, "path");
1561
+ return typeof path === "string" ? [path] : [];
1562
+ }))];
1563
+ const seen = /* @__PURE__ */ new Set();
1564
+ const binding = Object.freeze({
1565
+ instance: entry.name,
1566
+ basePath: init.basePath,
1567
+ bodyLimit: init.bodyLimit,
1568
+ staticOrigin: init.staticOrigin,
1569
+ matches: (pathname) => init.basePath === "/" ? pathname.startsWith("/") : pathname === init.basePath || pathname.startsWith(`${init.basePath}/`),
1570
+ payloadTooLarge: () => Response.json({
1571
+ code: "PAYLOAD_TOO_LARGE",
1572
+ message: "Request body exceeds the configured limit"
1573
+ }, { status: 413 }),
1574
+ handle: async (inbound) => {
1575
+ const response = await this.handle(entry, init, binding, inbound);
1576
+ if (response.status === 404 && (entry.options.http?.diagnostics ?? !production())) {
1577
+ const pathname = new URL(inbound.url).pathname;
1578
+ const key = `${inbound.method.toUpperCase()} ${pathname}`;
1579
+ if (binding.matches(pathname) && !seen.has(key) && seen.size < 100) {
1580
+ seen.add(key);
1581
+ const requested = pathname.slice(init.basePath === "/" ? 0 : init.basePath.length);
1582
+ const closest = paths.reduce((best, path) => best === void 0 || distance(requested, path) < distance(requested, best) ? path : best, void 0);
1583
+ const suggestion = closest ? ` Did you mean ${init.basePath === "/" ? "" : init.basePath}${closest}?` : "";
1584
+ init.logger.warn(`better-auth has no route for ${key} (mounted at ${init.basePath}).${suggestion}`);
1585
+ }
1586
+ }
1587
+ return response;
1588
+ }
1589
+ });
1590
+ return binding;
1591
+ }
1592
+ async handle(entry, init, binding, inbound) {
1593
+ const redactors = [new ErrorRedactor({
1594
+ secrets: [entry.context.secret],
1595
+ headers: inbound.headers,
1596
+ credentialHeaders: entry.credentialHeaders
1597
+ })];
1598
+ try {
1599
+ const method = inbound.method.toUpperCase();
1600
+ let body = null;
1601
+ if (method !== "GET" && method !== "HEAD") {
1602
+ if (inbound.body instanceof Uint8Array) {
1603
+ if (inbound.body.byteLength > init.bodyLimit) return binding.payloadTooLarge();
1604
+ body = inbound.body;
1605
+ } else if (inbound.body !== null) {
1606
+ const bounded = await readBoundedBody(inbound.body, { limit: init.bodyLimit });
1607
+ if (!bounded.ok) {
1608
+ if (bounded.reason === "too-large") return binding.payloadTooLarge();
1609
+ throw new Error("Authentication request body was aborted");
1610
+ }
1611
+ body = bounded.bytes;
1612
+ }
1613
+ }
1614
+ let url;
1615
+ try {
1616
+ url = new URL(inbound.url);
1617
+ if (!["http:", "https:"].includes(url.protocol) || url.username || url.password) throw new TypeError("Invalid request URL");
1618
+ if (init.staticOrigin) url = new URL(`${init.staticOrigin}${url.pathname}${url.search}`);
1619
+ } catch {
1620
+ return Response.json({
1621
+ code: "INVALID_REQUEST_URL",
1622
+ message: "Invalid request URL"
1623
+ }, { status: 400 });
1624
+ }
1625
+ const headers = new Headers(inbound.headers);
1626
+ const nominated = (headers.get("connection") ?? "").split(",").map((name) => name.trim()).filter(Boolean);
1627
+ for (const name of [...HOP_HEADERS, ...nominated]) headers.delete(name);
1628
+ const ipHeader = entry.bridge.clientIpHeader;
1629
+ for (const name of [...headers.keys()]) if (name.startsWith("x-nestjs-kit-better-auth-ip-") || name === ipHeader?.toLowerCase()) headers.delete(name);
1630
+ if (ipHeader && inbound.clientIp !== null) headers.set(ipHeader, inbound.clientIp);
1631
+ this.warnProxy(entry, init, inbound);
1632
+ const request = new Request(url, {
1633
+ method,
1634
+ headers,
1635
+ body: body === null ? null : Uint8Array.from(body),
1636
+ signal: inbound.signal
1637
+ });
1638
+ const interceptors = entry.options.http?.around ?? [];
1639
+ const dispatch = (index, current) => {
1640
+ redactors.push(new ErrorRedactor({
1641
+ headers: current.headers,
1642
+ credentialHeaders: entry.credentialHeaders
1643
+ }));
1644
+ const interceptor = interceptors[index];
1645
+ return interceptor ? interceptor({
1646
+ request: current,
1647
+ platformRequest: inbound.platformRequest,
1648
+ instance: entry.name
1649
+ }, (replacement = current) => dispatch(index + 1, replacement)) : this.scope.exit(() => entry.instance.handler(current));
1650
+ };
1651
+ return await dispatch(0, request);
1652
+ } catch (error) {
1653
+ if (sdkAPIError(error)) {
1654
+ if (entry.context.options.onAPIError?.throw) throw error;
1655
+ return Response.json(error.body ?? { message: error.message }, {
1656
+ status: error.statusCode,
1657
+ headers: getAPIErrorHeaders(error)
1658
+ });
1659
+ }
1660
+ let cause = error;
1661
+ for (const redactor of redactors) cause = redactor.redact(cause);
1662
+ const wrapped = createInfrastructureError(cause);
1663
+ if (entry.options.errors?.exposeRawCause) {
1664
+ const raw = createInfrastructureError(error, { exposeRawCause: true });
1665
+ Object.defineProperty(raw, "cause", { value: wrapped.cause });
1666
+ throw raw;
1667
+ }
1668
+ throw wrapped;
1669
+ }
1670
+ }
1671
+ warnProxy(entry, init, inbound) {
1672
+ if (this.#warnedProxy || !production() || init.proxyTrust?.mode !== "none" || ![
1673
+ "x-forwarded-for",
1674
+ "forwarded",
1675
+ "x-real-ip"
1676
+ ].some((name) => inbound.headers.has(name))) return;
1677
+ this.#warnedProxy = true;
1678
+ const ip = entry.context.options.advanced?.ipAddress;
1679
+ const bridgeHeader = entry.bridge.clientIpHeader;
1680
+ const bridgeHeaderFirst = bridgeHeader !== null && ip?.ipAddressHeaders?.[0]?.toLowerCase() === bridgeHeader.toLowerCase();
1681
+ let effect;
1682
+ if (!entry.context.rateLimit.enabled) {
1683
+ if (bridgeHeader === null) effect = "Rate limiting is disabled; the bridge does not insert this socket IP, so better-auth's configured IP resolution determines the session IP.";
1684
+ else if (!bridgeHeaderFirst) effect = "Rate limiting is disabled; a configured better-auth IP header precedes the bridge header, so this socket IP is not necessarily recorded on sessions.";
1685
+ else effect = "Rate limiting is disabled; this IP is recorded on sessions only.";
1686
+ } else if (bridgeHeader === null) effect = "The bridge does not insert this socket IP; better-auth's configured IP resolution determines the rate-limit key.";
1687
+ else if (!bridgeHeaderFirst) effect = "A configured better-auth IP header precedes the bridge header, so this socket IP is not necessarily the rate-limit key.";
1688
+ else if (ip?.trustedProxies?.length) effect = "If better-auth trustedProxies covers this address, no client IP remains and all clients share one rate-limit bucket. Otherwise this socket IP is the rate-limit key.";
1689
+ else effect = "This socket IP is the rate-limit key, so clients behind the proxy share its bucket.";
1690
+ init.logger.warn(`W_PROXY_UNTRUSTED: platform resolved socket IP ${inbound.clientIp ?? "unknown"}. ${effect} Configure platform proxy trust (${init.proxyTrust.detail}).`);
1691
+ }
1692
+ };
1693
+ /** Credentialed CORS for the auth mount, using the initialized SDK trusted origins. */
1694
+ function betterAuthCorsOrigin(auth, options) {
1695
+ const context = async () => await auth.$context;
1696
+ const trusted = new TrustedOrigins(context);
1697
+ return (origin, callback) => {
1698
+ (async () => {
1699
+ if (typeof (await context()).options.trustedOrigins === "function") throw new BetterAuthConfigurationError("DYNAMIC_CORS_ORIGIN", "Function-valued trustedOrigins needs a request that a CORS origin callback cannot provide", "Supply a request-aware CORS origin function instead of betterAuthCorsOrigin.");
1700
+ if (!origin) return false;
1701
+ if (options?.allowPatterns) return trusted.isTrusted(origin);
1702
+ return (await trusted.list()).some((candidate) => {
1703
+ try {
1704
+ const parsed = new URL(candidate);
1705
+ return (parsed.protocol === "http:" || parsed.protocol === "https:") && !candidate.includes("*") && candidate === parsed.origin && origin === candidate;
1706
+ } catch {
1707
+ return false;
1708
+ }
1709
+ });
1710
+ })().then((allow) => callback(null, allow), (error) => callback(error instanceof Error ? error : /* @__PURE__ */ new Error("Failed to resolve trusted CORS origins")));
1711
+ };
1712
+ }
1713
+ //#endregion
1714
+ //#region src/mount-coordinator.ts
1715
+ const MOUNTED = Symbol.for("@nestjs-kit/better-auth:mounted");
1716
+ function parseBodyLimit(input) {
1717
+ if (input === void 0) return 1048576;
1718
+ const match = typeof input === "string" ? /^(\d+(?:\.\d+)?)(b|kb|mb)$/i.exec(input) : null;
1719
+ const value = typeof input === "number" ? input : match ? Number(match[1]) * ({
1720
+ b: 1,
1721
+ kb: 1024,
1722
+ mb: 1048576
1723
+ }[match[2].toLowerCase()] ?? 0) : NaN;
1724
+ if (!Number.isSafeInteger(value) || value < 0) throw new BetterAuthConfigurationError("INVALID_BODY_LIMIT", `Invalid auth body limit '${input}'.`, "Use a nonnegative byte count or a value such as '1mb'.");
1725
+ return value;
1726
+ }
1727
+ let MountCoordinator = class MountCoordinator {
1728
+ host;
1729
+ registry;
1730
+ logger = new Logger("BetterAuth");
1731
+ #exchange;
1732
+ #bindings = /* @__PURE__ */ new Map();
1733
+ #platforms;
1734
+ #prepared = null;
1735
+ #selected;
1736
+ #preparationErrors = [];
1737
+ constructor(host, registry, scope) {
1738
+ this.host = host;
1739
+ this.registry = registry;
1740
+ this.#exchange = new AuthExchange(scope);
1741
+ host.init$.subscribe(() => this.tryPrepare(false));
1742
+ }
1743
+ registerPlatforms(platforms) {
1744
+ this.#platforms = platforms;
1745
+ this.tryPrepare(false);
1746
+ }
1747
+ get platform() {
1748
+ return this.#selected;
1749
+ }
1750
+ get adapter() {
1751
+ return this.host.httpAdapter ?? null;
1752
+ }
1753
+ tryPrepare(atInit) {
1754
+ const adapter = this.adapter;
1755
+ if (!adapter || !this.#platforms || this.#prepared === adapter) return;
1756
+ try {
1757
+ if (this.registry.state !== "new" && this.registry.adapter !== adapter) throw new BetterAuthConfigurationError("LATE_HTTP_ADAPTER", "The HTTP adapter changed while the application is active.", "Close the first application before starting another.");
1758
+ const supported = this.#platforms.filter((platform) => platform.supports(adapter));
1759
+ if (supported.length !== 1) throw new BetterAuthConfigurationError(supported.length ? "AMBIGUOUS_PLATFORM" : "NO_PLATFORM", `Expected exactly one supporting HTTP platform; found ${supported.length}.`, "Register the platform for your HTTP adapter on the default instance.");
1760
+ const platform = supported[0];
1761
+ if (atInit && this.#prepared !== null && !platform.capabilities?.prepareAtInit) throw new BetterAuthConfigurationError("APP_ADAPTER_CHANGED", "The application uses a different HTTP adapter after preparation.", "Compile a new TestingModule for each application (or use NestFactory).");
1762
+ platform.prepare?.({
1763
+ adapter,
1764
+ logger: this.logger,
1765
+ route: (pathname) => this.route(pathname)
1766
+ });
1767
+ this.#selected = platform;
1768
+ this.#prepared = adapter;
1769
+ } catch (error) {
1770
+ this.#preparationErrors.push(configurationIssue(error, "PLATFORM_PREPARATION_FAILED", "HTTP platform preparation failed."));
1771
+ }
1772
+ }
1773
+ prepareAtInit(issues) {
1774
+ this.tryPrepare(true);
1775
+ issues.push(...this.#preparationErrors);
1776
+ this.#preparationErrors = [];
1777
+ if (this.adapter && this.#selected) try {
1778
+ this.#selected.validate?.(this.adapter);
1779
+ } catch (error) {
1780
+ issues.push(configurationIssue(error, "PLATFORM_VALIDATION_FAILED", "HTTP platform validation failed."));
1781
+ }
1782
+ }
1783
+ resolve(issues) {
1784
+ this.#bindings.clear();
1785
+ const adapter = this.adapter;
1786
+ if (!adapter || !this.#selected) return;
1787
+ const server = adapter.getInstance();
1788
+ const mounted = Reflect.get(server, MOUNTED);
1789
+ for (const entry of this.registry.list()) {
1790
+ if (entry.options.http?.mount === false) continue;
1791
+ try {
1792
+ const raw = entry.context.baseURL ? new URL(entry.context.baseURL).pathname : entry.context.options.basePath || "/api/auth";
1793
+ const basePath = raw.length > 1 ? raw.replace(/\/+$/, "") : raw;
1794
+ if (!basePath.startsWith("/") || basePath === "/" && !entry.options.http?.allowRootMount) throw new BetterAuthConfigurationError("INVALID_MOUNT_PATH", `Invalid auth mount path '${basePath}'.`, "Use an absolute path; opt into root mounting with http.allowRootMount.");
1795
+ if (mounted?.has(basePath)) throw new BetterAuthConfigurationError("DUPLICATE_MOUNT", `Auth path '${basePath}' is already mounted on this server.`, "Create a fresh application and adapter.");
1796
+ for (const existing of this.#bindings.values()) if (basePath === "/" || existing.basePath === "/" || existing.matches(basePath) || basePath.startsWith(`${existing.basePath}/`) || existing.basePath.startsWith(`${basePath}/`)) throw new BetterAuthConfigurationError("OVERLAPPING_MOUNTS", `Mounts '${existing.basePath}' and '${basePath}' overlap.`, "Use separate basePath values for named instances.");
1797
+ this.#bindings.set(entry.name, this.#exchange.create(entry, {
1798
+ basePath,
1799
+ bodyLimit: parseBodyLimit(entry.options.http?.bodyLimit),
1800
+ staticOrigin: entry.context.baseURL ? new URL(entry.context.baseURL).origin : void 0,
1801
+ logger: this.logger,
1802
+ proxyTrust: this.#selected.proxyTrust?.(adapter)
1803
+ }));
1804
+ } catch (error) {
1805
+ issues.push(configurationIssue(error, "INVALID_MOUNT_PATH", `Cannot resolve mount for '${entry.name}'.`));
1806
+ }
1807
+ }
1808
+ }
1809
+ route(pathname) {
1810
+ return [...this.#bindings.values()].find((binding) => binding.matches(pathname));
1811
+ }
1812
+ binding(name) {
1813
+ return this.#bindings.get(name);
1814
+ }
1815
+ bindings() {
1816
+ return [...this.#bindings.values()];
1817
+ }
1818
+ normalizeApplicationRoute(path) {
1819
+ const adapter = this.adapter;
1820
+ return adapter && typeof adapter.normalizePath === "function" ? adapter.normalizePath(path) : path;
1821
+ }
1822
+ registerApplicationRoutes(routes, issues) {
1823
+ try {
1824
+ this.#selected?.applicationRoutes?.(routes, Object.freeze([...this.#bindings.values()]));
1825
+ } catch (error) {
1826
+ issues.push(configurationIssue(error, "APPLICATION_ROUTE_REGISTRATION_FAILED", "Cannot register application routes with the HTTP platform."));
1827
+ }
1828
+ }
1829
+ async mount() {
1830
+ const adapter = this.adapter;
1831
+ const platform = this.#selected;
1832
+ if (!adapter || !platform) return;
1833
+ const server = adapter.getInstance();
1834
+ let mounted = Reflect.get(server, MOUNTED);
1835
+ if (!mounted) {
1836
+ mounted = /* @__PURE__ */ new Set();
1837
+ Object.defineProperty(server, MOUNTED, { value: mounted });
1838
+ }
1839
+ for (const binding of this.#bindings.values()) {
1840
+ if (mounted.has(binding.basePath)) throw new BetterAuthConfigurationError("DUPLICATE_MOUNT", `Auth path '${binding.basePath}' is already mounted.`);
1841
+ await platform.mount({
1842
+ adapter,
1843
+ logger: this.logger,
1844
+ binding
1845
+ });
1846
+ mounted.add(binding.basePath);
1847
+ }
1848
+ }
1849
+ reset() {
1850
+ this.#bindings.clear();
1851
+ this.#preparationErrors = [];
1852
+ }
1853
+ };
1854
+ MountCoordinator = __decorate([
1855
+ __decorateParam(0, Inject(HttpAdapterHost)),
1856
+ __decorateParam(1, Inject(INSTANCE_REGISTRY)),
1857
+ __decorateParam(2, Inject(REQUEST_SCOPE)),
1858
+ __decorateMetadata("design:paramtypes", [
1859
+ typeof HttpAdapterHost === "undefined" ? Object : HttpAdapterHost,
1860
+ Object,
1861
+ Object
1862
+ ])
1863
+ ], MountCoordinator);
1864
+ //#endregion
1865
+ //#region src/policy-resolver.ts
1866
+ var PolicyResolver = class {
1867
+ moduleRef;
1868
+ cache = /* @__PURE__ */ new Map();
1869
+ constructor(moduleRef) {
1870
+ this.moduleRef = moduleRef;
1871
+ }
1872
+ resolve(reference) {
1873
+ const cached = this.cache.get(reference);
1874
+ if (cached) return cached;
1875
+ let policy;
1876
+ try {
1877
+ policy = typeof reference === "object" && reference !== null && "evaluate" in reference ? reference : this.moduleRef.get(reference, { strict: false });
1878
+ } catch {
1879
+ throw new BetterAuthConfigurationError("UNRESOLVED_POLICY", "A requirement references a policy that is not registered as a provider");
1880
+ }
1881
+ if (!policy || typeof policy.evaluate !== "function" || typeof policy.id !== "string") throw new BetterAuthConfigurationError("UNRESOLVED_POLICY", "A requirement must reference an authorization policy");
1882
+ this.cache.set(reference, policy);
1883
+ return policy;
1884
+ }
1885
+ };
1886
+ //#endregion
1887
+ //#region src/transport-registry.ts
1888
+ var TransportRegistry = class {
1889
+ transports = [];
1890
+ owners = /* @__PURE__ */ new WeakMap();
1891
+ kit = { http: null };
1892
+ register(transports) {
1893
+ this.transports = [...transports];
1894
+ }
1895
+ setKit(kit) {
1896
+ this.kit = kit;
1897
+ }
1898
+ list() {
1899
+ return [...this.transports];
1900
+ }
1901
+ defaultAccessFor(target, method) {
1902
+ for (const transport of this.transports) {
1903
+ const answer = transport.defaultAccessFor?.(target, method);
1904
+ if (answer !== void 0) return answer;
1905
+ }
1906
+ }
1907
+ find(context) {
1908
+ return this.transports.find((unit) => unit.handles(context));
1909
+ }
1910
+ select(context) {
1911
+ const transport = this.find(context);
1912
+ if (!transport) throw BetterAuthConfigurationError.atRequest("NO_TRANSPORT", "No registered transport handles this execution context");
1913
+ return transport;
1914
+ }
1915
+ idOf(call) {
1916
+ return this.owners.get(call) ?? "unknown";
1917
+ }
1918
+ describe(context, transport = this.select(context)) {
1919
+ const call = transport.describe(context, this.kit);
1920
+ this.owners.set(call, transport.id);
1921
+ return call;
1922
+ }
1923
+ };
1924
+ //#endregion
1925
+ //#region src/auth-core-module.ts
1926
+ const EVALUATOR = Symbol("authorization-evaluator");
1927
+ const exports = [
1928
+ INSTANCE_REGISTRY,
1929
+ REQUEST_SCOPE,
1930
+ PRINCIPAL_READINGS,
1931
+ MOUNT_COORDINATOR,
1932
+ POLICY_RESOLVER,
1933
+ ROUTE_PLANNER,
1934
+ TRANSPORT_REGISTRY,
1935
+ PRINCIPAL_RESOLVER,
1936
+ POLICY_INVOKER,
1937
+ GUARD_CORE,
1938
+ SCOPE_CORE,
1939
+ BetterAuthGuard,
1940
+ BetterAuthScopeInterceptor
1941
+ ];
1942
+ let BetterAuthCoreModule = class BetterAuthCoreModule {
1943
+ instances;
1944
+ scope;
1945
+ transports;
1946
+ mounts;
1947
+ hooks;
1948
+ bridges;
1949
+ validator;
1950
+ #initialization;
1951
+ #ready = false;
1952
+ constructor(instances, scope, transports, mounts, hooks, bridges, validator) {
1953
+ this.instances = instances;
1954
+ this.scope = scope;
1955
+ this.transports = transports;
1956
+ this.mounts = mounts;
1957
+ this.hooks = hooks;
1958
+ this.bridges = bridges;
1959
+ this.validator = validator;
1960
+ }
1961
+ onModuleInit() {
1962
+ if (!this.instances.begin(this.mounts.adapter)) return this.#initialization ?? Promise.resolve();
1963
+ this.#initialization = this.initialize();
1964
+ return this.#initialization;
1965
+ }
1966
+ async initialize() {
1967
+ const issues = [];
1968
+ await this.instances.initialize(issues);
1969
+ const hooks = this.hooks.discover(this.instances, issues);
1970
+ for (const entry of this.instances.list()) {
1971
+ const tables = hooks.get(entry.name) ?? emptyHookTables();
1972
+ const unbound = entry.bridge.unboundDispatches;
1973
+ const count = tables.before.length + tables.after.length + Object.values(tables.database).reduce((sum, value) => sum + value.before.length + value.after.length, 0);
1974
+ try {
1975
+ const result = this.bridges.bind(entry, {
1976
+ owner: this.instances.owner,
1977
+ instance: entry.name,
1978
+ state: "initialized",
1979
+ current: () => this.scope.current()?.view,
1980
+ credentialHeaders: entry.credentialHeaders,
1981
+ ...tables,
1982
+ onDropped: (path) => this.instances.logger.debug(`Direct auth call '${path}' used foreign credentials; cookies dropped for '${entry.name}'.`)
1983
+ });
1984
+ if (result.tookOverFrom) this.instances.logger.warn(`W_INSTANCE_TAKEN_OVER: '${entry.name}' took over from never-bootstrapped '${result.tookOverFrom}'; that application fails at bootstrap if it is still initializing.`);
1985
+ if (unbound > 0 && count > 0) this.instances.logger.warn(`W_CALLS_BEFORE_BIND: '${entry.name}': ${unbound} auth dispatches or database writes ran before Nest hooks; move seeding to onApplicationBootstrap.`);
1986
+ } catch (error) {
1987
+ issues.push(configurationIssue(error, "INSTANCE_ALREADY_BOUND", `Cannot bind '${entry.name}' to this application.`));
1988
+ }
1989
+ }
1990
+ this.mounts.prepareAtInit(issues);
1991
+ this.mounts.resolve(issues);
1992
+ this.transports.setKit({ http: this.mounts.platform?.requests ?? null });
1993
+ await this.validator.validate(issues);
1994
+ if (issues.length) {
1995
+ const failure = new BetterAuthConfigurationError("AUTH_BOOT_FAILED", `Authentication bootstrap failed:\n${issues.map((issue) => `[${issue.code}] ${issue.detail}${issue.hint ? ` Hint: ${issue.hint}` : ""}`).join("\n")}`, "Resolve every reported configuration problem before starting the application.");
1996
+ Object.defineProperty(failure, "issues", {
1997
+ value: Object.freeze([...issues]),
1998
+ enumerable: true
1999
+ });
2000
+ throw failure;
2001
+ }
2002
+ this.validator.summary(hooks);
2003
+ await this.mounts.mount();
2004
+ this.#ready = true;
2005
+ }
2006
+ onApplicationBootstrap() {
2007
+ if (this.#ready && this.instances.state !== "bootstrapped") {
2008
+ this.bridges.bootstrap();
2009
+ this.instances.state = "bootstrapped";
2010
+ }
2011
+ }
2012
+ onApplicationShutdown() {
2013
+ this.bridges.close();
2014
+ this.instances.reset();
2015
+ this.mounts.reset();
2016
+ this.#initialization = void 0;
2017
+ this.#ready = false;
2018
+ }
2019
+ };
2020
+ BetterAuthCoreModule = __decorate([
2021
+ Global(),
2022
+ Module({
2023
+ imports: [DiscoveryModule],
2024
+ providers: [
2025
+ MetadataScanner,
2026
+ {
2027
+ provide: REQUEST_SCOPE,
2028
+ useFactory: () => new RequestScope()
2029
+ },
2030
+ {
2031
+ provide: INSTANCE_REGISTRY,
2032
+ inject: [REQUEST_SCOPE],
2033
+ useFactory: (scope) => new InstanceRegistry(scope)
2034
+ },
2035
+ {
2036
+ provide: PRINCIPAL_READINGS,
2037
+ inject: [REQUEST_SCOPE],
2038
+ useFactory: (scope) => new PrincipalReadings(scope)
2039
+ },
2040
+ {
2041
+ provide: TRANSPORT_REGISTRY,
2042
+ useFactory: () => new TransportRegistry()
2043
+ },
2044
+ {
2045
+ provide: POLICY_RESOLVER,
2046
+ inject: [ModuleRef],
2047
+ useFactory: (ref) => new PolicyResolver(ref)
2048
+ },
2049
+ {
2050
+ provide: ROUTE_PLANNER,
2051
+ inject: [
2052
+ INSTANCE_REGISTRY,
2053
+ POLICY_RESOLVER,
2054
+ TRANSPORT_REGISTRY
2055
+ ],
2056
+ useFactory: (instances, policies, transports) => new RoutePlanner(instances, policies, transports)
2057
+ },
2058
+ {
2059
+ provide: MOUNT_COORDINATOR,
2060
+ inject: [
2061
+ HttpAdapterHost,
2062
+ INSTANCE_REGISTRY,
2063
+ REQUEST_SCOPE
2064
+ ],
2065
+ useFactory: (host, instances, scope) => new MountCoordinator(host, instances, scope)
2066
+ },
2067
+ {
2068
+ provide: PRINCIPAL_RESOLVER,
2069
+ inject: [
2070
+ INSTANCE_REGISTRY,
2071
+ REQUEST_SCOPE,
2072
+ TRANSPORT_REGISTRY
2073
+ ],
2074
+ useFactory: (instances, scope, transports) => new ChainPrincipalResolver(instances, scope, transports)
2075
+ },
2076
+ {
2077
+ provide: POLICY_INVOKER,
2078
+ useFactory: () => new DefaultPolicyInvoker()
2079
+ },
2080
+ {
2081
+ provide: EVALUATOR,
2082
+ inject: [
2083
+ POLICY_RESOLVER,
2084
+ POLICY_INVOKER,
2085
+ PRINCIPAL_RESOLVER,
2086
+ REQUEST_SCOPE
2087
+ ],
2088
+ useFactory: (policies, invoker, resolver, scope) => new AuthorizationEvaluator(policies, invoker, resolver, scope)
2089
+ },
2090
+ {
2091
+ provide: GUARD_CORE,
2092
+ inject: [
2093
+ INSTANCE_REGISTRY,
2094
+ ROUTE_PLANNER,
2095
+ TRANSPORT_REGISTRY,
2096
+ PRINCIPAL_RESOLVER,
2097
+ EVALUATOR,
2098
+ REQUEST_SCOPE,
2099
+ PRINCIPAL_READINGS
2100
+ ],
2101
+ useFactory: (instances, planner, transports, resolver, evaluator, scope, readings) => new GuardCore(instances, planner, transports, resolver, evaluator, scope, readings)
2102
+ },
2103
+ {
2104
+ provide: SCOPE_CORE,
2105
+ inject: [
2106
+ INSTANCE_REGISTRY,
2107
+ ROUTE_PLANNER,
2108
+ TRANSPORT_REGISTRY,
2109
+ REQUEST_SCOPE,
2110
+ PRINCIPAL_READINGS
2111
+ ],
2112
+ useFactory: (instances, planner, transports, scope, readings) => new ScopeCore(instances, planner, transports, scope, readings)
2113
+ },
2114
+ BetterAuthGuard,
2115
+ BetterAuthScopeInterceptor,
2116
+ HookBinder,
2117
+ BridgeClient,
2118
+ {
2119
+ provide: BootValidator,
2120
+ inject: [
2121
+ DiscoveryService,
2122
+ MetadataScanner,
2123
+ Reflector,
2124
+ ModuleRef,
2125
+ ApplicationConfig,
2126
+ INSTANCE_REGISTRY,
2127
+ ROUTE_PLANNER,
2128
+ POLICY_RESOLVER,
2129
+ TRANSPORT_REGISTRY,
2130
+ MOUNT_COORDINATOR
2131
+ ],
2132
+ useFactory: (discovery, scanner, reflector, moduleRef, appConfig, instances, planner, policies, transports, mounts) => new BootValidator(discovery, scanner, reflector, moduleRef, appConfig, instances, planner, policies, transports, mounts)
2133
+ }
2134
+ ],
2135
+ exports
2136
+ }),
2137
+ __decorateParam(0, Inject(INSTANCE_REGISTRY)),
2138
+ __decorateParam(1, Inject(REQUEST_SCOPE)),
2139
+ __decorateParam(2, Inject(TRANSPORT_REGISTRY)),
2140
+ __decorateParam(3, Inject(MOUNT_COORDINATOR)),
2141
+ __decorateParam(4, Inject(HookBinder)),
2142
+ __decorateParam(5, Inject(BridgeClient)),
2143
+ __decorateParam(6, Inject(BootValidator)),
2144
+ __decorateMetadata("design:paramtypes", [
2145
+ typeof InstanceRegistry === "undefined" ? Object : InstanceRegistry,
2146
+ typeof RequestScope === "undefined" ? Object : RequestScope,
2147
+ typeof TransportRegistry === "undefined" ? Object : TransportRegistry,
2148
+ typeof MountCoordinator === "undefined" ? Object : MountCoordinator,
2149
+ typeof HookBinder === "undefined" ? Object : HookBinder,
2150
+ typeof BridgeClient === "undefined" ? Object : BridgeClient,
2151
+ typeof BootValidator === "undefined" ? Object : BootValidator
2152
+ ])
2153
+ ], BetterAuthCoreModule);
2154
+ //#endregion
2155
+ //#region src/session-principal.ts
2156
+ const SESSION_PRINCIPAL_KIND = "session";
2157
+ const CurrentSession = definePrincipalParam({
2158
+ kind: SESSION_PRINCIPAL_KIND,
2159
+ reason: "SESSION_REQUIRED",
2160
+ project: (principal) => principal.session
2161
+ });
2162
+ const CurrentUser = definePrincipalParam({
2163
+ kind: SESSION_PRINCIPAL_KIND,
2164
+ reason: "SESSION_REQUIRED",
2165
+ project: (principal) => "user" in principal.session ? principal.session.user : void 0
2166
+ });
2167
+ function readCurrentSession(readings) {
2168
+ return readings.project(readings.current(), {
2169
+ kind: SESSION_PRINCIPAL_KIND,
2170
+ reason: "SESSION_REQUIRED",
2171
+ site: "BetterAuthService.getSession",
2172
+ project: (principal) => principal.session
2173
+ });
2174
+ }
2175
+ function sessionPrincipal(options = {}) {
2176
+ return {
2177
+ id: "better-auth:session",
2178
+ kinds: [SESSION_PRINCIPAL_KIND],
2179
+ acceptance: "default",
2180
+ credentialHeaders: ["cookie", "authorization"],
2181
+ sessionBacked: true,
2182
+ async resolve(request) {
2183
+ const context = await request.auth.context();
2184
+ const authoritative = request.freshness === "authoritative";
2185
+ const query = {
2186
+ ...authoritative && (context.options.database || context.options.secondaryStorage) ? { disableCookieCache: true } : {},
2187
+ ...request.cookies === null ? { disableRefresh: true } : {}
2188
+ };
2189
+ let result;
2190
+ try {
2191
+ result = await request.auth.run({
2192
+ cookies: request.cookies,
2193
+ forward: "none",
2194
+ internal: true
2195
+ }, () => request.auth.api.getSession({
2196
+ headers: request.headers,
2197
+ returnHeaders: true,
2198
+ query
2199
+ }));
2200
+ } catch (error) {
2201
+ const failure = AuthFailures.fromAPIError(error);
2202
+ if (!failure) throw error;
2203
+ const cookies = getAPIErrorHeaders(error).getSetCookie();
2204
+ if (cookies.length) request.cookies?.append(cookies);
2205
+ return rejected(failure);
2206
+ }
2207
+ const cookies = result.headers?.getSetCookie() ?? [];
2208
+ if (cookies.length) request.cookies?.append(cookies);
2209
+ if (result.response == null || authoritative && !request.auth.producedByEndpoint(result.response)) return absent();
2210
+ const userId = options.userId ? options.userId(result.response) : result.response.user?.id ?? null;
2211
+ if (typeof userId !== "string" || !userId) throw BetterAuthConfigurationError.atRequest("SESSION_USER_ID", "session.userId must return a nonempty user id for a resolved session");
2212
+ return authenticated({
2213
+ kind: SESSION_PRINCIPAL_KIND,
2214
+ source: "better-auth:session",
2215
+ userId,
2216
+ session: result.response
2217
+ });
2218
+ },
2219
+ advise(context) {
2220
+ const warnings = [];
2221
+ if (context.context.hasPlugin("jwt")) warnings.push({
2222
+ level: "info",
2223
+ code: "I_SESSION_JWT_COST",
2224
+ message: "Unless disableSettingJwtHeader is enabled, session reads may also sign a JWT."
2225
+ });
2226
+ if (context.context.hasPlugin("bearer")) warnings.push({
2227
+ level: "info",
2228
+ code: "I_SESSION_BEARER_COST",
2229
+ message: "Without requireSignature, foreign bearer tokens can trigger a session lookup."
2230
+ });
2231
+ const sites = context.handlers.filter(({ plan }) => (plan.access === "required" || plan.access === "optional") && [...plan.accepts].some((kind) => kind !== "session")).map(({ plan }) => plan.site);
2232
+ if (sites.length) warnings.push({
2233
+ level: "warn",
2234
+ code: "W_MIXED_KIND_SESSION_READER",
2235
+ message: `If these handlers read getSession(), another authenticated kind throws SESSION_REQUIRED: ${sites.slice(0, 10).join(", ")}`,
2236
+ hint: "Use getPrincipal() and narrow p.kind."
2237
+ });
2238
+ if (context.context.hasPlugin("api-key") && options.apiKeySessions !== false) {
2239
+ warnings.push({
2240
+ level: "warn",
2241
+ code: "W_API_KEY_FULL_SESSION",
2242
+ message: `${options.apiKeySessions ? "API-key sessions grant" : "If enableSessionForAPIKeys is enabled, API-key sessions grant"} full session access on ordinary authenticated routes.`
2243
+ });
2244
+ const presenting = context.policies.filter((policy) => policy.requires?.presentsCredentials).map((policy) => policy.id);
2245
+ if (presenting.length) warnings.push({
2246
+ level: "warn",
2247
+ code: "W_API_KEY_SESSION_MULTIPLIER",
2248
+ message: `${options.apiKeySessions ? "Each" : "If enableSessionForAPIKeys is enabled, each"} check of these policies validates the API key again and spends its quota once more per request: ${presenting.join(", ")}.`
2249
+ });
2250
+ }
2251
+ if (context.context.hasPlugin("expo") && context.originCheck.missingOrigin === "reject") warnings.push({
2252
+ level: "warn",
2253
+ code: "W_ORIGIN_CHECK_NATIVE_CLIENTS",
2254
+ message: "Native clients must send a trusted Origin for cookie-carrying operations."
2255
+ });
2256
+ return warnings;
2257
+ }
2258
+ };
2259
+ }
2260
+ const freshPolicy = {
2261
+ id: "better-auth:session/fresh",
2262
+ requires: { principals: [SESSION_PRINCIPAL_KIND] },
2263
+ validate(params) {
2264
+ if (params.maxAgeSeconds !== void 0 && (!Number.isFinite(params.maxAgeSeconds) || params.maxAgeSeconds < 0)) throw new BetterAuthConfigurationError("INVALID_FRESH_AGE", "maxAgeSeconds must be finite and nonnegative");
2265
+ },
2266
+ advise(context) {
2267
+ return context.context.hasPlugin("custom-session") ? [{
2268
+ level: "warn",
2269
+ code: "W_FRESH_SESSION_SHAPE",
2270
+ message: "freshSession requires custom session results to retain session.createdAt; unsupported shapes deny PRINCIPAL_NOT_SUPPORTED."
2271
+ }] : [];
2272
+ },
2273
+ async evaluate(params, context) {
2274
+ const createdAt = context.principal.session.session?.createdAt;
2275
+ const age = params.maxAgeSeconds ?? (await context.auth.context()).sessionConfig.freshAge;
2276
+ if (age === 0) return { effect: "allow" };
2277
+ if (!(createdAt instanceof Date) && typeof createdAt !== "string" && typeof createdAt !== "number") return {
2278
+ effect: "deny",
2279
+ reason: "PRINCIPAL_NOT_SUPPORTED"
2280
+ };
2281
+ const time = new Date(createdAt).getTime();
2282
+ return Number.isFinite(time) && Date.now() - time < age * 1e3 ? { effect: "allow" } : {
2283
+ effect: "deny",
2284
+ status: 403,
2285
+ reason: "SESSION_NOT_FRESH"
2286
+ };
2287
+ }
2288
+ };
2289
+ const freshSession = (options = {}) => ({
2290
+ policy: freshPolicy,
2291
+ params: options
2292
+ });
2293
+ const RequireFreshSession = (options) => Require(freshSession(options));
2294
+ //#endregion
2295
+ //#region src/auth-service.ts
2296
+ let BetterAuthService = class BetterAuthService {
2297
+ registry;
2298
+ scope;
2299
+ readings;
2300
+ planner;
2301
+ transports;
2302
+ coordinator;
2303
+ name;
2304
+ instance;
2305
+ api;
2306
+ #context;
2307
+ constructor(options, registry, scope, readings, planner, transports, coordinator) {
2308
+ this.registry = registry;
2309
+ this.scope = scope;
2310
+ this.readings = readings;
2311
+ this.planner = planner;
2312
+ this.transports = transports;
2313
+ this.coordinator = coordinator;
2314
+ const registration = registry.registrations().find((value) => value.options === options);
2315
+ if (!registration) throw new BetterAuthConfigurationError("UNKNOWN_INSTANCE", "The service options have no registered instance.");
2316
+ this.name = registration.name;
2317
+ this.instance = options.auth;
2318
+ this.api = options.auth?.api;
2319
+ this.#context = options.auth?.$context;
2320
+ }
2321
+ context() {
2322
+ return this.#context;
2323
+ }
2324
+ mount() {
2325
+ const binding = this.coordinator.binding(this.name);
2326
+ const platform = this.coordinator.platform;
2327
+ return binding && platform ? {
2328
+ basePath: binding.basePath,
2329
+ bodyLimit: binding.bodyLimit,
2330
+ platform: platform.id
2331
+ } : void 0;
2332
+ }
2333
+ getPrincipal() {
2334
+ const reading = this.readings.current();
2335
+ return Promise.resolve(this.readings.project(reading, {
2336
+ site: "BetterAuthService.getPrincipal",
2337
+ project: (principal) => principal
2338
+ }));
2339
+ }
2340
+ getSession() {
2341
+ return Promise.resolve(readCurrentSession(this.readings));
2342
+ }
2343
+ principalFor(context) {
2344
+ const plan = this.planner.forContext(context);
2345
+ const transport = this.transports.find(context);
2346
+ const lineage = transport?.lineage?.(context);
2347
+ const call = plan.access !== "public" && plan.access !== "inherit" && transport ? this.transports.describe(context, transport) : void 0;
2348
+ return this.readings.read({
2349
+ args: context.getArgs(),
2350
+ plan,
2351
+ call,
2352
+ lineage,
2353
+ beforeGuard: true
2354
+ });
2355
+ }
2356
+ headersFrom(platformRequest) {
2357
+ const accessor = this.coordinator.platform?.requests;
2358
+ if (!accessor?.isRequest(platformRequest)) throw BetterAuthConfigurationError.atRequest("NO_HTTP_REQUEST", "headersFrom needs a request from the selected HTTP platform.");
2359
+ const entry = this.registry.get(this.name);
2360
+ const headers = new Headers(accessor.headers(platformRequest));
2361
+ for (const name of [...headers.keys()]) if (name.startsWith(":") || name.startsWith("x-nestjs-kit-better-auth-ip-") || name === entry.bridge.clientIpHeader?.toLowerCase()) headers.delete(name);
2362
+ const clientIp = accessor.clientIp(platformRequest);
2363
+ if (entry.bridge.clientIpHeader && clientIp !== null) headers.set(entry.bridge.clientIpHeader, clientIp);
2364
+ return headers;
2365
+ }
2366
+ forwardForeignCookies(fn) {
2367
+ const outer = this.scope.current();
2368
+ if (!outer?.plan?.forwardDirectCalls) throw BetterAuthConfigurationError.atRequest("FORWARDING_NOT_DECLARED", "Declare cookie forwarding on the active handler before forwarding foreign credentials.", { hint: "Use @ForwardAuthCookies() on a guarded handler." });
2369
+ return this.scope.run(derivedScope(this.scope, {
2370
+ get cookies() {
2371
+ return outer.view.cookies;
2372
+ },
2373
+ get inbound() {
2374
+ return outer.view.inbound;
2375
+ },
2376
+ get internal() {
2377
+ return outer.view.internal;
2378
+ },
2379
+ forward: "any"
2380
+ }), fn);
2381
+ }
2382
+ withoutCookies(fn) {
2383
+ const outer = this.scope.current();
2384
+ return this.scope.run(derivedScope(this.scope, {
2385
+ cookies: null,
2386
+ forward: "none",
2387
+ get inbound() {
2388
+ return outer?.view.inbound;
2389
+ },
2390
+ get internal() {
2391
+ return outer?.view.internal ?? false;
2392
+ }
2393
+ }), fn);
2394
+ }
2395
+ runOutsideScope(fn) {
2396
+ return this.scope.exit(fn);
2397
+ }
2398
+ };
2399
+ BetterAuthService = __decorate([
2400
+ __decorateParam(0, Inject(getBetterAuthOptionsToken())),
2401
+ __decorateParam(1, Inject(INSTANCE_REGISTRY)),
2402
+ __decorateParam(2, Inject(REQUEST_SCOPE)),
2403
+ __decorateParam(3, Inject(PRINCIPAL_READINGS)),
2404
+ __decorateParam(4, Inject(ROUTE_PLANNER)),
2405
+ __decorateParam(5, Inject(TRANSPORT_REGISTRY)),
2406
+ __decorateParam(6, Inject(MOUNT_COORDINATOR)),
2407
+ __decorateMetadata("design:paramtypes", [
2408
+ Object,
2409
+ Object,
2410
+ Object,
2411
+ Object,
2412
+ Object,
2413
+ Object,
2414
+ Object
2415
+ ])
2416
+ ], BetterAuthService);
2417
+ //#endregion
2418
+ //#region src/http-transport.ts
2419
+ let HttpTransport = class HttpTransport {
2420
+ adapterHost;
2421
+ id = "http";
2422
+ constructor(adapterHost) {
2423
+ this.adapterHost = adapterHost;
2424
+ }
2425
+ handles(context) {
2426
+ return context.getType() === "http";
2427
+ }
2428
+ describe(context, kit) {
2429
+ const http = context.switchToHttp();
2430
+ const request = http.getRequest();
2431
+ const response = http.getResponse();
2432
+ const key = kit.http ? kit.http.key(request) : typeof request === "object" && request !== null ? request : context.getArgs();
2433
+ const accessor = () => {
2434
+ if (!kit.http) throw BetterAuthConfigurationError.atRequest("HTTP_PLATFORM_MISSING", "No HTTP platform is available for this request", { hint: "Register the HTTP platform that supports the application's adapter." });
2435
+ return kit.http;
2436
+ };
2437
+ return {
2438
+ key,
2439
+ invocation: typeof request === "object" && request !== null ? request : key,
2440
+ headers: () => accessor().headers(request),
2441
+ get clientIp() {
2442
+ return accessor().clientIp(request);
2443
+ },
2444
+ get cookies() {
2445
+ return accessor().cookieSink(request, response);
2446
+ },
2447
+ get request() {
2448
+ return accessor().request(request);
2449
+ },
2450
+ param: (name) => accessor().param(request, name),
2451
+ get browser() {
2452
+ const info = accessor().request(request);
2453
+ return {
2454
+ enforce: ![
2455
+ "GET",
2456
+ "HEAD",
2457
+ "OPTIONS"
2458
+ ].includes(info.method.toUpperCase()),
2459
+ headers: () => accessor().headers(request),
2460
+ url: info.url,
2461
+ key
2462
+ };
2463
+ }
2464
+ };
2465
+ }
2466
+ toException(failure, context) {
2467
+ const adapter = this.adapterHost.httpAdapter;
2468
+ const response = context.switchToHttp().getResponse();
2469
+ if (adapter && response) {
2470
+ if (failure.challenge) adapter.setHeader(response, "WWW-Authenticate", failure.challenge);
2471
+ const retryAfter = failure.headers?.get("retry-after");
2472
+ if (failure.status === 429 && retryAfter !== null && retryAfter !== void 0) adapter.setHeader(response, "Retry-After", retryAfter);
2473
+ }
2474
+ const body = {
2475
+ statusCode: failure.status,
2476
+ error: failure.status === 401 ? "Unauthorized" : failure.status === 403 ? "Forbidden" : "Too Many Requests",
2477
+ code: failure.code,
2478
+ ...failure.reason ? { reason: failure.reason } : {},
2479
+ message: failure.message
2480
+ };
2481
+ if (failure.status === 401) return new UnauthorizedException(body);
2482
+ if (failure.status === 403) return new ForbiddenException(body);
2483
+ return new HttpException(body, 429);
2484
+ }
2485
+ validate(context) {
2486
+ class Probe {
2487
+ route() {}
2488
+ }
2489
+ Get("/nestjs-kit-better-auth-canary/:id")(Probe.prototype, "route", Object.getOwnPropertyDescriptor(Probe.prototype, "route"));
2490
+ if (Reflect.getMetadata("path", Probe.prototype.route) !== "/nestjs-kit-better-auth-canary/:id" || Reflect.getMetadata("method", Probe.prototype.route) !== RequestMethod.GET) throw new BetterAuthConfigurationError("NEST_METADATA_KEY_CHANGED", "Nest HTTP route metadata failed its path/method canary");
2491
+ const scanner = new MetadataScanner();
2492
+ for (const wrapper of context.discovery.getControllers()) {
2493
+ const target = wrapper.metatype;
2494
+ if (!target?.prototype || wrapper.isAlias) continue;
2495
+ const controllerPaths = Reflect.getMetadata("path", target);
2496
+ for (const method of scanner.getAllMethodNames(target.prototype)) {
2497
+ const handler = target.prototype[method];
2498
+ if (typeof handler !== "function" || Reflect.getMetadata("method", handler) === void 0) continue;
2499
+ const paths = [controllerPaths, Reflect.getMetadata("path", handler)].flatMap((value) => typeof value === "string" ? [value] : Array.isArray(value) ? value.filter((item) => typeof item === "string") : []);
2500
+ const inputs = [...new Set(paths.flatMap((path) => [...path.matchAll(/:([A-Za-z0-9_]+)/g)].map((match) => match[1])))];
2501
+ context.claim(target, method, "global", {
2502
+ code: "ROUTE_UNGUARDED",
2503
+ inputs,
2504
+ hint: "Register the global guard (globalGuard: true) or add @UseBetterAuth() to the controller."
2505
+ });
2506
+ }
2507
+ }
2508
+ }
2509
+ };
2510
+ HttpTransport = __decorate([__decorateParam(0, Inject(HttpAdapterHost)), __decorateMetadata("design:paramtypes", [typeof HttpAdapterHost === "undefined" ? Object : HttpAdapterHost])], HttpTransport);
2511
+ function httpTransport() {
2512
+ return HttpTransport;
2513
+ }
2514
+ //#endregion
2515
+ //#region src/auth-module-definition.ts
2516
+ function defineExtension(definition) {
2517
+ return {
2518
+ ...definition,
2519
+ [EXTENSION_DEFINITION]: true
2520
+ };
2521
+ }
2522
+ function defineHttpPlatform(platform) {
2523
+ return platform;
2524
+ }
2525
+ function defineTransport(transport) {
2526
+ return transport;
2527
+ }
2528
+ const STATIC_KEYS = [
2529
+ "name",
2530
+ "global",
2531
+ "globalGuard",
2532
+ "globalScope",
2533
+ "principals",
2534
+ "platforms",
2535
+ "transports"
2536
+ ];
2537
+ function moduleDefinition(module, definition, async) {
2538
+ const name = definition.name || "default";
2539
+ const providers = [];
2540
+ const imports = [BetterAuthCoreModule];
2541
+ const exports = [];
2542
+ const registrationToken = Symbol(`registration:${name}`);
2543
+ const staticOptions = {
2544
+ name,
2545
+ global: definition.global,
2546
+ globalGuard: definition.globalGuard,
2547
+ globalScope: definition.globalScope,
2548
+ principals: definition.principals
2549
+ };
2550
+ const appOptions = {
2551
+ platforms: definition.platforms,
2552
+ transports: definition.transports
2553
+ };
2554
+ const optionsToken = getBetterAuthOptionsToken(name);
2555
+ if (async) {
2556
+ const factory = definition;
2557
+ imports.push(...factory.imports ?? []);
2558
+ providers.push({
2559
+ provide: optionsToken,
2560
+ inject: [...factory.inject ?? []],
2561
+ useFactory: async (...deps) => {
2562
+ const options = await factory.useFactory(...deps);
2563
+ for (const key of STATIC_KEYS) if (Object.hasOwn(options, key)) throw new BetterAuthConfigurationError("STATIC_OPTION_IN_FACTORY", `'${key}' was returned from useFactory.`, `Pass '${key}' next to useFactory in forRootAsync().`);
2564
+ return options;
2565
+ }
2566
+ });
2567
+ } else {
2568
+ const runtime = { ...definition };
2569
+ for (const key of STATIC_KEYS) delete runtime[key];
2570
+ providers.push({
2571
+ provide: optionsToken,
2572
+ useValue: runtime
2573
+ });
2574
+ }
2575
+ function extensions(point, refs) {
2576
+ return refs.map((ref, index) => {
2577
+ const token = getExtensionToken(point, name, index);
2578
+ if (typeof ref === "function") providers.push({
2579
+ provide: token,
2580
+ useClass: ref
2581
+ });
2582
+ else if (ref && typeof ref === "object" && Reflect.get(ref, EXTENSION_DEFINITION) === true) {
2583
+ const definition = ref;
2584
+ imports.push(...definition.imports ?? []);
2585
+ providers.push(...definition.providers ?? []);
2586
+ if ("useClass" in definition.use) providers.push({
2587
+ provide: token,
2588
+ useClass: definition.use.useClass
2589
+ });
2590
+ else if ("useExisting" in definition.use) providers.push({
2591
+ provide: token,
2592
+ useExisting: definition.use.useExisting
2593
+ });
2594
+ else providers.push({
2595
+ provide: token,
2596
+ useFactory: definition.use.useFactory,
2597
+ inject: [...definition.use.inject ?? []]
2598
+ });
2599
+ exports.push(...definition.exports ?? []);
2600
+ } else providers.push({
2601
+ provide: token,
2602
+ useValue: ref
2603
+ });
2604
+ return token;
2605
+ });
2606
+ }
2607
+ const sourceTokens = extensions("principals", definition.principals ?? []);
2608
+ providers.push({
2609
+ provide: getPrincipalSourcesToken(name),
2610
+ inject: [optionsToken, ...sourceTokens],
2611
+ useFactory: (options, ...sources) => {
2612
+ const builtIn = sessionPrincipal(options.session === false ? void 0 : options.session);
2613
+ return options.session === false ? sources : [...sources, builtIn];
2614
+ }
2615
+ });
2616
+ providers.push({
2617
+ provide: registrationToken,
2618
+ inject: [
2619
+ INSTANCE_REGISTRY,
2620
+ optionsToken,
2621
+ getPrincipalSourcesToken(name)
2622
+ ],
2623
+ useFactory: (registry, options, sources) => registry.register(options, staticOptions, sources, appOptions)
2624
+ });
2625
+ providers.push({
2626
+ provide: getBetterAuthInstanceToken(name),
2627
+ inject: [registrationToken],
2628
+ useFactory: (registration) => registration.instance
2629
+ });
2630
+ providers.push({
2631
+ provide: getBetterAuthHandleToken(name),
2632
+ inject: [registrationToken],
2633
+ useFactory: (registration) => registration.handle
2634
+ });
2635
+ const serviceProvider = {
2636
+ provide: getBetterAuthServiceToken(name),
2637
+ inject: [
2638
+ registrationToken,
2639
+ INSTANCE_REGISTRY,
2640
+ REQUEST_SCOPE,
2641
+ PRINCIPAL_READINGS,
2642
+ ROUTE_PLANNER,
2643
+ TRANSPORT_REGISTRY,
2644
+ MOUNT_COORDINATOR
2645
+ ],
2646
+ useFactory: (registration, registry, scope, readings, planner, transports, mounts) => new BetterAuthService(registration.options, registry, scope, readings, planner, transports, mounts)
2647
+ };
2648
+ if (name === "default") {
2649
+ providers.push({
2650
+ ...serviceProvider,
2651
+ provide: BetterAuthService
2652
+ }, {
2653
+ provide: getBetterAuthServiceToken(name),
2654
+ useExisting: BetterAuthService
2655
+ });
2656
+ exports.push(BetterAuthService);
2657
+ const platformTokens = extensions("platforms", definition.platforms ?? []);
2658
+ const transportTokens = extensions("transports", [...definition.transports ?? [], httpTransport()]);
2659
+ providers.push({
2660
+ provide: PLATFORMS,
2661
+ inject: [MOUNT_COORDINATOR, ...platformTokens],
2662
+ useFactory: (mounts, ...platforms) => {
2663
+ mounts.registerPlatforms(platforms);
2664
+ return platforms;
2665
+ }
2666
+ });
2667
+ providers.push({
2668
+ provide: TRANSPORTS,
2669
+ inject: [TRANSPORT_REGISTRY, ...transportTokens],
2670
+ useFactory: (registry, ...transports) => {
2671
+ registry.register(transports);
2672
+ return transports;
2673
+ }
2674
+ });
2675
+ } else providers.push(serviceProvider);
2676
+ if (definition.globalGuard ?? name === "default") providers.push({
2677
+ provide: APP_GUARD,
2678
+ useExisting: BetterAuthGuard
2679
+ });
2680
+ if (name === "default" && (definition.globalScope ?? true)) providers.push({
2681
+ provide: APP_INTERCEPTOR,
2682
+ useExisting: BetterAuthScopeInterceptor
2683
+ });
2684
+ exports.push(optionsToken, getBetterAuthInstanceToken(name), getBetterAuthHandleToken(name), getBetterAuthServiceToken(name));
2685
+ return {
2686
+ module,
2687
+ global: definition.global ?? true,
2688
+ imports,
2689
+ providers,
2690
+ exports
2691
+ };
2692
+ }
2693
+ //#endregion
2694
+ //#region src/auth-module.ts
2695
+ var _BetterAuthModule;
2696
+ let BetterAuthModule = _BetterAuthModule = class BetterAuthModule {
2697
+ static forRoot(options) {
2698
+ return moduleDefinition(_BetterAuthModule, options, false);
2699
+ }
2700
+ static forRootAsync(options) {
2701
+ return moduleDefinition(_BetterAuthModule, options, true);
2702
+ }
2703
+ };
2704
+ BetterAuthModule = _BetterAuthModule = __decorate([Module({})], BetterAuthModule);
2705
+ //#endregion
2706
+ export { deny as _, httpTransport as a, CurrentUser as c, freshSession as d, sessionPrincipal as f, definePolicy as g, allow as h, defineTransport as i, RequireFreshSession as l, AuthorizationEvaluator as m, defineExtension as n, BetterAuthService as o, betterAuthCorsOrigin as p, defineHttpPlatform as r, CurrentSession as s, BetterAuthModule as t, SESSION_PRINCIPAL_KIND as u };
2707
+
2708
+ //# sourceMappingURL=auth-module-CtHx3gRU.mjs.map