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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +1078 -2
  3. package/dist/admin.cjs +88 -0
  4. package/dist/admin.cjs.map +1 -0
  5. package/dist/admin.d.cts +14 -0
  6. package/dist/admin.d.cts.map +1 -0
  7. package/dist/admin.d.mts +14 -0
  8. package/dist/admin.d.mts.map +1 -0
  9. package/dist/admin.mjs +85 -0
  10. package/dist/admin.mjs.map +1 -0
  11. package/dist/api-key.cjs +164 -0
  12. package/dist/api-key.cjs.map +1 -0
  13. package/dist/api-key.d.cts +42 -0
  14. package/dist/api-key.d.cts.map +1 -0
  15. package/dist/api-key.d.mts +42 -0
  16. package/dist/api-key.d.mts.map +1 -0
  17. package/dist/api-key.mjs +160 -0
  18. package/dist/api-key.mjs.map +1 -0
  19. package/dist/auth-contracts-Brh_Qko9.d.cts +1654 -0
  20. package/dist/auth-contracts-Brh_Qko9.d.cts.map +1 -0
  21. package/dist/auth-contracts-Cz7C4_Ch.d.mts +1654 -0
  22. package/dist/auth-contracts-Cz7C4_Ch.d.mts.map +1 -0
  23. package/dist/auth-decorators-B1mFnGJ6.mjs +284 -0
  24. package/dist/auth-decorators-B1mFnGJ6.mjs.map +1 -0
  25. package/dist/auth-decorators-Cu_nxWnF.cjs +421 -0
  26. package/dist/auth-decorators-Cu_nxWnF.cjs.map +1 -0
  27. package/dist/auth-errors-C3rbTV9p.mjs +301 -0
  28. package/dist/auth-errors-C3rbTV9p.mjs.map +1 -0
  29. package/dist/auth-errors-DmiKl0L_.cjs +378 -0
  30. package/dist/auth-errors-DmiKl0L_.cjs.map +1 -0
  31. package/dist/auth-guard-CLyhAEEj.cjs +136 -0
  32. package/dist/auth-guard-CLyhAEEj.cjs.map +1 -0
  33. package/dist/auth-guard-h9QB_l11.mjs +125 -0
  34. package/dist/auth-guard-h9QB_l11.mjs.map +1 -0
  35. package/dist/auth-module-BgcY2HpA.cjs +2809 -0
  36. package/dist/auth-module-BgcY2HpA.cjs.map +1 -0
  37. package/dist/auth-module-CtHx3gRU.mjs +2708 -0
  38. package/dist/auth-module-CtHx3gRU.mjs.map +1 -0
  39. package/dist/auth-scope-interceptor-BjxAL1-S.cjs +290 -0
  40. package/dist/auth-scope-interceptor-BjxAL1-S.cjs.map +1 -0
  41. package/dist/auth-scope-interceptor-DYPn3b8M.mjs +231 -0
  42. package/dist/auth-scope-interceptor-DYPn3b8M.mjs.map +1 -0
  43. package/dist/bridge-protocol-BDw8opAG.cjs +25 -0
  44. package/dist/bridge-protocol-BDw8opAG.cjs.map +1 -0
  45. package/dist/bridge-protocol-c66aEkr5.mjs +8 -0
  46. package/dist/bridge-protocol-c66aEkr5.mjs.map +1 -0
  47. package/dist/decorate-Cr4Q7bNE.cjs +319 -0
  48. package/dist/decorate-Cr4Q7bNE.cjs.map +1 -0
  49. package/dist/decorate-Dt0e9K6R.mjs +80 -0
  50. package/dist/decorate-Dt0e9K6R.mjs.map +1 -0
  51. package/dist/express.cjs +256 -0
  52. package/dist/express.cjs.map +1 -0
  53. package/dist/express.d.cts +37 -0
  54. package/dist/express.d.cts.map +1 -0
  55. package/dist/express.d.mts +37 -0
  56. package/dist/express.d.mts.map +1 -0
  57. package/dist/express.mjs +254 -0
  58. package/dist/express.mjs.map +1 -0
  59. package/dist/fastify.cjs +218 -0
  60. package/dist/fastify.cjs.map +1 -0
  61. package/dist/fastify.d.cts +28 -0
  62. package/dist/fastify.d.cts.map +1 -0
  63. package/dist/fastify.d.mts +28 -0
  64. package/dist/fastify.d.mts.map +1 -0
  65. package/dist/fastify.mjs +216 -0
  66. package/dist/fastify.mjs.map +1 -0
  67. package/dist/graphql.cjs +509 -0
  68. package/dist/graphql.cjs.map +1 -0
  69. package/dist/graphql.d.cts +38 -0
  70. package/dist/graphql.d.cts.map +1 -0
  71. package/dist/graphql.d.mts +38 -0
  72. package/dist/graphql.d.mts.map +1 -0
  73. package/dist/graphql.mjs +503 -0
  74. package/dist/graphql.mjs.map +1 -0
  75. package/dist/index.cjs +86 -0
  76. package/dist/index.d.cts +2 -0
  77. package/dist/index.d.mts +2 -0
  78. package/dist/index.mjs +8 -0
  79. package/dist/microservices.cjs +231 -0
  80. package/dist/microservices.cjs.map +1 -0
  81. package/dist/microservices.d.cts +39 -0
  82. package/dist/microservices.d.cts.map +1 -0
  83. package/dist/microservices.d.mts +39 -0
  84. package/dist/microservices.d.mts.map +1 -0
  85. package/dist/microservices.mjs +222 -0
  86. package/dist/microservices.mjs.map +1 -0
  87. package/dist/organization.cjs +191 -0
  88. package/dist/organization.cjs.map +1 -0
  89. package/dist/organization.d.cts +40 -0
  90. package/dist/organization.d.cts.map +1 -0
  91. package/dist/organization.d.mts +40 -0
  92. package/dist/organization.d.mts.map +1 -0
  93. package/dist/organization.mjs +177 -0
  94. package/dist/organization.mjs.map +1 -0
  95. package/dist/platform-CZl7TY8j.mjs +414 -0
  96. package/dist/platform-CZl7TY8j.mjs.map +1 -0
  97. package/dist/platform-YICqqYv6.cjs +473 -0
  98. package/dist/platform-YICqqYv6.cjs.map +1 -0
  99. package/dist/platform.cjs +10 -0
  100. package/dist/platform.d.cts +43 -0
  101. package/dist/platform.d.cts.map +1 -0
  102. package/dist/platform.d.mts +43 -0
  103. package/dist/platform.d.mts.map +1 -0
  104. package/dist/platform.mjs +1 -0
  105. package/dist/plugin.cjs +323 -0
  106. package/dist/plugin.cjs.map +1 -0
  107. package/dist/plugin.d.cts +24 -0
  108. package/dist/plugin.d.cts.map +1 -0
  109. package/dist/plugin.d.mts +24 -0
  110. package/dist/plugin.d.mts.map +1 -0
  111. package/dist/plugin.mjs +321 -0
  112. package/dist/plugin.mjs.map +1 -0
  113. package/dist/principal-readings-Chmt6rQV.cjs +208 -0
  114. package/dist/principal-readings-Chmt6rQV.cjs.map +1 -0
  115. package/dist/principal-readings-Ied2staw.mjs +191 -0
  116. package/dist/principal-readings-Ied2staw.mjs.map +1 -0
  117. package/dist/rolldown-runtime-VH7oDXx4.cjs +28 -0
  118. package/dist/testing/conformance.cjs +6214 -0
  119. package/dist/testing/conformance.cjs.map +1 -0
  120. package/dist/testing/conformance.d.cts +528 -0
  121. package/dist/testing/conformance.d.cts.map +1 -0
  122. package/dist/testing/conformance.d.mts +528 -0
  123. package/dist/testing/conformance.d.mts.map +1 -0
  124. package/dist/testing/conformance.mjs +6204 -0
  125. package/dist/testing/conformance.mjs.map +1 -0
  126. package/dist/testing.cjs +221 -0
  127. package/dist/testing.cjs.map +1 -0
  128. package/dist/testing.d.cts +64 -0
  129. package/dist/testing.d.cts.map +1 -0
  130. package/dist/testing.d.mts +64 -0
  131. package/dist/testing.d.mts.map +1 -0
  132. package/dist/testing.mjs +214 -0
  133. package/dist/testing.mjs.map +1 -0
  134. package/dist/websockets.cjs +396 -0
  135. package/dist/websockets.cjs.map +1 -0
  136. package/dist/websockets.d.cts +67 -0
  137. package/dist/websockets.d.cts.map +1 -0
  138. package/dist/websockets.d.mts +67 -0
  139. package/dist/websockets.d.mts.map +1 -0
  140. package/dist/websockets.mjs +383 -0
  141. package/dist/websockets.mjs.map +1 -0
  142. package/package.json +327 -3
@@ -0,0 +1,221 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_auth_errors = require("./auth-errors-DmiKl0L_.cjs");
3
+ const require_decorate = require("./decorate-Cr4Q7bNE.cjs");
4
+ const require_auth_scope_interceptor = require("./auth-scope-interceptor-BjxAL1-S.cjs");
5
+ const require_bridge_protocol = require("./bridge-protocol-BDw8opAG.cjs");
6
+ const require_principal_readings = require("./principal-readings-Chmt6rQV.cjs");
7
+ let rxjs = require("rxjs");
8
+ //#region src/testing.ts
9
+ const TEST_APP = Symbol.for("@nestjs-kit/better-auth:test-app");
10
+ const TEST_APP_CLOSE = Symbol.for("@nestjs-kit/better-auth:test-app-close");
11
+ const TEST_OVERRIDES = Symbol.for("@nestjs-kit/better-auth:test-overrides");
12
+ const PLUGIN_ID = "@nestjs-kit/better-auth";
13
+ const TEST_PRINCIPAL_SOURCE = "@nestjs-kit/better-auth:test-principal";
14
+ /**
15
+ * TestingModuleBuilder keys overrides by token, so repeated helper calls share one provider and its configuration.
16
+ * The configuration lives on the builder under a global symbol, so the ESM and CommonJS copies of this entry compose.
17
+ */
18
+ function overridesOf(builder) {
19
+ let overrides = Reflect.get(builder, TEST_OVERRIDES);
20
+ if (!overrides) {
21
+ overrides = {
22
+ principals: /* @__PURE__ */ new Map(),
23
+ deciders: []
24
+ };
25
+ Object.defineProperty(builder, TEST_OVERRIDES, { value: overrides });
26
+ }
27
+ return overrides;
28
+ }
29
+ /** Make app.close() run Nest's shutdown once, whoever calls it: initTestApp, a registered hook or the test. */
30
+ function closeOnce(app) {
31
+ const existing = Reflect.get(app, TEST_APP_CLOSE);
32
+ if (existing) return existing;
33
+ const close = app.close.bind(app);
34
+ let pending;
35
+ const once = () => {
36
+ pending ??= close();
37
+ return pending;
38
+ };
39
+ Object.defineProperty(app, TEST_APP_CLOSE, { value: once });
40
+ app.close = once;
41
+ return once;
42
+ }
43
+ async function bridgeOf(auth) {
44
+ const plugin = (await auth.$context).getPlugin?.(PLUGIN_ID);
45
+ return plugin && typeof plugin === "object" ? Reflect.get(plugin, require_bridge_protocol.BRIDGE_HANDLE) : void 0;
46
+ }
47
+ function registryOf(app) {
48
+ try {
49
+ return app.get(require_decorate.INSTANCE_REGISTRY, { strict: false });
50
+ } catch {
51
+ return;
52
+ }
53
+ }
54
+ /**
55
+ * init() plus Fastify ready() when the adapter exposes it. Before init, closes the previous application that
56
+ * initTestApp initialized for the same Better Auth instance and that is still bound to it: an instance binds
57
+ * exclusively to one application (B06). A failed init() leaves its binding for the next application, which takes it
58
+ * over with W_INSTANCE_TAKEN_OVER. app.close() becomes idempotent, and `closeWith` receives it: a per-test cleanup such as
59
+ * Vitest's onTestFinished or node:test's t.after, or a list that a suite-level afterEach drains (Jest registers no hooks
60
+ * while a test runs). Without `closeWith`, close the last application yourself. On Express, compile one TestingModule per application: a
61
+ * second application of one container fails init() with APP_ADAPTER_CHANGED.
62
+ */
63
+ async function initTestApp(app, options = {}) {
64
+ const close = closeOnce(app);
65
+ const bridges = [];
66
+ for (const registration of registryOf(app)?.registrations() ?? []) {
67
+ const bridge = registration.instance ? await bridgeOf(registration.instance) : void 0;
68
+ if (!bridge) continue;
69
+ const previous = Reflect.get(bridge, TEST_APP);
70
+ if (previous && previous !== app && bridge.state === "bound") await previous.close();
71
+ Object.defineProperty(bridge, TEST_APP, {
72
+ value: void 0,
73
+ configurable: true,
74
+ writable: true
75
+ });
76
+ bridges.push(bridge);
77
+ }
78
+ await app.init();
79
+ const server = (app.getHttpAdapter?.())?.getInstance?.();
80
+ if (typeof server?.ready === "function") await server.ready();
81
+ for (const bridge of bridges) Object.defineProperty(bridge, TEST_APP, {
82
+ value: app,
83
+ configurable: true,
84
+ writable: true
85
+ });
86
+ options.closeWith?.(close);
87
+ return app;
88
+ }
89
+ /** Real session headers for `userId` through Better Auth's testUtils() plugin, which the instance must include. */
90
+ async function authHeadersFor(auth, userId) {
91
+ const context = await auth.$context;
92
+ if (typeof context.test?.getAuthHeaders !== "function") throw new require_auth_errors.BetterAuthConfigurationError("TEST_UTILS_MISSING", "authHeadersFor requires Better Auth's testUtils() plugin on the instance.", "Add testUtils() from better-auth/plugins to the test instance.");
93
+ return context.test.getAuthHeaders({ userId });
94
+ }
95
+ /**
96
+ * Replace principal resolution for one instance (default: 'default') without Better Auth I/O. Acceptance, the
97
+ * origin check and policies still run. A policy's re-classification read of a generic 401 answers absent,
98
+ * because the request carries no session; other instances keep the real resolver.
99
+ */
100
+ function overridePrincipal(builder, principal, options = {}) {
101
+ const configured = overridesOf(builder).principals;
102
+ configured.set(options.instance || "default", principal);
103
+ builder.overrideProvider(require_decorate.PRINCIPAL_RESOLVER).useFactory({
104
+ inject: [
105
+ require_decorate.INSTANCE_REGISTRY,
106
+ require_decorate.REQUEST_SCOPE,
107
+ require_decorate.TRANSPORT_REGISTRY
108
+ ],
109
+ factory: (instances, scope, transports) => {
110
+ const real = new require_auth_scope_interceptor.ChainPrincipalResolver(instances, scope, transports);
111
+ return { resolve(call, request) {
112
+ if (!configured.has(request.auth.name)) return real.resolve(call, request);
113
+ if (request.reclassify) return Promise.resolve(require_auth_scope_interceptor.absent());
114
+ const fixed = configured.get(request.auth.name);
115
+ const value = typeof fixed === "function" ? fixed(call) : fixed;
116
+ return Promise.resolve(value ? require_auth_scope_interceptor.authenticated(value) : require_auth_scope_interceptor.absent());
117
+ } };
118
+ }
119
+ });
120
+ return builder;
121
+ }
122
+ /**
123
+ * Replace the policy invoker: `decide` answers per requirement, and undefined runs the real policy. Acceptance,
124
+ * the delegation gate, the origin check and the decision memo still run. Later calls are consulted after earlier ones.
125
+ */
126
+ function overrideDecisions(builder, decide) {
127
+ const configured = overridesOf(builder).deciders;
128
+ configured.push(decide);
129
+ builder.overrideProvider(require_decorate.POLICY_INVOKER).useValue({ async invoke(policy, params, context) {
130
+ for (const decider of configured) {
131
+ const decision = await decider(policy.id, params, context.principal);
132
+ if (decision !== void 0) return decision;
133
+ }
134
+ return policy.evaluate(params, context);
135
+ } });
136
+ return builder;
137
+ }
138
+ /** A principal source that answers a fixed principal (or null for absent), for test module composition. */
139
+ function testPrincipal(principal, options = {}) {
140
+ const fixed = typeof principal === "function" ? void 0 : principal;
141
+ const id = fixed?.source ?? TEST_PRINCIPAL_SOURCE;
142
+ return {
143
+ id,
144
+ kinds: options.kinds ?? (fixed ? [fixed.kind] : ["session"]),
145
+ acceptance: options.acceptance ?? "default",
146
+ delegates: options.delegates ?? Boolean(fixed?.delegation),
147
+ async resolve(request) {
148
+ const value = typeof principal === "function" ? principal(request) : principal;
149
+ return value ? require_auth_scope_interceptor.authenticated({
150
+ ...value,
151
+ source: id
152
+ }) : require_auth_scope_interceptor.absent();
153
+ }
154
+ };
155
+ }
156
+ /**
157
+ * Publish a principal for this invocation from a hand-written guard. The stamp lives on ctx.getArgs() and its
158
+ * object elements. Readers accept it from the same args array (WS, RPC and GraphQL share it between guards and
159
+ * interceptors) or from another array with the same elements when at least two elements are objects (an HTTP request
160
+ * and its response); a lone shared element such as a socket never identifies an invocation. Pass `instance` for
161
+ * handlers of a named instance.
162
+ */
163
+ function stampPrincipal(context, principal, options = {}) {
164
+ require_principal_readings.stampResult(context.getArgs(), options.instance || "default", principal ? require_auth_scope_interceptor.authenticated(principal) : require_auth_scope_interceptor.absent());
165
+ }
166
+ var StampFirstReadings = class extends require_principal_readings.PrincipalReadings {
167
+ read(input) {
168
+ return (input.plan && input.plan.access !== "public" ? require_principal_readings.stampedReading(input.args, input.plan.instance) : void 0) ?? super.read(input);
169
+ }
170
+ };
171
+ function planInstance(planner, context) {
172
+ try {
173
+ return planner.forContext(context).instance;
174
+ } catch {
175
+ return "default";
176
+ }
177
+ }
178
+ /**
179
+ * Replace the guard and scope bodies everywhere with one override each of GUARD_CORE and SCOPE_CORE: the APP_GUARD
180
+ * alias and every @UseBetterAuth()/@UseGuards(BetterAuthGuard) site delegate to the same collaborator, which
181
+ * overrideProvider(BetterAuthGuard) plus overrideGuard(BetterAuthGuard) cannot achieve. With `{ principal }` the
182
+ * stand-in stamps the principal (for the handler's instance) and allows; with a CanActivate, that guard runs as the
183
+ * whole guard body and may call stampPrincipal(); an object with a canActivate method is always treated as a guard,
184
+ * even when it also has a `principal` property. The stand-in interceptor opens the same transport-described scope
185
+ * as the real one, reading the test stamp first, so readers and BetterAuthService perform no Better Auth I/O.
186
+ */
187
+ function overrideAuthGuard(builder, impl) {
188
+ builder.overrideProvider(require_decorate.GUARD_CORE).useFactory({
189
+ inject: [require_decorate.ROUTE_PLANNER],
190
+ factory: (planner) => ({ async canActivate(context) {
191
+ if (typeof impl.canActivate === "function") {
192
+ const result = impl.canActivate(context);
193
+ return (0, rxjs.isObservable)(result) ? (0, rxjs.lastValueFrom)(result) : result;
194
+ }
195
+ const { principal } = impl;
196
+ const value = typeof principal === "function" ? principal(context) : principal;
197
+ require_principal_readings.stampResult(context.getArgs(), planInstance(planner, context), value ? require_auth_scope_interceptor.authenticated(value) : require_auth_scope_interceptor.absent());
198
+ return true;
199
+ } })
200
+ });
201
+ builder.overrideProvider(require_decorate.SCOPE_CORE).useFactory({
202
+ inject: [
203
+ require_decorate.INSTANCE_REGISTRY,
204
+ require_decorate.ROUTE_PLANNER,
205
+ require_decorate.TRANSPORT_REGISTRY,
206
+ require_decorate.REQUEST_SCOPE
207
+ ],
208
+ factory: (instances, planner, transports, scope) => new require_auth_scope_interceptor.ScopeCore(instances, planner, transports, scope, new StampFirstReadings(scope))
209
+ });
210
+ return builder;
211
+ }
212
+ //#endregion
213
+ exports.authHeadersFor = authHeadersFor;
214
+ exports.initTestApp = initTestApp;
215
+ exports.overrideAuthGuard = overrideAuthGuard;
216
+ exports.overrideDecisions = overrideDecisions;
217
+ exports.overridePrincipal = overridePrincipal;
218
+ exports.stampPrincipal = stampPrincipal;
219
+ exports.testPrincipal = testPrincipal;
220
+
221
+ //# sourceMappingURL=testing.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.cjs","names":["BRIDGE_HANDLE","INSTANCE_REGISTRY","BetterAuthConfigurationError","PRINCIPAL_RESOLVER","REQUEST_SCOPE","TRANSPORT_REGISTRY","ChainPrincipalResolver","absent","authenticated","POLICY_INVOKER","PrincipalReadings","stampedReading","GUARD_CORE","ROUTE_PLANNER","isObservable","lastValueFrom","SCOPE_CORE","ScopeCore"],"sources":["../src/testing.ts"],"sourcesContent":["import type {\n CanActivate,\n ExecutionContext,\n INestApplication,\n} from \"@nestjs/common\";\nimport type { TestingModuleBuilder } from \"@nestjs/testing\";\nimport { isObservable, lastValueFrom } from \"rxjs\";\nimport type {\n AuthorizationDecision,\n PolicyInvoker,\n PrincipalRequest,\n PrincipalResolver,\n PrincipalResult,\n PrincipalSource,\n TransportCall,\n} from \"./auth-contracts.js\";\nimport { BetterAuthConfigurationError } from \"./auth-errors.js\";\nimport { ScopeCore } from \"./auth-scope-interceptor.js\";\nimport {\n GUARD_CORE,\n INSTANCE_REGISTRY,\n POLICY_INVOKER,\n PRINCIPAL_RESOLVER,\n REQUEST_SCOPE,\n ROUTE_PLANNER,\n SCOPE_CORE,\n TRANSPORT_REGISTRY,\n} from \"./auth-tokens.js\";\nimport type { AuthLike, AuthPrincipal } from \"./auth-types.js\";\nimport { BRIDGE_HANDLE, type BridgeHandle } from \"./bridge-protocol.js\";\nimport type { InstanceRegistry } from \"./instance-registry.js\";\nimport {\n PrincipalReadings,\n stampedReading,\n stampResult,\n type ReadingInput,\n} from \"./principal-readings.js\";\nimport {\n absent,\n authenticated,\n ChainPrincipalResolver,\n} from \"./principal-resolver.js\";\nimport type { RequestScope } from \"./request-scope.js\";\nimport type { RoutePlanner } from \"./route-planner.js\";\nimport type { TransportRegistry } from \"./transport-registry.js\";\n\nconst TEST_APP = Symbol.for(\"@nestjs-kit/better-auth:test-app\");\nconst TEST_APP_CLOSE = Symbol.for(\"@nestjs-kit/better-auth:test-app-close\");\nconst TEST_OVERRIDES = Symbol.for(\"@nestjs-kit/better-auth:test-overrides\");\nconst PLUGIN_ID = \"@nestjs-kit/better-auth\";\nconst TEST_PRINCIPAL_SOURCE = \"@nestjs-kit/better-auth:test-principal\";\n\ntype FixedPrincipal =\n | AuthPrincipal\n | null\n | ((call: TransportCall) => AuthPrincipal | null);\ntype Decide = (\n policyId: string,\n params: unknown,\n principal: AuthPrincipal,\n) =>\n | AuthorizationDecision\n | undefined\n | Promise<AuthorizationDecision | undefined>;\n\ninterface TestOverrides {\n readonly principals: Map<string, FixedPrincipal>;\n readonly deciders: Decide[];\n}\n\n/**\n * TestingModuleBuilder keys overrides by token, so repeated helper calls share one provider and its configuration.\n * The configuration lives on the builder under a global symbol, so the ESM and CommonJS copies of this entry compose.\n */\nfunction overridesOf(builder: TestingModuleBuilder): TestOverrides {\n let overrides = Reflect.get(builder, TEST_OVERRIDES) as\n | TestOverrides\n | undefined;\n if (!overrides) {\n overrides = { principals: new Map(), deciders: [] };\n Object.defineProperty(builder, TEST_OVERRIDES, { value: overrides });\n }\n return overrides;\n}\n\n/** Make app.close() run Nest's shutdown once, whoever calls it: initTestApp, a registered hook or the test. */\nfunction closeOnce(app: INestApplication): () => Promise<void> {\n const existing = Reflect.get(app, TEST_APP_CLOSE) as\n | (() => Promise<void>)\n | undefined;\n if (existing) {\n return existing;\n }\n const close = app.close.bind(app);\n let pending: Promise<void> | undefined;\n const once = () => {\n pending ??= close();\n return pending;\n };\n Object.defineProperty(app, TEST_APP_CLOSE, { value: once });\n app.close = once;\n return once;\n}\n\nasync function bridgeOf(auth: AuthLike): Promise<BridgeHandle | undefined> {\n const context = (await auth.$context) as {\n getPlugin?(id: string): unknown;\n };\n const plugin = context.getPlugin?.(PLUGIN_ID);\n return plugin && typeof plugin === \"object\"\n ? (Reflect.get(plugin, BRIDGE_HANDLE) as BridgeHandle | undefined)\n : undefined;\n}\n\nfunction registryOf(app: INestApplication): InstanceRegistry | undefined {\n try {\n return app.get<InstanceRegistry>(INSTANCE_REGISTRY, { strict: false });\n } catch {\n return undefined;\n }\n}\n\n/**\n * init() plus Fastify ready() when the adapter exposes it. Before init, closes the previous application that\n * initTestApp initialized for the same Better Auth instance and that is still bound to it: an instance binds\n * exclusively to one application (B06). A failed init() leaves its binding for the next application, which takes it\n * over with W_INSTANCE_TAKEN_OVER. app.close() becomes idempotent, and `closeWith` receives it: a per-test cleanup such as\n * Vitest's onTestFinished or node:test's t.after, or a list that a suite-level afterEach drains (Jest registers no hooks\n * while a test runs). Without `closeWith`, close the last application yourself. On Express, compile one TestingModule per application: a\n * second application of one container fails init() with APP_ADAPTER_CHANGED.\n */\nexport async function initTestApp<T extends INestApplication>(\n app: T,\n options: { closeWith?: (close: () => Promise<void>) => void } = {},\n): Promise<T> {\n const close = closeOnce(app);\n const bridges: BridgeHandle[] = [];\n for (const registration of registryOf(app)?.registrations() ?? []) {\n const bridge = registration.instance\n ? await bridgeOf(registration.instance)\n : undefined;\n if (!bridge) {\n continue;\n }\n const previous = Reflect.get(bridge, TEST_APP) as\n | INestApplication\n | undefined;\n if (previous && previous !== app && bridge.state === \"bound\") {\n // initTestApp made previous.close() idempotent, so a registered close of the same app does nothing later.\n await previous.close();\n }\n Object.defineProperty(bridge, TEST_APP, {\n value: undefined,\n configurable: true,\n writable: true,\n });\n bridges.push(bridge);\n }\n await app.init();\n const server = (\n app.getHttpAdapter?.() as { getInstance?(): unknown } | undefined\n )?.getInstance?.() as { ready?: () => unknown } | undefined;\n if (typeof server?.ready === \"function\") {\n await server.ready();\n }\n for (const bridge of bridges) {\n Object.defineProperty(bridge, TEST_APP, {\n value: app,\n configurable: true,\n writable: true,\n });\n }\n options.closeWith?.(close);\n return app;\n}\n\n/** Real session headers for `userId` through Better Auth's testUtils() plugin, which the instance must include. */\nexport async function authHeadersFor(\n auth: AuthLike,\n userId: string,\n): Promise<Headers> {\n const context = (await auth.$context) as {\n test?: { getAuthHeaders?(options: { userId: string }): Promise<Headers> };\n };\n if (typeof context.test?.getAuthHeaders !== \"function\") {\n throw new BetterAuthConfigurationError(\n \"TEST_UTILS_MISSING\",\n \"authHeadersFor requires Better Auth's testUtils() plugin on the instance.\",\n \"Add testUtils() from better-auth/plugins to the test instance.\",\n );\n }\n return context.test.getAuthHeaders({ userId });\n}\n\n/**\n * Replace principal resolution for one instance (default: 'default') without Better Auth I/O. Acceptance, the\n * origin check and policies still run. A policy's re-classification read of a generic 401 answers absent,\n * because the request carries no session; other instances keep the real resolver.\n */\nexport function overridePrincipal(\n builder: TestingModuleBuilder,\n principal: FixedPrincipal,\n options: { instance?: string } = {},\n): TestingModuleBuilder {\n const configured = overridesOf(builder).principals;\n configured.set(options.instance || \"default\", principal);\n builder.overrideProvider(PRINCIPAL_RESOLVER).useFactory({\n inject: [INSTANCE_REGISTRY, REQUEST_SCOPE, TRANSPORT_REGISTRY],\n factory: (\n instances: InstanceRegistry,\n scope: RequestScope,\n transports: TransportRegistry,\n ): PrincipalResolver => {\n const real = new ChainPrincipalResolver(instances, scope, transports);\n return {\n resolve(call, request): Promise<PrincipalResult> {\n if (!configured.has(request.auth.name)) {\n return real.resolve(call, request);\n }\n if (request.reclassify) {\n return Promise.resolve(absent());\n }\n const fixed = configured.get(request.auth.name);\n const value = typeof fixed === \"function\" ? fixed(call) : fixed;\n return Promise.resolve(value ? authenticated(value) : absent());\n },\n };\n },\n });\n return builder;\n}\n\n/**\n * Replace the policy invoker: `decide` answers per requirement, and undefined runs the real policy. Acceptance,\n * the delegation gate, the origin check and the decision memo still run. Later calls are consulted after earlier ones.\n */\nexport function overrideDecisions(\n builder: TestingModuleBuilder,\n decide: Decide,\n): TestingModuleBuilder {\n const configured = overridesOf(builder).deciders;\n configured.push(decide);\n const invoker: PolicyInvoker = {\n async invoke(policy, params, context) {\n for (const decider of configured) {\n const decision = await decider(policy.id, params, context.principal);\n if (decision !== undefined) {\n return decision;\n }\n }\n return policy.evaluate(params, context);\n },\n };\n builder.overrideProvider(POLICY_INVOKER).useValue(invoker);\n return builder;\n}\n\n/** A principal source that answers a fixed principal (or null for absent), for test module composition. */\nexport function testPrincipal(\n principal: AuthPrincipal | ((req: PrincipalRequest) => AuthPrincipal | null),\n options: {\n kinds?: readonly string[];\n acceptance?: \"default\" | \"explicit\";\n delegates?: boolean;\n } = {},\n): PrincipalSource {\n const fixed = typeof principal === \"function\" ? undefined : principal;\n const id = fixed?.source ?? TEST_PRINCIPAL_SOURCE;\n const kinds = options.kinds ?? (fixed ? [fixed.kind] : [\"session\"]);\n return {\n id,\n kinds: kinds as PrincipalSource[\"kinds\"],\n acceptance: options.acceptance ?? \"default\",\n delegates: options.delegates ?? Boolean(fixed?.delegation),\n async resolve(request) {\n const value =\n typeof principal === \"function\" ? principal(request) : principal;\n return value\n ? authenticated({ ...value, source: id } as AuthPrincipal)\n : absent();\n },\n };\n}\n\n/**\n * Publish a principal for this invocation from a hand-written guard. The stamp lives on ctx.getArgs() and its\n * object elements. Readers accept it from the same args array (WS, RPC and GraphQL share it between guards and\n * interceptors) or from another array with the same elements when at least two elements are objects (an HTTP request\n * and its response); a lone shared element such as a socket never identifies an invocation. Pass `instance` for\n * handlers of a named instance.\n */\nexport function stampPrincipal(\n context: ExecutionContext,\n principal: AuthPrincipal | null,\n options: { instance?: string } = {},\n): void {\n stampResult(\n context.getArgs(),\n options.instance || \"default\",\n principal ? authenticated(principal) : absent(),\n );\n}\n\nclass StampFirstReadings extends PrincipalReadings {\n override read(input: ReadingInput) {\n const stamped =\n input.plan && input.plan.access !== \"public\"\n ? stampedReading(input.args, input.plan.instance)\n : undefined;\n return stamped ?? super.read(input);\n }\n}\n\nfunction planInstance(planner: RoutePlanner, context: ExecutionContext) {\n try {\n return planner.forContext(context).instance;\n } catch {\n return \"default\";\n }\n}\n\n/**\n * Replace the guard and scope bodies everywhere with one override each of GUARD_CORE and SCOPE_CORE: the APP_GUARD\n * alias and every @UseBetterAuth()/@UseGuards(BetterAuthGuard) site delegate to the same collaborator, which\n * overrideProvider(BetterAuthGuard) plus overrideGuard(BetterAuthGuard) cannot achieve. With `{ principal }` the\n * stand-in stamps the principal (for the handler's instance) and allows; with a CanActivate, that guard runs as the\n * whole guard body and may call stampPrincipal(); an object with a canActivate method is always treated as a guard,\n * even when it also has a `principal` property. The stand-in interceptor opens the same transport-described scope\n * as the real one, reading the test stamp first, so readers and BetterAuthService perform no Better Auth I/O.\n */\nexport function overrideAuthGuard(\n builder: TestingModuleBuilder,\n impl:\n | CanActivate\n | {\n principal:\n | AuthPrincipal\n | null\n | ((ctx: ExecutionContext) => AuthPrincipal | null);\n },\n): TestingModuleBuilder {\n builder.overrideProvider(GUARD_CORE).useFactory({\n inject: [ROUTE_PLANNER],\n factory: (planner: RoutePlanner) => ({\n async canActivate(context: ExecutionContext): Promise<boolean> {\n // A CanActivate may hold its own `principal` field; its canActivate always runs.\n if (typeof (impl as Partial<CanActivate>).canActivate === \"function\") {\n const result = (impl as CanActivate).canActivate(context);\n return isObservable(result) ? lastValueFrom(result) : result;\n }\n const { principal } = impl as {\n principal:\n | AuthPrincipal\n | null\n | ((ctx: ExecutionContext) => AuthPrincipal | null);\n };\n const value =\n typeof principal === \"function\" ? principal(context) : principal;\n stampResult(\n context.getArgs(),\n planInstance(planner, context),\n value ? authenticated(value) : absent(),\n );\n return true;\n },\n }),\n });\n builder.overrideProvider(SCOPE_CORE).useFactory({\n inject: [\n INSTANCE_REGISTRY,\n ROUTE_PLANNER,\n TRANSPORT_REGISTRY,\n REQUEST_SCOPE,\n ],\n factory: (\n instances: InstanceRegistry,\n planner: RoutePlanner,\n transports: TransportRegistry,\n scope: RequestScope,\n ) =>\n new ScopeCore(\n instances,\n planner,\n transports,\n scope,\n new StampFirstReadings(scope),\n ),\n });\n return builder;\n}\n"],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import { Ct as AuthPrincipal, ht as TransportCall, it as PrincipalSource, tt as PrincipalRequest, u as AuthorizationDecision, xt as AuthLike } from "./auth-contracts-Brh_Qko9.cjs";
2
+ import { CanActivate, ExecutionContext, INestApplication } from "@nestjs/common";
3
+ import { TestingModuleBuilder } from "@nestjs/testing";
4
+ //#region src/testing.d.ts
5
+ type FixedPrincipal = AuthPrincipal | null | ((call: TransportCall) => AuthPrincipal | null);
6
+ type Decide = (policyId: string, params: unknown, principal: AuthPrincipal) => AuthorizationDecision | undefined | Promise<AuthorizationDecision | undefined>;
7
+ /**
8
+ * init() plus Fastify ready() when the adapter exposes it. Before init, closes the previous application that
9
+ * initTestApp initialized for the same Better Auth instance and that is still bound to it: an instance binds
10
+ * exclusively to one application (B06). A failed init() leaves its binding for the next application, which takes it
11
+ * over with W_INSTANCE_TAKEN_OVER. app.close() becomes idempotent, and `closeWith` receives it: a per-test cleanup such as
12
+ * Vitest's onTestFinished or node:test's t.after, or a list that a suite-level afterEach drains (Jest registers no hooks
13
+ * while a test runs). Without `closeWith`, close the last application yourself. On Express, compile one TestingModule per application: a
14
+ * second application of one container fails init() with APP_ADAPTER_CHANGED.
15
+ */
16
+ export declare function initTestApp<T extends INestApplication>(app: T, options?: {
17
+ closeWith?: (close: () => Promise<void>) => void;
18
+ }): Promise<T>;
19
+ /** Real session headers for `userId` through Better Auth's testUtils() plugin, which the instance must include. */
20
+ export declare function authHeadersFor(auth: AuthLike, userId: string): Promise<Headers>;
21
+ /**
22
+ * Replace principal resolution for one instance (default: 'default') without Better Auth I/O. Acceptance, the
23
+ * origin check and policies still run. A policy's re-classification read of a generic 401 answers absent,
24
+ * because the request carries no session; other instances keep the real resolver.
25
+ */
26
+ export declare function overridePrincipal(builder: TestingModuleBuilder, principal: FixedPrincipal, options?: {
27
+ instance?: string;
28
+ }): TestingModuleBuilder;
29
+ /**
30
+ * Replace the policy invoker: `decide` answers per requirement, and undefined runs the real policy. Acceptance,
31
+ * the delegation gate, the origin check and the decision memo still run. Later calls are consulted after earlier ones.
32
+ */
33
+ export declare function overrideDecisions(builder: TestingModuleBuilder, decide: Decide): TestingModuleBuilder;
34
+ /** A principal source that answers a fixed principal (or null for absent), for test module composition. */
35
+ export declare function testPrincipal(principal: AuthPrincipal | ((req: PrincipalRequest) => AuthPrincipal | null), options?: {
36
+ kinds?: readonly string[];
37
+ acceptance?: "default" | "explicit";
38
+ delegates?: boolean;
39
+ }): PrincipalSource;
40
+ /**
41
+ * Publish a principal for this invocation from a hand-written guard. The stamp lives on ctx.getArgs() and its
42
+ * object elements. Readers accept it from the same args array (WS, RPC and GraphQL share it between guards and
43
+ * interceptors) or from another array with the same elements when at least two elements are objects (an HTTP request
44
+ * and its response); a lone shared element such as a socket never identifies an invocation. Pass `instance` for
45
+ * handlers of a named instance.
46
+ */
47
+ export declare function stampPrincipal(context: ExecutionContext, principal: AuthPrincipal | null, options?: {
48
+ instance?: string;
49
+ }): void;
50
+ /**
51
+ * Replace the guard and scope bodies everywhere with one override each of GUARD_CORE and SCOPE_CORE: the APP_GUARD
52
+ * alias and every @UseBetterAuth()/@UseGuards(BetterAuthGuard) site delegate to the same collaborator, which
53
+ * overrideProvider(BetterAuthGuard) plus overrideGuard(BetterAuthGuard) cannot achieve. With `{ principal }` the
54
+ * stand-in stamps the principal (for the handler's instance) and allows; with a CanActivate, that guard runs as the
55
+ * whole guard body and may call stampPrincipal(); an object with a canActivate method is always treated as a guard,
56
+ * even when it also has a `principal` property. The stand-in interceptor opens the same transport-described scope
57
+ * as the real one, reading the test stamp first, so readers and BetterAuthService perform no Better Auth I/O.
58
+ */
59
+ export declare function overrideAuthGuard(builder: TestingModuleBuilder, impl: CanActivate | {
60
+ principal: AuthPrincipal | null | ((ctx: ExecutionContext) => AuthPrincipal | null);
61
+ }): TestingModuleBuilder;
62
+ //#endregion
63
+ export {};
64
+ //# sourceMappingURL=testing.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.cts","names":[],"sources":["../src/testing.ts"],"mappings":";;;;KAoDK,iBACD,yBAEE,MAAM,kBAAkB;KACzB,UACH,kBACA,iBACA,WAAW,kBAET,oCAEA,QAAQ;;;;;;;;;;wBAoEU,YAAY,UAAU,kBAC1C,KAAK,GACL;EAAW,aAAa,aAAa;IACpC,QAAQ;;wBA2CW,eACpB,MAAM,UACN,iBACC,QAAQ;;;;;;wBAmBK,kBACd,SAAS,sBACT,WAAW,gBACX;EAAW;IACV;;;;;wBAiCa,kBACd,SAAS,sBACT,QAAQ,SACP;;wBAmBa,cACd,WAAW,kBAAkB,KAAK,qBAAqB,uBACvD;EACE;EACA;EACA;IAED;;;;;;;;wBA0Ba,eACd,SAAS,kBACT,WAAW,sBACX;EAAW;;;;;;;;;;;wBAoCG,kBACd,SAAS,sBACT,MACI;EAEE,WACI,yBAEE,KAAK,qBAAqB;IAErC"}
@@ -0,0 +1,64 @@
1
+ import { Ct as AuthPrincipal, ht as TransportCall, it as PrincipalSource, tt as PrincipalRequest, u as AuthorizationDecision, xt as AuthLike } from "./auth-contracts-Cz7C4_Ch.mjs";
2
+ import { CanActivate, ExecutionContext, INestApplication } from "@nestjs/common";
3
+ import { TestingModuleBuilder } from "@nestjs/testing";
4
+ //#region src/testing.d.ts
5
+ type FixedPrincipal = AuthPrincipal | null | ((call: TransportCall) => AuthPrincipal | null);
6
+ type Decide = (policyId: string, params: unknown, principal: AuthPrincipal) => AuthorizationDecision | undefined | Promise<AuthorizationDecision | undefined>;
7
+ /**
8
+ * init() plus Fastify ready() when the adapter exposes it. Before init, closes the previous application that
9
+ * initTestApp initialized for the same Better Auth instance and that is still bound to it: an instance binds
10
+ * exclusively to one application (B06). A failed init() leaves its binding for the next application, which takes it
11
+ * over with W_INSTANCE_TAKEN_OVER. app.close() becomes idempotent, and `closeWith` receives it: a per-test cleanup such as
12
+ * Vitest's onTestFinished or node:test's t.after, or a list that a suite-level afterEach drains (Jest registers no hooks
13
+ * while a test runs). Without `closeWith`, close the last application yourself. On Express, compile one TestingModule per application: a
14
+ * second application of one container fails init() with APP_ADAPTER_CHANGED.
15
+ */
16
+ export declare function initTestApp<T extends INestApplication>(app: T, options?: {
17
+ closeWith?: (close: () => Promise<void>) => void;
18
+ }): Promise<T>;
19
+ /** Real session headers for `userId` through Better Auth's testUtils() plugin, which the instance must include. */
20
+ export declare function authHeadersFor(auth: AuthLike, userId: string): Promise<Headers>;
21
+ /**
22
+ * Replace principal resolution for one instance (default: 'default') without Better Auth I/O. Acceptance, the
23
+ * origin check and policies still run. A policy's re-classification read of a generic 401 answers absent,
24
+ * because the request carries no session; other instances keep the real resolver.
25
+ */
26
+ export declare function overridePrincipal(builder: TestingModuleBuilder, principal: FixedPrincipal, options?: {
27
+ instance?: string;
28
+ }): TestingModuleBuilder;
29
+ /**
30
+ * Replace the policy invoker: `decide` answers per requirement, and undefined runs the real policy. Acceptance,
31
+ * the delegation gate, the origin check and the decision memo still run. Later calls are consulted after earlier ones.
32
+ */
33
+ export declare function overrideDecisions(builder: TestingModuleBuilder, decide: Decide): TestingModuleBuilder;
34
+ /** A principal source that answers a fixed principal (or null for absent), for test module composition. */
35
+ export declare function testPrincipal(principal: AuthPrincipal | ((req: PrincipalRequest) => AuthPrincipal | null), options?: {
36
+ kinds?: readonly string[];
37
+ acceptance?: "default" | "explicit";
38
+ delegates?: boolean;
39
+ }): PrincipalSource;
40
+ /**
41
+ * Publish a principal for this invocation from a hand-written guard. The stamp lives on ctx.getArgs() and its
42
+ * object elements. Readers accept it from the same args array (WS, RPC and GraphQL share it between guards and
43
+ * interceptors) or from another array with the same elements when at least two elements are objects (an HTTP request
44
+ * and its response); a lone shared element such as a socket never identifies an invocation. Pass `instance` for
45
+ * handlers of a named instance.
46
+ */
47
+ export declare function stampPrincipal(context: ExecutionContext, principal: AuthPrincipal | null, options?: {
48
+ instance?: string;
49
+ }): void;
50
+ /**
51
+ * Replace the guard and scope bodies everywhere with one override each of GUARD_CORE and SCOPE_CORE: the APP_GUARD
52
+ * alias and every @UseBetterAuth()/@UseGuards(BetterAuthGuard) site delegate to the same collaborator, which
53
+ * overrideProvider(BetterAuthGuard) plus overrideGuard(BetterAuthGuard) cannot achieve. With `{ principal }` the
54
+ * stand-in stamps the principal (for the handler's instance) and allows; with a CanActivate, that guard runs as the
55
+ * whole guard body and may call stampPrincipal(); an object with a canActivate method is always treated as a guard,
56
+ * even when it also has a `principal` property. The stand-in interceptor opens the same transport-described scope
57
+ * as the real one, reading the test stamp first, so readers and BetterAuthService perform no Better Auth I/O.
58
+ */
59
+ export declare function overrideAuthGuard(builder: TestingModuleBuilder, impl: CanActivate | {
60
+ principal: AuthPrincipal | null | ((ctx: ExecutionContext) => AuthPrincipal | null);
61
+ }): TestingModuleBuilder;
62
+ //#endregion
63
+ export {};
64
+ //# sourceMappingURL=testing.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.mts","names":[],"sources":["../src/testing.ts"],"mappings":";;;;KAoDK,iBACD,yBAEE,MAAM,kBAAkB;KACzB,UACH,kBACA,iBACA,WAAW,kBAET,oCAEA,QAAQ;;;;;;;;;;wBAoEU,YAAY,UAAU,kBAC1C,KAAK,GACL;EAAW,aAAa,aAAa;IACpC,QAAQ;;wBA2CW,eACpB,MAAM,UACN,iBACC,QAAQ;;;;;;wBAmBK,kBACd,SAAS,sBACT,WAAW,gBACX;EAAW;IACV;;;;;wBAiCa,kBACd,SAAS,sBACT,QAAQ,SACP;;wBAmBa,cACd,WAAW,kBAAkB,KAAK,qBAAqB,uBACvD;EACE;EACA;EACA;IAED;;;;;;;;wBA0Ba,eACd,SAAS,kBACT,WAAW,sBACX;EAAW;;;;;;;;;;;wBAoCG,kBACd,SAAS,sBACT,MACI;EAEE,WACI,yBAEE,KAAK,qBAAqB;IAErC"}
@@ -0,0 +1,214 @@
1
+ import { n as BetterAuthConfigurationError } from "./auth-errors-C3rbTV9p.mjs";
2
+ import { C as PRINCIPAL_RESOLVER, D as ROUTE_PLANNER, M as TRANSPORT_REGISTRY, O as SCOPE_CORE, T as REQUEST_SCOPE, f as GUARD_CORE, m as INSTANCE_REGISTRY, y as POLICY_INVOKER } from "./decorate-Dt0e9K6R.mjs";
3
+ import { a as absent, n as ScopeCore, o as authenticated, r as ChainPrincipalResolver } from "./auth-scope-interceptor-DYPn3b8M.mjs";
4
+ import { t as BRIDGE_HANDLE } from "./bridge-protocol-c66aEkr5.mjs";
5
+ import { n as stampResult, r as stampedReading, t as PrincipalReadings } from "./principal-readings-Ied2staw.mjs";
6
+ import { isObservable, lastValueFrom } from "rxjs";
7
+ //#region src/testing.ts
8
+ const TEST_APP = Symbol.for("@nestjs-kit/better-auth:test-app");
9
+ const TEST_APP_CLOSE = Symbol.for("@nestjs-kit/better-auth:test-app-close");
10
+ const TEST_OVERRIDES = Symbol.for("@nestjs-kit/better-auth:test-overrides");
11
+ const PLUGIN_ID = "@nestjs-kit/better-auth";
12
+ const TEST_PRINCIPAL_SOURCE = "@nestjs-kit/better-auth:test-principal";
13
+ /**
14
+ * TestingModuleBuilder keys overrides by token, so repeated helper calls share one provider and its configuration.
15
+ * The configuration lives on the builder under a global symbol, so the ESM and CommonJS copies of this entry compose.
16
+ */
17
+ function overridesOf(builder) {
18
+ let overrides = Reflect.get(builder, TEST_OVERRIDES);
19
+ if (!overrides) {
20
+ overrides = {
21
+ principals: /* @__PURE__ */ new Map(),
22
+ deciders: []
23
+ };
24
+ Object.defineProperty(builder, TEST_OVERRIDES, { value: overrides });
25
+ }
26
+ return overrides;
27
+ }
28
+ /** Make app.close() run Nest's shutdown once, whoever calls it: initTestApp, a registered hook or the test. */
29
+ function closeOnce(app) {
30
+ const existing = Reflect.get(app, TEST_APP_CLOSE);
31
+ if (existing) return existing;
32
+ const close = app.close.bind(app);
33
+ let pending;
34
+ const once = () => {
35
+ pending ??= close();
36
+ return pending;
37
+ };
38
+ Object.defineProperty(app, TEST_APP_CLOSE, { value: once });
39
+ app.close = once;
40
+ return once;
41
+ }
42
+ async function bridgeOf(auth) {
43
+ const plugin = (await auth.$context).getPlugin?.(PLUGIN_ID);
44
+ return plugin && typeof plugin === "object" ? Reflect.get(plugin, BRIDGE_HANDLE) : void 0;
45
+ }
46
+ function registryOf(app) {
47
+ try {
48
+ return app.get(INSTANCE_REGISTRY, { strict: false });
49
+ } catch {
50
+ return;
51
+ }
52
+ }
53
+ /**
54
+ * init() plus Fastify ready() when the adapter exposes it. Before init, closes the previous application that
55
+ * initTestApp initialized for the same Better Auth instance and that is still bound to it: an instance binds
56
+ * exclusively to one application (B06). A failed init() leaves its binding for the next application, which takes it
57
+ * over with W_INSTANCE_TAKEN_OVER. app.close() becomes idempotent, and `closeWith` receives it: a per-test cleanup such as
58
+ * Vitest's onTestFinished or node:test's t.after, or a list that a suite-level afterEach drains (Jest registers no hooks
59
+ * while a test runs). Without `closeWith`, close the last application yourself. On Express, compile one TestingModule per application: a
60
+ * second application of one container fails init() with APP_ADAPTER_CHANGED.
61
+ */
62
+ async function initTestApp(app, options = {}) {
63
+ const close = closeOnce(app);
64
+ const bridges = [];
65
+ for (const registration of registryOf(app)?.registrations() ?? []) {
66
+ const bridge = registration.instance ? await bridgeOf(registration.instance) : void 0;
67
+ if (!bridge) continue;
68
+ const previous = Reflect.get(bridge, TEST_APP);
69
+ if (previous && previous !== app && bridge.state === "bound") await previous.close();
70
+ Object.defineProperty(bridge, TEST_APP, {
71
+ value: void 0,
72
+ configurable: true,
73
+ writable: true
74
+ });
75
+ bridges.push(bridge);
76
+ }
77
+ await app.init();
78
+ const server = (app.getHttpAdapter?.())?.getInstance?.();
79
+ if (typeof server?.ready === "function") await server.ready();
80
+ for (const bridge of bridges) Object.defineProperty(bridge, TEST_APP, {
81
+ value: app,
82
+ configurable: true,
83
+ writable: true
84
+ });
85
+ options.closeWith?.(close);
86
+ return app;
87
+ }
88
+ /** Real session headers for `userId` through Better Auth's testUtils() plugin, which the instance must include. */
89
+ async function authHeadersFor(auth, userId) {
90
+ const context = await auth.$context;
91
+ if (typeof context.test?.getAuthHeaders !== "function") throw new BetterAuthConfigurationError("TEST_UTILS_MISSING", "authHeadersFor requires Better Auth's testUtils() plugin on the instance.", "Add testUtils() from better-auth/plugins to the test instance.");
92
+ return context.test.getAuthHeaders({ userId });
93
+ }
94
+ /**
95
+ * Replace principal resolution for one instance (default: 'default') without Better Auth I/O. Acceptance, the
96
+ * origin check and policies still run. A policy's re-classification read of a generic 401 answers absent,
97
+ * because the request carries no session; other instances keep the real resolver.
98
+ */
99
+ function overridePrincipal(builder, principal, options = {}) {
100
+ const configured = overridesOf(builder).principals;
101
+ configured.set(options.instance || "default", principal);
102
+ builder.overrideProvider(PRINCIPAL_RESOLVER).useFactory({
103
+ inject: [
104
+ INSTANCE_REGISTRY,
105
+ REQUEST_SCOPE,
106
+ TRANSPORT_REGISTRY
107
+ ],
108
+ factory: (instances, scope, transports) => {
109
+ const real = new ChainPrincipalResolver(instances, scope, transports);
110
+ return { resolve(call, request) {
111
+ if (!configured.has(request.auth.name)) return real.resolve(call, request);
112
+ if (request.reclassify) return Promise.resolve(absent());
113
+ const fixed = configured.get(request.auth.name);
114
+ const value = typeof fixed === "function" ? fixed(call) : fixed;
115
+ return Promise.resolve(value ? authenticated(value) : absent());
116
+ } };
117
+ }
118
+ });
119
+ return builder;
120
+ }
121
+ /**
122
+ * Replace the policy invoker: `decide` answers per requirement, and undefined runs the real policy. Acceptance,
123
+ * the delegation gate, the origin check and the decision memo still run. Later calls are consulted after earlier ones.
124
+ */
125
+ function overrideDecisions(builder, decide) {
126
+ const configured = overridesOf(builder).deciders;
127
+ configured.push(decide);
128
+ builder.overrideProvider(POLICY_INVOKER).useValue({ async invoke(policy, params, context) {
129
+ for (const decider of configured) {
130
+ const decision = await decider(policy.id, params, context.principal);
131
+ if (decision !== void 0) return decision;
132
+ }
133
+ return policy.evaluate(params, context);
134
+ } });
135
+ return builder;
136
+ }
137
+ /** A principal source that answers a fixed principal (or null for absent), for test module composition. */
138
+ function testPrincipal(principal, options = {}) {
139
+ const fixed = typeof principal === "function" ? void 0 : principal;
140
+ const id = fixed?.source ?? TEST_PRINCIPAL_SOURCE;
141
+ return {
142
+ id,
143
+ kinds: options.kinds ?? (fixed ? [fixed.kind] : ["session"]),
144
+ acceptance: options.acceptance ?? "default",
145
+ delegates: options.delegates ?? Boolean(fixed?.delegation),
146
+ async resolve(request) {
147
+ const value = typeof principal === "function" ? principal(request) : principal;
148
+ return value ? authenticated({
149
+ ...value,
150
+ source: id
151
+ }) : absent();
152
+ }
153
+ };
154
+ }
155
+ /**
156
+ * Publish a principal for this invocation from a hand-written guard. The stamp lives on ctx.getArgs() and its
157
+ * object elements. Readers accept it from the same args array (WS, RPC and GraphQL share it between guards and
158
+ * interceptors) or from another array with the same elements when at least two elements are objects (an HTTP request
159
+ * and its response); a lone shared element such as a socket never identifies an invocation. Pass `instance` for
160
+ * handlers of a named instance.
161
+ */
162
+ function stampPrincipal(context, principal, options = {}) {
163
+ stampResult(context.getArgs(), options.instance || "default", principal ? authenticated(principal) : absent());
164
+ }
165
+ var StampFirstReadings = class extends PrincipalReadings {
166
+ read(input) {
167
+ return (input.plan && input.plan.access !== "public" ? stampedReading(input.args, input.plan.instance) : void 0) ?? super.read(input);
168
+ }
169
+ };
170
+ function planInstance(planner, context) {
171
+ try {
172
+ return planner.forContext(context).instance;
173
+ } catch {
174
+ return "default";
175
+ }
176
+ }
177
+ /**
178
+ * Replace the guard and scope bodies everywhere with one override each of GUARD_CORE and SCOPE_CORE: the APP_GUARD
179
+ * alias and every @UseBetterAuth()/@UseGuards(BetterAuthGuard) site delegate to the same collaborator, which
180
+ * overrideProvider(BetterAuthGuard) plus overrideGuard(BetterAuthGuard) cannot achieve. With `{ principal }` the
181
+ * stand-in stamps the principal (for the handler's instance) and allows; with a CanActivate, that guard runs as the
182
+ * whole guard body and may call stampPrincipal(); an object with a canActivate method is always treated as a guard,
183
+ * even when it also has a `principal` property. The stand-in interceptor opens the same transport-described scope
184
+ * as the real one, reading the test stamp first, so readers and BetterAuthService perform no Better Auth I/O.
185
+ */
186
+ function overrideAuthGuard(builder, impl) {
187
+ builder.overrideProvider(GUARD_CORE).useFactory({
188
+ inject: [ROUTE_PLANNER],
189
+ factory: (planner) => ({ async canActivate(context) {
190
+ if (typeof impl.canActivate === "function") {
191
+ const result = impl.canActivate(context);
192
+ return isObservable(result) ? lastValueFrom(result) : result;
193
+ }
194
+ const { principal } = impl;
195
+ const value = typeof principal === "function" ? principal(context) : principal;
196
+ stampResult(context.getArgs(), planInstance(planner, context), value ? authenticated(value) : absent());
197
+ return true;
198
+ } })
199
+ });
200
+ builder.overrideProvider(SCOPE_CORE).useFactory({
201
+ inject: [
202
+ INSTANCE_REGISTRY,
203
+ ROUTE_PLANNER,
204
+ TRANSPORT_REGISTRY,
205
+ REQUEST_SCOPE
206
+ ],
207
+ factory: (instances, planner, transports, scope) => new ScopeCore(instances, planner, transports, scope, new StampFirstReadings(scope))
208
+ });
209
+ return builder;
210
+ }
211
+ //#endregion
212
+ export { authHeadersFor, initTestApp, overrideAuthGuard, overrideDecisions, overridePrincipal, stampPrincipal, testPrincipal };
213
+
214
+ //# sourceMappingURL=testing.mjs.map