@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
@@ -0,0 +1,762 @@
1
+ import { BadRequestException, ForbiddenException, Logger } from '@nestjs/common';
2
+
3
+ import { ConfigService } from '../../services/config.service.js';
4
+ import type {
5
+ DataStore,
6
+ DataStoreUpdate,
7
+ FindOptions,
8
+ PushOptions,
9
+ StoreCapabilities,
10
+ StoredRecord,
11
+ UpdateOptions,
12
+ Where,
13
+ } from '../data-store.interface.js';
14
+ import { generateId, isWellFormedId } from '../id-format.js';
15
+ import type { FieldDescriptor, ModelDescriptor } from '../model-descriptor.interface.js';
16
+ import { resolveAuditActorId, stampAuditOnUpdate, stampAuditOnWrite } from '../policies/audit.policy.js';
17
+ import { hashPassword, hashUpdatePassword } from '../policies/password.policy.js';
18
+ import { isRoleChangeAllowed, stripRoleChanges, updateChangesRoles } from '../policies/role-guard.policy.js';
19
+ import { assertNoSystemRoles, assertUpdateHasNoSystemRoles } from '../policies/system-role.policy.js';
20
+ import {
21
+ assertOwnTenant,
22
+ assertRowsInTenantScope,
23
+ resolveSingleTenantId,
24
+ resolveTenantFilter,
25
+ resolveTenantScope,
26
+ } from '../policies/tenant.policy.js';
27
+ import {
28
+ assertAccessReason,
29
+ assertArrayField,
30
+ assertInteger,
31
+ assertNonNegativeInteger,
32
+ assertProjection,
33
+ assertSampleSize,
34
+ assertSelectFields,
35
+ } from '../store-guards.js';
36
+ import { computedDefault } from '../mongo/declared-schema.view.js';
37
+ import type { DeclaredSchema } from '../mongo/model-declarations.js';
38
+ import { tableName } from './sql-schema.shared.js';
39
+ import { compilePostgresUpdate, pullExpression } from './postgres/postgres-update.compiler.js';
40
+ import { compilePostgresPath, compilePostgresWhere } from './postgres/postgres-where.compiler.js';
41
+ import { castFilterValue } from './sql-cast.js';
42
+ import { DuplicateKeyError, UnsupportedQueryError } from './sql-errors.js';
43
+ import type { SqlExecutor, SqlStatement } from './sql-executor.interface.js';
44
+ import { columnName, quoteIdentifier, SqlParams } from './sql-naming.js';
45
+
46
+ /** What PostgreSQL offers through the SQL store (plan §3.6) */
47
+ export const POSTGRES_STORE_CAPABILITIES: Readonly<StoreCapabilities> = Object.freeze({
48
+ aggregatePipeline: false,
49
+ changeFeed: false,
50
+ gridfs: false,
51
+ idType: 'objectId',
52
+ mongoosePopulate: false,
53
+ rawFilterArm: false,
54
+ rls: true,
55
+ textSearch: false,
56
+ transactions: true,
57
+ });
58
+
59
+ export interface SqlDataStoreOptions {
60
+ /**
61
+ * The model's Mongoose schema, when there is one: computes the defaults a descriptor cannot carry (functions such
62
+ * as `Date.now`). Literal defaults come from the column defaults.
63
+ */
64
+ schema?: DeclaredSchema | { path(name: string): any };
65
+ }
66
+
67
+ const SCALAR_KINDS = new Set([
68
+ 'bigint',
69
+ 'boolean',
70
+ 'buffer',
71
+ 'date',
72
+ 'decimal',
73
+ 'enum',
74
+ 'id',
75
+ 'number',
76
+ 'ref',
77
+ 'string',
78
+ 'uuid',
79
+ ]);
80
+
81
+ /**
82
+ * How often `updateOne` tries again when its chosen row stopped matching before the write while another one still
83
+ * matches. The locking sub-select already skips such rows, so a retry is the rare case; the bound keeps a filter that
84
+ * concurrent writers keep invalidating from looping forever.
85
+ */
86
+ const UPDATE_ONE_ATTEMPTS = 3;
87
+
88
+ /**
89
+ * The SQL data store: the neutral port over one table (plan §3.1).
90
+ *
91
+ * The same three layers as on MongoDB (§3.4) — the policies the Mongoose plugins call run here BEFORE every statement
92
+ * (tenant filter and stamping, foreign-tenant refusal and safety net, audit fields, password hashing, role guard,
93
+ * system roles), row-level security binds the statement in the database, and the tenant row check runs AFTER every
94
+ * read. Statements are compiled by the framework and executed per operation in their own transaction with the
95
+ * request's tenant scope (§3.8), so the engine behind the executor cannot change what an operation does.
96
+ *
97
+ * Records look like the MongoDB store's: the id as a string under `id`, no `_id`, and a field that is NULL in the
98
+ * table is absent from the record — SQL cannot tell a stored null from an absent value.
99
+ */
100
+ export class SqlDataStore<T = Record<string, any>> implements DataStore<T> {
101
+ readonly capabilities: Readonly<StoreCapabilities> = POSTGRES_STORE_CAPABILITIES;
102
+ readonly driver: string = 'postgres';
103
+ protected readonly logger = new Logger(SqlDataStore.name);
104
+ protected readonly table: string;
105
+ /** column → field */
106
+ protected readonly fields = new Map<string, FieldDescriptor>();
107
+ protected collationWarned = false;
108
+
109
+ constructor(
110
+ protected readonly executor: SqlExecutor,
111
+ protected readonly model: ModelDescriptor,
112
+ protected readonly options: SqlDataStoreOptions = {},
113
+ ) {
114
+ this.table = this.quote(tableName(model));
115
+ for (const field of Object.values(model.fields)) {
116
+ this.fields.set(columnName(field.name), field);
117
+ }
118
+ }
119
+
120
+ get modelName(): string {
121
+ return this.model.name;
122
+ }
123
+
124
+ // -- reads ----------------------------------------------------------------------------------------------
125
+
126
+ async count(where?: Where<T>): Promise<number> {
127
+ const params = new SqlParams();
128
+ const { rows } = await this.run<{ n: number }>({
129
+ params: params.values,
130
+ text: `SELECT count(*)::int AS n FROM ${this.table} WHERE ${this.where(where, params)}`,
131
+ });
132
+ return rows[0].n;
133
+ }
134
+
135
+ async sum(field: string, where?: Where<T>): Promise<{ count: number; sum: number }> {
136
+ assertSelectFields([field]);
137
+ const params = new SqlParams();
138
+ const path = compilePostgresPath(this.model, field, params);
139
+ if (path.kind === 'array') {
140
+ throw new UnsupportedQueryError(`sum() needs a single value, "${field}" is an array`);
141
+ }
142
+ // A JSON value counts only when it is a number, as MongoDB's $sum ignores other types
143
+ const value =
144
+ path.kind === 'column'
145
+ ? path.expr
146
+ : `CASE WHEN jsonb_typeof(${path.expr}) = 'number' THEN (${path.expr})::numeric END`;
147
+ const { rows } = await this.run<{ n: number; total: number | string }>({
148
+ params: params.values,
149
+ text: `SELECT count(*)::int AS n, COALESCE(sum(${value}), 0)::float8 AS total FROM ${this.table} WHERE ${this.where(where, params)}`,
150
+ });
151
+ return { count: rows[0].n, sum: Number(rows[0].total) };
152
+ }
153
+
154
+ async distinct(field: string, where?: Where<T>): Promise<unknown[]> {
155
+ const params = new SqlParams();
156
+ const path = compilePostgresPath(this.model, field, params);
157
+ const condition = this.where(where, params);
158
+ const text =
159
+ path.kind === 'array'
160
+ ? `SELECT DISTINCT d.v AS v FROM ${this.table}, jsonb_array_elements(CASE WHEN jsonb_typeof(${path.expr}) = 'array' THEN ${path.expr} ELSE '[]'::jsonb END) AS d(v) WHERE ${condition}`
161
+ : `SELECT DISTINCT ${path.expr} AS v FROM ${this.table} WHERE ${condition} AND ${path.expr} IS NOT NULL`;
162
+ const { rows } = await this.run<{ v: unknown }>({ params: params.values, text });
163
+ return rows.map((row) => (path.kind === 'column' && path.field ? this.fromColumn(path.field, row.v) : row.v));
164
+ }
165
+
166
+ async find(where?: Where<T>, options: FindOptions = {}): Promise<StoredRecord<T>[]> {
167
+ const sample = options.sample === undefined ? undefined : assertSampleSize(options.sample);
168
+ const params = new SqlParams();
169
+ let text = `SELECT ${this.selectList(options.select, options.exclude)} FROM ${this.table} WHERE ${this.where(where, params)}`;
170
+ text += this.orderBy(options, params);
171
+ if (options.limit !== undefined) {
172
+ text += ` LIMIT ${params.add(assertNonNegativeInteger(options.limit, 'limit'))}`;
173
+ }
174
+ if (options.skip !== undefined) {
175
+ text += ` OFFSET ${params.add(assertNonNegativeInteger(options.skip, 'skip'))}`;
176
+ }
177
+ if (sample !== undefined) {
178
+ // The page first, then the draw — MongoDB's `$sample` after `$sort`, `$skip` and `$limit`
179
+ text = `SELECT * FROM (${text}) AS "page" ORDER BY random() LIMIT ${params.add(sample)}`;
180
+ }
181
+ const { rows } = await this.run({ params: params.values, text });
182
+ return this.toRecords(rows, options.select);
183
+ }
184
+
185
+ async findById(id: string, options: Pick<FindOptions, 'exclude' | 'select'> = {}): Promise<null | StoredRecord<T>> {
186
+ return this.findOne({ id } as Where<T>, options);
187
+ }
188
+
189
+ async findOne(where: Where<T>, options: Omit<FindOptions, 'limit' | 'skip'> = {}): Promise<null | StoredRecord<T>> {
190
+ const [record] = await this.find(where, { ...options, limit: 1 });
191
+ return record ?? null;
192
+ }
193
+
194
+ // -- writes ---------------------------------------------------------------------------------------------
195
+
196
+ /** The columns and bound values of a prepared document, the id first (a well-formed supplied one, else a new one) */
197
+ protected insertValues(
198
+ doc: Record<string, any>,
199
+ params: SqlParams,
200
+ ): { columns: string[]; id: string; values: string[] } {
201
+ const supplied = doc.id ?? doc._id;
202
+ const id =
203
+ supplied !== undefined && isWellFormedId(String(supplied)) ? String(supplied).toLowerCase() : generateId();
204
+ const columns = [this.quote('id')];
205
+ const values = [params.add(id)];
206
+ for (const [key, value] of Object.entries(doc)) {
207
+ const field = key === 'id' || key === '_id' ? undefined : this.model.fields[key];
208
+ if (!field || value === undefined) {
209
+ // Like Mongoose in strict mode: a key the model does not declare is not stored
210
+ continue;
211
+ }
212
+ columns.push(this.quote(columnName(key)));
213
+ values.push(this.toColumn(field, key, value, params));
214
+ }
215
+ return { columns, id, values };
216
+ }
217
+
218
+ async insert(record: Partial<T>): Promise<StoredRecord<T>> {
219
+ const doc = await this.beforeInsert({ ...(record as Record<string, any>) });
220
+ const params = new SqlParams();
221
+ const { columns, values } = this.insertValues(doc, params);
222
+ const { rows } = await this.run({
223
+ params: params.values,
224
+ text: `INSERT INTO ${this.table} (${columns.join(', ')}) VALUES (${values.join(', ')}) RETURNING ${this.selectList()}`,
225
+ });
226
+ return this.toRecords(rows)[0];
227
+ }
228
+
229
+ async updateById(
230
+ id: string,
231
+ update: DataStoreUpdate<T>,
232
+ options: UpdateOptions = {},
233
+ ): Promise<null | StoredRecord<T>> {
234
+ const patch = await this.preparePatch(update, options);
235
+ const params = new SqlParams();
236
+ const assignments = this.compileUpdate(patch, params);
237
+ if (!assignments.length) {
238
+ return this.findById(id);
239
+ }
240
+ const { rows } = await this.run({
241
+ params: params.values,
242
+ text: `UPDATE ${this.table} SET ${assignments.join(', ')} WHERE ${this.where({ id } as Where<T>, params)} RETURNING ${this.selectList()}`,
243
+ });
244
+ return this.toRecords(rows)[0] ?? null;
245
+ }
246
+
247
+ async updateOne(where: Where<T>, update: DataStoreUpdate<T>): Promise<null | StoredRecord<T>> {
248
+ const patch = await this.preparePatch(update);
249
+ for (let attempt = 1; ; attempt++) {
250
+ const params = new SqlParams();
251
+ const assignments = this.compileUpdate(patch, params);
252
+ if (!assignments.length) {
253
+ return this.findOne(where);
254
+ }
255
+ // The sub-select locks the row it picks, and a row a concurrent caller changed meanwhile is re-checked and
256
+ // skipped for the next match; the condition is repeated outside so the row provably still matches when it is
257
+ // written — the guarantee of MongoDB's findOneAndUpdate
258
+ const match = this.where(where, params);
259
+ const recheck = this.where(where, params);
260
+ const { rows } = await this.run({
261
+ params: params.values,
262
+ text: `UPDATE ${this.table} SET ${assignments.join(', ')} WHERE "id" = (SELECT "id" FROM ${this.table} WHERE ${match} LIMIT 1 FOR UPDATE) AND ${recheck} RETURNING ${this.selectList()}`,
263
+ });
264
+ if (rows.length) {
265
+ return this.toRecords(rows)[0];
266
+ }
267
+ // Nothing written: nothing matches any more — or the chosen row stopped matching between the pick and the write
268
+ // while another one still does. Only the second is retried
269
+ if (attempt >= UPDATE_ONE_ATTEMPTS || !(await this.exists(where))) {
270
+ return null;
271
+ }
272
+ }
273
+ }
274
+
275
+ async deleteById(id: string): Promise<null | StoredRecord<T>> {
276
+ const params = new SqlParams();
277
+ const { rows } = await this.run({
278
+ params: params.values,
279
+ text: `DELETE FROM ${this.table} WHERE ${this.where({ id } as Where<T>, params)} RETURNING ${this.selectList()}`,
280
+ });
281
+ return this.toRecords(rows)[0] ?? null;
282
+ }
283
+
284
+ async deleteMany(where: Where<T>): Promise<number> {
285
+ const params = new SqlParams();
286
+ const { rowCount } = await this.run({
287
+ params: params.values,
288
+ text: `DELETE FROM ${this.table} WHERE ${this.where(where, params)}`,
289
+ });
290
+ return rowCount;
291
+ }
292
+
293
+ async deleteOne(where: Where<T>): Promise<null | StoredRecord<T>> {
294
+ // Picks and locks one match, and re-checks it at the write — the same guarantee as updateOne()
295
+ const params = new SqlParams();
296
+ const match = this.where(where, params);
297
+ const recheck = this.where(where, params);
298
+ const { rows } = await this.run({
299
+ params: params.values,
300
+ text: `DELETE FROM ${this.table} WHERE "id" = (SELECT "id" FROM ${this.table} WHERE ${match} LIMIT 1 FOR UPDATE) AND ${recheck} RETURNING ${this.selectList()}`,
301
+ });
302
+ return this.toRecords(rows)[0] ?? null;
303
+ }
304
+
305
+ async updateMany(where: Where<T>, update: DataStoreUpdate<T>): Promise<number> {
306
+ const patch = await this.preparePatch(update);
307
+ const params = new SqlParams();
308
+ const assignments = this.compileUpdate(patch, params);
309
+ if (!assignments.length) {
310
+ return this.count(where);
311
+ }
312
+ const { rowCount } = await this.run({
313
+ params: params.values,
314
+ text: `UPDATE ${this.table} SET ${assignments.join(', ')} WHERE ${this.where(where, params)}`,
315
+ });
316
+ return rowCount;
317
+ }
318
+
319
+ async pushToArray(id: string, field: string, items: readonly unknown[], options: PushOptions = {}): Promise<void> {
320
+ assertArrayField(field, 'pushToArray');
321
+ if (!items.length) {
322
+ return;
323
+ }
324
+ const descriptor = this.arrayField(field, 'pushToArray');
325
+ if (field === 'roles') {
326
+ if (this.roleGuardEnabled() && !isRoleChangeAllowed()) {
327
+ return;
328
+ }
329
+ assertNoSystemRoles(items);
330
+ }
331
+ const params = new SqlParams();
332
+ const column = quoteIdentifier(columnName(field));
333
+ const existing = `COALESCE(${column}, '[]'::jsonb)`;
334
+ const added = `${params.add(JSON.stringify(items.map((item) => this.castItem(descriptor, field, item))))}::jsonb`;
335
+
336
+ // MongoDB order: insert at the position, then sort, then slice
337
+ let value = `(${existing} || ${added})`;
338
+ if (options.position !== undefined) {
339
+ const position = assertInteger(options.position, 'position');
340
+ const at = `(CASE WHEN ${params.add(position)}::int < 0 THEN GREATEST(jsonb_array_length(${existing}) + ${params.add(position)}::int, 0) ELSE ${params.add(position)}::int END)`;
341
+ value = `(SELECT COALESCE(jsonb_agg(x.v ORDER BY x.g, x.n), '[]'::jsonb) FROM (SELECT o.v, o.n, CASE WHEN o.n <= ${at} THEN 0 ELSE 2 END AS g FROM jsonb_array_elements(${existing}) WITH ORDINALITY AS o(v, n) UNION ALL SELECT a.v, a.n, 1 FROM jsonb_array_elements(${added}) WITH ORDINALITY AS a(v, n)) AS x)`;
342
+ }
343
+ if (options.sort) {
344
+ value = `(SELECT COALESCE(jsonb_agg(s.v ORDER BY ${this.itemOrder(options.sort, params)}), '[]'::jsonb) FROM jsonb_array_elements(${value}) WITH ORDINALITY AS s(v, n))`;
345
+ }
346
+ if (options.slice !== undefined) {
347
+ const slice = assertInteger(options.slice, 'slice');
348
+ const keep =
349
+ slice < 0
350
+ ? `l.n > jsonb_array_length(${value}) - ${params.add(-slice)}::int`
351
+ : `l.n <= ${params.add(slice)}::int`;
352
+ value = `(SELECT COALESCE(jsonb_agg(l.v ORDER BY l.n), '[]'::jsonb) FROM jsonb_array_elements(${value}) WITH ORDINALITY AS l(v, n) WHERE ${keep})`;
353
+ }
354
+ await this.updateColumn(id, column, value, params);
355
+ }
356
+
357
+ async pullFromArray(id: string, field: string, condition: unknown): Promise<void> {
358
+ assertArrayField(field, 'pullFromArray');
359
+ this.arrayField(field, 'pullFromArray');
360
+ if (field === 'roles' && this.roleGuardEnabled() && !isRoleChangeAllowed()) {
361
+ return;
362
+ }
363
+ const params = new SqlParams();
364
+ const column = quoteIdentifier(columnName(field));
365
+ await this.updateColumn(id, column, pullExpression(this.model, field, column, condition, params), params);
366
+ }
367
+
368
+ // -- native access --------------------------------------------------------------------------------------
369
+
370
+ native<N = unknown>(reason: string): N {
371
+ assertAccessReason(reason, 'native');
372
+ this.logger.debug(`Native engine access: ${reason} (Model: ${this.modelName}, engine: ${this.executor.engine})`);
373
+ return this.executor.native<N>();
374
+ }
375
+
376
+ raw<N = unknown>(reason: string): N {
377
+ assertAccessReason(reason, 'raw');
378
+ this.logger.warn(`[SECURITY] Raw driver access: ${reason} (Model: ${this.modelName})`);
379
+ return this.executor.driverPool as N;
380
+ }
381
+
382
+ // -- before-layer ---------------------------------------------------------------------------------------
383
+
384
+ /** The policies the Mongoose plugins apply to `save()`, in their registration order */
385
+ protected async beforeInsert(doc: Record<string, any>): Promise<Record<string, any>> {
386
+ if (this.model.tenantScoped) {
387
+ const tenantId = resolveSingleTenantId(this.modelName);
388
+ if (tenantId) {
389
+ if (doc.tenantId) {
390
+ assertOwnTenant(doc.tenantId, tenantId, true);
391
+ } else {
392
+ doc.tenantId = tenantId;
393
+ }
394
+ }
395
+ }
396
+ this.applyFunctionDefaults(doc);
397
+ if (this.model.fields.password && this.passwordEnabled() && doc.password) {
398
+ doc.password = await hashPassword(doc.password);
399
+ }
400
+ if (this.model.fields.roles && doc.roles !== undefined) {
401
+ if (this.roleGuardEnabled() && !isRoleChangeAllowed()) {
402
+ doc.roles = [];
403
+ }
404
+ assertNoSystemRoles(doc.roles);
405
+ }
406
+ const actor = this.auditEnabled() ? resolveAuditActorId() : undefined;
407
+ if (actor) {
408
+ stampAuditOnWrite(doc, actor, this.auditFields(), true);
409
+ }
410
+ const { createdAt, updatedAt } = this.timestampFields();
411
+ const now = new Date();
412
+ if (createdAt && doc[createdAt] === undefined) {
413
+ doc[createdAt] = now;
414
+ }
415
+ if (updatedAt) {
416
+ doc[updatedAt] = now;
417
+ }
418
+ return doc;
419
+ }
420
+
421
+ /** The policies the Mongoose plugins apply to `findOneAndUpdate()` */
422
+ protected async beforeUpdate(update: Record<string, any>, options: UpdateOptions = {}): Promise<Record<string, any>> {
423
+ if (this.model.tenantScoped) {
424
+ const single = resolveSingleTenantId(this.modelName);
425
+ if (single) {
426
+ for (const candidate of [update.tenantId, update.$set?.tenantId, update.$setOnInsert?.tenantId]) {
427
+ if (candidate !== undefined) {
428
+ assertOwnTenant(candidate, single, false);
429
+ }
430
+ }
431
+ }
432
+ }
433
+ if (this.model.fields.password && this.passwordEnabled()) {
434
+ await hashUpdatePassword(update);
435
+ }
436
+ if (this.model.fields.roles) {
437
+ if (this.roleGuardEnabled() && updateChangesRoles(update) && !isRoleChangeAllowed()) {
438
+ stripRoleChanges(update);
439
+ }
440
+ assertUpdateHasNoSystemRoles(update);
441
+ }
442
+ const actor = this.auditEnabled() ? resolveAuditActorId() : undefined;
443
+ if (actor) {
444
+ stampAuditOnUpdate(update, actor, this.auditFields());
445
+ }
446
+ const { updatedAt } = this.timestampFields();
447
+ if (updatedAt && options.timestamps !== false && !hasOperator(update, updatedAt)) {
448
+ update.$set = { ...update.$set, [updatedAt]: new Date() };
449
+ }
450
+ return update;
451
+ }
452
+
453
+ /** The update as the store writes it: the caller's object untouched, the policies applied, undeclared paths dropped */
454
+ protected async preparePatch(update: DataStoreUpdate<T>, options: UpdateOptions = {}): Promise<Record<string, any>> {
455
+ return this.dropUndeclared(await this.beforeUpdate(clone(update), options));
456
+ }
457
+
458
+ /**
459
+ * Like Mongoose in strict mode: an update path whose field the model does not declare is not written. CrudService
460
+ * merges `updatedBy` into every update with a current user, also for a model without that field
461
+ */
462
+
463
+ private dropUndeclared(update: Record<string, any>): Record<string, any> {
464
+ const declared = (path: string) => {
465
+ const head = path.split('.', 1)[0];
466
+ // The id stays, so the compiler refuses changing it instead of the update ignoring it
467
+ return head === 'id' || Object.prototype.hasOwnProperty.call(this.model.fields, head);
468
+ };
469
+ for (const [key, value] of Object.entries(update)) {
470
+ if (!key.startsWith('$')) {
471
+ if (!declared(key)) {
472
+ delete update[key];
473
+ }
474
+ } else if (value && typeof value === 'object' && !Array.isArray(value)) {
475
+ for (const path of Object.keys(value)) {
476
+ if (!declared(path)) {
477
+ delete value[path];
478
+ }
479
+ }
480
+ }
481
+ }
482
+ return update;
483
+ }
484
+
485
+ // -- statements -----------------------------------------------------------------------------------------
486
+
487
+ /** The caller's filter AND the tenant filter; throws the safety net's 403 before any statement runs */
488
+ protected where(where: undefined | Where<T>, params: SqlParams): string {
489
+ const tenantFilter = this.model.tenantScoped ? resolveTenantFilter(this.modelName) : undefined;
490
+ const combined =
491
+ tenantFilter && Object.keys(tenantFilter).length ? { $and: [where ?? {}, tenantFilter] } : (where ?? {});
492
+ return this.compileWhere(combined as Where, params);
493
+ }
494
+
495
+ /** The condition of a neutral filter in this database's SQL */
496
+ protected compileWhere(where: Where, params: SqlParams): string {
497
+ return compilePostgresWhere(this.model, where, params);
498
+ }
499
+
500
+ protected async exists(where: undefined | Where<T>): Promise<boolean> {
501
+ const params = new SqlParams();
502
+ const { rows } = await this.run<{ found: boolean }>({
503
+ params: params.values,
504
+ text: `SELECT EXISTS (SELECT 1 FROM ${this.table} WHERE ${this.where(where, params)}) AS found`,
505
+ });
506
+ return rows[0].found;
507
+ }
508
+
509
+ protected async run<R = Record<string, any>>(statement: SqlStatement): Promise<{ rowCount: number; rows: R[] }> {
510
+ try {
511
+ return await this.executor.run<R>(statement, resolveTenantScope());
512
+ } catch (error) {
513
+ throw this.mapError(error);
514
+ }
515
+ }
516
+
517
+ protected async updateColumn(id: string, column: string, value: string, params: SqlParams): Promise<void> {
518
+ // The audit and timestamp fields the update hooks stamp, flat or under `$set` — the compiler reads both
519
+ const { $setOnInsert: _insertOnly, ...stamped } = await this.beforeUpdate({});
520
+ const assignments = [`${column} = ${value}`, ...this.compileUpdate(stamped, params)];
521
+ await this.run({
522
+ params: params.values,
523
+ text: `UPDATE ${this.table} SET ${assignments.join(', ')} WHERE ${this.where({ id } as Where<T>, params)}`,
524
+ });
525
+ }
526
+
527
+ /** Columns to read: the selected fields (`select: false` fields only when named), the id, and `tenant_id` for the row check */
528
+ protected selectList(select?: readonly string[], exclude?: readonly string[]): string {
529
+ assertProjection({ exclude, select });
530
+ const wanted = new Set(select?.map((name) => (name === '_id' ? 'id' : name)));
531
+ const left = new Set(exclude);
532
+ const columns = new Set<string>(['id']);
533
+ for (const field of Object.values(this.model.fields)) {
534
+ const column = columnName(field.name);
535
+ if (select ? wanted.has(field.name) : field.select !== false && !left.has(field.name)) {
536
+ columns.add(column);
537
+ }
538
+ }
539
+ for (const name of exclude ?? []) {
540
+ if (!this.model.fields[name]) {
541
+ throw new UnsupportedQueryError(`Unknown field "${name}" on ${this.modelName}`);
542
+ }
543
+ }
544
+ if (select) {
545
+ for (const name of wanted) {
546
+ if (name !== 'id' && !this.model.fields[name]) {
547
+ throw new UnsupportedQueryError(`Unknown field "${name}" on ${this.modelName}`);
548
+ }
549
+ }
550
+ }
551
+ if (this.model.tenantScoped) {
552
+ columns.add('tenant_id');
553
+ }
554
+ return [...columns].map((column) => this.quote(column)).join(', ');
555
+ }
556
+
557
+ /** An identifier as this database quotes it */
558
+ protected quote(name: string): string {
559
+ return quoteIdentifier(name);
560
+ }
561
+
562
+ /** The assignments of an `UPDATE … SET` for the neutral update */
563
+ protected compileUpdate(update: DataStoreUpdate, params: SqlParams): string[] {
564
+ return compilePostgresUpdate(this.model, update, params);
565
+ }
566
+
567
+ /** MongoDB sorts a missing value first ascending and last descending; the id breaks ties so paging is stable */
568
+ protected orderBy(options: FindOptions, params: SqlParams): string {
569
+ if (options.collation && !this.collationWarned) {
570
+ this.collationWarned = true;
571
+ this.logger.warn(`${this.modelName}: a collation is not applied on PostgreSQL; strings compare byte-wise`);
572
+ }
573
+ const parts: string[] = [];
574
+ for (const [path, direction] of Object.entries(options.sort ?? {})) {
575
+ if (direction !== 1 && direction !== -1) {
576
+ throw new UnsupportedQueryError(`sort direction for "${path}" must be 1 or -1`);
577
+ }
578
+ const { expr, kind } = compilePostgresPath(this.model, path, params);
579
+ if (kind === 'array') {
580
+ throw new UnsupportedQueryError(`sort by the array field "${path}"`);
581
+ }
582
+ parts.push(`${expr} ${direction === 1 ? 'ASC NULLS FIRST' : 'DESC NULLS LAST'}`);
583
+ }
584
+ if (!parts.length && options.skip === undefined && options.limit === undefined) {
585
+ return '';
586
+ }
587
+ if (!Object.keys(options.sort ?? {}).some((path) => path === 'id' || path === '_id')) {
588
+ parts.push('"id" ASC');
589
+ }
590
+ return ` ORDER BY ${parts.join(', ')}`;
591
+ }
592
+
593
+ protected itemOrder(sort: Record<string, -1 | 1> | number, params: SqlParams): string {
594
+ if (typeof sort === 'number') {
595
+ return `s.v ${sort === -1 ? 'DESC' : 'ASC'}, s.n`;
596
+ }
597
+ return [
598
+ ...Object.entries(sort).map(([path, direction]) => {
599
+ const segments = path.split('.');
600
+ return `(s.v #> ${params.add(segments)}::text[]) ${direction === -1 ? 'DESC' : 'ASC'}`;
601
+ }),
602
+ 's.n',
603
+ ].join(', ');
604
+ }
605
+
606
+ // -- values ---------------------------------------------------------------------------------------------
607
+
608
+ /** A value bound for a column: typed, or jsonb for arrays, objects and undeclared content */
609
+ protected toColumn(field: FieldDescriptor, path: string, value: unknown, params: SqlParams): string {
610
+ if (value === null || value === undefined) {
611
+ return 'NULL';
612
+ }
613
+ if (field.isArray) {
614
+ const items = Array.isArray(value) ? value : [value];
615
+ return `${params.add(JSON.stringify(items.map((item) => this.castItem(field, path, item))))}::jsonb`;
616
+ }
617
+ if (SCALAR_KINDS.has(field.kind)) {
618
+ return params.add(castFilterValue(field, path, value));
619
+ }
620
+ return `${params.add(JSON.stringify(value))}::jsonb`;
621
+ }
622
+
623
+ protected castItem(field: FieldDescriptor, path: string, item: unknown): unknown {
624
+ const element = { ...field, isArray: false };
625
+ return SCALAR_KINDS.has(element.kind) ? castFilterValue(element, path, item) : item;
626
+ }
627
+
628
+ protected fromColumn(field: FieldDescriptor, value: unknown): unknown {
629
+ if (value === null || value === undefined) {
630
+ return undefined;
631
+ }
632
+ if (!field.isArray && field.kind === 'bigint') {
633
+ return BigInt(value as string);
634
+ }
635
+ return value;
636
+ }
637
+
638
+ protected toRecords(rows: Record<string, any>[], select?: readonly string[]): StoredRecord<T>[] {
639
+ if (this.model.tenantScoped) {
640
+ assertRowsInTenantScope(
641
+ this.modelName,
642
+ rows.map((row) => ({ tenantId: row.tenant_id })),
643
+ );
644
+ }
645
+ const keepTenant = !select || select.includes('tenantId');
646
+ return rows.map((row) => {
647
+ const record: Record<string, any> = { id: row.id };
648
+ for (const [column, value] of Object.entries(row)) {
649
+ const field = this.fields.get(column);
650
+ if (!field || column === 'id' || (column === 'tenant_id' && !keepTenant)) {
651
+ continue;
652
+ }
653
+ const mapped = this.fromColumn(field, value);
654
+ if (mapped !== undefined) {
655
+ record[field.name] = mapped;
656
+ }
657
+ }
658
+ return record as StoredRecord<T>;
659
+ });
660
+ }
661
+
662
+ // -- helpers --------------------------------------------------------------------------------------------
663
+
664
+ protected arrayField(field: string, method: string): FieldDescriptor {
665
+ const descriptor = this.model.fields[field];
666
+ if (!descriptor?.isArray) {
667
+ throw new UnsupportedQueryError(`${method}: "${field}" is not an array field of ${this.modelName}`);
668
+ }
669
+ return descriptor;
670
+ }
671
+
672
+ private applyFunctionDefaults(doc: Record<string, any>): void {
673
+ const schema = this.options.schema;
674
+ if (!schema) {
675
+ return;
676
+ }
677
+ for (const field of Object.values(this.model.fields)) {
678
+ // Arrays default to [] through their column default; Mongoose's array default needs a document to run in
679
+ if (
680
+ field.hasDefault &&
681
+ !field.isArray &&
682
+ !('default' in field) &&
683
+ doc[field.name] === undefined &&
684
+ field.name !== '_id'
685
+ ) {
686
+ const value = computedDefault(schema, field.name, doc);
687
+ if (value !== undefined) {
688
+ doc[field.name] = value;
689
+ }
690
+ }
691
+ }
692
+ }
693
+
694
+ private auditFields(): { createdBy: boolean; updatedBy: boolean } {
695
+ return { createdBy: !!this.model.fields.createdBy, updatedBy: !!this.model.fields.updatedBy };
696
+ }
697
+
698
+ private timestampFields(): { createdAt?: string; updatedAt?: string } {
699
+ const timestamps = this.model.timestamps;
700
+ if (!timestamps) {
701
+ return {};
702
+ }
703
+ if (timestamps === true) {
704
+ return { createdAt: 'createdAt', updatedAt: 'updatedAt' };
705
+ }
706
+ return {
707
+ createdAt: timestamps.createdAt || undefined,
708
+ updatedAt: timestamps.updatedAt || undefined,
709
+ };
710
+ }
711
+
712
+ private passwordEnabled(): boolean {
713
+ return ConfigService.configFastButReadOnly?.security?.mongoosePasswordPlugin !== false;
714
+ }
715
+
716
+ protected roleGuardEnabled(): boolean {
717
+ return ConfigService.configFastButReadOnly?.security?.mongooseRoleGuardPlugin !== false;
718
+ }
719
+
720
+ private auditEnabled(): boolean {
721
+ return ConfigService.configFastButReadOnly?.security?.mongooseAuditFieldsPlugin !== false;
722
+ }
723
+
724
+ /** Postgres errors as the answers the MongoDB path gives for the same situation */
725
+ protected mapError(error: unknown): unknown {
726
+ const code = (error as { code?: unknown })?.code;
727
+ switch (code) {
728
+ case '23505':
729
+ return new DuplicateKeyError(this.modelName);
730
+ case '23514':
731
+ // The roles CHECK is the only check constraint the framework creates
732
+ return new BadRequestException(`${this.modelName}: the value is not allowed`);
733
+ case '42501':
734
+ return new ForbiddenException(
735
+ 'Tenant isolation: this data is tenant-scoped and outside the caller’s tenant context',
736
+ );
737
+ default:
738
+ return error;
739
+ }
740
+ }
741
+ }
742
+
743
+ /**
744
+ * Copies the containers the update policies write into (plain objects, arrays) and keeps every other object as it is.
745
+ * `structuredClone` turned an ObjectId (or any class instance) into a plain object, which the cast then refused
746
+ */
747
+ function clone<V>(value: V): V {
748
+ if (Array.isArray(value)) {
749
+ return value.map((item) => clone(item)) as V;
750
+ }
751
+ if (value && typeof value === 'object') {
752
+ const proto = Object.getPrototypeOf(value);
753
+ if (proto === Object.prototype || proto === null) {
754
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, clone(item)])) as V;
755
+ }
756
+ }
757
+ return value;
758
+ }
759
+
760
+ function hasOperator(update: Record<string, any>, field: string): boolean {
761
+ return Object.values(update).some((part) => part && typeof part === 'object' && field in part) || field in update;
762
+ }