@lenne.tech/nest-server 11.27.5 → 11.27.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/rules/architecture.md +66 -0
- package/.claude/rules/better-auth.md +88 -2
- package/.claude/rules/configurable-features.md +2 -2
- package/.claude/rules/package-management.md +23 -14
- package/.claude/rules/testing.md +27 -9
- package/CLAUDE.md +12 -4
- package/FRAMEWORK-API.md +1 -1
- package/bin/migrate.js +84 -25
- package/dist/core/common/decorators/restricted.decorator.d.ts +4 -4
- package/dist/core/common/decorators/restricted.decorator.js +21 -22
- package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
- package/dist/core/common/helpers/clone.helper.d.ts +6 -0
- package/dist/core/common/helpers/clone.helper.js +67 -0
- package/dist/core/common/helpers/clone.helper.js.map +1 -0
- package/dist/core/common/helpers/cookies.helper.d.ts +1 -0
- package/dist/core/common/helpers/cookies.helper.js +33 -16
- package/dist/core/common/helpers/cookies.helper.js.map +1 -1
- package/dist/core/common/helpers/db.helper.d.ts +2 -14
- package/dist/core/common/helpers/db.helper.js +13 -106
- package/dist/core/common/helpers/db.helper.js.map +1 -1
- package/dist/core/common/helpers/id.helper.d.ts +15 -0
- package/dist/core/common/helpers/id.helper.js +104 -0
- package/dist/core/common/helpers/id.helper.js.map +1 -0
- package/dist/core/common/helpers/input.helper.d.ts +1 -6
- package/dist/core/common/helpers/input.helper.js +11 -70
- package/dist/core/common/helpers/input.helper.js.map +1 -1
- package/dist/core/common/inputs/combined-filter.input.d.ts +1 -12
- package/dist/core/common/inputs/combined-filter.input.js +2 -51
- package/dist/core/common/inputs/combined-filter.input.js.map +1 -1
- package/dist/core/common/inputs/filter.input.d.ts +10 -1
- package/dist/core/common/inputs/filter.input.js +40 -5
- package/dist/core/common/inputs/filter.input.js.map +1 -1
- package/dist/core/common/services/config.service.js +5 -5
- package/dist/core/common/services/config.service.js.map +1 -1
- package/dist/core/modules/ai/core-ai.constants.d.ts +22 -0
- package/dist/core/modules/ai/core-ai.constants.js +26 -0
- package/dist/core/modules/ai/core-ai.constants.js.map +1 -0
- package/dist/core/modules/ai/index.d.ts +1 -0
- package/dist/core/modules/ai/index.js +1 -0
- package/dist/core/modules/ai/index.js.map +1 -1
- package/dist/core/modules/ai/interfaces/ai-interaction-record.interface.d.ts +17 -0
- package/dist/core/modules/ai/interfaces/ai-interaction-record.interface.js +3 -0
- package/dist/core/modules/ai/interfaces/ai-interaction-record.interface.js.map +1 -0
- package/dist/core/modules/ai/services/core-ai-budget.service.d.ts +1 -2
- package/dist/core/modules/ai/services/core-ai-budget.service.js +7 -5
- package/dist/core/modules/ai/services/core-ai-budget.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-connection-preference.service.d.ts +1 -2
- package/dist/core/modules/ai/services/core-ai-connection-preference.service.js +7 -5
- package/dist/core/modules/ai/services/core-ai-connection-preference.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-connection.service.d.ts +1 -2
- package/dist/core/modules/ai/services/core-ai-connection.service.js +7 -5
- package/dist/core/modules/ai/services/core-ai-connection.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-conversation.service.d.ts +1 -2
- package/dist/core/modules/ai/services/core-ai-conversation.service.js +7 -5
- package/dist/core/modules/ai/services/core-ai-conversation.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-interaction.service.d.ts +2 -3
- package/dist/core/modules/ai/services/core-ai-interaction.service.js +7 -5
- package/dist/core/modules/ai/services/core-ai-interaction.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-mode.service.d.ts +1 -2
- package/dist/core/modules/ai/services/core-ai-mode.service.js +7 -5
- package/dist/core/modules/ai/services/core-ai-mode.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-prompt-hint.service.d.ts +1 -3
- package/dist/core/modules/ai/services/core-ai-prompt-hint.service.js +7 -5
- package/dist/core/modules/ai/services/core-ai-prompt-hint.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-prompt.service.d.ts +1 -2
- package/dist/core/modules/ai/services/core-ai-prompt.service.js +7 -5
- package/dist/core/modules/ai/services/core-ai-prompt.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-slot.service.d.ts +1 -2
- package/dist/core/modules/ai/services/core-ai-slot.service.js +7 -5
- package/dist/core/modules/ai/services/core-ai-slot.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-tool-grant.service.d.ts +1 -2
- package/dist/core/modules/ai/services/core-ai-tool-grant.service.js +7 -5
- package/dist/core/modules/ai/services/core-ai-tool-grant.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-tool-policy.service.d.ts +1 -2
- package/dist/core/modules/ai/services/core-ai-tool-policy.service.js +7 -5
- package/dist/core/modules/ai/services/core-ai-tool-policy.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai.service.d.ts +2 -17
- package/dist/core/modules/ai/services/core-ai.service.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth-roles.guard.js +2 -2
- package/dist/core/modules/better-auth/better-auth-roles.guard.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.config.js +12 -8
- package/dist/core/modules/better-auth/better-auth.config.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth.constants.d.ts +3 -0
- package/dist/core/modules/better-auth/core-better-auth.constants.js +7 -0
- package/dist/core/modules/better-auth/core-better-auth.constants.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth.module.d.ts +1 -2
- package/dist/core/modules/better-auth/core-better-auth.module.js +23 -20
- package/dist/core/modules/better-auth/core-better-auth.module.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth.registry.d.ts +4 -0
- package/dist/core/modules/better-auth/core-better-auth.registry.js +16 -0
- package/dist/core/modules/better-auth/core-better-auth.registry.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth.service.d.ts +1 -2
- package/dist/core/modules/better-auth/core-better-auth.service.js +7 -6
- package/dist/core/modules/better-auth/core-better-auth.service.js.map +1 -1
- package/dist/core/modules/better-auth/index.d.ts +1 -0
- package/dist/core/modules/better-auth/index.js +1 -0
- package/dist/core/modules/better-auth/index.js.map +1 -1
- package/dist/core/modules/migrate/migration-runner.d.ts +1 -0
- package/dist/core/modules/migrate/migration-runner.js +3 -2
- package/dist/core/modules/migrate/migration-runner.js.map +1 -1
- package/dist/core/modules/tus/tus.constants.d.ts +1 -0
- package/dist/core/modules/tus/tus.constants.js +5 -0
- package/dist/core/modules/tus/tus.constants.js.map +1 -0
- package/dist/core/modules/tus/tus.module.d.ts +1 -1
- package/dist/core/modules/tus/tus.module.js +8 -6
- package/dist/core/modules/tus/tus.module.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/docs/REQUEST-LIFECYCLE.md +17 -4
- package/migration-guides/11.27.5-to-11.27.6.md +359 -0
- package/migration-guides/11.27.6-to-11.27.7.md +374 -0
- package/package.json +11 -104
- package/src/core/common/decorators/restricted.decorator.ts +33 -8
- package/src/core/common/helpers/clone.helper.ts +110 -0
- package/src/core/common/helpers/cookies.helper.ts +101 -26
- package/src/core/common/helpers/db.helper.ts +14 -161
- package/src/core/common/helpers/id.helper.ts +198 -0
- package/src/core/common/helpers/input.helper.ts +12 -84
- package/src/core/common/inputs/combined-filter.input.ts +10 -57
- package/src/core/common/inputs/filter.input.ts +92 -1
- package/src/core/common/interfaces/server-options.interface.ts +35 -6
- package/src/core/common/services/config.service.ts +4 -1
- package/src/core/modules/ai/core-ai.constants.ts +92 -0
- package/src/core/modules/ai/index.ts +1 -0
- package/src/core/modules/ai/interfaces/ai-interaction-record.interface.ts +34 -0
- package/src/core/modules/ai/services/core-ai-budget.service.ts +8 -4
- package/src/core/modules/ai/services/core-ai-connection-preference.service.ts +8 -4
- package/src/core/modules/ai/services/core-ai-connection.service.ts +5 -6
- package/src/core/modules/ai/services/core-ai-conversation.service.ts +5 -6
- package/src/core/modules/ai/services/core-ai-interaction.service.ts +6 -7
- package/src/core/modules/ai/services/core-ai-mode.service.ts +8 -2
- package/src/core/modules/ai/services/core-ai-prompt-hint.service.ts +7 -4
- package/src/core/modules/ai/services/core-ai-prompt.service.ts +8 -2
- package/src/core/modules/ai/services/core-ai-slot.service.ts +7 -4
- package/src/core/modules/ai/services/core-ai-tool-grant.service.ts +8 -4
- package/src/core/modules/ai/services/core-ai-tool-policy.service.ts +8 -4
- package/src/core/modules/ai/services/core-ai.service.ts +8 -11
- package/src/core/modules/better-auth/README.md +10 -4
- package/src/core/modules/better-auth/better-auth-roles.guard.ts +11 -5
- package/src/core/modules/better-auth/better-auth.config.ts +90 -15
- package/src/core/modules/better-auth/core-better-auth.constants.ts +73 -0
- package/src/core/modules/better-auth/core-better-auth.module.ts +34 -8
- package/src/core/modules/better-auth/core-better-auth.registry.ts +53 -0
- package/src/core/modules/better-auth/core-better-auth.service.ts +19 -12
- package/src/core/modules/better-auth/index.ts +9 -0
- package/src/core/modules/migrate/migration-runner.ts +16 -2
- package/src/core/modules/tus/tus.constants.ts +25 -0
- package/src/core/modules/tus/tus.module.ts +7 -2
|
@@ -20,6 +20,7 @@ export * from './inputs/core-ai-slot-update.input';
|
|
|
20
20
|
export * from './inputs/core-ai-prompt.input';
|
|
21
21
|
export * from './hooks/ai-hook.base';
|
|
22
22
|
export * from './hooks/ai-hook.registry';
|
|
23
|
+
export * from './interfaces/ai-interaction-record.interface';
|
|
23
24
|
export * from './interfaces/ai-hook.interface';
|
|
24
25
|
export * from './interfaces/ai-placeholder.interface';
|
|
25
26
|
export * from './interfaces/ai-tool.interface';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The record passed to `CoreAiService.audit()` for each prompt run.
|
|
3
|
+
*
|
|
4
|
+
* It lives here, in an import-free leaf, rather than in `core-ai.service.ts` — and that is
|
|
5
|
+
* load-bearing, not tidiness.
|
|
6
|
+
*
|
|
7
|
+
* `core-ai.service` imports `core-ai-interaction.service` (it needs the class), and
|
|
8
|
+
* `core-ai-interaction.service` needs this type back. Declared in the service, that pair forms an
|
|
9
|
+
* import cycle. It never crashed for exactly one reason: the type was pulled in with `import type`,
|
|
10
|
+
* which both tsc and SWC erase, so no `require()` was emitted and the cycle was never real at
|
|
11
|
+
* runtime.
|
|
12
|
+
*
|
|
13
|
+
* That is a single keyword of protection, on a file where `core-ai.service` has a constructor
|
|
14
|
+
* `@Inject`, so `emitDecoratorMetadata` emits `design:paramtypes` at module-evaluation time — the
|
|
15
|
+
* exact eval-time dereference that turns a cycle fatal. An IDE "organize imports" or a lint autofix
|
|
16
|
+
* widening `import type` to a plain `import` would have armed it silently, and `tsc`, `pnpm test`
|
|
17
|
+
* and `oxlint` are ALL blind to that (vitest runs SWC through Vite's cycle-tolerant module runner).
|
|
18
|
+
* Only `pnpm run check:swc-tdz` would have caught it — after the fact.
|
|
19
|
+
*
|
|
20
|
+
* Moving the type out removes the edge entirely, so the cycle no longer exists rather than merely
|
|
21
|
+
* being disarmed. `core-ai.service` re-exports it, so every existing import path still resolves.
|
|
22
|
+
*
|
|
23
|
+
* See .claude/rules/architecture.md → "DI Token Placement (SWC-Safe)".
|
|
24
|
+
*/
|
|
25
|
+
export interface AiInteractionRecord {
|
|
26
|
+
actions: { name: string; success: boolean }[];
|
|
27
|
+
connectionId: string;
|
|
28
|
+
iterations: number;
|
|
29
|
+
prompt: string;
|
|
30
|
+
responseText: string;
|
|
31
|
+
tenantId?: string;
|
|
32
|
+
usage?: { completionTokens?: number; promptTokens?: number; totalTokens?: number };
|
|
33
|
+
userId?: string;
|
|
34
|
+
}
|
|
@@ -11,10 +11,14 @@ import { CoreAiBudgetLimitInput } from '../inputs/core-ai-budget-limit.input';
|
|
|
11
11
|
import { AiBudgetLimitDocument, CoreAiBudgetLimit } from '../models/core-ai-budget-limit.model';
|
|
12
12
|
import { CoreAiBudgetSummary, CoreAiUsageInfo, CoreAiUsageScope } from '../models/core-ai-usage-info.model';
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
|
|
14
|
+
import { AI_BUDGET_LIMIT_CLASS, AI_BUDGET_LIMIT_MODEL } from '../core-ai.constants';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @deprecated Import from `../core-ai.constants` instead. Re-exported only so existing deep imports
|
|
18
|
+
* keep working; the tokens are declared in an import-free leaf so no cycle can form around them
|
|
19
|
+
* (SWC-safe — see core-ai.constants.ts).
|
|
20
|
+
*/
|
|
21
|
+
export { AI_BUDGET_LIMIT_CLASS, AI_BUDGET_LIMIT_MODEL } from '../core-ai.constants';
|
|
18
22
|
|
|
19
23
|
/** Resolved effective limit for a scope. */
|
|
20
24
|
export interface ResolvedAiBudgetLimit {
|
|
@@ -10,10 +10,14 @@ import {
|
|
|
10
10
|
CoreAiConnectionPreference,
|
|
11
11
|
} from '../models/core-ai-connection-preference.model';
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
|
|
13
|
+
import { AI_CONNECTION_PREFERENCE_CLASS, AI_CONNECTION_PREFERENCE_MODEL } from '../core-ai.constants';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @deprecated Import from `../core-ai.constants` instead. Re-exported only so existing deep imports
|
|
17
|
+
* keep working; the tokens are declared in an import-free leaf so no cycle can form around them
|
|
18
|
+
* (SWC-safe — see core-ai.constants.ts).
|
|
19
|
+
*/
|
|
20
|
+
export { AI_CONNECTION_PREFERENCE_CLASS, AI_CONNECTION_PREFERENCE_MODEL } from '../core-ai.constants';
|
|
17
21
|
|
|
18
22
|
/**
|
|
19
23
|
* CRUD + lookup for {@link CoreAiConnectionPreference} (tenant/user connection
|
|
@@ -23,15 +23,14 @@ import { LlmProviderFactory } from '../providers/llm-provider.factory';
|
|
|
23
23
|
import { AiCryptoService } from './ai-crypto.service';
|
|
24
24
|
import { CoreAiConnectionPreferenceService } from './core-ai-connection-preference.service';
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
* Mongoose injection token for the AI connection model.
|
|
28
|
-
*/
|
|
29
|
-
export const AI_CONNECTION_MODEL = 'AiConnection';
|
|
26
|
+
import { AI_CONNECTION_CLASS, AI_CONNECTION_MODEL } from '../core-ai.constants';
|
|
30
27
|
|
|
31
28
|
/**
|
|
32
|
-
*
|
|
29
|
+
* @deprecated Import from `../core-ai.constants` instead. Re-exported only so existing deep imports
|
|
30
|
+
* keep working; the tokens are declared in an import-free leaf so no cycle can form around them
|
|
31
|
+
* (SWC-safe — see core-ai.constants.ts).
|
|
33
32
|
*/
|
|
34
|
-
export
|
|
33
|
+
export { AI_CONNECTION_CLASS, AI_CONNECTION_MODEL } from '../core-ai.constants';
|
|
35
34
|
|
|
36
35
|
/**
|
|
37
36
|
* CRUD service for {@link CoreAiConnection} — the database-backed LLM
|
|
@@ -8,15 +8,14 @@ import { CoreAiConversationCreateInput } from '../inputs/core-ai-conversation-cr
|
|
|
8
8
|
import { CoreAiConversationInput } from '../inputs/core-ai-conversation.input';
|
|
9
9
|
import { AiConversationDocument, CoreAiConversation } from '../models/core-ai-conversation.model';
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
* Mongoose injection token for the AI conversation model.
|
|
13
|
-
*/
|
|
14
|
-
export const AI_CONVERSATION_MODEL = 'AiConversation';
|
|
11
|
+
import { AI_CONVERSATION_CLASS, AI_CONVERSATION_MODEL } from '../core-ai.constants';
|
|
15
12
|
|
|
16
13
|
/**
|
|
17
|
-
*
|
|
14
|
+
* @deprecated Import from `../core-ai.constants` instead. Re-exported only so existing deep imports
|
|
15
|
+
* keep working; the tokens are declared in an import-free leaf so no cycle can form around them
|
|
16
|
+
* (SWC-safe — see core-ai.constants.ts).
|
|
18
17
|
*/
|
|
19
|
-
export
|
|
18
|
+
export { AI_CONVERSATION_CLASS, AI_CONVERSATION_MODEL } from '../core-ai.constants';
|
|
20
19
|
|
|
21
20
|
/**
|
|
22
21
|
* CRUD service for multi-turn {@link CoreAiConversation}s.
|
|
@@ -5,17 +5,16 @@ import { Model } from 'mongoose';
|
|
|
5
5
|
import { CrudService } from '../../../common/services/crud.service';
|
|
6
6
|
import { CoreModelConstructor } from '../../../common/types/core-model-constructor.type';
|
|
7
7
|
import { AiInteractionDocument, CoreAiInteraction } from '../models/core-ai-interaction.model';
|
|
8
|
-
import type { AiInteractionRecord } from '
|
|
8
|
+
import type { AiInteractionRecord } from '../interfaces/ai-interaction-record.interface';
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
* Mongoose injection token for the AI interaction model.
|
|
12
|
-
*/
|
|
13
|
-
export const AI_INTERACTION_MODEL = 'AiInteraction';
|
|
10
|
+
import { AI_INTERACTION_CLASS, AI_INTERACTION_MODEL } from '../core-ai.constants';
|
|
14
11
|
|
|
15
12
|
/**
|
|
16
|
-
*
|
|
13
|
+
* @deprecated Import from `../core-ai.constants` instead. Re-exported only so existing deep imports
|
|
14
|
+
* keep working; the tokens are declared in an import-free leaf so no cycle can form around them
|
|
15
|
+
* (SWC-safe — see core-ai.constants.ts).
|
|
17
16
|
*/
|
|
18
|
-
export
|
|
17
|
+
export { AI_INTERACTION_CLASS, AI_INTERACTION_MODEL } from '../core-ai.constants';
|
|
19
18
|
|
|
20
19
|
/**
|
|
21
20
|
* CRUD + write service for {@link CoreAiInteraction} audit records.
|
|
@@ -6,8 +6,14 @@ import { CrudService } from '../../../common/services/crud.service';
|
|
|
6
6
|
import { CoreModelConstructor } from '../../../common/types/core-model-constructor.type';
|
|
7
7
|
import { AiModeDocument, CoreAiMode } from '../models/core-ai-mode.model';
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
import { AI_MODE_CLASS, AI_MODE_MODEL } from '../core-ai.constants';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @deprecated Import from `../core-ai.constants` instead. Re-exported only so existing deep imports
|
|
13
|
+
* keep working; the tokens are declared in an import-free leaf so no cycle can form around them
|
|
14
|
+
* (SWC-safe — see core-ai.constants.ts).
|
|
15
|
+
*/
|
|
16
|
+
export { AI_MODE_CLASS, AI_MODE_MODEL } from '../core-ai.constants';
|
|
11
17
|
|
|
12
18
|
/**
|
|
13
19
|
* Named-mode store. See {@link CoreAiMode}. Override via
|
|
@@ -9,11 +9,14 @@ import { CoreAiPromptHintCreateInput } from '../inputs/core-ai-prompt-hint-creat
|
|
|
9
9
|
import { CoreAiPromptHintInput } from '../inputs/core-ai-prompt-hint.input';
|
|
10
10
|
import { AiPromptHintDocument, CoreAiPromptHint } from '../models/core-ai-prompt-hint.model';
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
export const AI_PROMPT_HINT_MODEL = 'AiPromptHint';
|
|
12
|
+
import { AI_PROMPT_HINT_CLASS, AI_PROMPT_HINT_MODEL } from '../core-ai.constants';
|
|
14
13
|
|
|
15
|
-
/**
|
|
16
|
-
|
|
14
|
+
/**
|
|
15
|
+
* @deprecated Import from `../core-ai.constants` instead. Re-exported only so existing deep imports
|
|
16
|
+
* keep working; the tokens are declared in an import-free leaf so no cycle can form around them
|
|
17
|
+
* (SWC-safe — see core-ai.constants.ts).
|
|
18
|
+
*/
|
|
19
|
+
export { AI_PROMPT_HINT_CLASS, AI_PROMPT_HINT_MODEL } from '../core-ai.constants';
|
|
17
20
|
|
|
18
21
|
/** A failure signal recorded by the orchestrator for the learning loop. */
|
|
19
22
|
export interface AiPromptFeedbackSignal {
|
|
@@ -10,8 +10,14 @@ import { CoreAiPromptCreateInput } from '../inputs/core-ai-prompt-create.input';
|
|
|
10
10
|
import { CoreAiPromptUpdateInput } from '../inputs/core-ai-prompt-update.input';
|
|
11
11
|
import { AiPromptDocument, CoreAiPrompt } from '../models/core-ai-prompt.model';
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
import { AI_PROMPT_CLASS, AI_PROMPT_MODEL } from '../core-ai.constants';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @deprecated Import from `../core-ai.constants` instead. Re-exported only so existing deep imports
|
|
17
|
+
* keep working; the tokens are declared in an import-free leaf so no cycle can form around them
|
|
18
|
+
* (SWC-safe — see core-ai.constants.ts).
|
|
19
|
+
*/
|
|
20
|
+
export { AI_PROMPT_CLASS, AI_PROMPT_MODEL } from '../core-ai.constants';
|
|
15
21
|
|
|
16
22
|
const VALID_SCOPES = new Set(['tenant', 'user']);
|
|
17
23
|
|
|
@@ -11,11 +11,14 @@ import { CoreAiSlotCreateInput } from '../inputs/core-ai-slot-create.input';
|
|
|
11
11
|
import { CoreAiSlotUpdateInput } from '../inputs/core-ai-slot-update.input';
|
|
12
12
|
import { AiSlotDocument, CoreAiSlot } from '../models/core-ai-slot.model';
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
export const AI_SLOT_MODEL = 'AiSlot';
|
|
14
|
+
import { AI_SLOT_CLASS, AI_SLOT_MODEL } from '../core-ai.constants';
|
|
16
15
|
|
|
17
|
-
/**
|
|
18
|
-
|
|
16
|
+
/**
|
|
17
|
+
* @deprecated Import from `../core-ai.constants` instead. Re-exported only so existing deep imports
|
|
18
|
+
* keep working; the tokens are declared in an import-free leaf so no cycle can form around them
|
|
19
|
+
* (SWC-safe — see core-ai.constants.ts).
|
|
20
|
+
*/
|
|
21
|
+
export { AI_SLOT_CLASS, AI_SLOT_MODEL } from '../core-ai.constants';
|
|
19
22
|
|
|
20
23
|
/** A resolved prompt fragment ready for placeholder rendering + assembly. */
|
|
21
24
|
export interface ResolvedPromptFragment {
|
|
@@ -6,10 +6,14 @@ import { CrudService } from '../../../common/services/crud.service';
|
|
|
6
6
|
import { CoreModelConstructor } from '../../../common/types/core-model-constructor.type';
|
|
7
7
|
import { AiToolGrantDocument, CoreAiToolGrant } from '../models/core-ai-tool-grant.model';
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
|
|
9
|
+
import { AI_TOOL_GRANT_CLASS, AI_TOOL_GRANT_MODEL } from '../core-ai.constants';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @deprecated Import from `../core-ai.constants` instead. Re-exported only so existing deep imports
|
|
13
|
+
* keep working; the tokens are declared in an import-free leaf so no cycle can form around them
|
|
14
|
+
* (SWC-safe — see core-ai.constants.ts).
|
|
15
|
+
*/
|
|
16
|
+
export { AI_TOOL_GRANT_CLASS, AI_TOOL_GRANT_MODEL } from '../core-ai.constants';
|
|
13
17
|
|
|
14
18
|
/** Scope of a persisted permission decision. */
|
|
15
19
|
export type AiToolGrantScope = 'conversation' | 'tenant' | 'user';
|
|
@@ -6,10 +6,14 @@ import { CrudService } from '../../../common/services/crud.service';
|
|
|
6
6
|
import { CoreModelConstructor } from '../../../common/types/core-model-constructor.type';
|
|
7
7
|
import { AiToolPolicyDocument, CoreAiToolPolicy } from '../models/core-ai-tool-policy.model';
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
|
|
9
|
+
import { AI_TOOL_POLICY_CLASS, AI_TOOL_POLICY_MODEL } from '../core-ai.constants';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @deprecated Import from `../core-ai.constants` instead. Re-exported only so existing deep imports
|
|
13
|
+
* keep working; the tokens are declared in an import-free leaf so no cycle can form around them
|
|
14
|
+
* (SWC-safe — see core-ai.constants.ts).
|
|
15
|
+
*/
|
|
16
|
+
export { AI_TOOL_POLICY_CLASS, AI_TOOL_POLICY_MODEL } from '../core-ai.constants';
|
|
13
17
|
|
|
14
18
|
export interface AiToolPolicyDecision {
|
|
15
19
|
/** Final decision: 'allow', 'deny' or 'ask'. */
|
|
@@ -27,20 +27,17 @@ import { AiPromptFeedbackSignal, CoreAiPromptHintService } from './core-ai-promp
|
|
|
27
27
|
import { AiToolGrantScope, CoreAiToolGrantService } from './core-ai-tool-grant.service';
|
|
28
28
|
import { CoreAiToolPolicyService } from './core-ai-tool-policy.service';
|
|
29
29
|
import { CoreAiModeService } from './core-ai-mode.service';
|
|
30
|
+
import type { AiInteractionRecord } from '../interfaces/ai-interaction-record.interface';
|
|
30
31
|
|
|
31
32
|
/**
|
|
32
|
-
*
|
|
33
|
+
* @deprecated Import from `../interfaces/ai-interaction-record.interface` instead. Re-exported only
|
|
34
|
+
* so existing imports keep working.
|
|
35
|
+
*
|
|
36
|
+
* The type was moved out of this file because `core-ai-interaction.service` needs it while this file
|
|
37
|
+
* imports THAT service — an import cycle held apart only by an `import type`. See the interface's
|
|
38
|
+
* docblock.
|
|
33
39
|
*/
|
|
34
|
-
export
|
|
35
|
-
actions: { name: string; success: boolean }[];
|
|
36
|
-
connectionId: string;
|
|
37
|
-
iterations: number;
|
|
38
|
-
prompt: string;
|
|
39
|
-
responseText: string;
|
|
40
|
-
tenantId?: string;
|
|
41
|
-
usage?: { completionTokens?: number; promptTokens?: number; totalTokens?: number };
|
|
42
|
-
userId?: string;
|
|
43
|
-
}
|
|
40
|
+
export type { AiInteractionRecord } from '../interfaces/ai-interaction-record.interface';
|
|
44
41
|
|
|
45
42
|
/**
|
|
46
43
|
* Event emitted by {@link CoreAiService.promptStream} over SSE.
|
|
@@ -262,10 +262,10 @@ Read the security section below for production deployments.
|
|
|
262
262
|
|
|
263
263
|
**Global server-level settings that affect BetterAuth behavior (since v11.25.0):**
|
|
264
264
|
|
|
265
|
-
| Setting (top-level `IServerOptions`) | Technical Purpose | Impact of Wrong Value
|
|
266
|
-
| ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
267
|
-
| `cookies` (`boolean \| ICookiesConfig`, default: `true`) | Controls cookie-parser middleware and session cookie setting. `cookies.exposeTokenInBody` additionally returns the token in the response body (test-only; **forbidden in production**) | Tokens missing from response body surprises test clients; `exposeTokenInBody` in prod = XSS-risk, framework throws at startup
|
|
268
|
-
| `cors` (`boolean \| ICorsConfig`, default: enabled with auto-derived origins) | Unified CORS config — propagates to GraphQL (Apollo), REST (Express), and BetterAuth `trustedOrigins` from a single source | `cors.enabled: false` disables
|
|
265
|
+
| Setting (top-level `IServerOptions`) | Technical Purpose | Impact of Wrong Value |
|
|
266
|
+
| ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
267
|
+
| `cookies` (`boolean \| ICookiesConfig`, default: `true`) | Controls cookie-parser middleware and session cookie setting. `cookies.exposeTokenInBody` additionally returns the token in the response body (test-only; **forbidden in production**) | Tokens missing from response body surprises test clients; `exposeTokenInBody` in prod = XSS-risk, framework throws at startup |
|
|
268
|
+
| `cors` (`boolean \| ICorsConfig`, default: enabled with auto-derived origins) | Unified CORS config — propagates to GraphQL (Apollo), REST (Express), and BetterAuth `trustedOrigins` from a single source | `cors.enabled: false` disables the REST/GraphQL layers; `cors.allowAll` mirrors any request origin for REST/GraphQL (dev only) but BetterAuth keeps restricting to `appUrl` (its origin check has no "allow all" mode) |
|
|
269
269
|
|
|
270
270
|
**For Development:** The defaults (`http://localhost:3000`, `/iam`) are correct.
|
|
271
271
|
|
|
@@ -725,6 +725,12 @@ const config = {
|
|
|
725
725
|
},
|
|
726
726
|
advanced: {
|
|
727
727
|
cookiePrefix: 'my-app',
|
|
728
|
+
// Since v11.27.6 the framework pins `useSecureCookies: false` so Better-Auth's native
|
|
729
|
+
// handlers read the same UNPREFIXED cookie the nest-server helper writes (the `Secure`
|
|
730
|
+
// attribute is still applied on https via `advanced.defaultCookieAttributes`). Only set
|
|
731
|
+
// `true` if Better-Auth manages your cookies entirely — otherwise its native handlers
|
|
732
|
+
// look for a `__Secure-`-prefixed cookie that is never written and answer 401 on
|
|
733
|
+
// 2FA / passkey / `/token`.
|
|
728
734
|
useSecureCookies: true,
|
|
729
735
|
},
|
|
730
736
|
},
|
|
@@ -13,7 +13,7 @@ import { ErrorCode } from '../error-code';
|
|
|
13
13
|
import { isMultiTenancyActive, isSystemRole, mergeRolesMetadata } from '../tenant/core-tenant.helpers';
|
|
14
14
|
import { BetterAuthTokenService } from './better-auth-token.service';
|
|
15
15
|
import { BetterAuthenticatedUser } from './better-auth.types';
|
|
16
|
-
import {
|
|
16
|
+
import { getBetterAuthTokenService } from './core-better-auth.registry';
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* BetterAuth Roles Guard
|
|
@@ -33,7 +33,8 @@ import { CoreBetterAuthModule } from './core-better-auth.module';
|
|
|
33
33
|
* IMPORTANT: This guard has NO constructor dependencies. This is intentional because
|
|
34
34
|
* NestJS DI has issues resolving Reflector/ModuleRef for APP_GUARD providers in dynamic modules.
|
|
35
35
|
* Instead, we use Reflect.getMetadata directly to read decorator metadata, and access
|
|
36
|
-
* BetterAuthTokenService via
|
|
36
|
+
* BetterAuthTokenService via the core-better-auth.registry leaf module — NOT via
|
|
37
|
+
* CoreBetterAuthModule, which would make guard and module import each other.
|
|
37
38
|
*/
|
|
38
39
|
@Injectable()
|
|
39
40
|
export class BetterAuthRolesGuard implements CanActivate {
|
|
@@ -41,12 +42,17 @@ export class BetterAuthRolesGuard implements CanActivate {
|
|
|
41
42
|
private tokenService: BetterAuthTokenService | null = null;
|
|
42
43
|
|
|
43
44
|
/**
|
|
44
|
-
* Get BetterAuthTokenService lazily from
|
|
45
|
-
* This avoids DI issues while still allowing token verification
|
|
45
|
+
* Get BetterAuthTokenService lazily from the better-auth registry.
|
|
46
|
+
* This avoids DI issues while still allowing token verification.
|
|
47
|
+
*
|
|
48
|
+
* The lookup goes through core-better-auth.registry.ts rather than
|
|
49
|
+
* CoreBetterAuthModule on purpose: importing the module here would make guard
|
|
50
|
+
* and module import each other, and that cycle is one decorator away from the
|
|
51
|
+
* SWC temporal-dead-zone crash documented in core-better-auth.constants.ts.
|
|
46
52
|
*/
|
|
47
53
|
private getTokenService(): BetterAuthTokenService | null {
|
|
48
54
|
if (!this.tokenService) {
|
|
49
|
-
this.tokenService =
|
|
55
|
+
this.tokenService = getBetterAuthTokenService();
|
|
50
56
|
}
|
|
51
57
|
return this.tokenService;
|
|
52
58
|
}
|
|
@@ -7,7 +7,7 @@ import * as crypto from 'crypto';
|
|
|
7
7
|
import * as fs from 'fs';
|
|
8
8
|
import * as path from 'path';
|
|
9
9
|
|
|
10
|
-
import { resolveServerUrls } from '../../common/helpers/cookies.helper';
|
|
10
|
+
import { resolveServerUrls, strippedApiHostname } from '../../common/helpers/cookies.helper';
|
|
11
11
|
import { IBetterAuth, ICorsConfig } from '../../common/interfaces/server-options.interface';
|
|
12
12
|
import { detectCookiePrefixDrift, resolveBetterAuthCookiePrefix } from './better-auth-cookie-prefix.helper';
|
|
13
13
|
|
|
@@ -380,15 +380,47 @@ export function createBetterAuthInstance(options: CreateBetterAuthOptions): Crea
|
|
|
380
380
|
);
|
|
381
381
|
}
|
|
382
382
|
|
|
383
|
+
// Better-Auth base URL (resolved localhost defaults → explicit config → fallback).
|
|
384
|
+
const betterAuthBaseUrl = resolvedUrls.baseUrl || config.baseUrl || 'http://localhost:3000';
|
|
385
|
+
// The `Secure` attribute Better-Auth WOULD derive for its cookies if `useSecureCookies`
|
|
386
|
+
// were not pinned below: an `https://` baseURL is Better-Auth's own signal
|
|
387
|
+
// (createCookieGetter → `baseURLString.startsWith('https://')`). We keep exactly this value so
|
|
388
|
+
// the pinned `useSecureCookies: false` (needed for name alignment, see below) does not silently
|
|
389
|
+
// strip `Secure` from the cookies Better-Auth's native handlers forward.
|
|
390
|
+
const secureCookies = betterAuthBaseUrl.startsWith('https://');
|
|
391
|
+
|
|
383
392
|
const betterAuthConfig: Record<string, unknown> = {
|
|
384
393
|
advanced: {
|
|
385
394
|
cookiePrefix,
|
|
386
395
|
...(crossSubDomain.enabled && {
|
|
387
396
|
crossSubDomainCookies: { domain: crossSubDomain.domain, enabled: true },
|
|
388
397
|
}),
|
|
398
|
+
// Keep Better-Auth's NATIVE handlers on the SAME cookie name the
|
|
399
|
+
// nest-server cookie helpers actually write. Those helpers always emit
|
|
400
|
+
// the UNPREFIXED `<cookiePrefix>.session_token`. Better-Auth, left to its
|
|
401
|
+
// own devices on an `https://` baseURL, auto-enables secure cookies AND
|
|
402
|
+
// prefixes the name with `__Secure-`, so its native handlers (2FA
|
|
403
|
+
// enable/disable, passkey register/list, backup codes, `/token`) look for
|
|
404
|
+
// `__Secure-<cookiePrefix>.session_token` — a cookie that is never written
|
|
405
|
+
// — and return `401 UNAUTHORIZED`, while the project's own session
|
|
406
|
+
// resolver (which reads the unprefixed cookie) still returns a session: a
|
|
407
|
+
// split-brain where `GET /iam/get-session` is 200 but every sensitive
|
|
408
|
+
// Better-Auth endpoint is 401. Pinning `useSecureCookies: false` keeps the
|
|
409
|
+
// native read path aligned with the cookie helper.
|
|
410
|
+
useSecureCookies: false,
|
|
411
|
+
// …BUT `useSecureCookies: false` also forces `secure: false` on EVERY cookie
|
|
412
|
+
// Better-Auth sets (createCookieGetter → `secure: !!secureCookiePrefix`), and the
|
|
413
|
+
// native handlers forward their `Set-Cookie` VERBATIM via `sendWebResponse`
|
|
414
|
+
// (2FA verify, social callback, magic link, passkey) WITHOUT passing through the
|
|
415
|
+
// cookie helper — so those session cookies would ship without `Secure` in
|
|
416
|
+
// production. Restore the exact Secure flag Better-Auth itself would have derived
|
|
417
|
+
// (an `https://` baseURL), keeping the unprefixed NAME while preserving the
|
|
418
|
+
// `Secure` TRANSPORT flag. Only injected on https so http/local and a consumer's
|
|
419
|
+
// own `options.advanced.useSecureCookies` override are left untouched.
|
|
420
|
+
...(secureCookies && { defaultCookieAttributes: { secure: true } }),
|
|
389
421
|
},
|
|
390
422
|
basePath,
|
|
391
|
-
baseURL:
|
|
423
|
+
baseURL: betterAuthBaseUrl,
|
|
392
424
|
database: mongodbAdapter(db),
|
|
393
425
|
// Enable email/password authentication by default (required by Better-Auth 1.x)
|
|
394
426
|
// Can be disabled by setting config.emailAndPassword.enabled = false
|
|
@@ -444,6 +476,22 @@ export function createBetterAuthInstance(options: CreateBetterAuthOptions): Crea
|
|
|
444
476
|
finalConfig.advanced = {
|
|
445
477
|
...(betterAuthConfig.advanced as Record<string, unknown>),
|
|
446
478
|
...(optionsAdvanced as Record<string, unknown>),
|
|
479
|
+
// `defaultCookieAttributes` needs a DEEP merge, not the shallow spread above.
|
|
480
|
+
// It is the key that carries our restored `secure: true` (see the `useSecureCookies: false`
|
|
481
|
+
// block), so a consumer setting it for an entirely unrelated reason — `{ partitioned: true }`,
|
|
482
|
+
// `{ domain: … }` — would wholesale-replace the object and silently strip `Secure` from every
|
|
483
|
+
// session cookie the native handlers forward (2FA verify, social callback, magic link) on an
|
|
484
|
+
// https deployment. Session-establishing cookies, in the clear, with no error anywhere.
|
|
485
|
+
// Re-apply `secure: true` as the BASE and let the consumer's keys spread over it, so an
|
|
486
|
+
// EXPLICIT `secure: false` still wins while an unrelated key can no longer clobber it.
|
|
487
|
+
...(secureCookies && {
|
|
488
|
+
defaultCookieAttributes: {
|
|
489
|
+
secure: true,
|
|
490
|
+
...((optionsAdvanced as Record<string, unknown>).defaultCookieAttributes as
|
|
491
|
+
| Record<string, unknown>
|
|
492
|
+
| undefined),
|
|
493
|
+
},
|
|
494
|
+
}),
|
|
447
495
|
};
|
|
448
496
|
}
|
|
449
497
|
} else {
|
|
@@ -681,12 +729,20 @@ function buildSocialProviders(config: IBetterAuth): Record<string, SocialProvide
|
|
|
681
729
|
*
|
|
682
730
|
* Behavior (in priority order):
|
|
683
731
|
* 1. Explicit betterAuth.trustedOrigins → use those (always takes precedence)
|
|
684
|
-
* 2. Server CORS disabled → empty array (
|
|
685
|
-
*
|
|
732
|
+
* 2. Server CORS disabled → empty array (adds no extra trusted origins beyond BetterAuth's own
|
|
733
|
+
* baseURL — see NOTE; it does NOT switch BetterAuth's origin check off)
|
|
734
|
+
* 3. Server CORS allowAll → fall through to rules 4-7 (see below)
|
|
686
735
|
* 4. Server CORS allowedOrigins → merge with appUrl/baseUrl
|
|
687
736
|
* 5. Passkey trustedOrigins → use from normalizePasskeyConfig()
|
|
688
737
|
* 6. Fallback to resolved appUrl
|
|
689
|
-
* 7. Otherwise → undefined (
|
|
738
|
+
* 7. Otherwise → undefined (Better-Auth then trusts only its own baseURL)
|
|
739
|
+
*
|
|
740
|
+
* NOTE on rules 2 and 7: neither an empty array (rule 2) nor `undefined` (rule 7) means "any
|
|
741
|
+
* origin is allowed", and neither disables BetterAuth's origin check. In both cases BetterAuth
|
|
742
|
+
* still derives and trusts its own `baseURL` origin (see `getTrustedOrigins()` in
|
|
743
|
+
* `better-auth/context`, which unconditionally pushes `new URL(baseURL).origin` before appending
|
|
744
|
+
* `options.trustedOrigins`), so `[]` and `undefined` are behaviorally identical here: a
|
|
745
|
+
* separately hosted frontend is still rejected by every origin-checked endpoint.
|
|
690
746
|
*
|
|
691
747
|
* @param config - Better-auth configuration
|
|
692
748
|
* @param options - Passkey normalization, resolved URLs, and server CORS context
|
|
@@ -706,15 +762,25 @@ export function buildTrustedOrigins(
|
|
|
706
762
|
return config.trustedOrigins;
|
|
707
763
|
}
|
|
708
764
|
|
|
709
|
-
// 2. Server CORS disabled → BetterAuth
|
|
765
|
+
// 2. Server CORS disabled → add no extra trusted origins. BetterAuth still trusts its own
|
|
766
|
+
// baseURL (see the NOTE above), so this does NOT turn its origin check off — it only means
|
|
767
|
+
// "no separately hosted frontend is trusted". Behaviorally identical to rule 7's undefined.
|
|
710
768
|
if (serverCorsConfig === false || (typeof serverCorsConfig === 'object' && serverCorsConfig?.enabled === false)) {
|
|
711
769
|
return [];
|
|
712
770
|
}
|
|
713
771
|
|
|
714
|
-
// 3. Server CORS allowAll →
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
772
|
+
// 3. Server CORS allowAll → fall through to the appUrl / passkey rules below.
|
|
773
|
+
//
|
|
774
|
+
// This used to `return undefined` in the belief that BetterAuth then "allows all
|
|
775
|
+
// origins". It does not: without trustedOrigins BetterAuth trusts only its own
|
|
776
|
+
// baseURL, so the *app* origin is rejected and every origin-checked endpoint
|
|
777
|
+
// (two-factor/enable, passkey) answers 403 INVALID_ORIGIN. That silently broke
|
|
778
|
+
// 2FA and passkeys in exactly the setups that use allowAll — local dev and CI.
|
|
779
|
+
//
|
|
780
|
+
// An origin check has no meaningful "allow everything" mode — that is the very
|
|
781
|
+
// thing it defends against — so allowAll now yields the known-good origins
|
|
782
|
+
// (appUrl, passkey) instead of none. Projects that really do want to accept
|
|
783
|
+
// arbitrary origins can still set `betterAuth.trustedOrigins` explicitly (rule 1).
|
|
718
784
|
|
|
719
785
|
// 4. Server allowedOrigins → merge with appUrl/baseUrl.
|
|
720
786
|
// Order (appUrl, baseUrl, allowedOrigins) mirrors buildCorsConfig() in cookies.helper.ts
|
|
@@ -1183,6 +1249,17 @@ export function resolveCrossSubDomainCookies(
|
|
|
1183
1249
|
* 3. Use baseUrl hostname as-is
|
|
1184
1250
|
*
|
|
1185
1251
|
* Returns undefined for localhost (cross-subdomain not meaningful there).
|
|
1252
|
+
*
|
|
1253
|
+
* The `api.` strip reuses {@link strippedApiHostname} so it shares that helper's guard against
|
|
1254
|
+
* bogus results: `api.dev` → `dev` would set a public-suffix cookie domain that browsers reject
|
|
1255
|
+
* outright (dropping every session cookie), so `api.dev` is kept as-is instead.
|
|
1256
|
+
*
|
|
1257
|
+
* KNOWN LIMITATION (fails closed, no leak): the single-label guard cannot recognise a MULTI-label
|
|
1258
|
+
* public suffix — `api.co.uk` still strips to `co.uk`, a Public-Suffix-List entry browsers also
|
|
1259
|
+
* reject, so cross-subdomain auth would break (cookie dropped) rather than over-scope. A full PSL
|
|
1260
|
+
* check would need a dependency, which this security-critical module deliberately avoids; for such
|
|
1261
|
+
* apex domains set `betterAuth.crossSubDomainCookies.domain` explicitly instead of relying on
|
|
1262
|
+
* derivation. See migration guide 11.27.5 → 11.27.6.
|
|
1186
1263
|
*/
|
|
1187
1264
|
function deriveCookieDomainFromUrls(appUrl?: string, baseUrl?: string): string | undefined {
|
|
1188
1265
|
// Priority 1: appUrl hostname (this IS the parent domain in typical setups)
|
|
@@ -1204,11 +1281,9 @@ function deriveCookieDomainFromUrls(appUrl?: string, baseUrl?: string): string |
|
|
|
1204
1281
|
if (hostname === 'localhost' || hostname === '127.0.0.1') {
|
|
1205
1282
|
return undefined;
|
|
1206
1283
|
}
|
|
1207
|
-
// Strip api. prefix to get parent domain
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
}
|
|
1211
|
-
return hostname;
|
|
1284
|
+
// Strip api. prefix to get parent domain — but keep the host unchanged when stripping
|
|
1285
|
+
// would leave a bare TLD (`api.dev`) or empty host (`api.`).
|
|
1286
|
+
return strippedApiHostname(hostname) ?? hostname;
|
|
1212
1287
|
} catch {
|
|
1213
1288
|
return undefined;
|
|
1214
1289
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency-injection tokens of the better-auth module.
|
|
3
|
+
*
|
|
4
|
+
* These live in a dedicated file — instead of core-better-auth.module.ts /
|
|
5
|
+
* core-better-auth.service.ts — so that module and service never have to import
|
|
6
|
+
* each other. The tokens used to be split across both files
|
|
7
|
+
* (BETTER_AUTH_INSTANCE in the module, BETTER_AUTH_CONFIG /
|
|
8
|
+
* BETTER_AUTH_COOKIE_DOMAIN in the service), which made the two files import
|
|
9
|
+
* each other in a cycle. That cycle happened to work under tsc/CommonJS by
|
|
10
|
+
* evaluation-order luck, but crashed SWC-compiled builds (`nest start -b swc`)
|
|
11
|
+
* with a temporal-dead-zone error:
|
|
12
|
+
*
|
|
13
|
+
* ReferenceError: Cannot access 'BETTER_AUTH_INSTANCE' before initialization
|
|
14
|
+
*
|
|
15
|
+
* The lethal ingredient is not the cycle by itself — it is a cycle PLUS a read of
|
|
16
|
+
* the cyclic binding at module-evaluation time. `@Inject(BETTER_AUTH_INSTANCE)` is
|
|
17
|
+
* a constructor-parameter decorator, and decorator arguments are evaluated when the
|
|
18
|
+
* class is defined, i.e. while the module is still initializing. On a cycle the
|
|
19
|
+
* importing side then reads a `const` that has not been initialized yet.
|
|
20
|
+
*
|
|
21
|
+
* This file imports nothing, so it can never be mid-evaluation when someone
|
|
22
|
+
* imports it — in any module system, under any compiler. That makes the
|
|
23
|
+
* initialization order of the tokens deterministic everywhere.
|
|
24
|
+
*
|
|
25
|
+
* WHY THIS MATTERS FOR FUTURE CHANGES
|
|
26
|
+
* -----------------------------------
|
|
27
|
+
* `tsc` does NOT catch a regression here, and neither does the test suite: vitest
|
|
28
|
+
* runs SWC through Vite's module runner, whose getter-based live bindings tolerate
|
|
29
|
+
* cycles. Only the SWC → CommonJS → `require()` path fails, which is exactly what
|
|
30
|
+
* `pnpm run check:swc-tdz` exercises. If you move a token back into the module or
|
|
31
|
+
* the service, everything stays green locally and breaks for consumers.
|
|
32
|
+
*
|
|
33
|
+
* The rule, in short: **DI tokens belong in an import-free leaf file.**
|
|
34
|
+
* See .claude/rules/better-auth.md §6.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Token for injecting the better-auth instance.
|
|
39
|
+
*
|
|
40
|
+
* Injected type: `BetterAuthInstance | null` — null when better-auth is disabled.
|
|
41
|
+
* Declared `@Optional()` in the CoreBetterAuthService constructor.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* import { Inject, Injectable, Optional } from '@nestjs/common';
|
|
46
|
+
* import { BETTER_AUTH_INSTANCE, BetterAuthInstance } from '@lenne.tech/nest-server';
|
|
47
|
+
*
|
|
48
|
+
* @Injectable()
|
|
49
|
+
* export class MyService {
|
|
50
|
+
* constructor(
|
|
51
|
+
* @Optional() @Inject(BETTER_AUTH_INSTANCE) private readonly auth: BetterAuthInstance | null,
|
|
52
|
+
* ) {}
|
|
53
|
+
* }
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export const BETTER_AUTH_INSTANCE = 'BETTER_AUTH_INSTANCE';
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Injection token for the resolved BetterAuth configuration.
|
|
60
|
+
*
|
|
61
|
+
* Injected type: `IBetterAuth | null` — null when better-auth is disabled.
|
|
62
|
+
* Declared `@Optional()` in the CoreBetterAuthService constructor.
|
|
63
|
+
*/
|
|
64
|
+
export const BETTER_AUTH_CONFIG = 'BETTER_AUTH_CONFIG';
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Injection token for the resolved cross-subdomain cookie domain.
|
|
68
|
+
* Set during Better-Auth instance creation, undefined if disabled.
|
|
69
|
+
*
|
|
70
|
+
* Injected type: `string | null | undefined`.
|
|
71
|
+
* Declared `@Optional()` in the CoreBetterAuthService constructor.
|
|
72
|
+
*/
|
|
73
|
+
export const BETTER_AUTH_COOKIE_DOMAIN = 'BETTER_AUTH_COOKIE_DOMAIN';
|