@lenne.tech/nest-server 11.27.6 → 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.
Files changed (132) hide show
  1. package/.claude/rules/architecture.md +66 -0
  2. package/.claude/rules/better-auth.md +88 -2
  3. package/.claude/rules/package-management.md +23 -14
  4. package/CLAUDE.md +8 -2
  5. package/FRAMEWORK-API.md +1 -1
  6. package/dist/core/common/decorators/restricted.decorator.d.ts +4 -4
  7. package/dist/core/common/decorators/restricted.decorator.js +21 -22
  8. package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
  9. package/dist/core/common/helpers/clone.helper.d.ts +6 -0
  10. package/dist/core/common/helpers/clone.helper.js +67 -0
  11. package/dist/core/common/helpers/clone.helper.js.map +1 -0
  12. package/dist/core/common/helpers/db.helper.d.ts +2 -14
  13. package/dist/core/common/helpers/db.helper.js +13 -106
  14. package/dist/core/common/helpers/db.helper.js.map +1 -1
  15. package/dist/core/common/helpers/id.helper.d.ts +15 -0
  16. package/dist/core/common/helpers/id.helper.js +104 -0
  17. package/dist/core/common/helpers/id.helper.js.map +1 -0
  18. package/dist/core/common/helpers/input.helper.d.ts +1 -6
  19. package/dist/core/common/helpers/input.helper.js +11 -70
  20. package/dist/core/common/helpers/input.helper.js.map +1 -1
  21. package/dist/core/common/inputs/combined-filter.input.d.ts +1 -12
  22. package/dist/core/common/inputs/combined-filter.input.js +2 -51
  23. package/dist/core/common/inputs/combined-filter.input.js.map +1 -1
  24. package/dist/core/common/inputs/filter.input.d.ts +10 -1
  25. package/dist/core/common/inputs/filter.input.js +40 -5
  26. package/dist/core/common/inputs/filter.input.js.map +1 -1
  27. package/dist/core/common/services/config.service.js +5 -5
  28. package/dist/core/common/services/config.service.js.map +1 -1
  29. package/dist/core/modules/ai/core-ai.constants.d.ts +22 -0
  30. package/dist/core/modules/ai/core-ai.constants.js +26 -0
  31. package/dist/core/modules/ai/core-ai.constants.js.map +1 -0
  32. package/dist/core/modules/ai/index.d.ts +1 -0
  33. package/dist/core/modules/ai/index.js +1 -0
  34. package/dist/core/modules/ai/index.js.map +1 -1
  35. package/dist/core/modules/ai/interfaces/ai-interaction-record.interface.d.ts +17 -0
  36. package/dist/core/modules/ai/interfaces/ai-interaction-record.interface.js +3 -0
  37. package/dist/core/modules/ai/interfaces/ai-interaction-record.interface.js.map +1 -0
  38. package/dist/core/modules/ai/services/core-ai-budget.service.d.ts +1 -2
  39. package/dist/core/modules/ai/services/core-ai-budget.service.js +7 -5
  40. package/dist/core/modules/ai/services/core-ai-budget.service.js.map +1 -1
  41. package/dist/core/modules/ai/services/core-ai-connection-preference.service.d.ts +1 -2
  42. package/dist/core/modules/ai/services/core-ai-connection-preference.service.js +7 -5
  43. package/dist/core/modules/ai/services/core-ai-connection-preference.service.js.map +1 -1
  44. package/dist/core/modules/ai/services/core-ai-connection.service.d.ts +1 -2
  45. package/dist/core/modules/ai/services/core-ai-connection.service.js +7 -5
  46. package/dist/core/modules/ai/services/core-ai-connection.service.js.map +1 -1
  47. package/dist/core/modules/ai/services/core-ai-conversation.service.d.ts +1 -2
  48. package/dist/core/modules/ai/services/core-ai-conversation.service.js +7 -5
  49. package/dist/core/modules/ai/services/core-ai-conversation.service.js.map +1 -1
  50. package/dist/core/modules/ai/services/core-ai-interaction.service.d.ts +2 -3
  51. package/dist/core/modules/ai/services/core-ai-interaction.service.js +7 -5
  52. package/dist/core/modules/ai/services/core-ai-interaction.service.js.map +1 -1
  53. package/dist/core/modules/ai/services/core-ai-mode.service.d.ts +1 -2
  54. package/dist/core/modules/ai/services/core-ai-mode.service.js +7 -5
  55. package/dist/core/modules/ai/services/core-ai-mode.service.js.map +1 -1
  56. package/dist/core/modules/ai/services/core-ai-prompt-hint.service.d.ts +1 -3
  57. package/dist/core/modules/ai/services/core-ai-prompt-hint.service.js +7 -5
  58. package/dist/core/modules/ai/services/core-ai-prompt-hint.service.js.map +1 -1
  59. package/dist/core/modules/ai/services/core-ai-prompt.service.d.ts +1 -2
  60. package/dist/core/modules/ai/services/core-ai-prompt.service.js +7 -5
  61. package/dist/core/modules/ai/services/core-ai-prompt.service.js.map +1 -1
  62. package/dist/core/modules/ai/services/core-ai-slot.service.d.ts +1 -2
  63. package/dist/core/modules/ai/services/core-ai-slot.service.js +7 -5
  64. package/dist/core/modules/ai/services/core-ai-slot.service.js.map +1 -1
  65. package/dist/core/modules/ai/services/core-ai-tool-grant.service.d.ts +1 -2
  66. package/dist/core/modules/ai/services/core-ai-tool-grant.service.js +7 -5
  67. package/dist/core/modules/ai/services/core-ai-tool-grant.service.js.map +1 -1
  68. package/dist/core/modules/ai/services/core-ai-tool-policy.service.d.ts +1 -2
  69. package/dist/core/modules/ai/services/core-ai-tool-policy.service.js +7 -5
  70. package/dist/core/modules/ai/services/core-ai-tool-policy.service.js.map +1 -1
  71. package/dist/core/modules/ai/services/core-ai.service.d.ts +2 -17
  72. package/dist/core/modules/ai/services/core-ai.service.js.map +1 -1
  73. package/dist/core/modules/better-auth/better-auth-roles.guard.js +2 -2
  74. package/dist/core/modules/better-auth/better-auth-roles.guard.js.map +1 -1
  75. package/dist/core/modules/better-auth/better-auth.config.js +6 -0
  76. package/dist/core/modules/better-auth/better-auth.config.js.map +1 -1
  77. package/dist/core/modules/better-auth/core-better-auth.constants.d.ts +3 -0
  78. package/dist/core/modules/better-auth/core-better-auth.constants.js +7 -0
  79. package/dist/core/modules/better-auth/core-better-auth.constants.js.map +1 -0
  80. package/dist/core/modules/better-auth/core-better-auth.module.d.ts +1 -2
  81. package/dist/core/modules/better-auth/core-better-auth.module.js +23 -20
  82. package/dist/core/modules/better-auth/core-better-auth.module.js.map +1 -1
  83. package/dist/core/modules/better-auth/core-better-auth.registry.d.ts +4 -0
  84. package/dist/core/modules/better-auth/core-better-auth.registry.js +16 -0
  85. package/dist/core/modules/better-auth/core-better-auth.registry.js.map +1 -0
  86. package/dist/core/modules/better-auth/core-better-auth.service.d.ts +1 -2
  87. package/dist/core/modules/better-auth/core-better-auth.service.js +7 -6
  88. package/dist/core/modules/better-auth/core-better-auth.service.js.map +1 -1
  89. package/dist/core/modules/better-auth/index.d.ts +1 -0
  90. package/dist/core/modules/better-auth/index.js +1 -0
  91. package/dist/core/modules/better-auth/index.js.map +1 -1
  92. package/dist/core/modules/tus/tus.constants.d.ts +1 -0
  93. package/dist/core/modules/tus/tus.constants.js +5 -0
  94. package/dist/core/modules/tus/tus.constants.js.map +1 -0
  95. package/dist/core/modules/tus/tus.module.d.ts +1 -1
  96. package/dist/core/modules/tus/tus.module.js +8 -6
  97. package/dist/core/modules/tus/tus.module.js.map +1 -1
  98. package/dist/tsconfig.build.tsbuildinfo +1 -1
  99. package/migration-guides/11.27.6-to-11.27.7.md +374 -0
  100. package/package.json +6 -104
  101. package/src/core/common/decorators/restricted.decorator.ts +33 -8
  102. package/src/core/common/helpers/clone.helper.ts +110 -0
  103. package/src/core/common/helpers/db.helper.ts +14 -161
  104. package/src/core/common/helpers/id.helper.ts +198 -0
  105. package/src/core/common/helpers/input.helper.ts +12 -84
  106. package/src/core/common/inputs/combined-filter.input.ts +10 -57
  107. package/src/core/common/inputs/filter.input.ts +92 -1
  108. package/src/core/common/services/config.service.ts +4 -1
  109. package/src/core/modules/ai/core-ai.constants.ts +92 -0
  110. package/src/core/modules/ai/index.ts +1 -0
  111. package/src/core/modules/ai/interfaces/ai-interaction-record.interface.ts +34 -0
  112. package/src/core/modules/ai/services/core-ai-budget.service.ts +8 -4
  113. package/src/core/modules/ai/services/core-ai-connection-preference.service.ts +8 -4
  114. package/src/core/modules/ai/services/core-ai-connection.service.ts +5 -6
  115. package/src/core/modules/ai/services/core-ai-conversation.service.ts +5 -6
  116. package/src/core/modules/ai/services/core-ai-interaction.service.ts +6 -7
  117. package/src/core/modules/ai/services/core-ai-mode.service.ts +8 -2
  118. package/src/core/modules/ai/services/core-ai-prompt-hint.service.ts +7 -4
  119. package/src/core/modules/ai/services/core-ai-prompt.service.ts +8 -2
  120. package/src/core/modules/ai/services/core-ai-slot.service.ts +7 -4
  121. package/src/core/modules/ai/services/core-ai-tool-grant.service.ts +8 -4
  122. package/src/core/modules/ai/services/core-ai-tool-policy.service.ts +8 -4
  123. package/src/core/modules/ai/services/core-ai.service.ts +8 -11
  124. package/src/core/modules/better-auth/better-auth-roles.guard.ts +11 -5
  125. package/src/core/modules/better-auth/better-auth.config.ts +16 -0
  126. package/src/core/modules/better-auth/core-better-auth.constants.ts +73 -0
  127. package/src/core/modules/better-auth/core-better-auth.module.ts +34 -8
  128. package/src/core/modules/better-auth/core-better-auth.registry.ts +53 -0
  129. package/src/core/modules/better-auth/core-better-auth.service.ts +19 -12
  130. package/src/core/modules/better-auth/index.ts +9 -0
  131. package/src/core/modules/tus/tus.constants.ts +25 -0
  132. package/src/core/modules/tus/tus.module.ts +7 -2
@@ -1,57 +1,10 @@
1
- import { InputType } from '@nestjs/graphql';
2
-
3
- import { Restricted } from '../decorators/restricted.decorator';
4
- import { UnifiedField } from '../decorators/unified-field.decorator';
5
- import { LogicalOperatorEnum } from '../enums/logical-operator.enum';
6
- import { RoleEnum } from '../enums/role.enum';
7
- import { maps } from '../helpers/model.helper';
8
- import { CoreInput } from './core-input.input';
9
- import { FilterInput } from './filter.input';
10
-
11
- @InputType({
12
- description: 'Combination of multiple filters via logical operator',
13
- })
14
- @Restricted(RoleEnum.S_EVERYONE)
15
- export class CombinedFilterInput extends CoreInput {
16
- /**
17
- * Logical Operator to combine filters
18
- */
19
- @UnifiedField({
20
- description: 'Logical Operator to combine filters',
21
- enum: LogicalOperatorEnum,
22
- roles: RoleEnum.S_EVERYONE,
23
- })
24
- logicalOperator: LogicalOperatorEnum = undefined;
25
-
26
- /**
27
- * Filters to combine via logical operator
28
- */
29
- @UnifiedField({
30
- description: 'Filters to combine via logical operator',
31
- isOptional: true,
32
- roles: RoleEnum.S_EVERYONE,
33
- type: () => FilterInput,
34
- })
35
- filters: FilterInput[] = undefined;
36
-
37
- // ===================================================================================================================
38
- // Methods
39
- // ===================================================================================================================
40
-
41
- /**
42
- * Mapping for Subtypes
43
- */
44
- override map(
45
- data: Partial<this> | Record<string, any>,
46
- options: {
47
- cloneDeep?: boolean;
48
- funcAllowed?: boolean;
49
- mapId?: boolean;
50
- } = {},
51
- ): this {
52
- super.map(data, options);
53
- this.filters = maps(data.filters, FilterInput, options.cloneDeep);
54
- Object.keys(this).forEach((key) => this[key] === undefined && delete this[key]);
55
- return this;
56
- }
57
- }
1
+ /**
2
+ * `CombinedFilterInput` is declared in `./filter.input` — the two classes are mutually recursive
3
+ * and must live in one module, or the cycle between them crashes SWC-compiled builds with
4
+ * `ReferenceError: Cannot access 'CombinedFilterInput' before initialization`. See the docblock in
5
+ * `./filter.input` for the full analysis.
6
+ *
7
+ * This file remains only so existing deep imports of `.../inputs/combined-filter.input` keep
8
+ * resolving. Do NOT move the class back here.
9
+ */
10
+ export { CombinedFilterInput } from './filter.input';
@@ -1,12 +1,103 @@
1
+ /**
2
+ * Filter inputs.
3
+ *
4
+ * `CombinedFilterInput` and `FilterInput` are mutually recursive: a filter may contain a combined
5
+ * filter, and a combined filter contains a list of filters. They therefore live in ONE module, and
6
+ * `CombinedFilterInput` is declared FIRST — that ordering is load-bearing, not cosmetic.
7
+ *
8
+ * They used to sit in two files that imported each other. `FilterInput` referenced
9
+ * `CombinedFilterInput` EAGERLY — in a decorator argument (`type: CombinedFilterInput`) and in the
10
+ * `design:type` metadata `emitDecoratorMetadata` emits for `combinedFilter?: CombinedFilterInput`.
11
+ * Decorator arguments and that metadata are evaluated at CLASS-DEFINITION time, i.e. while the
12
+ * module is still initializing. On a cycle that reads a class binding still in its temporal dead
13
+ * zone, and under SWC → CommonJS it threw:
14
+ *
15
+ * ReferenceError: Cannot access 'CombinedFilterInput' before initialization
16
+ *
17
+ * That was not hypothetical — `require('.../combined-filter.input.js')` crashed. It only stayed
18
+ * hidden because the package barrel happens to pull `filter.input` in first via other importers
19
+ * (`filter.args`, `filter.helper`), so entering through the barrel masked it. A deep import, a unit
20
+ * test importing the input directly, or a reordering of `src/index.ts` would all have detonated it.
21
+ *
22
+ * Making the reference lazy does NOT fix it: `type: () => CombinedFilterInput` still leaves the
23
+ * eager `design:type` metadata behind, and SWC's `typeof` guard does not protect the member
24
+ * expression it compiles to. The only real fix is to remove the import edge — hence one module.
25
+ *
26
+ * Within a single module the problem disappears: `FilterInput` reads `CombinedFilterInput` at
27
+ * definition time, and by then it is already initialized. The reverse direction is safe because
28
+ * `CombinedFilterInput` only reaches `FilterInput` lazily (a `() => FilterInput` thunk and a method
29
+ * body), never at definition time.
30
+ *
31
+ * `combined-filter.input.ts` remains as a re-export so existing deep imports keep working.
32
+ * See .claude/rules/architecture.md → "DI Token Placement (SWC-Safe)" for the general rule.
33
+ */
1
34
  import { InputType } from '@nestjs/graphql';
2
35
 
3
36
  import { Restricted } from '../decorators/restricted.decorator';
4
37
  import { UnifiedField } from '../decorators/unified-field.decorator';
38
+ import { LogicalOperatorEnum } from '../enums/logical-operator.enum';
5
39
  import { RoleEnum } from '../enums/role.enum';
6
- import { CombinedFilterInput } from './combined-filter.input';
40
+ import { maps } from '../helpers/model.helper';
7
41
  import { CoreInput } from './core-input.input';
8
42
  import { SingleFilterInput } from './single-filter.input';
9
43
 
44
+ /**
45
+ * Combination of multiple filters via logical operator.
46
+ *
47
+ * Declared BEFORE `FilterInput` on purpose — `FilterInput` dereferences this class at
48
+ * class-definition time, so it must already be initialized. See the module docblock.
49
+ */
50
+ @InputType({
51
+ description: 'Combination of multiple filters via logical operator',
52
+ })
53
+ @Restricted(RoleEnum.S_EVERYONE)
54
+ export class CombinedFilterInput extends CoreInput {
55
+ /**
56
+ * Logical Operator to combine filters
57
+ */
58
+ @UnifiedField({
59
+ description: 'Logical Operator to combine filters',
60
+ enum: LogicalOperatorEnum,
61
+ roles: RoleEnum.S_EVERYONE,
62
+ })
63
+ logicalOperator: LogicalOperatorEnum = undefined;
64
+
65
+ /**
66
+ * Filters to combine via logical operator
67
+ *
68
+ * The `() => FilterInput` thunk is required: `FilterInput` is declared below, so an eager
69
+ * reference here would read it before initialization.
70
+ */
71
+ @UnifiedField({
72
+ description: 'Filters to combine via logical operator',
73
+ isOptional: true,
74
+ roles: RoleEnum.S_EVERYONE,
75
+ type: () => FilterInput,
76
+ })
77
+ filters: FilterInput[] = undefined;
78
+
79
+ // ===================================================================================================================
80
+ // Methods
81
+ // ===================================================================================================================
82
+
83
+ /**
84
+ * Mapping for Subtypes
85
+ */
86
+ override map(
87
+ data: Partial<this> | Record<string, any>,
88
+ options: {
89
+ cloneDeep?: boolean;
90
+ funcAllowed?: boolean;
91
+ mapId?: boolean;
92
+ } = {},
93
+ ): this {
94
+ super.map(data, options);
95
+ this.filters = maps(data.filters, FilterInput, options.cloneDeep);
96
+ Object.keys(this).forEach((key) => this[key] === undefined && delete this[key]);
97
+ return this;
98
+ }
99
+ }
100
+
10
101
  /**
11
102
  * Input for filtering. The `singleFilter` will be ignored if the `combinedFilter` is set.
12
103
  */
@@ -4,7 +4,10 @@ import { BehaviorSubject, firstValueFrom } from 'rxjs';
4
4
  import { filter, map } from 'rxjs/operators';
5
5
 
6
6
  import { merge } from '../helpers/config.helper';
7
- import { clone, deepFreeze } from '../helpers/input.helper';
7
+ // Import from the clone.helper LEAF, never from input.helper: input.helper imports
8
+ // restricted.decorator, which reaches back here via core-tenant.helpers — that cycle is what the
9
+ // extraction removed. See clone.helper's docblock.
10
+ import { clone, deepFreeze } from '../helpers/clone.helper';
8
11
  import { IServerOptions } from '../interfaces/server-options.interface';
9
12
 
10
13
  /**
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Dependency-injection tokens of the AI module.
3
+ *
4
+ * They live in a dedicated, import-free leaf file — never in a service or in `core-ai.module.ts` —
5
+ * so that no file needing a token has to import the file that declares it, and no import cycle can
6
+ * form around one.
7
+ *
8
+ * On a cycle, a token read at MODULE-EVALUATION time — inside an `@Inject()` decorator argument, in
9
+ * the `design:paramtypes` metadata `emitDecoratorMetadata` emits, or in a static field initializer —
10
+ * reads a `const` still in its temporal dead zone, and SWC-compiled builds (`nest start -b swc`) die
11
+ * at startup with:
12
+ *
13
+ * ReferenceError: Cannot access 'AI_CONNECTION_MODEL' before initialization
14
+ *
15
+ * These tokens were previously declared across eleven `*.service.ts` files, every one of which is
16
+ * imported by `core-ai.module.ts` and injects tokens through constructor decorators. Nothing had
17
+ * crashed — the graph happened to be acyclic — but a single back-import from any of those services
18
+ * would have armed it, and `tsc`, `pnpm test` and `oxlint` are ALL blind to that. Only
19
+ * `pnpm run check:swc-tdz` sees it.
20
+ *
21
+ * A file that imports nothing can never be mid-evaluation when someone imports it, in any module
22
+ * system, under any compiler. Keep this file import-free.
23
+ *
24
+ * The old locations re-export their tokens, so every existing import path still resolves.
25
+ * See .claude/rules/architecture.md → "DI Token Placement (SWC-Safe)".
26
+ */
27
+
28
+ // ---- budget ----
29
+ /** DI token for the model constructor (used by CrudService mapping). */
30
+ export const AI_BUDGET_LIMIT_CLASS = 'AI_BUDGET_LIMIT_CLASS';
31
+ /** Mongoose injection token. */
32
+ export const AI_BUDGET_LIMIT_MODEL = 'AiBudgetLimit';
33
+
34
+ // ---- connection-preference ----
35
+ /** DI token for the model constructor (used by CrudService mapping). */
36
+ export const AI_CONNECTION_PREFERENCE_CLASS = 'AI_CONNECTION_PREFERENCE_CLASS';
37
+ /** Mongoose injection token. */
38
+ export const AI_CONNECTION_PREFERENCE_MODEL = 'AiConnectionPreference';
39
+
40
+ // ---- connection ----
41
+ /** DI token for the model constructor (used by CrudService mapping). */
42
+ export const AI_CONNECTION_CLASS = 'AI_CONNECTION_CLASS';
43
+ /** Mongoose injection token. */
44
+ export const AI_CONNECTION_MODEL = 'AiConnection';
45
+
46
+ // ---- conversation ----
47
+ /** DI token for the model constructor (used by CrudService mapping). */
48
+ export const AI_CONVERSATION_CLASS = 'AI_CONVERSATION_CLASS';
49
+ /** Mongoose injection token. */
50
+ export const AI_CONVERSATION_MODEL = 'AiConversation';
51
+
52
+ // ---- interaction ----
53
+ /** DI token for the model constructor (used by CrudService mapping). */
54
+ export const AI_INTERACTION_CLASS = 'AI_INTERACTION_CLASS';
55
+ /** Mongoose injection token. */
56
+ export const AI_INTERACTION_MODEL = 'AiInteraction';
57
+
58
+ // ---- mode ----
59
+ /** DI token for the model constructor (used by CrudService mapping). */
60
+ export const AI_MODE_CLASS = 'AI_MODE_CLASS';
61
+ /** Mongoose injection token. */
62
+ export const AI_MODE_MODEL = 'AiMode';
63
+
64
+ // ---- prompt-hint ----
65
+ /** DI token for the model constructor (used by CrudService mapping). */
66
+ export const AI_PROMPT_HINT_CLASS = 'AI_PROMPT_HINT_CLASS';
67
+ /** Mongoose injection token. */
68
+ export const AI_PROMPT_HINT_MODEL = 'AiPromptHint';
69
+
70
+ // ---- prompt ----
71
+ /** DI token for the model constructor (used by CrudService mapping). */
72
+ export const AI_PROMPT_CLASS = 'AI_PROMPT_CLASS';
73
+ /** Mongoose injection token. */
74
+ export const AI_PROMPT_MODEL = 'AiPrompt';
75
+
76
+ // ---- slot ----
77
+ /** DI token for the model constructor (used by CrudService mapping). */
78
+ export const AI_SLOT_CLASS = 'AI_SLOT_CLASS';
79
+ /** Mongoose injection token. */
80
+ export const AI_SLOT_MODEL = 'AiSlot';
81
+
82
+ // ---- tool-grant ----
83
+ /** DI token for the model constructor (used by CrudService mapping). */
84
+ export const AI_TOOL_GRANT_CLASS = 'AI_TOOL_GRANT_CLASS';
85
+ /** Mongoose injection token. */
86
+ export const AI_TOOL_GRANT_MODEL = 'AiToolGrant';
87
+
88
+ // ---- tool-policy ----
89
+ /** DI token for the model constructor (used by CrudService mapping). */
90
+ export const AI_TOOL_POLICY_CLASS = 'AI_TOOL_POLICY_CLASS';
91
+ /** Mongoose injection token. */
92
+ export const AI_TOOL_POLICY_MODEL = 'AiToolPolicy';
@@ -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
- /** Mongoose injection token for the budget-limit model. */
15
- export const AI_BUDGET_LIMIT_MODEL = 'AiBudgetLimit';
16
- /** DI token for the budget-limit model constructor. */
17
- export const AI_BUDGET_LIMIT_CLASS = 'AI_BUDGET_LIMIT_CLASS';
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
- /** Mongoose injection token for the connection-preference model. */
14
- export const AI_CONNECTION_PREFERENCE_MODEL = 'AiConnectionPreference';
15
- /** DI token for the connection-preference model constructor. */
16
- export const AI_CONNECTION_PREFERENCE_CLASS = 'AI_CONNECTION_PREFERENCE_CLASS';
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
- * DI token for the AI connection model constructor (used by CrudService mapping).
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 const AI_CONNECTION_CLASS = 'AI_CONNECTION_CLASS';
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
- * DI token for the AI conversation model constructor.
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 const AI_CONVERSATION_CLASS = 'AI_CONVERSATION_CLASS';
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 './core-ai.service';
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
- * DI token for the AI interaction model constructor.
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 const AI_INTERACTION_CLASS = 'AI_INTERACTION_CLASS';
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
- export const AI_MODE_MODEL = 'AiMode';
10
- export const AI_MODE_CLASS = 'AI_MODE_CLASS';
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
- /** Mongoose injection token for the prompt-hint model. */
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
- /** DI token for the prompt-hint model constructor. */
16
- export const AI_PROMPT_HINT_CLASS = 'AI_PROMPT_HINT_CLASS';
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
- export const AI_PROMPT_MODEL = 'AiPrompt';
14
- export const AI_PROMPT_CLASS = 'AI_PROMPT_CLASS';
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
- /** Mongoose injection token for the slot model. */
15
- export const AI_SLOT_MODEL = 'AiSlot';
14
+ import { AI_SLOT_CLASS, AI_SLOT_MODEL } from '../core-ai.constants';
16
15
 
17
- /** DI token for the slot model constructor. */
18
- export const AI_SLOT_CLASS = 'AI_SLOT_CLASS';
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
- /** Mongoose injection token. */
10
- export const AI_TOOL_GRANT_MODEL = 'AiToolGrant';
11
- /** DI token for the model constructor. */
12
- export const AI_TOOL_GRANT_CLASS = 'AI_TOOL_GRANT_CLASS';
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
- /** Mongoose injection token. */
10
- export const AI_TOOL_POLICY_MODEL = 'AiToolPolicy';
11
- /** DI token for the model constructor. */
12
- export const AI_TOOL_POLICY_CLASS = 'AI_TOOL_POLICY_CLASS';
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
- * Record passed to {@link CoreAiService.audit} for each prompt run.
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 interface AiInteractionRecord {
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.
@@ -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 { CoreBetterAuthModule } from './core-better-auth.module';
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 CoreBetterAuthModule static reference.
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 CoreBetterAuthModule
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 = CoreBetterAuthModule.getTokenServiceInstance();
55
+ this.tokenService = getBetterAuthTokenService();
50
56
  }
51
57
  return this.tokenService;
52
58
  }
@@ -476,6 +476,22 @@ export function createBetterAuthInstance(options: CreateBetterAuthOptions): Crea
476
476
  finalConfig.advanced = {
477
477
  ...(betterAuthConfig.advanced as Record<string, unknown>),
478
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
+ }),
479
495
  };
480
496
  }
481
497
  } else {