@lenne.tech/nest-server 12.0.0 → 12.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (693) hide show
  1. package/.claude/rules/architecture.md +6 -1
  2. package/.claude/rules/better-auth.md +46 -0
  3. package/.claude/rules/configurable-features.md +3 -2
  4. package/.claude/rules/package-management.md +24 -2
  5. package/.claude/rules/persistence.md +100 -0
  6. package/.claude/rules/role-system.md +9 -0
  7. package/.claude/rules/testing.md +116 -18
  8. package/CLAUDE.md +21 -2
  9. package/FRAMEWORK-API.md +6 -4
  10. package/README.md +106 -34
  11. package/dist/core/common/decorators/unified-field.decorator.d.ts +2 -1
  12. package/dist/core/common/decorators/unified-field.decorator.js +4 -1
  13. package/dist/core/common/decorators/unified-field.decorator.js.map +1 -1
  14. package/dist/core/common/filters/mongoose-cast-error.filter.d.ts +7 -5
  15. package/dist/core/common/filters/mongoose-cast-error.filter.js +3 -2
  16. package/dist/core/common/filters/mongoose-cast-error.filter.js.map +1 -1
  17. package/dist/core/common/helpers/db.helper.d.ts +2 -1
  18. package/dist/core/common/helpers/db.helper.js +32 -26
  19. package/dist/core/common/helpers/db.helper.js.map +1 -1
  20. package/dist/core/common/helpers/decorator.helper.d.ts +1 -1
  21. package/dist/core/common/helpers/filter.helper.d.ts +2 -1
  22. package/dist/core/common/helpers/filter.helper.js.map +1 -1
  23. package/dist/core/common/helpers/gridfs.helper.d.ts +4 -4
  24. package/dist/core/common/helpers/gridfs.helper.js +14 -6
  25. package/dist/core/common/helpers/gridfs.helper.js.map +1 -1
  26. package/dist/core/common/helpers/id.helper.d.ts +4 -1
  27. package/dist/core/common/helpers/id.helper.js +10 -4
  28. package/dist/core/common/helpers/id.helper.js.map +1 -1
  29. package/dist/core/common/helpers/model.helper.js +5 -5
  30. package/dist/core/common/helpers/model.helper.js.map +1 -1
  31. package/dist/core/common/helpers/service.helper.js +3 -3
  32. package/dist/core/common/helpers/service.helper.js.map +1 -1
  33. package/dist/core/common/interfaces/server-options.interface.d.ts +18 -2
  34. package/dist/core/common/interfaces/service-options.interface.d.ts +2 -2
  35. package/dist/core/common/models/core-persistence.model.d.ts +1 -2
  36. package/dist/core/common/models/core-persistence.model.js +3 -3
  37. package/dist/core/common/models/core-persistence.model.js.map +1 -1
  38. package/dist/core/common/persistence/core-persistence.lifecycle.d.ts +9 -0
  39. package/dist/core/common/persistence/core-persistence.lifecycle.js +38 -0
  40. package/dist/core/common/persistence/core-persistence.lifecycle.js.map +1 -0
  41. package/dist/core/common/persistence/core-persistence.module.d.ts +9 -0
  42. package/dist/core/common/persistence/core-persistence.module.js +76 -0
  43. package/dist/core/common/persistence/core-persistence.module.js.map +1 -0
  44. package/dist/core/common/persistence/data-source.config.d.ts +33 -0
  45. package/dist/core/common/persistence/data-source.config.js +151 -0
  46. package/dist/core/common/persistence/data-source.config.js.map +1 -0
  47. package/dist/core/common/persistence/data-source.runtime.d.ts +45 -0
  48. package/dist/core/common/persistence/data-source.runtime.js +278 -0
  49. package/dist/core/common/persistence/data-source.runtime.js.map +1 -0
  50. package/dist/core/common/persistence/data-store.interface.d.ts +73 -0
  51. package/dist/core/common/persistence/data-store.interface.js +2 -0
  52. package/dist/core/common/persistence/data-store.interface.js.map +1 -0
  53. package/dist/core/common/persistence/expiry-sweep.d.ts +12 -0
  54. package/dist/core/common/persistence/expiry-sweep.js +55 -0
  55. package/dist/core/common/persistence/expiry-sweep.js.map +1 -0
  56. package/dist/core/common/persistence/id-format.d.ts +3 -0
  57. package/dist/core/common/persistence/id-format.js +21 -0
  58. package/dist/core/common/persistence/id-format.js.map +1 -0
  59. package/dist/core/common/persistence/infrastructure-locks.d.ts +13 -0
  60. package/dist/core/common/persistence/infrastructure-locks.js +35 -0
  61. package/dist/core/common/persistence/infrastructure-locks.js.map +1 -0
  62. package/dist/core/common/persistence/inject-store.decorator.d.ts +1 -0
  63. package/dist/core/common/persistence/inject-store.decorator.js +6 -0
  64. package/dist/core/common/persistence/inject-store.decorator.js.map +1 -0
  65. package/dist/core/common/persistence/lock-store.interface.d.ts +5 -0
  66. package/dist/core/common/persistence/lock-store.interface.js +2 -0
  67. package/dist/core/common/persistence/lock-store.interface.js.map +1 -0
  68. package/dist/core/common/persistence/model-descriptor.interface.d.ts +36 -0
  69. package/dist/core/common/persistence/model-descriptor.interface.js +2 -0
  70. package/dist/core/common/persistence/model-descriptor.interface.js.map +1 -0
  71. package/dist/core/common/persistence/model-metadata.registry.d.ts +14 -0
  72. package/dist/core/common/persistence/model-metadata.registry.js +194 -0
  73. package/dist/core/common/persistence/model-metadata.registry.js.map +1 -0
  74. package/dist/core/common/persistence/mongo/collection-name.d.ts +2 -0
  75. package/dist/core/common/persistence/mongo/collection-name.js +73 -0
  76. package/dist/core/common/persistence/mongo/collection-name.js.map +1 -0
  77. package/dist/core/common/persistence/mongo/declared-schema.view.d.ts +11 -0
  78. package/dist/core/common/persistence/mongo/declared-schema.view.js +292 -0
  79. package/dist/core/common/persistence/mongo/declared-schema.view.js.map +1 -0
  80. package/dist/core/common/persistence/mongo/hex-object-id.d.ts +13 -0
  81. package/dist/core/common/persistence/mongo/hex-object-id.js +54 -0
  82. package/dist/core/common/persistence/mongo/hex-object-id.js.map +1 -0
  83. package/dist/core/common/persistence/mongo/model-declarations.d.ts +35 -0
  84. package/dist/core/common/persistence/mongo/model-declarations.js +80 -0
  85. package/dist/core/common/persistence/mongo/model-declarations.js.map +1 -0
  86. package/dist/core/common/persistence/mongo/mongo-packages.d.ts +15 -0
  87. package/dist/core/common/persistence/mongo/mongo-packages.js +66 -0
  88. package/dist/core/common/persistence/mongo/mongo-packages.js.map +1 -0
  89. package/dist/core/common/persistence/mongo/mongoose-compat.d.ts +22 -0
  90. package/dist/core/common/persistence/mongo/mongoose-compat.js +65 -0
  91. package/dist/core/common/persistence/mongo/mongoose-compat.js.map +1 -0
  92. package/dist/core/common/persistence/mongo/no-mongo-connection.module.d.ts +2 -0
  93. package/dist/core/common/persistence/mongo/no-mongo-connection.module.js +19 -0
  94. package/dist/core/common/persistence/mongo/no-mongo-connection.module.js.map +1 -0
  95. package/dist/core/common/persistence/mongo-lock-store.d.ts +21 -0
  96. package/dist/core/common/persistence/mongo-lock-store.js +72 -0
  97. package/dist/core/common/persistence/mongo-lock-store.js.map +1 -0
  98. package/dist/core/common/persistence/mongoose-data-store.d.ts +33 -0
  99. package/dist/core/common/persistence/mongoose-data-store.js +187 -0
  100. package/dist/core/common/persistence/mongoose-data-store.js.map +1 -0
  101. package/dist/core/common/persistence/neutral-populate.d.ts +6 -0
  102. package/dist/core/common/persistence/neutral-populate.js +72 -0
  103. package/dist/core/common/persistence/neutral-populate.js.map +1 -0
  104. package/dist/core/common/persistence/persisted-model.decorator.d.ts +6 -0
  105. package/dist/core/common/persistence/persisted-model.decorator.js +24 -0
  106. package/dist/core/common/persistence/persisted-model.decorator.js.map +1 -0
  107. package/dist/core/common/persistence/persistence-runtime.registry.d.ts +61 -0
  108. package/dist/core/common/persistence/persistence-runtime.registry.js +47 -0
  109. package/dist/core/common/persistence/persistence-runtime.registry.js.map +1 -0
  110. package/dist/core/common/persistence/persistence.tokens.d.ts +1 -0
  111. package/dist/core/common/persistence/persistence.tokens.js +4 -0
  112. package/dist/core/common/persistence/persistence.tokens.js.map +1 -0
  113. package/dist/core/common/persistence/policies/audit.policy.d.ts +8 -0
  114. package/dist/core/common/persistence/policies/audit.policy.js +37 -0
  115. package/dist/core/common/persistence/policies/audit.policy.js.map +1 -0
  116. package/dist/core/common/persistence/policies/password.policy.d.ts +3 -0
  117. package/dist/core/common/persistence/policies/password.policy.js +46 -0
  118. package/dist/core/common/persistence/policies/password.policy.js.map +1 -0
  119. package/dist/core/common/persistence/policies/role-guard.policy.d.ts +3 -0
  120. package/dist/core/common/persistence/policies/role-guard.policy.js +46 -0
  121. package/dist/core/common/persistence/policies/role-guard.policy.js.map +1 -0
  122. package/dist/core/common/persistence/policies/system-role.policy.d.ts +5 -0
  123. package/dist/core/common/persistence/policies/system-role.policy.js +46 -0
  124. package/dist/core/common/persistence/policies/system-role.policy.js.map +1 -0
  125. package/dist/core/common/persistence/policies/tenant.policy.d.ts +19 -0
  126. package/dist/core/common/persistence/policies/tenant.policy.js +120 -0
  127. package/dist/core/common/persistence/policies/tenant.policy.js.map +1 -0
  128. package/dist/core/common/persistence/routed-model.guard.d.ts +4 -0
  129. package/dist/core/common/persistence/routed-model.guard.js +73 -0
  130. package/dist/core/common/persistence/routed-model.guard.js.map +1 -0
  131. package/dist/core/common/persistence/sql/mysql/mysql-data-store.d.ts +46 -0
  132. package/dist/core/common/persistence/sql/mysql/mysql-data-store.js +350 -0
  133. package/dist/core/common/persistence/sql/mysql/mysql-data-store.js.map +1 -0
  134. package/dist/core/common/persistence/sql/mysql/mysql-executor.d.ts +13 -0
  135. package/dist/core/common/persistence/sql/mysql/mysql-executor.js +194 -0
  136. package/dist/core/common/persistence/sql/mysql/mysql-executor.js.map +1 -0
  137. package/dist/core/common/persistence/sql/mysql/mysql-lock-store.d.ts +11 -0
  138. package/dist/core/common/persistence/sql/mysql/mysql-lock-store.js +55 -0
  139. package/dist/core/common/persistence/sql/mysql/mysql-lock-store.js.map +1 -0
  140. package/dist/core/common/persistence/sql/mysql/mysql-schema.compiler.d.ts +36 -0
  141. package/dist/core/common/persistence/sql/mysql/mysql-schema.compiler.js +288 -0
  142. package/dist/core/common/persistence/sql/mysql/mysql-schema.compiler.js.map +1 -0
  143. package/dist/core/common/persistence/sql/mysql/mysql-schema.migrator.d.ts +19 -0
  144. package/dist/core/common/persistence/sql/mysql/mysql-schema.migrator.js +328 -0
  145. package/dist/core/common/persistence/sql/mysql/mysql-schema.migrator.js.map +1 -0
  146. package/dist/core/common/persistence/sql/mysql/mysql-session.d.ts +13 -0
  147. package/dist/core/common/persistence/sql/mysql/mysql-session.js +85 -0
  148. package/dist/core/common/persistence/sql/mysql/mysql-session.js.map +1 -0
  149. package/dist/core/common/persistence/sql/mysql/mysql-update.compiler.d.ts +9 -0
  150. package/dist/core/common/persistence/sql/mysql/mysql-update.compiler.js +264 -0
  151. package/dist/core/common/persistence/sql/mysql/mysql-update.compiler.js.map +1 -0
  152. package/dist/core/common/persistence/sql/mysql/mysql-where.compiler.d.ts +20 -0
  153. package/dist/core/common/persistence/sql/mysql/mysql-where.compiler.js +519 -0
  154. package/dist/core/common/persistence/sql/mysql/mysql-where.compiler.js.map +1 -0
  155. package/dist/core/common/persistence/sql/postgres/postgres-lock-store.d.ts +11 -0
  156. package/dist/core/common/persistence/sql/postgres/postgres-lock-store.js +44 -0
  157. package/dist/core/common/persistence/sql/postgres/postgres-lock-store.js.map +1 -0
  158. package/dist/core/common/persistence/sql/postgres/postgres-schema.compiler.d.ts +42 -0
  159. package/dist/core/common/persistence/sql/postgres/postgres-schema.compiler.js +220 -0
  160. package/dist/core/common/persistence/sql/postgres/postgres-schema.compiler.js.map +1 -0
  161. package/dist/core/common/persistence/sql/postgres/postgres-schema.migrator.d.ts +12 -0
  162. package/dist/core/common/persistence/sql/postgres/postgres-schema.migrator.js +315 -0
  163. package/dist/core/common/persistence/sql/postgres/postgres-schema.migrator.js.map +1 -0
  164. package/dist/core/common/persistence/sql/postgres/postgres-update.compiler.d.ts +5 -0
  165. package/dist/core/common/persistence/sql/postgres/postgres-update.compiler.js +225 -0
  166. package/dist/core/common/persistence/sql/postgres/postgres-update.compiler.js.map +1 -0
  167. package/dist/core/common/persistence/sql/postgres/postgres-where.compiler.d.ts +13 -0
  168. package/dist/core/common/persistence/sql/postgres/postgres-where.compiler.js +484 -0
  169. package/dist/core/common/persistence/sql/postgres/postgres-where.compiler.js.map +1 -0
  170. package/dist/core/common/persistence/sql/sql-cast.d.ts +2 -0
  171. package/dist/core/common/persistence/sql/sql-cast.js +194 -0
  172. package/dist/core/common/persistence/sql/sql-cast.js.map +1 -0
  173. package/dist/core/common/persistence/sql/sql-data-store.d.ts +79 -0
  174. package/dist/core/common/persistence/sql/sql-data-store.js +592 -0
  175. package/dist/core/common/persistence/sql/sql-data-store.js.map +1 -0
  176. package/dist/core/common/persistence/sql/sql-dialect.d.ts +20 -0
  177. package/dist/core/common/persistence/sql/sql-dialect.js +38 -0
  178. package/dist/core/common/persistence/sql/sql-dialect.js.map +1 -0
  179. package/dist/core/common/persistence/sql/sql-errors.d.ts +17 -0
  180. package/dist/core/common/persistence/sql/sql-errors.js +25 -0
  181. package/dist/core/common/persistence/sql/sql-errors.js.map +1 -0
  182. package/dist/core/common/persistence/sql/sql-executor.factory.d.ts +11 -0
  183. package/dist/core/common/persistence/sql/sql-executor.factory.js +199 -0
  184. package/dist/core/common/persistence/sql/sql-executor.factory.js.map +1 -0
  185. package/dist/core/common/persistence/sql/sql-executor.interface.d.ts +32 -0
  186. package/dist/core/common/persistence/sql/sql-executor.interface.js +2 -0
  187. package/dist/core/common/persistence/sql/sql-executor.interface.js.map +1 -0
  188. package/dist/core/common/persistence/sql/sql-naming.d.ts +15 -0
  189. package/dist/core/common/persistence/sql/sql-naming.js +90 -0
  190. package/dist/core/common/persistence/sql/sql-naming.js.map +1 -0
  191. package/dist/core/common/persistence/sql/sql-schema.shared.d.ts +25 -0
  192. package/dist/core/common/persistence/sql/sql-schema.shared.js +69 -0
  193. package/dist/core/common/persistence/sql/sql-schema.shared.js.map +1 -0
  194. package/dist/core/common/persistence/sql/sql-session.d.ts +6 -0
  195. package/dist/core/common/persistence/sql/sql-session.js +78 -0
  196. package/dist/core/common/persistence/sql/sql-session.js.map +1 -0
  197. package/dist/core/common/persistence/sql/where-shared.d.ts +18 -0
  198. package/dist/core/common/persistence/sql/where-shared.js +93 -0
  199. package/dist/core/common/persistence/sql/where-shared.js.map +1 -0
  200. package/dist/core/common/persistence/store-guards.d.ts +10 -0
  201. package/dist/core/common/persistence/store-guards.js +49 -0
  202. package/dist/core/common/persistence/store-guards.js.map +1 -0
  203. package/dist/core/common/persistence/store-upsert.d.ts +2 -0
  204. package/dist/core/common/persistence/store-upsert.js +20 -0
  205. package/dist/core/common/persistence/store-upsert.js.map +1 -0
  206. package/dist/core/common/persistence/unavailable-store.d.ts +2 -0
  207. package/dist/core/common/persistence/unavailable-store.js +53 -0
  208. package/dist/core/common/persistence/unavailable-store.js.map +1 -0
  209. package/dist/core/common/persistence/unsupported-capability.error.d.ts +3 -0
  210. package/dist/core/common/persistence/unsupported-capability.error.js +4 -0
  211. package/dist/core/common/persistence/unsupported-capability.error.js.map +1 -0
  212. package/dist/core/common/plugins/mongoose-audit-fields.plugin.js +24 -68
  213. package/dist/core/common/plugins/mongoose-audit-fields.plugin.js.map +1 -1
  214. package/dist/core/common/plugins/mongoose-id.plugin.js +3 -0
  215. package/dist/core/common/plugins/mongoose-id.plugin.js.map +1 -1
  216. package/dist/core/common/plugins/mongoose-password.plugin.d.ts +1 -3
  217. package/dist/core/common/plugins/mongoose-password.plugin.js +2 -45
  218. package/dist/core/common/plugins/mongoose-password.plugin.js.map +1 -1
  219. package/dist/core/common/plugins/mongoose-role-guard.plugin.js +5 -46
  220. package/dist/core/common/plugins/mongoose-role-guard.plugin.js.map +1 -1
  221. package/dist/core/common/plugins/mongoose-system-role.plugin.js +2 -43
  222. package/dist/core/common/plugins/mongoose-system-role.plugin.js.map +1 -1
  223. package/dist/core/common/plugins/mongoose-tenant.plugin.js +10 -70
  224. package/dist/core/common/plugins/mongoose-tenant.plugin.js.map +1 -1
  225. package/dist/core/common/scalars/upload.scalar.js +1 -1
  226. package/dist/core/common/scalars/upload.scalar.js.map +1 -1
  227. package/dist/core/common/services/core-cron-jobs.initializer.js +1 -1
  228. package/dist/core/common/services/core-cron-jobs.initializer.js.map +1 -1
  229. package/dist/core/common/services/core-cron-jobs.service.d.ts +1 -0
  230. package/dist/core/common/services/core-cron-jobs.service.js +23 -14
  231. package/dist/core/common/services/core-cron-jobs.service.js.map +1 -1
  232. package/dist/core/common/services/crud.service.d.ts +4 -1
  233. package/dist/core/common/services/crud.service.js +132 -23
  234. package/dist/core/common/services/crud.service.js.map +1 -1
  235. package/dist/core/common/services/model-doc.service.js +1 -1
  236. package/dist/core/common/services/model-doc.service.js.map +1 -1
  237. package/dist/core/common/services/module.service.d.ts +9 -2
  238. package/dist/core/common/services/module.service.js +51 -6
  239. package/dist/core/common/services/module.service.js.map +1 -1
  240. package/dist/core/common/types/field-selection.type.d.ts +1 -1
  241. package/dist/core/common/types/id.type.d.ts +4 -4
  242. package/dist/core/common/types/object-id-like.type.d.ts +3 -0
  243. package/dist/core/common/types/object-id-like.type.js +2 -0
  244. package/dist/core/common/types/object-id-like.type.js.map +1 -0
  245. package/dist/core/common/types/populate-config.type.d.ts +1 -1
  246. package/dist/core/common/types/string-or-object-id.type.d.ts +2 -2
  247. package/dist/core/modules/ai/core-ai.constants.d.ts +4 -0
  248. package/dist/core/modules/ai/core-ai.constants.js +4 -0
  249. package/dist/core/modules/ai/core-ai.constants.js.map +1 -1
  250. package/dist/core/modules/ai/core-ai.module.js +21 -17
  251. package/dist/core/modules/ai/core-ai.module.js.map +1 -1
  252. package/dist/core/modules/ai/models/core-ai-budget-limit.model.d.ts +2 -92
  253. package/dist/core/modules/ai/models/core-ai-budget-limit.model.js +1 -1
  254. package/dist/core/modules/ai/models/core-ai-budget-limit.model.js.map +1 -1
  255. package/dist/core/modules/ai/models/core-ai-connection-preference.model.d.ts +2 -87
  256. package/dist/core/modules/ai/models/core-ai-connection-preference.model.js +1 -1
  257. package/dist/core/modules/ai/models/core-ai-connection-preference.model.js.map +1 -1
  258. package/dist/core/modules/ai/models/core-ai-connection.model.d.ts +2 -177
  259. package/dist/core/modules/ai/models/core-ai-connection.model.js +1 -1
  260. package/dist/core/modules/ai/models/core-ai-connection.model.js.map +1 -1
  261. package/dist/core/modules/ai/models/core-ai-conversation.model.d.ts +2 -92
  262. package/dist/core/modules/ai/models/core-ai-conversation.model.js +3 -4
  263. package/dist/core/modules/ai/models/core-ai-conversation.model.js.map +1 -1
  264. package/dist/core/modules/ai/models/core-ai-interaction.model.d.ts +2 -120
  265. package/dist/core/modules/ai/models/core-ai-interaction.model.js +2 -3
  266. package/dist/core/modules/ai/models/core-ai-interaction.model.js.map +1 -1
  267. package/dist/core/modules/ai/models/core-ai-mcp-oauth.models.d.ts +3 -0
  268. package/dist/core/modules/ai/models/core-ai-mcp-oauth.models.js +21 -0
  269. package/dist/core/modules/ai/models/core-ai-mcp-oauth.models.js.map +1 -0
  270. package/dist/core/modules/ai/models/core-ai-mode.model.d.ts +2 -102
  271. package/dist/core/modules/ai/models/core-ai-mode.model.js +1 -1
  272. package/dist/core/modules/ai/models/core-ai-mode.model.js.map +1 -1
  273. package/dist/core/modules/ai/models/core-ai-prompt-hint.model.d.ts +2 -97
  274. package/dist/core/modules/ai/models/core-ai-prompt-hint.model.js +1 -1
  275. package/dist/core/modules/ai/models/core-ai-prompt-hint.model.js.map +1 -1
  276. package/dist/core/modules/ai/models/core-ai-prompt.model.d.ts +2 -112
  277. package/dist/core/modules/ai/models/core-ai-prompt.model.js +1 -1
  278. package/dist/core/modules/ai/models/core-ai-prompt.model.js.map +1 -1
  279. package/dist/core/modules/ai/models/core-ai-slot.model.d.ts +2 -112
  280. package/dist/core/modules/ai/models/core-ai-slot.model.js +1 -1
  281. package/dist/core/modules/ai/models/core-ai-slot.model.js.map +1 -1
  282. package/dist/core/modules/ai/models/core-ai-tool-grant.model.d.ts +2 -97
  283. package/dist/core/modules/ai/models/core-ai-tool-grant.model.js +1 -1
  284. package/dist/core/modules/ai/models/core-ai-tool-grant.model.js.map +1 -1
  285. package/dist/core/modules/ai/models/core-ai-tool-policy.model.d.ts +2 -103
  286. package/dist/core/modules/ai/models/core-ai-tool-policy.model.js +1 -1
  287. package/dist/core/modules/ai/models/core-ai-tool-policy.model.js.map +1 -1
  288. package/dist/core/modules/ai/services/core-ai-budget.service.d.ts +3 -1
  289. package/dist/core/modules/ai/services/core-ai-budget.service.js +15 -5
  290. package/dist/core/modules/ai/services/core-ai-budget.service.js.map +1 -1
  291. package/dist/core/modules/ai/services/core-ai-connection-preference.service.d.ts +1 -1
  292. package/dist/core/modules/ai/services/core-ai-connection-preference.service.js +6 -10
  293. package/dist/core/modules/ai/services/core-ai-connection-preference.service.js.map +1 -1
  294. package/dist/core/modules/ai/services/core-ai-connection.service.d.ts +1 -1
  295. package/dist/core/modules/ai/services/core-ai-connection.service.js +20 -42
  296. package/dist/core/modules/ai/services/core-ai-connection.service.js.map +1 -1
  297. package/dist/core/modules/ai/services/core-ai-conversation.service.d.ts +1 -1
  298. package/dist/core/modules/ai/services/core-ai-conversation.service.js +28 -29
  299. package/dist/core/modules/ai/services/core-ai-conversation.service.js.map +1 -1
  300. package/dist/core/modules/ai/services/core-ai-interaction.service.d.ts +1 -1
  301. package/dist/core/modules/ai/services/core-ai-interaction.service.js +3 -4
  302. package/dist/core/modules/ai/services/core-ai-interaction.service.js.map +1 -1
  303. package/dist/core/modules/ai/services/core-ai-mcp-oauth.service.d.ts +8 -0
  304. package/dist/core/modules/ai/services/core-ai-mcp-oauth.service.js +70 -18
  305. package/dist/core/modules/ai/services/core-ai-mcp-oauth.service.js.map +1 -1
  306. package/dist/core/modules/ai/services/core-ai-mode.service.d.ts +1 -1
  307. package/dist/core/modules/ai/services/core-ai-mode.service.js +3 -7
  308. package/dist/core/modules/ai/services/core-ai-mode.service.js.map +1 -1
  309. package/dist/core/modules/ai/services/core-ai-prompt-hint.service.d.ts +8 -1
  310. package/dist/core/modules/ai/services/core-ai-prompt-hint.service.js +32 -26
  311. package/dist/core/modules/ai/services/core-ai-prompt-hint.service.js.map +1 -1
  312. package/dist/core/modules/ai/services/core-ai-prompt.service.d.ts +1 -1
  313. package/dist/core/modules/ai/services/core-ai-prompt.service.js +10 -13
  314. package/dist/core/modules/ai/services/core-ai-prompt.service.js.map +1 -1
  315. package/dist/core/modules/ai/services/core-ai-slot.service.d.ts +1 -1
  316. package/dist/core/modules/ai/services/core-ai-slot.service.js +14 -25
  317. package/dist/core/modules/ai/services/core-ai-slot.service.js.map +1 -1
  318. package/dist/core/modules/ai/services/core-ai-tool-grant.service.d.ts +1 -1
  319. package/dist/core/modules/ai/services/core-ai-tool-grant.service.js +6 -18
  320. package/dist/core/modules/ai/services/core-ai-tool-grant.service.js.map +1 -1
  321. package/dist/core/modules/ai/services/core-ai-tool-policy.service.d.ts +1 -1
  322. package/dist/core/modules/ai/services/core-ai-tool-policy.service.js +4 -8
  323. package/dist/core/modules/ai/services/core-ai-tool-policy.service.js.map +1 -1
  324. package/dist/core/modules/api-token/core-api-token.model.js +1 -1
  325. package/dist/core/modules/api-token/core-api-token.model.js.map +1 -1
  326. package/dist/core/modules/api-token/core-api-token.module.js +3 -2
  327. package/dist/core/modules/api-token/core-api-token.module.js.map +1 -1
  328. package/dist/core/modules/api-token/core-api-token.service.d.ts +8 -2
  329. package/dist/core/modules/api-token/core-api-token.service.js +106 -39
  330. package/dist/core/modules/api-token/core-api-token.service.js.map +1 -1
  331. package/dist/core/modules/auth/core-auth.module.js +2 -0
  332. package/dist/core/modules/auth/core-auth.module.js.map +1 -1
  333. package/dist/core/modules/auth/guards/legacy-auth-rate-limit.guard.js +1 -2
  334. package/dist/core/modules/auth/guards/legacy-auth-rate-limit.guard.js.map +1 -1
  335. package/dist/core/modules/auth/guards/role-guard.token.d.ts +1 -0
  336. package/dist/core/modules/auth/guards/role-guard.token.js +2 -0
  337. package/dist/core/modules/auth/guards/role-guard.token.js.map +1 -0
  338. package/dist/core/modules/auth/services/core-auth.service.js +4 -2
  339. package/dist/core/modules/auth/services/core-auth.service.js.map +1 -1
  340. package/dist/core/modules/better-auth/better-auth-sql.helper.d.ts +6 -0
  341. package/dist/core/modules/better-auth/better-auth-sql.helper.js +113 -0
  342. package/dist/core/modules/better-auth/better-auth-sql.helper.js.map +1 -0
  343. package/dist/core/modules/better-auth/better-auth-token.service.d.ts +3 -1
  344. package/dist/core/modules/better-auth/better-auth-token.service.js +15 -24
  345. package/dist/core/modules/better-auth/better-auth-token.service.js.map +1 -1
  346. package/dist/core/modules/better-auth/better-auth.config.d.ts +7 -0
  347. package/dist/core/modules/better-auth/better-auth.config.js +16 -5
  348. package/dist/core/modules/better-auth/better-auth.config.js.map +1 -1
  349. package/dist/core/modules/better-auth/better-auth.types.d.ts +1 -1
  350. package/dist/core/modules/better-auth/core-better-auth-api.middleware.d.ts +1 -0
  351. package/dist/core/modules/better-auth/core-better-auth-api.middleware.js +6 -1
  352. package/dist/core/modules/better-auth/core-better-auth-api.middleware.js.map +1 -1
  353. package/dist/core/modules/better-auth/core-better-auth-challenge.service.js +30 -15
  354. package/dist/core/modules/better-auth/core-better-auth-challenge.service.js.map +1 -1
  355. package/dist/core/modules/better-auth/core-better-auth-mongo.users.d.ts +10 -0
  356. package/dist/core/modules/better-auth/core-better-auth-mongo.users.js +24 -0
  357. package/dist/core/modules/better-auth/core-better-auth-mongo.users.js.map +1 -0
  358. package/dist/core/modules/better-auth/core-better-auth-sql.registry.d.ts +3 -0
  359. package/dist/core/modules/better-auth/core-better-auth-sql.registry.js +8 -0
  360. package/dist/core/modules/better-auth/core-better-auth-sql.registry.js.map +1 -0
  361. package/dist/core/modules/better-auth/core-better-auth-sql.store.d.ts +71 -0
  362. package/dist/core/modules/better-auth/core-better-auth-sql.store.js +190 -0
  363. package/dist/core/modules/better-auth/core-better-auth-sql.store.js.map +1 -0
  364. package/dist/core/modules/better-auth/core-better-auth-user.mapper.d.ts +24 -1
  365. package/dist/core/modules/better-auth/core-better-auth-user.mapper.js +308 -32
  366. package/dist/core/modules/better-auth/core-better-auth-user.mapper.js.map +1 -1
  367. package/dist/core/modules/better-auth/core-better-auth.module.d.ts +2 -0
  368. package/dist/core/modules/better-auth/core-better-auth.module.js +54 -32
  369. package/dist/core/modules/better-auth/core-better-auth.module.js.map +1 -1
  370. package/dist/core/modules/better-auth/core-better-auth.service.d.ts +7 -2
  371. package/dist/core/modules/better-auth/core-better-auth.service.js +61 -9
  372. package/dist/core/modules/better-auth/core-better-auth.service.js.map +1 -1
  373. package/dist/core/modules/file/core-file-info.model.d.ts +1 -2
  374. package/dist/core/modules/file/core-file-info.model.js +2 -2
  375. package/dist/core/modules/file/core-file-info.model.js.map +1 -1
  376. package/dist/core/modules/file/core-file.service.d.ts +5 -3
  377. package/dist/core/modules/file/core-file.service.js +16 -9
  378. package/dist/core/modules/file/core-file.service.js.map +1 -1
  379. package/dist/core/modules/file/file-metadata.helper.d.ts +1 -1
  380. package/dist/core/modules/file/file-metadata.helper.js +2 -2
  381. package/dist/core/modules/file/file-metadata.helper.js.map +1 -1
  382. package/dist/core/modules/file/file-metadata.models.d.ts +35 -0
  383. package/dist/core/modules/file/file-metadata.models.js +20 -0
  384. package/dist/core/modules/file/file-metadata.models.js.map +1 -0
  385. package/dist/core/modules/file/file-metadata.store.d.ts +6 -0
  386. package/dist/core/modules/file/file-metadata.store.js +77 -0
  387. package/dist/core/modules/file/file-metadata.store.js.map +1 -0
  388. package/dist/core/modules/file/file-storage.helper.d.ts +1 -0
  389. package/dist/core/modules/file/file-storage.helper.js +34 -0
  390. package/dist/core/modules/file/file-storage.helper.js.map +1 -1
  391. package/dist/core/modules/file/filesystem-file.helper.d.ts +1 -1
  392. package/dist/core/modules/file/filesystem-file.helper.js +4 -4
  393. package/dist/core/modules/file/filesystem-file.helper.js.map +1 -1
  394. package/dist/core/modules/file/s3-file.helper.d.ts +1 -1
  395. package/dist/core/modules/file/s3-file.helper.js +5 -5
  396. package/dist/core/modules/file/s3-file.helper.js.map +1 -1
  397. package/dist/core/modules/health-check/core-health-check.module.js +12 -2
  398. package/dist/core/modules/health-check/core-health-check.module.js.map +1 -1
  399. package/dist/core/modules/health-check/core-health-check.service.d.ts +4 -3
  400. package/dist/core/modules/health-check/core-health-check.service.js +26 -5
  401. package/dist/core/modules/health-check/core-health-check.service.js.map +1 -1
  402. package/dist/core/modules/hub/core-hub.controller.d.ts +1 -0
  403. package/dist/core/modules/hub/core-hub.controller.js +10 -0
  404. package/dist/core/modules/hub/core-hub.controller.js.map +1 -1
  405. package/dist/core/modules/hub/core-hub.service.js +1 -1
  406. package/dist/core/modules/hub/core-hub.service.js.map +1 -1
  407. package/dist/core/modules/hub/helpers/hub-client-js.helper.js +23 -0
  408. package/dist/core/modules/hub/helpers/hub-client-js.helper.js.map +1 -1
  409. package/dist/core/modules/hub/hub-action-messages.d.ts +2 -0
  410. package/dist/core/modules/hub/hub-action-messages.js +2 -0
  411. package/dist/core/modules/hub/hub-action-messages.js.map +1 -1
  412. package/dist/core/modules/hub/hub-nav.js +1 -0
  413. package/dist/core/modules/hub/hub-nav.js.map +1 -1
  414. package/dist/core/modules/hub/interfaces/hub-panels.interface.d.ts +17 -1
  415. package/dist/core/modules/hub/services/core-hub-db.service.d.ts +6 -1
  416. package/dist/core/modules/hub/services/core-hub-db.service.js +16 -14
  417. package/dist/core/modules/hub/services/core-hub-db.service.js.map +1 -1
  418. package/dist/core/modules/hub/services/core-hub-migrations.service.d.ts +5 -0
  419. package/dist/core/modules/hub/services/core-hub-migrations.service.js +74 -1
  420. package/dist/core/modules/hub/services/core-hub-migrations.service.js.map +1 -1
  421. package/dist/core/modules/hub/services/core-hub-sources.service.d.ts +2 -1
  422. package/dist/core/modules/hub/services/core-hub-sources.service.js +32 -0
  423. package/dist/core/modules/hub/services/core-hub-sources.service.js.map +1 -1
  424. package/dist/core/modules/hub/services/hub-query-profiler.service.js +1 -1
  425. package/dist/core/modules/hub/services/hub-query-profiler.service.js.map +1 -1
  426. package/dist/core/modules/migrate/cli/migrate-cli.d.ts +2 -0
  427. package/dist/core/modules/migrate/cli/migrate-cli.js +35 -1
  428. package/dist/core/modules/migrate/cli/migrate-cli.js.map +1 -1
  429. package/dist/core/modules/migrate/index.d.ts +3 -0
  430. package/dist/core/modules/migrate/index.js +3 -0
  431. package/dist/core/modules/migrate/index.js.map +1 -1
  432. package/dist/core/modules/migrate/migration-runner.d.ts +2 -2
  433. package/dist/core/modules/migrate/migration-runner.js.map +1 -1
  434. package/dist/core/modules/migrate/migration-state-store.interface.d.ts +16 -0
  435. package/dist/core/modules/migrate/migration-state-store.interface.js +2 -0
  436. package/dist/core/modules/migrate/migration-state-store.interface.js.map +1 -0
  437. package/dist/core/modules/migrate/migration.helper.d.ts +3 -2
  438. package/dist/core/modules/migrate/migration.helper.js +21 -6
  439. package/dist/core/modules/migrate/migration.helper.js.map +1 -1
  440. package/dist/core/modules/migrate/mongo-state-store.d.ts +5 -12
  441. package/dist/core/modules/migrate/mongo-state-store.js +30 -24
  442. package/dist/core/modules/migrate/mongo-state-store.js.map +1 -1
  443. package/dist/core/modules/migrate/sql-migration.helper.d.ts +4 -0
  444. package/dist/core/modules/migrate/sql-migration.helper.js +34 -0
  445. package/dist/core/modules/migrate/sql-migration.helper.js.map +1 -0
  446. package/dist/core/modules/migrate/sql-state-store.d.ts +24 -0
  447. package/dist/core/modules/migrate/sql-state-store.js +129 -0
  448. package/dist/core/modules/migrate/sql-state-store.js.map +1 -0
  449. package/dist/core/modules/system-setup/core-system-setup.service.d.ts +8 -0
  450. package/dist/core/modules/system-setup/core-system-setup.service.js +41 -25
  451. package/dist/core/modules/system-setup/core-system-setup.service.js.map +1 -1
  452. package/dist/core/modules/tenant/core-tenant-member.model.js +1 -1
  453. package/dist/core/modules/tenant/core-tenant-member.model.js.map +1 -1
  454. package/dist/core/modules/tenant/core-tenant-member.store.d.ts +2 -0
  455. package/dist/core/modules/tenant/core-tenant-member.store.js +6 -0
  456. package/dist/core/modules/tenant/core-tenant-member.store.js.map +1 -0
  457. package/dist/core/modules/tenant/core-tenant.guard.d.ts +8 -3
  458. package/dist/core/modules/tenant/core-tenant.guard.js +57 -28
  459. package/dist/core/modules/tenant/core-tenant.guard.js.map +1 -1
  460. package/dist/core/modules/tenant/core-tenant.module.js +3 -2
  461. package/dist/core/modules/tenant/core-tenant.module.js.map +1 -1
  462. package/dist/core/modules/tenant/core-tenant.service.d.ts +3 -1
  463. package/dist/core/modules/tenant/core-tenant.service.js +48 -28
  464. package/dist/core/modules/tenant/core-tenant.service.js.map +1 -1
  465. package/dist/core/modules/tus/core-tus.service.d.ts +1 -1
  466. package/dist/core/modules/tus/core-tus.service.js +8 -7
  467. package/dist/core/modules/tus/core-tus.service.js.map +1 -1
  468. package/dist/core/modules/tus/tus.module.js +1 -1
  469. package/dist/core/modules/tus/tus.module.js.map +1 -1
  470. package/dist/core/modules/user/core-user.model.d.ts +1 -1
  471. package/dist/core/modules/user/core-user.model.js +1 -1
  472. package/dist/core/modules/user/core-user.model.js.map +1 -1
  473. package/dist/core/modules/user/core-user.service.d.ts +3 -1
  474. package/dist/core/modules/user/core-user.service.js +71 -16
  475. package/dist/core/modules/user/core-user.service.js.map +1 -1
  476. package/dist/core.module.js +39 -5
  477. package/dist/core.module.js.map +1 -1
  478. package/dist/index.d.ts +26 -0
  479. package/dist/index.js +26 -0
  480. package/dist/index.js.map +1 -1
  481. package/dist/server/common/models/persistence.model.d.ts +1 -1
  482. package/dist/server/common/models/persistence.model.js +3 -3
  483. package/dist/server/common/models/persistence.model.js.map +1 -1
  484. package/dist/server/modules/file/file-info.model.d.ts +1 -77
  485. package/dist/server/modules/file/file-info.model.js +1 -1
  486. package/dist/server/modules/file/file-info.model.js.map +1 -1
  487. package/dist/server/modules/file/file.service.js +1 -1
  488. package/dist/server/modules/file/file.service.js.map +1 -1
  489. package/dist/server/modules/user/user.model.d.ts +2 -186
  490. package/dist/server/modules/user/user.model.js +4 -5
  491. package/dist/server/modules/user/user.model.js.map +1 -1
  492. package/dist/server/modules/user/user.module.js +4 -3
  493. package/dist/server/modules/user/user.module.js.map +1 -1
  494. package/dist/server/modules/user/user.service.d.ts +1 -1
  495. package/dist/server/modules/user/user.service.js +5 -8
  496. package/dist/server/modules/user/user.service.js.map +1 -1
  497. package/dist/test/test.helper.js +2 -2
  498. package/dist/test/test.helper.js.map +1 -1
  499. package/dist/types/graphql-upload.d.ts +10 -7
  500. package/docs/REQUEST-LIFECYCLE.md +6 -3
  501. package/docs/choosing-a-database.md +107 -0
  502. package/docs/native-driver-security.md +29 -0
  503. package/docs/security-overrides.md +15 -6
  504. package/migration-guides/12.0.x-to-12.1.x.md +255 -0
  505. package/package.json +75 -29
  506. package/src/core/common/decorators/unified-field.decorator.ts +11 -1
  507. package/src/core/common/filters/mongoose-cast-error.filter.ts +12 -6
  508. package/src/core/common/helpers/db.helper.ts +40 -25
  509. package/src/core/common/helpers/decorator.helper.ts +1 -1
  510. package/src/core/common/helpers/filter.helper.ts +2 -1
  511. package/src/core/common/helpers/gridfs.helper.ts +28 -9
  512. package/src/core/common/helpers/id.helper.ts +21 -4
  513. package/src/core/common/helpers/model.helper.ts +5 -6
  514. package/src/core/common/helpers/service.helper.ts +3 -3
  515. package/src/core/common/interfaces/server-options.interface.ts +118 -10
  516. package/src/core/common/interfaces/service-options.interface.ts +2 -2
  517. package/src/core/common/models/core-persistence.model.ts +3 -3
  518. package/src/core/common/persistence/core-persistence.lifecycle.ts +47 -0
  519. package/src/core/common/persistence/core-persistence.module.ts +109 -0
  520. package/src/core/common/persistence/data-source.config.ts +271 -0
  521. package/src/core/common/persistence/data-source.runtime.ts +351 -0
  522. package/src/core/common/persistence/data-store.interface.ts +155 -0
  523. package/src/core/common/persistence/expiry-sweep.ts +105 -0
  524. package/src/core/common/persistence/id-format.ts +43 -0
  525. package/src/core/common/persistence/infrastructure-locks.ts +66 -0
  526. package/src/core/common/persistence/inject-store.decorator.ts +16 -0
  527. package/src/core/common/persistence/lock-store.interface.ts +17 -0
  528. package/src/core/common/persistence/model-descriptor.interface.ts +90 -0
  529. package/src/core/common/persistence/model-metadata.registry.ts +285 -0
  530. package/src/core/common/persistence/mongo/collection-name.ts +87 -0
  531. package/src/core/common/persistence/mongo/declared-schema.view.ts +378 -0
  532. package/src/core/common/persistence/mongo/hex-object-id.ts +75 -0
  533. package/src/core/common/persistence/mongo/model-declarations.ts +135 -0
  534. package/src/core/common/persistence/mongo/mongo-packages.ts +130 -0
  535. package/src/core/common/persistence/mongo/mongoose-compat.ts +129 -0
  536. package/src/core/common/persistence/mongo/no-mongo-connection.module.ts +18 -0
  537. package/src/core/common/persistence/mongo-lock-store.ts +121 -0
  538. package/src/core/common/persistence/mongoose-data-store.ts +232 -0
  539. package/src/core/common/persistence/neutral-populate.ts +98 -0
  540. package/src/core/common/persistence/persisted-model.decorator.ts +61 -0
  541. package/src/core/common/persistence/persistence-runtime.registry.ts +152 -0
  542. package/src/core/common/persistence/persistence.tokens.ts +9 -0
  543. package/src/core/common/persistence/policies/audit.policy.ts +80 -0
  544. package/src/core/common/persistence/policies/password.policy.ts +69 -0
  545. package/src/core/common/persistence/policies/role-guard.policy.ts +74 -0
  546. package/src/core/common/persistence/policies/system-role.policy.ts +90 -0
  547. package/src/core/common/persistence/policies/tenant.policy.ts +245 -0
  548. package/src/core/common/persistence/routed-model.guard.ts +107 -0
  549. package/src/core/common/persistence/sql/mysql/mysql-data-store.ts +503 -0
  550. package/src/core/common/persistence/sql/mysql/mysql-executor.ts +281 -0
  551. package/src/core/common/persistence/sql/mysql/mysql-lock-store.ts +87 -0
  552. package/src/core/common/persistence/sql/mysql/mysql-schema.compiler.ts +424 -0
  553. package/src/core/common/persistence/sql/mysql/mysql-schema.migrator.ts +453 -0
  554. package/src/core/common/persistence/sql/mysql/mysql-session.ts +131 -0
  555. package/src/core/common/persistence/sql/mysql/mysql-update.compiler.ts +379 -0
  556. package/src/core/common/persistence/sql/mysql/mysql-where.compiler.ts +686 -0
  557. package/src/core/common/persistence/sql/postgres/postgres-lock-store.ts +76 -0
  558. package/src/core/common/persistence/sql/postgres/postgres-schema.compiler.ts +328 -0
  559. package/src/core/common/persistence/sql/postgres/postgres-schema.migrator.ts +448 -0
  560. package/src/core/common/persistence/sql/postgres/postgres-update.compiler.ts +310 -0
  561. package/src/core/common/persistence/sql/postgres/postgres-where.compiler.ts +638 -0
  562. package/src/core/common/persistence/sql/sql-cast.ts +227 -0
  563. package/src/core/common/persistence/sql/sql-data-store.ts +762 -0
  564. package/src/core/common/persistence/sql/sql-dialect.ts +85 -0
  565. package/src/core/common/persistence/sql/sql-errors.ts +49 -0
  566. package/src/core/common/persistence/sql/sql-executor.factory.ts +282 -0
  567. package/src/core/common/persistence/sql/sql-executor.interface.ts +57 -0
  568. package/src/core/common/persistence/sql/sql-naming.ts +133 -0
  569. package/src/core/common/persistence/sql/sql-schema.shared.ts +105 -0
  570. package/src/core/common/persistence/sql/sql-session.ts +158 -0
  571. package/src/core/common/persistence/sql/where-shared.ts +118 -0
  572. package/src/core/common/persistence/store-guards.ts +73 -0
  573. package/src/core/common/persistence/store-upsert.ts +36 -0
  574. package/src/core/common/persistence/unavailable-store.ts +63 -0
  575. package/src/core/common/persistence/unsupported-capability.error.ts +7 -0
  576. package/src/core/common/plugins/mongoose-audit-fields.plugin.ts +33 -74
  577. package/src/core/common/plugins/mongoose-id.plugin.ts +9 -0
  578. package/src/core/common/plugins/mongoose-password.plugin.ts +6 -62
  579. package/src/core/common/plugins/mongoose-role-guard.plugin.ts +11 -63
  580. package/src/core/common/plugins/mongoose-system-role.plugin.ts +7 -78
  581. package/src/core/common/plugins/mongoose-tenant.plugin.ts +23 -144
  582. package/src/core/common/scalars/upload.scalar.ts +8 -3
  583. package/src/core/common/services/core-cron-jobs.initializer.ts +1 -1
  584. package/src/core/common/services/core-cron-jobs.service.ts +33 -22
  585. package/src/core/common/services/crud.service.ts +166 -35
  586. package/src/core/common/services/model-doc.service.ts +1 -1
  587. package/src/core/common/services/module.service.ts +78 -6
  588. package/src/core/common/types/field-selection.type.ts +1 -1
  589. package/src/core/common/types/id.type.ts +2 -2
  590. package/src/core/common/types/object-id-like.type.ts +11 -0
  591. package/src/core/common/types/populate-config.type.ts +1 -1
  592. package/src/core/common/types/string-or-object-id.type.ts +2 -2
  593. package/src/core/modules/ai/README.md +15 -2
  594. package/src/core/modules/ai/core-ai.constants.ts +10 -0
  595. package/src/core/modules/ai/core-ai.module.ts +32 -17
  596. package/src/core/modules/ai/models/core-ai-budget-limit.model.ts +2 -2
  597. package/src/core/modules/ai/models/core-ai-connection-preference.model.ts +2 -2
  598. package/src/core/modules/ai/models/core-ai-connection.model.ts +2 -2
  599. package/src/core/modules/ai/models/core-ai-conversation.model.ts +8 -4
  600. package/src/core/modules/ai/models/core-ai-interaction.model.ts +3 -3
  601. package/src/core/modules/ai/models/core-ai-mcp-oauth.models.ts +46 -0
  602. package/src/core/modules/ai/models/core-ai-mode.model.ts +2 -2
  603. package/src/core/modules/ai/models/core-ai-prompt-hint.model.ts +2 -2
  604. package/src/core/modules/ai/models/core-ai-prompt.model.ts +2 -2
  605. package/src/core/modules/ai/models/core-ai-slot.model.ts +2 -2
  606. package/src/core/modules/ai/models/core-ai-tool-grant.model.ts +2 -2
  607. package/src/core/modules/ai/models/core-ai-tool-policy.model.ts +2 -2
  608. package/src/core/modules/ai/services/core-ai-budget.service.ts +22 -4
  609. package/src/core/modules/ai/services/core-ai-connection-preference.service.ts +6 -13
  610. package/src/core/modules/ai/services/core-ai-connection.service.ts +34 -44
  611. package/src/core/modules/ai/services/core-ai-conversation.service.ts +31 -29
  612. package/src/core/modules/ai/services/core-ai-interaction.service.ts +3 -3
  613. package/src/core/modules/ai/services/core-ai-mcp-oauth.service.ts +93 -20
  614. package/src/core/modules/ai/services/core-ai-mode.service.ts +3 -6
  615. package/src/core/modules/ai/services/core-ai-prompt-hint.service.ts +43 -26
  616. package/src/core/modules/ai/services/core-ai-prompt.service.ts +15 -12
  617. package/src/core/modules/ai/services/core-ai-slot.service.ts +20 -24
  618. package/src/core/modules/ai/services/core-ai-tool-grant.service.ts +17 -27
  619. package/src/core/modules/ai/services/core-ai-tool-policy.service.ts +7 -10
  620. package/src/core/modules/api-token/core-api-token.model.ts +1 -1
  621. package/src/core/modules/api-token/core-api-token.module.ts +4 -2
  622. package/src/core/modules/api-token/core-api-token.service.ts +118 -43
  623. package/src/core/modules/auth/core-auth.module.ts +2 -0
  624. package/src/core/modules/auth/guards/legacy-auth-rate-limit.guard.ts +4 -3
  625. package/src/core/modules/auth/guards/role-guard.token.ts +7 -0
  626. package/src/core/modules/auth/services/core-auth.service.ts +16 -2
  627. package/src/core/modules/better-auth/INTEGRATION-CHECKLIST.md +1 -1
  628. package/src/core/modules/better-auth/README.md +10 -1
  629. package/src/core/modules/better-auth/better-auth-sql.helper.ts +191 -0
  630. package/src/core/modules/better-auth/better-auth-token.service.ts +26 -35
  631. package/src/core/modules/better-auth/better-auth.config.ts +43 -7
  632. package/src/core/modules/better-auth/better-auth.types.ts +1 -1
  633. package/src/core/modules/better-auth/core-better-auth-api.middleware.ts +13 -1
  634. package/src/core/modules/better-auth/core-better-auth-challenge.service.ts +36 -18
  635. package/src/core/modules/better-auth/core-better-auth-mongo.users.ts +35 -0
  636. package/src/core/modules/better-auth/core-better-auth-sql.registry.ts +20 -0
  637. package/src/core/modules/better-auth/core-better-auth-sql.store.ts +309 -0
  638. package/src/core/modules/better-auth/core-better-auth-user.mapper.ts +418 -40
  639. package/src/core/modules/better-auth/core-better-auth.module.ts +71 -35
  640. package/src/core/modules/better-auth/core-better-auth.service.ts +81 -12
  641. package/src/core/modules/file/README.md +16 -3
  642. package/src/core/modules/file/core-file-info.model.ts +2 -2
  643. package/src/core/modules/file/core-file.service.ts +27 -18
  644. package/src/core/modules/file/file-metadata.helper.ts +7 -3
  645. package/src/core/modules/file/file-metadata.models.ts +44 -0
  646. package/src/core/modules/file/file-metadata.store.ts +109 -0
  647. package/src/core/modules/file/file-storage.helper.ts +47 -2
  648. package/src/core/modules/file/filesystem-file.helper.ts +11 -8
  649. package/src/core/modules/file/s3-file.helper.ts +11 -7
  650. package/src/core/modules/health-check/core-health-check.module.ts +16 -2
  651. package/src/core/modules/health-check/core-health-check.service.ts +25 -3
  652. package/src/core/modules/hub/README.md +20 -11
  653. package/src/core/modules/hub/core-hub.controller.ts +5 -0
  654. package/src/core/modules/hub/core-hub.service.ts +1 -1
  655. package/src/core/modules/hub/helpers/hub-client-js.helper.ts +23 -0
  656. package/src/core/modules/hub/hub-action-messages.ts +6 -0
  657. package/src/core/modules/hub/hub-nav.ts +1 -0
  658. package/src/core/modules/hub/interfaces/hub-panels.interface.ts +8 -1
  659. package/src/core/modules/hub/services/core-hub-db.service.ts +35 -17
  660. package/src/core/modules/hub/services/core-hub-migrations.service.ts +96 -2
  661. package/src/core/modules/hub/services/core-hub-sources.service.ts +47 -1
  662. package/src/core/modules/hub/services/hub-query-profiler.service.ts +1 -1
  663. package/src/core/modules/migrate/README.md +54 -0
  664. package/src/core/modules/migrate/cli/migrate-cli.ts +44 -3
  665. package/src/core/modules/migrate/index.ts +3 -0
  666. package/src/core/modules/migrate/migration-runner.ts +3 -2
  667. package/src/core/modules/migrate/migration-state-store.interface.ts +24 -0
  668. package/src/core/modules/migrate/migration.helper.ts +35 -10
  669. package/src/core/modules/migrate/mongo-state-store.ts +64 -65
  670. package/src/core/modules/migrate/sql-migration.helper.ts +48 -0
  671. package/src/core/modules/migrate/sql-state-store.ts +198 -0
  672. package/src/core/modules/system-setup/README.md +14 -2
  673. package/src/core/modules/system-setup/core-system-setup.service.ts +77 -45
  674. package/src/core/modules/tenant/README.md +9 -2
  675. package/src/core/modules/tenant/core-tenant-member.model.ts +1 -1
  676. package/src/core/modules/tenant/core-tenant-member.store.ts +13 -0
  677. package/src/core/modules/tenant/core-tenant.guard.ts +77 -24
  678. package/src/core/modules/tenant/core-tenant.module.ts +6 -3
  679. package/src/core/modules/tenant/core-tenant.service.ts +59 -42
  680. package/src/core/modules/tus/core-tus.service.ts +11 -12
  681. package/src/core/modules/tus/tus.module.ts +1 -1
  682. package/src/core/modules/user/core-user.model.ts +2 -2
  683. package/src/core/modules/user/core-user.service.ts +98 -20
  684. package/src/core.module.ts +60 -5
  685. package/src/index.ts +26 -0
  686. package/src/server/common/models/persistence.model.ts +4 -4
  687. package/src/server/modules/file/file-info.model.ts +1 -1
  688. package/src/server/modules/file/file.service.ts +1 -1
  689. package/src/server/modules/user/user.model.ts +10 -5
  690. package/src/server/modules/user/user.module.ts +9 -3
  691. package/src/server/modules/user/user.service.ts +9 -11
  692. package/src/test/test.helper.ts +2 -2
  693. package/src/types/graphql-upload.d.ts +10 -7
@@ -1,4 +1,10 @@
1
- import { RequestContext } from '../services/request-context.service.js';
1
+ import {
2
+ type AuditFieldPresence,
3
+ resolveAuditActorId,
4
+ stampAuditOnReplace,
5
+ stampAuditOnUpdate,
6
+ stampAuditOnWrite,
7
+ } from '../persistence/policies/audit.policy.js';
2
8
 
3
9
  /**
4
10
  * Mongoose plugin that automatically sets createdBy/updatedBy fields.
@@ -16,36 +22,35 @@ import { RequestContext } from '../services/request-context.service.js';
16
22
  * - Bulk operations: sets createdBy/updatedBy per document/operation
17
23
  *
18
24
  * Only activates on schemas that have createdBy and/or updatedBy fields defined.
25
+ *
26
+ * The stamping decisions live in the audit policy (`persistence/policies/audit.policy.ts`), which every
27
+ * data store shares; this plugin only applies them to Mongoose's hook shapes.
19
28
  */
20
29
  export function mongooseAuditFieldsPlugin(schema) {
21
- const hasCreatedBy = !!schema.path('createdBy');
22
- const hasUpdatedBy = !!schema.path('updatedBy');
30
+ const fields: AuditFieldPresence = {
31
+ createdBy: !!schema.path('createdBy'),
32
+ updatedBy: !!schema.path('updatedBy'),
33
+ };
23
34
 
24
35
  // Skip schemas without audit fields (e.g. BetterAuth sessions, third-party schemas)
25
- if (!hasCreatedBy && !hasUpdatedBy) {
36
+ if (!fields.createdBy && !fields.updatedBy) {
26
37
  return;
27
38
  }
28
39
 
29
40
  // Pre-save hook
30
41
  schema.pre('save', function () {
31
- const currentUser = RequestContext.getCurrentUser();
32
- if (!currentUser?.id) {
42
+ const actorId = resolveAuditActorId();
43
+ if (!actorId) {
33
44
  return;
34
45
  }
35
-
36
- if (hasCreatedBy && this.isNew && !this['createdBy']) {
37
- this['createdBy'] = currentUser.id;
38
- }
39
- if (hasUpdatedBy) {
40
- this['updatedBy'] = currentUser.id;
41
- }
46
+ stampAuditOnWrite(this, actorId, fields, this.isNew);
42
47
  });
43
48
 
44
49
  // Pre-update hooks (only needed for updatedBy)
45
- if (hasUpdatedBy) {
50
+ if (fields.updatedBy) {
46
51
  const updateHook = function () {
47
- const currentUser = RequestContext.getCurrentUser();
48
- if (!currentUser?.id) {
52
+ const actorId = resolveAuditActorId();
53
+ if (!actorId) {
49
54
  return;
50
55
  }
51
56
 
@@ -54,22 +59,7 @@ export function mongooseAuditFieldsPlugin(schema) {
54
59
  return;
55
60
  }
56
61
 
57
- // Handle both flat updates and $set operator
58
- update['updatedBy'] = currentUser.id;
59
- if (update.$set) {
60
- update.$set['updatedBy'] = currentUser.id;
61
- }
62
-
63
- // Handle upsert: set createdBy on insert via $setOnInsert
64
- // Only add if createdBy is not already in the update (avoids MongoDB path conflict)
65
- if (hasCreatedBy && !update['createdBy'] && !update.$set?.['createdBy']) {
66
- if (!update.$setOnInsert) {
67
- update.$setOnInsert = {};
68
- }
69
- if (!update.$setOnInsert['createdBy']) {
70
- update.$setOnInsert['createdBy'] = currentUser.id;
71
- }
72
- }
62
+ stampAuditOnUpdate(update, actorId, fields);
73
63
  };
74
64
 
75
65
  schema.pre('findOneAndUpdate', updateHook);
@@ -78,8 +68,8 @@ export function mongooseAuditFieldsPlugin(schema) {
78
68
 
79
69
  // Replace hooks: flat replacement doc (no $set/$setOnInsert)
80
70
  const replaceHook = function () {
81
- const currentUser = RequestContext.getCurrentUser();
82
- if (!currentUser?.id) {
71
+ const actorId = resolveAuditActorId();
72
+ if (!actorId) {
83
73
  return;
84
74
  }
85
75
 
@@ -88,12 +78,7 @@ export function mongooseAuditFieldsPlugin(schema) {
88
78
  return;
89
79
  }
90
80
 
91
- replacement['updatedBy'] = currentUser.id;
92
-
93
- // For upsert replacements: set createdBy if not already present
94
- if (hasCreatedBy && !replacement['createdBy']) {
95
- replacement['createdBy'] = currentUser.id;
96
- }
81
+ stampAuditOnReplace(replacement, actorId, fields);
97
82
  };
98
83
 
99
84
  schema.pre('replaceOne', replaceHook);
@@ -103,55 +88,29 @@ export function mongooseAuditFieldsPlugin(schema) {
103
88
  // Bulk operation hooks (needed when either createdBy or updatedBy is present)
104
89
  // Pre-insertMany hook (Mongoose 9: first arg is docs array)
105
90
  schema.pre('insertMany', function (docs) {
106
- const currentUser = RequestContext.getCurrentUser();
107
- if (!currentUser?.id) return;
91
+ const actorId = resolveAuditActorId();
92
+ if (!actorId) return;
108
93
  if (!Array.isArray(docs)) return;
109
94
 
110
95
  for (const doc of docs) {
111
- if (hasCreatedBy && !doc.createdBy) {
112
- doc.createdBy = currentUser.id;
113
- }
114
- if (hasUpdatedBy) {
115
- doc.updatedBy = currentUser.id;
116
- }
96
+ stampAuditOnWrite(doc, actorId, fields, true);
117
97
  }
118
98
  });
119
99
 
120
100
  // Pre-bulkWrite hook
121
101
  schema.pre('bulkWrite', function (ops) {
122
- const currentUser = RequestContext.getCurrentUser();
123
- if (!currentUser?.id) return;
102
+ const actorId = resolveAuditActorId();
103
+ if (!actorId) return;
124
104
 
125
105
  for (const op of ops) {
126
106
  if ('insertOne' in op) {
127
- if (hasCreatedBy && !op.insertOne.document.createdBy) {
128
- op.insertOne.document.createdBy = currentUser.id;
129
- }
130
- if (hasUpdatedBy) {
131
- op.insertOne.document.updatedBy = currentUser.id;
132
- }
107
+ stampAuditOnWrite(op.insertOne.document, actorId, fields, true);
133
108
  } else if ('updateOne' in op || 'updateMany' in op) {
134
109
  const update = 'updateOne' in op ? op.updateOne.update : op.updateMany.update;
135
110
  if (!update) continue;
136
- if (hasUpdatedBy) {
137
- update['updatedBy'] = currentUser.id;
138
- if (update.$set) {
139
- update.$set['updatedBy'] = currentUser.id;
140
- }
141
- }
142
- if (hasCreatedBy && !update['createdBy'] && !update.$set?.['createdBy']) {
143
- if (!update.$setOnInsert) update.$setOnInsert = {};
144
- if (!update.$setOnInsert['createdBy']) {
145
- update.$setOnInsert['createdBy'] = currentUser.id;
146
- }
147
- }
111
+ stampAuditOnUpdate(update, actorId, fields);
148
112
  } else if ('replaceOne' in op) {
149
- if (hasUpdatedBy) {
150
- op.replaceOne.replacement['updatedBy'] = currentUser.id;
151
- }
152
- if (hasCreatedBy && !op.replaceOne.replacement['createdBy']) {
153
- op.replaceOne.replacement['createdBy'] = currentUser.id;
154
- }
113
+ stampAuditOnReplace(op.replaceOne.replacement, actorId, fields);
155
114
  }
156
115
  }
157
116
  });
@@ -6,6 +6,15 @@ export function mongooseIdPlugin(schema) {
6
6
  schema.post(/.*/, (docs) => {
7
7
  const docsArray = Array.isArray(docs) ? docs : [docs];
8
8
  for (const doc of docsArray) {
9
+ // A bare ObjectId is a VALUE, not a document — `distinct()` on an ObjectId field returns an
10
+ // array of them. Mongoose gives `ObjectId.prototype` an `_id` getter returning itself, so the
11
+ // check below matched, and the assignment then hit bson's own `id` SETTER (the 12-byte
12
+ // buffer). bson < 7.3 silently accepted the hex string; bson >= 7.3 (mongodb >= 7.7) throws
13
+ // `BSONError: Cannot create Buffer from the passed potentialBuffer`, turning every
14
+ // `distinct()` over an ObjectId field into a 500.
15
+ if (doc?._bsontype === 'ObjectId') {
16
+ continue;
17
+ }
9
18
  if (doc?._id && typeof doc._id.toHexString === 'function') {
10
19
  doc.id = doc._id.toHexString();
11
20
  }
@@ -1,7 +1,7 @@
1
- import bcrypt from 'bcrypt';
2
- import { sha256 } from 'js-sha256';
1
+ import { hashPassword, hashUpdatePassword } from '../persistence/policies/password.policy.js';
3
2
 
4
- import { ConfigService } from '../services/config.service.js';
3
+ // Public API since before the policy extraction — keep every import path working
4
+ export { hashPassword, hashUpdatePassword, resetSkipPatternsCache } from '../persistence/policies/password.policy.js';
5
5
 
6
6
  /**
7
7
  * Mongoose plugin that automatically hashes passwords before saving to the database.
@@ -15,6 +15,9 @@ import { ConfigService } from '../services/config.service.js';
15
15
  *
16
16
  * For sentinel/lock values (e.g. '!LOCKED:REQUIRES_PASSWORD_RESET'), configure
17
17
  * skipPatterns in security.mongoosePasswordPlugin to preserve them as-is.
18
+ *
19
+ * The hashing decisions live in the password policy (`persistence/policies/password.policy.ts`), which
20
+ * every data store shares; this plugin only applies them to Mongoose's hook shapes.
18
21
  */
19
22
  export function mongoosePasswordPlugin(schema) {
20
23
  // Pre-save hook
@@ -79,62 +82,3 @@ export function mongoosePasswordPlugin(schema) {
79
82
  }
80
83
  });
81
84
  }
82
-
83
- export async function hashUpdatePassword(update: any) {
84
- if (!update) {
85
- return;
86
- }
87
- if (update.password) {
88
- update.password = await hashPassword(update.password);
89
- }
90
- if (update.$set?.password) {
91
- update.$set.password = await hashPassword(update.$set.password);
92
- }
93
- }
94
-
95
- // Compiled RegExp cache (built once on first call)
96
- let compiledSkipPatterns: RegExp[] | null = null;
97
-
98
- /**
99
- * Reset the compiled skip patterns cache.
100
- * Use in test environments where ConfigService is reset between test suites.
101
- */
102
- export function resetSkipPatternsCache(): void {
103
- compiledSkipPatterns = null;
104
- }
105
-
106
- function getSkipPatterns(): RegExp[] {
107
- if (compiledSkipPatterns !== null) {
108
- return compiledSkipPatterns;
109
- }
110
- const pluginConfig = ConfigService.configFastButReadOnly?.security?.mongoosePasswordPlugin;
111
- if (pluginConfig && typeof pluginConfig === 'object' && 'skipPatterns' in pluginConfig && pluginConfig.skipPatterns) {
112
- compiledSkipPatterns = (pluginConfig.skipPatterns as (string | RegExp)[]).map((p) =>
113
- p instanceof RegExp ? p : new RegExp(p),
114
- );
115
- } else {
116
- compiledSkipPatterns = [];
117
- }
118
- return compiledSkipPatterns;
119
- }
120
-
121
- export async function hashPassword(password: string): Promise<string> {
122
- // Already BCrypt-hashed → skip
123
- if (/^\$2[aby]\$\d+\$/.test(password)) {
124
- return password;
125
- }
126
-
127
- // Check configured skip patterns (e.g. sentinel/lock values)
128
- const skipPatterns = getSkipPatterns();
129
- if (skipPatterns.some((pattern) => pattern.test(password))) {
130
- return password;
131
- }
132
-
133
- // SHA256 pre-hash if configured and password is not already SHA256
134
- const sha256Enabled = ConfigService.configFastButReadOnly?.sha256;
135
- if (sha256Enabled && !/^[a-f0-9]{64}$/i.test(password)) {
136
- password = sha256(password);
137
- }
138
-
139
- return bcrypt.hash(password, 10);
140
- }
@@ -1,7 +1,10 @@
1
1
  import { Logger } from '@nestjs/common';
2
2
 
3
- import { RoleEnum } from '../enums/role.enum.js';
4
- import { ConfigService } from '../services/config.service.js';
3
+ import {
4
+ isRoleChangeAllowed,
5
+ stripRoleChanges,
6
+ updateChangesRoles,
7
+ } from '../persistence/policies/role-guard.policy.js';
5
8
  import { RequestContext } from '../services/request-context.service.js';
6
9
 
7
10
  const logger = new Logger('mongooseRoleGuardPlugin');
@@ -117,8 +120,8 @@ export function mongooseRoleGuardPlugin(schema) {
117
120
  schema.pre('bulkWrite', function (ops) {
118
121
  const hasAnyRoleChange = ops.some((op) => {
119
122
  if ('insertOne' in op) return !!op.insertOne.document?.roles?.length;
120
- if ('updateOne' in op) return hasRolesInUpdate(op.updateOne.update);
121
- if ('updateMany' in op) return hasRolesInUpdate(op.updateMany.update);
123
+ if ('updateOne' in op) return updateChangesRoles(op.updateOne.update);
124
+ if ('updateMany' in op) return updateChangesRoles(op.updateMany.update);
122
125
  if ('replaceOne' in op) return !!op.replaceOne.replacement?.roles?.length;
123
126
  return false;
124
127
  });
@@ -146,58 +149,15 @@ export function mongooseRoleGuardPlugin(schema) {
146
149
  }
147
150
 
148
151
  /**
149
- * Check if the current user is allowed to modify roles.
150
- * Returns true if:
151
- * - No user context (system operation)
152
- * - bypassRoleGuard is active (via RequestContext.runWithBypassRoleGuard())
153
- * - User has ADMIN role
154
- * - User has one of the configured allowedRoles
152
+ * Strip an unauthorized role change from an update object. The decisions live in the role-guard policy
153
+ * (`persistence/policies/role-guard.policy.ts`), which every data store shares.
155
154
  */
156
- function isRoleChangeAllowed(): boolean {
157
- const currentUser = RequestContext.getCurrentUser();
158
- // No user context (system operation) → allow
159
- if (!currentUser) {
160
- return true;
161
- }
162
- // Explicit bypass (e.g. signUp with default roles, authorized service operations)
163
- if (RequestContext.isBypassRoleGuard()) {
164
- return true;
165
- }
166
- // Admin → always allow
167
- if (currentUser.hasRole?.([RoleEnum.ADMIN]) || currentUser.roles?.includes(RoleEnum.ADMIN)) {
168
- return true;
169
- }
170
-
171
- // Check configured allowedRoles
172
- const pluginConfig = ConfigService.configFastButReadOnly?.security?.mongooseRoleGuardPlugin;
173
- if (pluginConfig && typeof pluginConfig === 'object' && 'allowedRoles' in pluginConfig && pluginConfig.allowedRoles) {
174
- const allowedRoles: string[] = pluginConfig.allowedRoles as string[];
175
- if (currentUser.roles?.some((role: string) => allowedRoles.includes(role))) {
176
- return true;
177
- }
178
- }
179
-
180
- return false;
181
- }
182
-
183
- function hasRolesInUpdate(update: any): boolean {
184
- return !!(
185
- update?.roles ||
186
- update?.$set?.roles ||
187
- update?.$push?.roles ||
188
- update?.$addToSet?.roles ||
189
- update?.$pull?.roles
190
- );
191
- }
192
-
193
155
  function handleUpdateRoleGuard(update: any) {
194
156
  if (!update) {
195
157
  return;
196
158
  }
197
159
 
198
- const hasRolesUpdate =
199
- update.roles || update.$set?.roles || update?.$push?.roles || update.$addToSet?.roles || update.$pull?.roles;
200
- if (!hasRolesUpdate) {
160
+ if (!updateChangesRoles(update)) {
201
161
  return;
202
162
  }
203
163
 
@@ -208,17 +168,5 @@ function handleUpdateRoleGuard(update: any) {
208
168
  // Unauthorized: remove roles changes with debug log
209
169
  const currentUser = RequestContext.getCurrentUser();
210
170
  logger.debug(`Stripped unauthorized roles change from user ${currentUser?.id || 'unknown'}`);
211
- delete update.roles;
212
- if (update.$set?.roles) {
213
- delete update.$set.roles;
214
- }
215
- if (update.$push?.roles) {
216
- delete update.$push.roles;
217
- }
218
- if (update.$addToSet?.roles) {
219
- delete update.$addToSet.roles;
220
- }
221
- if (update.$pull?.roles) {
222
- delete update.$pull.roles;
223
- }
171
+ stripRoleChanges(update);
224
172
  }
@@ -1,6 +1,9 @@
1
- import { BadRequestException } from '@nestjs/common';
2
-
3
- import { looksLikeSystemRole, SYSTEM_ROLE_PREFIX } from '../enums/role.enum.js';
1
+ import {
2
+ assertNoSystemRoles,
3
+ assertUpdateHasNoSystemRoles,
4
+ toRoleArray,
5
+ updateMayIntroduceSystemRoles,
6
+ } from '../persistence/policies/system-role.policy.js';
4
7
 
5
8
  /**
6
9
  * Mongoose plugin that refuses to STORE system roles (`s_*`) in a `roles` array.
@@ -93,38 +96,6 @@ export function mongooseSystemRolePlugin(schema) {
93
96
  });
94
97
  }
95
98
 
96
- /** Normalize an unknown value to a string array; anything else becomes an empty baseline. */
97
- function toRoleArray(value: unknown): string[] {
98
- return Array.isArray(value) ? value.filter((entry): entry is string => typeof entry === 'string') : [];
99
- }
100
-
101
- /**
102
- * Throw if the write INTRODUCES a system role.
103
- *
104
- * `alreadyStored` is the document's current value. A system role that is already in there is NOT
105
- * refused, and this is load-bearing rather than lenient: `CrudService.update()` writes the whole
106
- * object back, so a login (`updateRefreshToken` → `update`) re-sends the stored `roles` verbatim.
107
- * Refusing that would lock every already-contaminated account out of the system on upgrade — and
108
- * would do the same, fleet-wide, to any project whose own role name happens to start with `s_`.
109
- * An upgrade must not cause a login outage; cleaning up stored values is what the migration guide's
110
- * audit query is for.
111
- *
112
- * Accepts anything: a non-array (nothing to check) and non-string elements (cannot be a system
113
- * role) pass through untouched — type validation is not this plugin's job.
114
- */
115
- function assertNoSystemRoles(roles: unknown, alreadyStored: string[] = []): void {
116
- if (!Array.isArray(roles)) {
117
- return;
118
- }
119
-
120
- const introduced = roles.filter((role) => looksLikeSystemRole(role) && !alreadyStored.includes(role));
121
- if (introduced.length) {
122
- throw new BadRequestException(
123
- `System roles (${SYSTEM_ROLE_PREFIX}*) must never be stored in user.roles: ${introduced.join(', ')}`,
124
- );
125
- }
126
- }
127
-
128
99
  /**
129
100
  * Query-level variant: resolves the affected document's stored roles before deciding.
130
101
  *
@@ -133,7 +104,7 @@ function assertNoSystemRoles(roles: unknown, alreadyStored: string[] = []): void
133
104
  */
134
105
  async function assertUpdateIntroducesNoSystemRoles(query: any): Promise<void> {
135
106
  const update = query.getUpdate();
136
- if (!update || !collectRoleValues(update).some((role) => looksLikeSystemRole(role))) {
107
+ if (!update || !updateMayIntroduceSystemRoles(update)) {
137
108
  return;
138
109
  }
139
110
 
@@ -149,45 +120,3 @@ async function assertUpdateIntroducesNoSystemRoles(query: any): Promise<void> {
149
120
 
150
121
  assertUpdateHasNoSystemRoles(update, stored);
151
122
  }
152
-
153
- /** Every roles value the update could write, flattened across the operators that can carry one. */
154
- function collectRoleValues(update: any): unknown[] {
155
- const values: unknown[] = [
156
- ...toRoleArray(update.roles),
157
- ...toRoleArray(update.$set?.roles),
158
- ...toRoleArray(update.$setOnInsert?.roles),
159
- ];
160
-
161
- for (const operator of [update.$push?.roles, update.$addToSet?.roles]) {
162
- if (operator === undefined || operator === null) {
163
- continue;
164
- }
165
- values.push(...(Array.isArray(operator?.$each) ? operator.$each : [operator]));
166
- }
167
-
168
- return values;
169
- }
170
-
171
- /**
172
- * Check every operator through which a roles value can reach the document.
173
- *
174
- * `$pull` is deliberately NOT checked — removing an `s_*` value from a document that already has
175
- * one is exactly the cleanup this release asks projects to perform, so it must stay possible.
176
- */
177
- function assertUpdateHasNoSystemRoles(update: any, alreadyStored: string[] = []): void {
178
- if (!update) {
179
- return;
180
- }
181
-
182
- assertNoSystemRoles(update.roles, alreadyStored);
183
- assertNoSystemRoles(update.$set?.roles, alreadyStored);
184
- assertNoSystemRoles(update.$setOnInsert?.roles, alreadyStored);
185
-
186
- // $push / $addToSet take either a bare value or a { $each: [...] } modifier
187
- for (const operator of [update.$push?.roles, update.$addToSet?.roles]) {
188
- if (operator === undefined || operator === null) {
189
- continue;
190
- }
191
- assertNoSystemRoles(Array.isArray(operator?.$each) ? operator.$each : [operator], alreadyStored);
192
- }
193
- }
@@ -1,47 +1,12 @@
1
- import { ForbiddenException, Logger } from '@nestjs/common';
1
+ import { ForbiddenException } from '@nestjs/common';
2
2
 
3
- import { ConfigService } from '../services/config.service.js';
4
- import { RequestContext } from '../services/request-context.service.js';
5
-
6
- /** Models already warned about, so the message appears once per process rather than per query. */
7
- const isolationDisabledWarned = new Set<string>();
8
-
9
- /**
10
- * Say out loud that isolation is off for a schema that was built for it.
11
- *
12
- * `excludeSchemas` is a legitimate feature — a genuinely global lookup table has no tenant. But this
13
- * plugin only ever attaches to schemas that DECLARE a `tenantId` field, so reaching here means the
14
- * author of that schema intended per-tenant rows and the configuration silently overrides them.
15
- * Nothing said so, and the shape is easy to arrive at by accident: the framework's own documentation
16
- * carried `excludeSchemas: ['User', 'Session']` in a copyable `@example` from 11.20.0 onwards, which
17
- * on a project with per-tenant users switches filtering off for the user collection.
18
- *
19
- * A warning, not a boot failure. An ambiguous ROLE vocabulary fails the boot because its access
20
- * decisions cannot be resolved coherently; this configuration is perfectly coherent — it is simply
21
- * one nobody may make without noticing.
22
- *
23
- * The membership model is skipped: `CoreModule` adds it to `excludeSchemas` itself because
24
- * membership is tenant-spanning by design, so warning about it would be the framework complaining
25
- * about its own correct default.
26
- */
27
- function warnIsolationDisabled(modelName: string): void {
28
- if (isolationDisabledWarned.has(modelName)) {
29
- return;
30
- }
31
- isolationDisabledWarned.add(modelName);
32
-
33
- const membershipModel = ConfigService.configFastButReadOnly?.multiTenancy?.membershipModel ?? 'TenantMember';
34
- if (modelName === membershipModel) {
35
- return;
36
- }
37
-
38
- new Logger('mongooseTenantPlugin').warn(
39
- `Tenant isolation is DISABLED for "${modelName}": the schema declares a tenantId field, but ` +
40
- `"${modelName}" is listed in multiTenancy.excludeSchemas — so no tenant filter is applied and ` +
41
- `every query on it sees every tenant's rows. Remove it from excludeSchemas unless this ` +
42
- `collection is genuinely global.`,
43
- );
44
- }
3
+ import {
4
+ assertOwnTenant,
5
+ assertRowsInTenantScope,
6
+ assertUnfilterableCountAllowed,
7
+ resolveSingleTenantId,
8
+ resolveTenantFilter,
9
+ } from '../persistence/policies/tenant.policy.js';
45
10
 
46
11
  /**
47
12
  * Mongoose plugin that provides automatic tenant-based data isolation.
@@ -71,7 +36,7 @@ export function mongooseTenantPlugin(schema) {
71
36
  // Only activate on schemas with a tenantId path.
72
37
  // CoreTenantMemberModel uses 'tenant' (not 'tenantId') intentionally, so this check
73
38
  // excludes it at registration time. Additionally, 'TenantMember' is auto-added to
74
- // excludeSchemas in CoreModule as defense-in-depth (see shouldBypass()).
39
+ // excludeSchemas in CoreModule as defense-in-depth (see isTenantPolicyBypassed() in the tenant policy).
75
40
  if (!schema.path('tenantId')) {
76
41
  return;
77
42
  }
@@ -119,6 +84,19 @@ export function mongooseTenantPlugin(schema) {
119
84
  });
120
85
  }
121
86
 
87
+ // === After-layer: every returned row must belong to the caller's scope ===
88
+ // The pre hooks above decide WHICH rows a query may touch; this checks what actually came back, with
89
+ // the same policy a SQL store applies after its reads. Populated references are checked by their own
90
+ // model's hooks. Aggregations are not checked here: their output is reshaped and need not carry tenantId.
91
+ const rowCheckHooks = ['find', 'findOne', 'findOneAndUpdate', 'findOneAndDelete', 'findOneAndReplace'];
92
+ for (const hookName of rowCheckHooks) {
93
+ schema.post(hookName, function (result: any) {
94
+ // includeResultMetadata: findOneAnd* resolves to { value, lastErrorObject, ok }
95
+ const rows = result && typeof result === 'object' && 'lastErrorObject' in result ? result.value : result;
96
+ assertRowsInTenantScope(this.model?.modelName, rows);
97
+ });
98
+ }
99
+
122
100
  // === Save: set tenantId automatically on new documents ===
123
101
  // Intentional asymmetry: writes only set tenantId when truthy (not null).
124
102
  // Only uses single tenantId from header — tenantIds array is for reads only.
@@ -194,18 +172,7 @@ export function mongooseTenantPlugin(schema) {
194
172
  // collection, i.e. every other tenant's volume. Throwing is the only honest option; callers that
195
173
  // want a tenant's own count use countDocuments(), which IS filtered.
196
174
  schema.pre('estimatedDocumentCount', function () {
197
- const modelName = (this as any).model?.modelName;
198
- if (shouldBypass(modelName)) {
199
- return;
200
- }
201
- const context = RequestContext.get();
202
- if (context?.isAdminBypass && !context?.tenantId) {
203
- return; // platform admin without a tenant header legitimately sees everything
204
- }
205
- throw new ForbiddenException(
206
- "estimatedDocumentCount() cannot be tenant-filtered and would expose other tenants' row counts — " +
207
- 'use countDocuments() instead',
208
- );
175
+ assertUnfilterableCountAllowed((this as any).model?.modelName);
209
176
  });
210
177
 
211
178
  // === Aggregate: prepend $match stage, and secure every cross-collection stage ===
@@ -340,91 +307,3 @@ function filterForCollection(collectionName: unknown, sourceModel: any): Record<
340
307
 
341
308
  return undefined;
342
309
  }
343
-
344
- /**
345
- * Refuse a tenantId that is not the caller's own.
346
- *
347
- * Covers both directions of the same boundary:
348
- * - creating a row with a FOREIGN tenantId → planting data in someone else's tenant;
349
- * - changing an existing row's tenantId → moving data out of this one.
350
- *
351
- * Throwing rather than overwriting is deliberate. Silently rewriting the value would make a request
352
- * that asked for something impossible look like it succeeded, and for tenant-scoped medical data
353
- * "the write went somewhere other than you asked" is not a recoverable ambiguity.
354
- */
355
- function assertOwnTenant(value: unknown, ownTenantId: string, isNew: boolean): void {
356
- if (value === undefined || value === null || value === ownTenantId) {
357
- return;
358
- }
359
- throw new ForbiddenException(
360
- isNew
361
- ? `Cannot create a document in a foreign tenant (got "${String(value)}", own tenant is "${ownTenantId}")`
362
- : `Cannot move a document to a foreign tenant (got "${String(value)}", own tenant is "${ownTenantId}")`,
363
- );
364
- }
365
-
366
- /**
367
- * Check common bypass conditions.
368
- *
369
- * @returns `true` if filtering should be skipped, `false` otherwise
370
- */
371
- function shouldBypass(modelName?: string): boolean {
372
- const mtConfig = ConfigService.configFastButReadOnly?.multiTenancy;
373
- if (!mtConfig || mtConfig.enabled === false) return true;
374
-
375
- const context = RequestContext.get();
376
- if (!context) return true;
377
- if (context.bypassTenantGuard) return true;
378
- if (modelName && mtConfig.excludeSchemas?.includes(modelName)) {
379
- warnIsolationDisabled(modelName);
380
- return true;
381
- }
382
-
383
- return false;
384
- }
385
-
386
- /**
387
- * Resolve tenant filter from RequestContext for read operations (queries, aggregates).
388
- *
389
- * Defense-in-depth: If a schema has tenantId but there is no valid tenant context,
390
- * throws ForbiddenException instead of returning unfiltered data (Safety Net).
391
- *
392
- * @returns
393
- * - `undefined` → no filter should be applied (bypass active or plugin disabled)
394
- * - `{}` → empty filter (admin bypass without header — sees all data)
395
- * - `{ tenantId: string }` → filter by single validated tenant
396
- * - `{ tenantId: { $in: string[] } }` → filter by user's tenant memberships
397
- * @throws ForbiddenException when tenantId-schema is accessed without valid tenant context
398
- */
399
- function resolveTenantFilter(modelName?: string): Record<string, any> | undefined {
400
- if (shouldBypass(modelName)) return undefined;
401
-
402
- const context = RequestContext.get();
403
-
404
- // Validated tenant ID (set by CoreTenantGuard) → filter by it
405
- const tenantId = context?.tenantId;
406
- if (tenantId) return { tenantId };
407
-
408
- // User has resolved memberships → filter by their tenants
409
- const tenantIds = context?.tenantIds;
410
- if (tenantIds) return { tenantId: { $in: tenantIds } };
411
-
412
- // Admin bypass without header → no filter, sees all data
413
- if (context?.isAdminBypass) return {};
414
-
415
- // SAFETY NET: Schema has tenantId but no valid tenant context.
416
- // Throw instead of returning unfiltered data to prevent data leaks.
417
- throw new ForbiddenException(
418
- 'Tenant context required: this data is tenant-scoped but no valid tenant context was provided',
419
- );
420
- }
421
-
422
- /**
423
- * Resolve single tenant ID for write operations (save, insertMany).
424
- * Only returns a value when a specific tenant header is set.
425
- */
426
- function resolveSingleTenantId(modelName?: string): string | undefined {
427
- if (shouldBypass(modelName)) return undefined;
428
-
429
- return RequestContext.get()?.tenantId || undefined;
430
- }