@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.
- package/.claude/rules/architecture.md +6 -1
- package/.claude/rules/better-auth.md +46 -0
- package/.claude/rules/configurable-features.md +3 -2
- package/.claude/rules/package-management.md +24 -2
- package/.claude/rules/persistence.md +100 -0
- package/.claude/rules/role-system.md +9 -0
- package/.claude/rules/testing.md +116 -18
- package/CLAUDE.md +21 -2
- package/FRAMEWORK-API.md +6 -4
- package/README.md +106 -34
- package/dist/core/common/decorators/unified-field.decorator.d.ts +2 -1
- package/dist/core/common/decorators/unified-field.decorator.js +4 -1
- package/dist/core/common/decorators/unified-field.decorator.js.map +1 -1
- package/dist/core/common/filters/mongoose-cast-error.filter.d.ts +7 -5
- package/dist/core/common/filters/mongoose-cast-error.filter.js +3 -2
- package/dist/core/common/filters/mongoose-cast-error.filter.js.map +1 -1
- package/dist/core/common/helpers/db.helper.d.ts +2 -1
- package/dist/core/common/helpers/db.helper.js +32 -26
- package/dist/core/common/helpers/db.helper.js.map +1 -1
- package/dist/core/common/helpers/decorator.helper.d.ts +1 -1
- package/dist/core/common/helpers/filter.helper.d.ts +2 -1
- package/dist/core/common/helpers/filter.helper.js.map +1 -1
- package/dist/core/common/helpers/gridfs.helper.d.ts +4 -4
- package/dist/core/common/helpers/gridfs.helper.js +14 -6
- package/dist/core/common/helpers/gridfs.helper.js.map +1 -1
- package/dist/core/common/helpers/id.helper.d.ts +4 -1
- package/dist/core/common/helpers/id.helper.js +10 -4
- package/dist/core/common/helpers/id.helper.js.map +1 -1
- package/dist/core/common/helpers/model.helper.js +5 -5
- package/dist/core/common/helpers/model.helper.js.map +1 -1
- package/dist/core/common/helpers/service.helper.js +3 -3
- package/dist/core/common/helpers/service.helper.js.map +1 -1
- package/dist/core/common/interfaces/server-options.interface.d.ts +18 -2
- package/dist/core/common/interfaces/service-options.interface.d.ts +2 -2
- package/dist/core/common/models/core-persistence.model.d.ts +1 -2
- package/dist/core/common/models/core-persistence.model.js +3 -3
- package/dist/core/common/models/core-persistence.model.js.map +1 -1
- package/dist/core/common/persistence/core-persistence.lifecycle.d.ts +9 -0
- package/dist/core/common/persistence/core-persistence.lifecycle.js +38 -0
- package/dist/core/common/persistence/core-persistence.lifecycle.js.map +1 -0
- package/dist/core/common/persistence/core-persistence.module.d.ts +9 -0
- package/dist/core/common/persistence/core-persistence.module.js +76 -0
- package/dist/core/common/persistence/core-persistence.module.js.map +1 -0
- package/dist/core/common/persistence/data-source.config.d.ts +33 -0
- package/dist/core/common/persistence/data-source.config.js +151 -0
- package/dist/core/common/persistence/data-source.config.js.map +1 -0
- package/dist/core/common/persistence/data-source.runtime.d.ts +45 -0
- package/dist/core/common/persistence/data-source.runtime.js +278 -0
- package/dist/core/common/persistence/data-source.runtime.js.map +1 -0
- package/dist/core/common/persistence/data-store.interface.d.ts +73 -0
- package/dist/core/common/persistence/data-store.interface.js +2 -0
- package/dist/core/common/persistence/data-store.interface.js.map +1 -0
- package/dist/core/common/persistence/expiry-sweep.d.ts +12 -0
- package/dist/core/common/persistence/expiry-sweep.js +55 -0
- package/dist/core/common/persistence/expiry-sweep.js.map +1 -0
- package/dist/core/common/persistence/id-format.d.ts +3 -0
- package/dist/core/common/persistence/id-format.js +21 -0
- package/dist/core/common/persistence/id-format.js.map +1 -0
- package/dist/core/common/persistence/infrastructure-locks.d.ts +13 -0
- package/dist/core/common/persistence/infrastructure-locks.js +35 -0
- package/dist/core/common/persistence/infrastructure-locks.js.map +1 -0
- package/dist/core/common/persistence/inject-store.decorator.d.ts +1 -0
- package/dist/core/common/persistence/inject-store.decorator.js +6 -0
- package/dist/core/common/persistence/inject-store.decorator.js.map +1 -0
- package/dist/core/common/persistence/lock-store.interface.d.ts +5 -0
- package/dist/core/common/persistence/lock-store.interface.js +2 -0
- package/dist/core/common/persistence/lock-store.interface.js.map +1 -0
- package/dist/core/common/persistence/model-descriptor.interface.d.ts +36 -0
- package/dist/core/common/persistence/model-descriptor.interface.js +2 -0
- package/dist/core/common/persistence/model-descriptor.interface.js.map +1 -0
- package/dist/core/common/persistence/model-metadata.registry.d.ts +14 -0
- package/dist/core/common/persistence/model-metadata.registry.js +194 -0
- package/dist/core/common/persistence/model-metadata.registry.js.map +1 -0
- package/dist/core/common/persistence/mongo/collection-name.d.ts +2 -0
- package/dist/core/common/persistence/mongo/collection-name.js +73 -0
- package/dist/core/common/persistence/mongo/collection-name.js.map +1 -0
- package/dist/core/common/persistence/mongo/declared-schema.view.d.ts +11 -0
- package/dist/core/common/persistence/mongo/declared-schema.view.js +292 -0
- package/dist/core/common/persistence/mongo/declared-schema.view.js.map +1 -0
- package/dist/core/common/persistence/mongo/hex-object-id.d.ts +13 -0
- package/dist/core/common/persistence/mongo/hex-object-id.js +54 -0
- package/dist/core/common/persistence/mongo/hex-object-id.js.map +1 -0
- package/dist/core/common/persistence/mongo/model-declarations.d.ts +35 -0
- package/dist/core/common/persistence/mongo/model-declarations.js +80 -0
- package/dist/core/common/persistence/mongo/model-declarations.js.map +1 -0
- package/dist/core/common/persistence/mongo/mongo-packages.d.ts +15 -0
- package/dist/core/common/persistence/mongo/mongo-packages.js +66 -0
- package/dist/core/common/persistence/mongo/mongo-packages.js.map +1 -0
- package/dist/core/common/persistence/mongo/mongoose-compat.d.ts +22 -0
- package/dist/core/common/persistence/mongo/mongoose-compat.js +65 -0
- package/dist/core/common/persistence/mongo/mongoose-compat.js.map +1 -0
- package/dist/core/common/persistence/mongo/no-mongo-connection.module.d.ts +2 -0
- package/dist/core/common/persistence/mongo/no-mongo-connection.module.js +19 -0
- package/dist/core/common/persistence/mongo/no-mongo-connection.module.js.map +1 -0
- package/dist/core/common/persistence/mongo-lock-store.d.ts +21 -0
- package/dist/core/common/persistence/mongo-lock-store.js +72 -0
- package/dist/core/common/persistence/mongo-lock-store.js.map +1 -0
- package/dist/core/common/persistence/mongoose-data-store.d.ts +33 -0
- package/dist/core/common/persistence/mongoose-data-store.js +187 -0
- package/dist/core/common/persistence/mongoose-data-store.js.map +1 -0
- package/dist/core/common/persistence/neutral-populate.d.ts +6 -0
- package/dist/core/common/persistence/neutral-populate.js +72 -0
- package/dist/core/common/persistence/neutral-populate.js.map +1 -0
- package/dist/core/common/persistence/persisted-model.decorator.d.ts +6 -0
- package/dist/core/common/persistence/persisted-model.decorator.js +24 -0
- package/dist/core/common/persistence/persisted-model.decorator.js.map +1 -0
- package/dist/core/common/persistence/persistence-runtime.registry.d.ts +61 -0
- package/dist/core/common/persistence/persistence-runtime.registry.js +47 -0
- package/dist/core/common/persistence/persistence-runtime.registry.js.map +1 -0
- package/dist/core/common/persistence/persistence.tokens.d.ts +1 -0
- package/dist/core/common/persistence/persistence.tokens.js +4 -0
- package/dist/core/common/persistence/persistence.tokens.js.map +1 -0
- package/dist/core/common/persistence/policies/audit.policy.d.ts +8 -0
- package/dist/core/common/persistence/policies/audit.policy.js +37 -0
- package/dist/core/common/persistence/policies/audit.policy.js.map +1 -0
- package/dist/core/common/persistence/policies/password.policy.d.ts +3 -0
- package/dist/core/common/persistence/policies/password.policy.js +46 -0
- package/dist/core/common/persistence/policies/password.policy.js.map +1 -0
- package/dist/core/common/persistence/policies/role-guard.policy.d.ts +3 -0
- package/dist/core/common/persistence/policies/role-guard.policy.js +46 -0
- package/dist/core/common/persistence/policies/role-guard.policy.js.map +1 -0
- package/dist/core/common/persistence/policies/system-role.policy.d.ts +5 -0
- package/dist/core/common/persistence/policies/system-role.policy.js +46 -0
- package/dist/core/common/persistence/policies/system-role.policy.js.map +1 -0
- package/dist/core/common/persistence/policies/tenant.policy.d.ts +19 -0
- package/dist/core/common/persistence/policies/tenant.policy.js +120 -0
- package/dist/core/common/persistence/policies/tenant.policy.js.map +1 -0
- package/dist/core/common/persistence/routed-model.guard.d.ts +4 -0
- package/dist/core/common/persistence/routed-model.guard.js +73 -0
- package/dist/core/common/persistence/routed-model.guard.js.map +1 -0
- package/dist/core/common/persistence/sql/mysql/mysql-data-store.d.ts +46 -0
- package/dist/core/common/persistence/sql/mysql/mysql-data-store.js +350 -0
- package/dist/core/common/persistence/sql/mysql/mysql-data-store.js.map +1 -0
- package/dist/core/common/persistence/sql/mysql/mysql-executor.d.ts +13 -0
- package/dist/core/common/persistence/sql/mysql/mysql-executor.js +194 -0
- package/dist/core/common/persistence/sql/mysql/mysql-executor.js.map +1 -0
- package/dist/core/common/persistence/sql/mysql/mysql-lock-store.d.ts +11 -0
- package/dist/core/common/persistence/sql/mysql/mysql-lock-store.js +55 -0
- package/dist/core/common/persistence/sql/mysql/mysql-lock-store.js.map +1 -0
- package/dist/core/common/persistence/sql/mysql/mysql-schema.compiler.d.ts +36 -0
- package/dist/core/common/persistence/sql/mysql/mysql-schema.compiler.js +288 -0
- package/dist/core/common/persistence/sql/mysql/mysql-schema.compiler.js.map +1 -0
- package/dist/core/common/persistence/sql/mysql/mysql-schema.migrator.d.ts +19 -0
- package/dist/core/common/persistence/sql/mysql/mysql-schema.migrator.js +328 -0
- package/dist/core/common/persistence/sql/mysql/mysql-schema.migrator.js.map +1 -0
- package/dist/core/common/persistence/sql/mysql/mysql-session.d.ts +13 -0
- package/dist/core/common/persistence/sql/mysql/mysql-session.js +85 -0
- package/dist/core/common/persistence/sql/mysql/mysql-session.js.map +1 -0
- package/dist/core/common/persistence/sql/mysql/mysql-update.compiler.d.ts +9 -0
- package/dist/core/common/persistence/sql/mysql/mysql-update.compiler.js +264 -0
- package/dist/core/common/persistence/sql/mysql/mysql-update.compiler.js.map +1 -0
- package/dist/core/common/persistence/sql/mysql/mysql-where.compiler.d.ts +20 -0
- package/dist/core/common/persistence/sql/mysql/mysql-where.compiler.js +519 -0
- package/dist/core/common/persistence/sql/mysql/mysql-where.compiler.js.map +1 -0
- package/dist/core/common/persistence/sql/postgres/postgres-lock-store.d.ts +11 -0
- package/dist/core/common/persistence/sql/postgres/postgres-lock-store.js +44 -0
- package/dist/core/common/persistence/sql/postgres/postgres-lock-store.js.map +1 -0
- package/dist/core/common/persistence/sql/postgres/postgres-schema.compiler.d.ts +42 -0
- package/dist/core/common/persistence/sql/postgres/postgres-schema.compiler.js +220 -0
- package/dist/core/common/persistence/sql/postgres/postgres-schema.compiler.js.map +1 -0
- package/dist/core/common/persistence/sql/postgres/postgres-schema.migrator.d.ts +12 -0
- package/dist/core/common/persistence/sql/postgres/postgres-schema.migrator.js +315 -0
- package/dist/core/common/persistence/sql/postgres/postgres-schema.migrator.js.map +1 -0
- package/dist/core/common/persistence/sql/postgres/postgres-update.compiler.d.ts +5 -0
- package/dist/core/common/persistence/sql/postgres/postgres-update.compiler.js +225 -0
- package/dist/core/common/persistence/sql/postgres/postgres-update.compiler.js.map +1 -0
- package/dist/core/common/persistence/sql/postgres/postgres-where.compiler.d.ts +13 -0
- package/dist/core/common/persistence/sql/postgres/postgres-where.compiler.js +484 -0
- package/dist/core/common/persistence/sql/postgres/postgres-where.compiler.js.map +1 -0
- package/dist/core/common/persistence/sql/sql-cast.d.ts +2 -0
- package/dist/core/common/persistence/sql/sql-cast.js +194 -0
- package/dist/core/common/persistence/sql/sql-cast.js.map +1 -0
- package/dist/core/common/persistence/sql/sql-data-store.d.ts +79 -0
- package/dist/core/common/persistence/sql/sql-data-store.js +592 -0
- package/dist/core/common/persistence/sql/sql-data-store.js.map +1 -0
- package/dist/core/common/persistence/sql/sql-dialect.d.ts +20 -0
- package/dist/core/common/persistence/sql/sql-dialect.js +38 -0
- package/dist/core/common/persistence/sql/sql-dialect.js.map +1 -0
- package/dist/core/common/persistence/sql/sql-errors.d.ts +17 -0
- package/dist/core/common/persistence/sql/sql-errors.js +25 -0
- package/dist/core/common/persistence/sql/sql-errors.js.map +1 -0
- package/dist/core/common/persistence/sql/sql-executor.factory.d.ts +11 -0
- package/dist/core/common/persistence/sql/sql-executor.factory.js +199 -0
- package/dist/core/common/persistence/sql/sql-executor.factory.js.map +1 -0
- package/dist/core/common/persistence/sql/sql-executor.interface.d.ts +32 -0
- package/dist/core/common/persistence/sql/sql-executor.interface.js +2 -0
- package/dist/core/common/persistence/sql/sql-executor.interface.js.map +1 -0
- package/dist/core/common/persistence/sql/sql-naming.d.ts +15 -0
- package/dist/core/common/persistence/sql/sql-naming.js +90 -0
- package/dist/core/common/persistence/sql/sql-naming.js.map +1 -0
- package/dist/core/common/persistence/sql/sql-schema.shared.d.ts +25 -0
- package/dist/core/common/persistence/sql/sql-schema.shared.js +69 -0
- package/dist/core/common/persistence/sql/sql-schema.shared.js.map +1 -0
- package/dist/core/common/persistence/sql/sql-session.d.ts +6 -0
- package/dist/core/common/persistence/sql/sql-session.js +78 -0
- package/dist/core/common/persistence/sql/sql-session.js.map +1 -0
- package/dist/core/common/persistence/sql/where-shared.d.ts +18 -0
- package/dist/core/common/persistence/sql/where-shared.js +93 -0
- package/dist/core/common/persistence/sql/where-shared.js.map +1 -0
- package/dist/core/common/persistence/store-guards.d.ts +10 -0
- package/dist/core/common/persistence/store-guards.js +49 -0
- package/dist/core/common/persistence/store-guards.js.map +1 -0
- package/dist/core/common/persistence/store-upsert.d.ts +2 -0
- package/dist/core/common/persistence/store-upsert.js +20 -0
- package/dist/core/common/persistence/store-upsert.js.map +1 -0
- package/dist/core/common/persistence/unavailable-store.d.ts +2 -0
- package/dist/core/common/persistence/unavailable-store.js +53 -0
- package/dist/core/common/persistence/unavailable-store.js.map +1 -0
- package/dist/core/common/persistence/unsupported-capability.error.d.ts +3 -0
- package/dist/core/common/persistence/unsupported-capability.error.js +4 -0
- package/dist/core/common/persistence/unsupported-capability.error.js.map +1 -0
- package/dist/core/common/plugins/mongoose-audit-fields.plugin.js +24 -68
- package/dist/core/common/plugins/mongoose-audit-fields.plugin.js.map +1 -1
- package/dist/core/common/plugins/mongoose-id.plugin.js +3 -0
- package/dist/core/common/plugins/mongoose-id.plugin.js.map +1 -1
- package/dist/core/common/plugins/mongoose-password.plugin.d.ts +1 -3
- package/dist/core/common/plugins/mongoose-password.plugin.js +2 -45
- package/dist/core/common/plugins/mongoose-password.plugin.js.map +1 -1
- package/dist/core/common/plugins/mongoose-role-guard.plugin.js +5 -46
- package/dist/core/common/plugins/mongoose-role-guard.plugin.js.map +1 -1
- package/dist/core/common/plugins/mongoose-system-role.plugin.js +2 -43
- package/dist/core/common/plugins/mongoose-system-role.plugin.js.map +1 -1
- package/dist/core/common/plugins/mongoose-tenant.plugin.js +10 -70
- package/dist/core/common/plugins/mongoose-tenant.plugin.js.map +1 -1
- package/dist/core/common/scalars/upload.scalar.js +1 -1
- package/dist/core/common/scalars/upload.scalar.js.map +1 -1
- package/dist/core/common/services/core-cron-jobs.initializer.js +1 -1
- package/dist/core/common/services/core-cron-jobs.initializer.js.map +1 -1
- package/dist/core/common/services/core-cron-jobs.service.d.ts +1 -0
- package/dist/core/common/services/core-cron-jobs.service.js +23 -14
- package/dist/core/common/services/core-cron-jobs.service.js.map +1 -1
- package/dist/core/common/services/crud.service.d.ts +4 -1
- package/dist/core/common/services/crud.service.js +132 -23
- package/dist/core/common/services/crud.service.js.map +1 -1
- package/dist/core/common/services/model-doc.service.js +1 -1
- package/dist/core/common/services/model-doc.service.js.map +1 -1
- package/dist/core/common/services/module.service.d.ts +9 -2
- package/dist/core/common/services/module.service.js +51 -6
- package/dist/core/common/services/module.service.js.map +1 -1
- package/dist/core/common/types/field-selection.type.d.ts +1 -1
- package/dist/core/common/types/id.type.d.ts +4 -4
- package/dist/core/common/types/object-id-like.type.d.ts +3 -0
- package/dist/core/common/types/object-id-like.type.js +2 -0
- package/dist/core/common/types/object-id-like.type.js.map +1 -0
- package/dist/core/common/types/populate-config.type.d.ts +1 -1
- package/dist/core/common/types/string-or-object-id.type.d.ts +2 -2
- package/dist/core/modules/ai/core-ai.constants.d.ts +4 -0
- package/dist/core/modules/ai/core-ai.constants.js +4 -0
- package/dist/core/modules/ai/core-ai.constants.js.map +1 -1
- package/dist/core/modules/ai/core-ai.module.js +21 -17
- package/dist/core/modules/ai/core-ai.module.js.map +1 -1
- package/dist/core/modules/ai/models/core-ai-budget-limit.model.d.ts +2 -92
- package/dist/core/modules/ai/models/core-ai-budget-limit.model.js +1 -1
- package/dist/core/modules/ai/models/core-ai-budget-limit.model.js.map +1 -1
- package/dist/core/modules/ai/models/core-ai-connection-preference.model.d.ts +2 -87
- package/dist/core/modules/ai/models/core-ai-connection-preference.model.js +1 -1
- package/dist/core/modules/ai/models/core-ai-connection-preference.model.js.map +1 -1
- package/dist/core/modules/ai/models/core-ai-connection.model.d.ts +2 -177
- package/dist/core/modules/ai/models/core-ai-connection.model.js +1 -1
- package/dist/core/modules/ai/models/core-ai-connection.model.js.map +1 -1
- package/dist/core/modules/ai/models/core-ai-conversation.model.d.ts +2 -92
- package/dist/core/modules/ai/models/core-ai-conversation.model.js +3 -4
- package/dist/core/modules/ai/models/core-ai-conversation.model.js.map +1 -1
- package/dist/core/modules/ai/models/core-ai-interaction.model.d.ts +2 -120
- package/dist/core/modules/ai/models/core-ai-interaction.model.js +2 -3
- package/dist/core/modules/ai/models/core-ai-interaction.model.js.map +1 -1
- package/dist/core/modules/ai/models/core-ai-mcp-oauth.models.d.ts +3 -0
- package/dist/core/modules/ai/models/core-ai-mcp-oauth.models.js +21 -0
- package/dist/core/modules/ai/models/core-ai-mcp-oauth.models.js.map +1 -0
- package/dist/core/modules/ai/models/core-ai-mode.model.d.ts +2 -102
- package/dist/core/modules/ai/models/core-ai-mode.model.js +1 -1
- package/dist/core/modules/ai/models/core-ai-mode.model.js.map +1 -1
- package/dist/core/modules/ai/models/core-ai-prompt-hint.model.d.ts +2 -97
- package/dist/core/modules/ai/models/core-ai-prompt-hint.model.js +1 -1
- package/dist/core/modules/ai/models/core-ai-prompt-hint.model.js.map +1 -1
- package/dist/core/modules/ai/models/core-ai-prompt.model.d.ts +2 -112
- package/dist/core/modules/ai/models/core-ai-prompt.model.js +1 -1
- package/dist/core/modules/ai/models/core-ai-prompt.model.js.map +1 -1
- package/dist/core/modules/ai/models/core-ai-slot.model.d.ts +2 -112
- package/dist/core/modules/ai/models/core-ai-slot.model.js +1 -1
- package/dist/core/modules/ai/models/core-ai-slot.model.js.map +1 -1
- package/dist/core/modules/ai/models/core-ai-tool-grant.model.d.ts +2 -97
- package/dist/core/modules/ai/models/core-ai-tool-grant.model.js +1 -1
- package/dist/core/modules/ai/models/core-ai-tool-grant.model.js.map +1 -1
- package/dist/core/modules/ai/models/core-ai-tool-policy.model.d.ts +2 -103
- package/dist/core/modules/ai/models/core-ai-tool-policy.model.js +1 -1
- package/dist/core/modules/ai/models/core-ai-tool-policy.model.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-budget.service.d.ts +3 -1
- package/dist/core/modules/ai/services/core-ai-budget.service.js +15 -5
- package/dist/core/modules/ai/services/core-ai-budget.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-connection-preference.service.d.ts +1 -1
- package/dist/core/modules/ai/services/core-ai-connection-preference.service.js +6 -10
- package/dist/core/modules/ai/services/core-ai-connection-preference.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-connection.service.d.ts +1 -1
- package/dist/core/modules/ai/services/core-ai-connection.service.js +20 -42
- package/dist/core/modules/ai/services/core-ai-connection.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-conversation.service.d.ts +1 -1
- package/dist/core/modules/ai/services/core-ai-conversation.service.js +28 -29
- package/dist/core/modules/ai/services/core-ai-conversation.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-interaction.service.d.ts +1 -1
- package/dist/core/modules/ai/services/core-ai-interaction.service.js +3 -4
- package/dist/core/modules/ai/services/core-ai-interaction.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-mcp-oauth.service.d.ts +8 -0
- package/dist/core/modules/ai/services/core-ai-mcp-oauth.service.js +70 -18
- package/dist/core/modules/ai/services/core-ai-mcp-oauth.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-mode.service.d.ts +1 -1
- package/dist/core/modules/ai/services/core-ai-mode.service.js +3 -7
- package/dist/core/modules/ai/services/core-ai-mode.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-prompt-hint.service.d.ts +8 -1
- package/dist/core/modules/ai/services/core-ai-prompt-hint.service.js +32 -26
- package/dist/core/modules/ai/services/core-ai-prompt-hint.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-prompt.service.d.ts +1 -1
- package/dist/core/modules/ai/services/core-ai-prompt.service.js +10 -13
- package/dist/core/modules/ai/services/core-ai-prompt.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-slot.service.d.ts +1 -1
- package/dist/core/modules/ai/services/core-ai-slot.service.js +14 -25
- package/dist/core/modules/ai/services/core-ai-slot.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-tool-grant.service.d.ts +1 -1
- package/dist/core/modules/ai/services/core-ai-tool-grant.service.js +6 -18
- package/dist/core/modules/ai/services/core-ai-tool-grant.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-tool-policy.service.d.ts +1 -1
- package/dist/core/modules/ai/services/core-ai-tool-policy.service.js +4 -8
- package/dist/core/modules/ai/services/core-ai-tool-policy.service.js.map +1 -1
- package/dist/core/modules/api-token/core-api-token.model.js +1 -1
- package/dist/core/modules/api-token/core-api-token.model.js.map +1 -1
- package/dist/core/modules/api-token/core-api-token.module.js +3 -2
- package/dist/core/modules/api-token/core-api-token.module.js.map +1 -1
- package/dist/core/modules/api-token/core-api-token.service.d.ts +8 -2
- package/dist/core/modules/api-token/core-api-token.service.js +106 -39
- package/dist/core/modules/api-token/core-api-token.service.js.map +1 -1
- package/dist/core/modules/auth/core-auth.module.js +2 -0
- package/dist/core/modules/auth/core-auth.module.js.map +1 -1
- package/dist/core/modules/auth/guards/legacy-auth-rate-limit.guard.js +1 -2
- package/dist/core/modules/auth/guards/legacy-auth-rate-limit.guard.js.map +1 -1
- package/dist/core/modules/auth/guards/role-guard.token.d.ts +1 -0
- package/dist/core/modules/auth/guards/role-guard.token.js +2 -0
- package/dist/core/modules/auth/guards/role-guard.token.js.map +1 -0
- package/dist/core/modules/auth/services/core-auth.service.js +4 -2
- package/dist/core/modules/auth/services/core-auth.service.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth-sql.helper.d.ts +6 -0
- package/dist/core/modules/better-auth/better-auth-sql.helper.js +113 -0
- package/dist/core/modules/better-auth/better-auth-sql.helper.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth-token.service.d.ts +3 -1
- package/dist/core/modules/better-auth/better-auth-token.service.js +15 -24
- package/dist/core/modules/better-auth/better-auth-token.service.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.config.d.ts +7 -0
- package/dist/core/modules/better-auth/better-auth.config.js +16 -5
- package/dist/core/modules/better-auth/better-auth.config.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.types.d.ts +1 -1
- package/dist/core/modules/better-auth/core-better-auth-api.middleware.d.ts +1 -0
- package/dist/core/modules/better-auth/core-better-auth-api.middleware.js +6 -1
- package/dist/core/modules/better-auth/core-better-auth-api.middleware.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth-challenge.service.js +30 -15
- package/dist/core/modules/better-auth/core-better-auth-challenge.service.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth-mongo.users.d.ts +10 -0
- package/dist/core/modules/better-auth/core-better-auth-mongo.users.js +24 -0
- package/dist/core/modules/better-auth/core-better-auth-mongo.users.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth-sql.registry.d.ts +3 -0
- package/dist/core/modules/better-auth/core-better-auth-sql.registry.js +8 -0
- package/dist/core/modules/better-auth/core-better-auth-sql.registry.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth-sql.store.d.ts +71 -0
- package/dist/core/modules/better-auth/core-better-auth-sql.store.js +190 -0
- package/dist/core/modules/better-auth/core-better-auth-sql.store.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth-user.mapper.d.ts +24 -1
- package/dist/core/modules/better-auth/core-better-auth-user.mapper.js +308 -32
- package/dist/core/modules/better-auth/core-better-auth-user.mapper.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth.module.d.ts +2 -0
- package/dist/core/modules/better-auth/core-better-auth.module.js +54 -32
- package/dist/core/modules/better-auth/core-better-auth.module.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth.service.d.ts +7 -2
- package/dist/core/modules/better-auth/core-better-auth.service.js +61 -9
- package/dist/core/modules/better-auth/core-better-auth.service.js.map +1 -1
- package/dist/core/modules/file/core-file-info.model.d.ts +1 -2
- package/dist/core/modules/file/core-file-info.model.js +2 -2
- package/dist/core/modules/file/core-file-info.model.js.map +1 -1
- package/dist/core/modules/file/core-file.service.d.ts +5 -3
- package/dist/core/modules/file/core-file.service.js +16 -9
- package/dist/core/modules/file/core-file.service.js.map +1 -1
- package/dist/core/modules/file/file-metadata.helper.d.ts +1 -1
- package/dist/core/modules/file/file-metadata.helper.js +2 -2
- package/dist/core/modules/file/file-metadata.helper.js.map +1 -1
- package/dist/core/modules/file/file-metadata.models.d.ts +35 -0
- package/dist/core/modules/file/file-metadata.models.js +20 -0
- package/dist/core/modules/file/file-metadata.models.js.map +1 -0
- package/dist/core/modules/file/file-metadata.store.d.ts +6 -0
- package/dist/core/modules/file/file-metadata.store.js +77 -0
- package/dist/core/modules/file/file-metadata.store.js.map +1 -0
- package/dist/core/modules/file/file-storage.helper.d.ts +1 -0
- package/dist/core/modules/file/file-storage.helper.js +34 -0
- package/dist/core/modules/file/file-storage.helper.js.map +1 -1
- package/dist/core/modules/file/filesystem-file.helper.d.ts +1 -1
- package/dist/core/modules/file/filesystem-file.helper.js +4 -4
- package/dist/core/modules/file/filesystem-file.helper.js.map +1 -1
- package/dist/core/modules/file/s3-file.helper.d.ts +1 -1
- package/dist/core/modules/file/s3-file.helper.js +5 -5
- package/dist/core/modules/file/s3-file.helper.js.map +1 -1
- package/dist/core/modules/health-check/core-health-check.module.js +12 -2
- package/dist/core/modules/health-check/core-health-check.module.js.map +1 -1
- package/dist/core/modules/health-check/core-health-check.service.d.ts +4 -3
- package/dist/core/modules/health-check/core-health-check.service.js +26 -5
- package/dist/core/modules/health-check/core-health-check.service.js.map +1 -1
- package/dist/core/modules/hub/core-hub.controller.d.ts +1 -0
- package/dist/core/modules/hub/core-hub.controller.js +10 -0
- package/dist/core/modules/hub/core-hub.controller.js.map +1 -1
- package/dist/core/modules/hub/core-hub.service.js +1 -1
- package/dist/core/modules/hub/core-hub.service.js.map +1 -1
- package/dist/core/modules/hub/helpers/hub-client-js.helper.js +23 -0
- package/dist/core/modules/hub/helpers/hub-client-js.helper.js.map +1 -1
- package/dist/core/modules/hub/hub-action-messages.d.ts +2 -0
- package/dist/core/modules/hub/hub-action-messages.js +2 -0
- package/dist/core/modules/hub/hub-action-messages.js.map +1 -1
- package/dist/core/modules/hub/hub-nav.js +1 -0
- package/dist/core/modules/hub/hub-nav.js.map +1 -1
- package/dist/core/modules/hub/interfaces/hub-panels.interface.d.ts +17 -1
- package/dist/core/modules/hub/services/core-hub-db.service.d.ts +6 -1
- package/dist/core/modules/hub/services/core-hub-db.service.js +16 -14
- package/dist/core/modules/hub/services/core-hub-db.service.js.map +1 -1
- package/dist/core/modules/hub/services/core-hub-migrations.service.d.ts +5 -0
- package/dist/core/modules/hub/services/core-hub-migrations.service.js +74 -1
- package/dist/core/modules/hub/services/core-hub-migrations.service.js.map +1 -1
- package/dist/core/modules/hub/services/core-hub-sources.service.d.ts +2 -1
- package/dist/core/modules/hub/services/core-hub-sources.service.js +32 -0
- package/dist/core/modules/hub/services/core-hub-sources.service.js.map +1 -1
- package/dist/core/modules/hub/services/hub-query-profiler.service.js +1 -1
- package/dist/core/modules/hub/services/hub-query-profiler.service.js.map +1 -1
- package/dist/core/modules/migrate/cli/migrate-cli.d.ts +2 -0
- package/dist/core/modules/migrate/cli/migrate-cli.js +35 -1
- package/dist/core/modules/migrate/cli/migrate-cli.js.map +1 -1
- package/dist/core/modules/migrate/index.d.ts +3 -0
- package/dist/core/modules/migrate/index.js +3 -0
- package/dist/core/modules/migrate/index.js.map +1 -1
- package/dist/core/modules/migrate/migration-runner.d.ts +2 -2
- package/dist/core/modules/migrate/migration-runner.js.map +1 -1
- package/dist/core/modules/migrate/migration-state-store.interface.d.ts +16 -0
- package/dist/core/modules/migrate/migration-state-store.interface.js +2 -0
- package/dist/core/modules/migrate/migration-state-store.interface.js.map +1 -0
- package/dist/core/modules/migrate/migration.helper.d.ts +3 -2
- package/dist/core/modules/migrate/migration.helper.js +21 -6
- package/dist/core/modules/migrate/migration.helper.js.map +1 -1
- package/dist/core/modules/migrate/mongo-state-store.d.ts +5 -12
- package/dist/core/modules/migrate/mongo-state-store.js +30 -24
- package/dist/core/modules/migrate/mongo-state-store.js.map +1 -1
- package/dist/core/modules/migrate/sql-migration.helper.d.ts +4 -0
- package/dist/core/modules/migrate/sql-migration.helper.js +34 -0
- package/dist/core/modules/migrate/sql-migration.helper.js.map +1 -0
- package/dist/core/modules/migrate/sql-state-store.d.ts +24 -0
- package/dist/core/modules/migrate/sql-state-store.js +129 -0
- package/dist/core/modules/migrate/sql-state-store.js.map +1 -0
- package/dist/core/modules/system-setup/core-system-setup.service.d.ts +8 -0
- package/dist/core/modules/system-setup/core-system-setup.service.js +41 -25
- package/dist/core/modules/system-setup/core-system-setup.service.js.map +1 -1
- package/dist/core/modules/tenant/core-tenant-member.model.js +1 -1
- package/dist/core/modules/tenant/core-tenant-member.model.js.map +1 -1
- package/dist/core/modules/tenant/core-tenant-member.store.d.ts +2 -0
- package/dist/core/modules/tenant/core-tenant-member.store.js +6 -0
- package/dist/core/modules/tenant/core-tenant-member.store.js.map +1 -0
- package/dist/core/modules/tenant/core-tenant.guard.d.ts +8 -3
- package/dist/core/modules/tenant/core-tenant.guard.js +57 -28
- package/dist/core/modules/tenant/core-tenant.guard.js.map +1 -1
- package/dist/core/modules/tenant/core-tenant.module.js +3 -2
- package/dist/core/modules/tenant/core-tenant.module.js.map +1 -1
- package/dist/core/modules/tenant/core-tenant.service.d.ts +3 -1
- package/dist/core/modules/tenant/core-tenant.service.js +48 -28
- package/dist/core/modules/tenant/core-tenant.service.js.map +1 -1
- package/dist/core/modules/tus/core-tus.service.d.ts +1 -1
- package/dist/core/modules/tus/core-tus.service.js +8 -7
- package/dist/core/modules/tus/core-tus.service.js.map +1 -1
- package/dist/core/modules/tus/tus.module.js +1 -1
- package/dist/core/modules/tus/tus.module.js.map +1 -1
- package/dist/core/modules/user/core-user.model.d.ts +1 -1
- package/dist/core/modules/user/core-user.model.js +1 -1
- package/dist/core/modules/user/core-user.model.js.map +1 -1
- package/dist/core/modules/user/core-user.service.d.ts +3 -1
- package/dist/core/modules/user/core-user.service.js +71 -16
- package/dist/core/modules/user/core-user.service.js.map +1 -1
- package/dist/core.module.js +39 -5
- package/dist/core.module.js.map +1 -1
- package/dist/index.d.ts +26 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -1
- package/dist/server/common/models/persistence.model.d.ts +1 -1
- package/dist/server/common/models/persistence.model.js +3 -3
- package/dist/server/common/models/persistence.model.js.map +1 -1
- package/dist/server/modules/file/file-info.model.d.ts +1 -77
- package/dist/server/modules/file/file-info.model.js +1 -1
- package/dist/server/modules/file/file-info.model.js.map +1 -1
- package/dist/server/modules/file/file.service.js +1 -1
- package/dist/server/modules/file/file.service.js.map +1 -1
- package/dist/server/modules/user/user.model.d.ts +2 -186
- package/dist/server/modules/user/user.model.js +4 -5
- package/dist/server/modules/user/user.model.js.map +1 -1
- package/dist/server/modules/user/user.module.js +4 -3
- package/dist/server/modules/user/user.module.js.map +1 -1
- package/dist/server/modules/user/user.service.d.ts +1 -1
- package/dist/server/modules/user/user.service.js +5 -8
- package/dist/server/modules/user/user.service.js.map +1 -1
- package/dist/test/test.helper.js +2 -2
- package/dist/test/test.helper.js.map +1 -1
- package/dist/types/graphql-upload.d.ts +10 -7
- package/docs/REQUEST-LIFECYCLE.md +6 -3
- package/docs/choosing-a-database.md +107 -0
- package/docs/native-driver-security.md +29 -0
- package/docs/security-overrides.md +15 -6
- package/migration-guides/12.0.x-to-12.1.x.md +255 -0
- package/package.json +75 -29
- package/src/core/common/decorators/unified-field.decorator.ts +11 -1
- package/src/core/common/filters/mongoose-cast-error.filter.ts +12 -6
- package/src/core/common/helpers/db.helper.ts +40 -25
- package/src/core/common/helpers/decorator.helper.ts +1 -1
- package/src/core/common/helpers/filter.helper.ts +2 -1
- package/src/core/common/helpers/gridfs.helper.ts +28 -9
- package/src/core/common/helpers/id.helper.ts +21 -4
- package/src/core/common/helpers/model.helper.ts +5 -6
- package/src/core/common/helpers/service.helper.ts +3 -3
- package/src/core/common/interfaces/server-options.interface.ts +118 -10
- package/src/core/common/interfaces/service-options.interface.ts +2 -2
- package/src/core/common/models/core-persistence.model.ts +3 -3
- package/src/core/common/persistence/core-persistence.lifecycle.ts +47 -0
- package/src/core/common/persistence/core-persistence.module.ts +109 -0
- package/src/core/common/persistence/data-source.config.ts +271 -0
- package/src/core/common/persistence/data-source.runtime.ts +351 -0
- package/src/core/common/persistence/data-store.interface.ts +155 -0
- package/src/core/common/persistence/expiry-sweep.ts +105 -0
- package/src/core/common/persistence/id-format.ts +43 -0
- package/src/core/common/persistence/infrastructure-locks.ts +66 -0
- package/src/core/common/persistence/inject-store.decorator.ts +16 -0
- package/src/core/common/persistence/lock-store.interface.ts +17 -0
- package/src/core/common/persistence/model-descriptor.interface.ts +90 -0
- package/src/core/common/persistence/model-metadata.registry.ts +285 -0
- package/src/core/common/persistence/mongo/collection-name.ts +87 -0
- package/src/core/common/persistence/mongo/declared-schema.view.ts +378 -0
- package/src/core/common/persistence/mongo/hex-object-id.ts +75 -0
- package/src/core/common/persistence/mongo/model-declarations.ts +135 -0
- package/src/core/common/persistence/mongo/mongo-packages.ts +130 -0
- package/src/core/common/persistence/mongo/mongoose-compat.ts +129 -0
- package/src/core/common/persistence/mongo/no-mongo-connection.module.ts +18 -0
- package/src/core/common/persistence/mongo-lock-store.ts +121 -0
- package/src/core/common/persistence/mongoose-data-store.ts +232 -0
- package/src/core/common/persistence/neutral-populate.ts +98 -0
- package/src/core/common/persistence/persisted-model.decorator.ts +61 -0
- package/src/core/common/persistence/persistence-runtime.registry.ts +152 -0
- package/src/core/common/persistence/persistence.tokens.ts +9 -0
- package/src/core/common/persistence/policies/audit.policy.ts +80 -0
- package/src/core/common/persistence/policies/password.policy.ts +69 -0
- package/src/core/common/persistence/policies/role-guard.policy.ts +74 -0
- package/src/core/common/persistence/policies/system-role.policy.ts +90 -0
- package/src/core/common/persistence/policies/tenant.policy.ts +245 -0
- package/src/core/common/persistence/routed-model.guard.ts +107 -0
- package/src/core/common/persistence/sql/mysql/mysql-data-store.ts +503 -0
- package/src/core/common/persistence/sql/mysql/mysql-executor.ts +281 -0
- package/src/core/common/persistence/sql/mysql/mysql-lock-store.ts +87 -0
- package/src/core/common/persistence/sql/mysql/mysql-schema.compiler.ts +424 -0
- package/src/core/common/persistence/sql/mysql/mysql-schema.migrator.ts +453 -0
- package/src/core/common/persistence/sql/mysql/mysql-session.ts +131 -0
- package/src/core/common/persistence/sql/mysql/mysql-update.compiler.ts +379 -0
- package/src/core/common/persistence/sql/mysql/mysql-where.compiler.ts +686 -0
- package/src/core/common/persistence/sql/postgres/postgres-lock-store.ts +76 -0
- package/src/core/common/persistence/sql/postgres/postgres-schema.compiler.ts +328 -0
- package/src/core/common/persistence/sql/postgres/postgres-schema.migrator.ts +448 -0
- package/src/core/common/persistence/sql/postgres/postgres-update.compiler.ts +310 -0
- package/src/core/common/persistence/sql/postgres/postgres-where.compiler.ts +638 -0
- package/src/core/common/persistence/sql/sql-cast.ts +227 -0
- package/src/core/common/persistence/sql/sql-data-store.ts +762 -0
- package/src/core/common/persistence/sql/sql-dialect.ts +85 -0
- package/src/core/common/persistence/sql/sql-errors.ts +49 -0
- package/src/core/common/persistence/sql/sql-executor.factory.ts +282 -0
- package/src/core/common/persistence/sql/sql-executor.interface.ts +57 -0
- package/src/core/common/persistence/sql/sql-naming.ts +133 -0
- package/src/core/common/persistence/sql/sql-schema.shared.ts +105 -0
- package/src/core/common/persistence/sql/sql-session.ts +158 -0
- package/src/core/common/persistence/sql/where-shared.ts +118 -0
- package/src/core/common/persistence/store-guards.ts +73 -0
- package/src/core/common/persistence/store-upsert.ts +36 -0
- package/src/core/common/persistence/unavailable-store.ts +63 -0
- package/src/core/common/persistence/unsupported-capability.error.ts +7 -0
- package/src/core/common/plugins/mongoose-audit-fields.plugin.ts +33 -74
- package/src/core/common/plugins/mongoose-id.plugin.ts +9 -0
- package/src/core/common/plugins/mongoose-password.plugin.ts +6 -62
- package/src/core/common/plugins/mongoose-role-guard.plugin.ts +11 -63
- package/src/core/common/plugins/mongoose-system-role.plugin.ts +7 -78
- package/src/core/common/plugins/mongoose-tenant.plugin.ts +23 -144
- package/src/core/common/scalars/upload.scalar.ts +8 -3
- package/src/core/common/services/core-cron-jobs.initializer.ts +1 -1
- package/src/core/common/services/core-cron-jobs.service.ts +33 -22
- package/src/core/common/services/crud.service.ts +166 -35
- package/src/core/common/services/model-doc.service.ts +1 -1
- package/src/core/common/services/module.service.ts +78 -6
- package/src/core/common/types/field-selection.type.ts +1 -1
- package/src/core/common/types/id.type.ts +2 -2
- package/src/core/common/types/object-id-like.type.ts +11 -0
- package/src/core/common/types/populate-config.type.ts +1 -1
- package/src/core/common/types/string-or-object-id.type.ts +2 -2
- package/src/core/modules/ai/README.md +15 -2
- package/src/core/modules/ai/core-ai.constants.ts +10 -0
- package/src/core/modules/ai/core-ai.module.ts +32 -17
- package/src/core/modules/ai/models/core-ai-budget-limit.model.ts +2 -2
- package/src/core/modules/ai/models/core-ai-connection-preference.model.ts +2 -2
- package/src/core/modules/ai/models/core-ai-connection.model.ts +2 -2
- package/src/core/modules/ai/models/core-ai-conversation.model.ts +8 -4
- package/src/core/modules/ai/models/core-ai-interaction.model.ts +3 -3
- package/src/core/modules/ai/models/core-ai-mcp-oauth.models.ts +46 -0
- package/src/core/modules/ai/models/core-ai-mode.model.ts +2 -2
- package/src/core/modules/ai/models/core-ai-prompt-hint.model.ts +2 -2
- package/src/core/modules/ai/models/core-ai-prompt.model.ts +2 -2
- package/src/core/modules/ai/models/core-ai-slot.model.ts +2 -2
- package/src/core/modules/ai/models/core-ai-tool-grant.model.ts +2 -2
- package/src/core/modules/ai/models/core-ai-tool-policy.model.ts +2 -2
- package/src/core/modules/ai/services/core-ai-budget.service.ts +22 -4
- package/src/core/modules/ai/services/core-ai-connection-preference.service.ts +6 -13
- package/src/core/modules/ai/services/core-ai-connection.service.ts +34 -44
- package/src/core/modules/ai/services/core-ai-conversation.service.ts +31 -29
- package/src/core/modules/ai/services/core-ai-interaction.service.ts +3 -3
- package/src/core/modules/ai/services/core-ai-mcp-oauth.service.ts +93 -20
- package/src/core/modules/ai/services/core-ai-mode.service.ts +3 -6
- package/src/core/modules/ai/services/core-ai-prompt-hint.service.ts +43 -26
- package/src/core/modules/ai/services/core-ai-prompt.service.ts +15 -12
- package/src/core/modules/ai/services/core-ai-slot.service.ts +20 -24
- package/src/core/modules/ai/services/core-ai-tool-grant.service.ts +17 -27
- package/src/core/modules/ai/services/core-ai-tool-policy.service.ts +7 -10
- package/src/core/modules/api-token/core-api-token.model.ts +1 -1
- package/src/core/modules/api-token/core-api-token.module.ts +4 -2
- package/src/core/modules/api-token/core-api-token.service.ts +118 -43
- package/src/core/modules/auth/core-auth.module.ts +2 -0
- package/src/core/modules/auth/guards/legacy-auth-rate-limit.guard.ts +4 -3
- package/src/core/modules/auth/guards/role-guard.token.ts +7 -0
- package/src/core/modules/auth/services/core-auth.service.ts +16 -2
- package/src/core/modules/better-auth/INTEGRATION-CHECKLIST.md +1 -1
- package/src/core/modules/better-auth/README.md +10 -1
- package/src/core/modules/better-auth/better-auth-sql.helper.ts +191 -0
- package/src/core/modules/better-auth/better-auth-token.service.ts +26 -35
- package/src/core/modules/better-auth/better-auth.config.ts +43 -7
- package/src/core/modules/better-auth/better-auth.types.ts +1 -1
- package/src/core/modules/better-auth/core-better-auth-api.middleware.ts +13 -1
- package/src/core/modules/better-auth/core-better-auth-challenge.service.ts +36 -18
- package/src/core/modules/better-auth/core-better-auth-mongo.users.ts +35 -0
- package/src/core/modules/better-auth/core-better-auth-sql.registry.ts +20 -0
- package/src/core/modules/better-auth/core-better-auth-sql.store.ts +309 -0
- package/src/core/modules/better-auth/core-better-auth-user.mapper.ts +418 -40
- package/src/core/modules/better-auth/core-better-auth.module.ts +71 -35
- package/src/core/modules/better-auth/core-better-auth.service.ts +81 -12
- package/src/core/modules/file/README.md +16 -3
- package/src/core/modules/file/core-file-info.model.ts +2 -2
- package/src/core/modules/file/core-file.service.ts +27 -18
- package/src/core/modules/file/file-metadata.helper.ts +7 -3
- package/src/core/modules/file/file-metadata.models.ts +44 -0
- package/src/core/modules/file/file-metadata.store.ts +109 -0
- package/src/core/modules/file/file-storage.helper.ts +47 -2
- package/src/core/modules/file/filesystem-file.helper.ts +11 -8
- package/src/core/modules/file/s3-file.helper.ts +11 -7
- package/src/core/modules/health-check/core-health-check.module.ts +16 -2
- package/src/core/modules/health-check/core-health-check.service.ts +25 -3
- package/src/core/modules/hub/README.md +20 -11
- package/src/core/modules/hub/core-hub.controller.ts +5 -0
- package/src/core/modules/hub/core-hub.service.ts +1 -1
- package/src/core/modules/hub/helpers/hub-client-js.helper.ts +23 -0
- package/src/core/modules/hub/hub-action-messages.ts +6 -0
- package/src/core/modules/hub/hub-nav.ts +1 -0
- package/src/core/modules/hub/interfaces/hub-panels.interface.ts +8 -1
- package/src/core/modules/hub/services/core-hub-db.service.ts +35 -17
- package/src/core/modules/hub/services/core-hub-migrations.service.ts +96 -2
- package/src/core/modules/hub/services/core-hub-sources.service.ts +47 -1
- package/src/core/modules/hub/services/hub-query-profiler.service.ts +1 -1
- package/src/core/modules/migrate/README.md +54 -0
- package/src/core/modules/migrate/cli/migrate-cli.ts +44 -3
- package/src/core/modules/migrate/index.ts +3 -0
- package/src/core/modules/migrate/migration-runner.ts +3 -2
- package/src/core/modules/migrate/migration-state-store.interface.ts +24 -0
- package/src/core/modules/migrate/migration.helper.ts +35 -10
- package/src/core/modules/migrate/mongo-state-store.ts +64 -65
- package/src/core/modules/migrate/sql-migration.helper.ts +48 -0
- package/src/core/modules/migrate/sql-state-store.ts +198 -0
- package/src/core/modules/system-setup/README.md +14 -2
- package/src/core/modules/system-setup/core-system-setup.service.ts +77 -45
- package/src/core/modules/tenant/README.md +9 -2
- package/src/core/modules/tenant/core-tenant-member.model.ts +1 -1
- package/src/core/modules/tenant/core-tenant-member.store.ts +13 -0
- package/src/core/modules/tenant/core-tenant.guard.ts +77 -24
- package/src/core/modules/tenant/core-tenant.module.ts +6 -3
- package/src/core/modules/tenant/core-tenant.service.ts +59 -42
- package/src/core/modules/tus/core-tus.service.ts +11 -12
- package/src/core/modules/tus/tus.module.ts +1 -1
- package/src/core/modules/user/core-user.model.ts +2 -2
- package/src/core/modules/user/core-user.service.ts +98 -20
- package/src/core.module.ts +60 -5
- package/src/index.ts +26 -0
- package/src/server/common/models/persistence.model.ts +4 -4
- package/src/server/modules/file/file-info.model.ts +1 -1
- package/src/server/modules/file/file.service.ts +1 -1
- package/src/server/modules/user/user.model.ts +10 -5
- package/src/server/modules/user/user.module.ts +9 -3
- package/src/server/modules/user/user.service.ts +9 -11
- package/src/test/test.helper.ts +2 -2
- package/src/types/graphql-upload.d.ts +10 -7
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
import { Logger } from '@nestjs/common';
|
|
2
|
+
|
|
3
|
+
import { ConfigService } from '../services/config.service.js';
|
|
4
|
+
import { RequestContext } from '../services/request-context.service.js';
|
|
5
|
+
import type { DataStore } from './data-store.interface.js';
|
|
6
|
+
import { expiredRecordsFilter } from './expiry-sweep.js';
|
|
7
|
+
import type { LockStore } from './lock-store.interface.js';
|
|
8
|
+
import {
|
|
9
|
+
redactUrl,
|
|
10
|
+
type ResolvedDataSource,
|
|
11
|
+
type ResolvedDataSources,
|
|
12
|
+
resolveModelSource,
|
|
13
|
+
} from './data-source.config.js';
|
|
14
|
+
import type { ModelDescriptor } from './model-descriptor.interface.js';
|
|
15
|
+
import { getModelDescriptor } from './model-metadata.registry.js';
|
|
16
|
+
import { MongooseDataStore } from './mongoose-data-store.js';
|
|
17
|
+
import {
|
|
18
|
+
getPersistedModels,
|
|
19
|
+
type IamSqlStorage,
|
|
20
|
+
type PersistenceOverview,
|
|
21
|
+
type PersistenceRuntime,
|
|
22
|
+
} from './persistence-runtime.registry.js';
|
|
23
|
+
import { grantStatement } from './sql/postgres/postgres-schema.compiler.js';
|
|
24
|
+
import { assertSafeApplicationRole, SCHEMA_LOCK_SQL } from './sql/postgres/postgres-schema.migrator.js';
|
|
25
|
+
import { type SqlDialect, sqlDialect } from './sql/sql-dialect.js';
|
|
26
|
+
import { createSqlExecutor } from './sql/sql-executor.factory.js';
|
|
27
|
+
import type { SqlExecutor } from './sql/sql-executor.interface.js';
|
|
28
|
+
import { LOCK_MODEL, type SchemaChange, tableName } from './sql/sql-schema.shared.js';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Routes every model registered with `CorePersistenceModule.forFeature()` to its data source (plan §3.9: the
|
|
32
|
+
* configuration, then `@PersistedModel`, then the default). A SQL source is connected once, as the application role;
|
|
33
|
+
* each model's table is brought up to date when its store is created — as the owner, or only verified without
|
|
34
|
+
* `ownerUrl` — and the application role is checked against it: a role row-level security cannot bind stops the boot.
|
|
35
|
+
* Per model, not per registry: the registry is process-wide, the stores Nest creates belong to THIS application.
|
|
36
|
+
* What differs between PostgreSQL and MySQL / MariaDB is the source's `SqlDialect`.
|
|
37
|
+
* Installed by `CoreModule.forRoot()` only when a SQL source is configured.
|
|
38
|
+
*/
|
|
39
|
+
export class DataSourceRuntime implements PersistenceRuntime {
|
|
40
|
+
protected readonly logger = new Logger('DataSources');
|
|
41
|
+
private bootReleased = false;
|
|
42
|
+
private readonly dialects = new Map<string, SqlDialect>();
|
|
43
|
+
private readonly executors = new Map<string, Promise<SqlExecutor>>();
|
|
44
|
+
/** Sources whose application-only tenant isolation was reported */
|
|
45
|
+
private readonly isolationReported = new Set<string>();
|
|
46
|
+
private lockStore?: Promise<LockStore | undefined>;
|
|
47
|
+
private readonly owners = new Map<string, Promise<SqlExecutor>>();
|
|
48
|
+
private readonly stores = new Map<string, Promise<DataStore<any>>>();
|
|
49
|
+
|
|
50
|
+
constructor(protected readonly resolved: ResolvedDataSources) {}
|
|
51
|
+
|
|
52
|
+
async close(): Promise<void> {
|
|
53
|
+
const executors = [...this.executors.values()];
|
|
54
|
+
this.executors.clear();
|
|
55
|
+
this.lockStore = undefined;
|
|
56
|
+
this.stores.clear();
|
|
57
|
+
await Promise.all([this.releaseBootConnections(), ...executors.map(closeExecutor)]);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The framework's own leases (system-setup claim, cron, migrations) live in the default source. Opened once — as the
|
|
62
|
+
* owner it brings the lease table up to date, which a model of the default source would otherwise have done
|
|
63
|
+
*/
|
|
64
|
+
defaultLockStore(): Promise<LockStore | undefined> {
|
|
65
|
+
this.lockStore ??= this.openLockStore();
|
|
66
|
+
return this.lockStore;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async releaseBootConnections(): Promise<void> {
|
|
70
|
+
this.bootReleased = true;
|
|
71
|
+
const owners = [...this.owners.values()];
|
|
72
|
+
this.owners.clear();
|
|
73
|
+
await Promise.all(owners.map(closeExecutor));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async resolveStore(name: string, mongoModel: unknown): Promise<DataStore<any>> {
|
|
77
|
+
const source = this.sourceOf(name);
|
|
78
|
+
if (!source || source.driver === 'mongodb') {
|
|
79
|
+
return new MongooseDataStore(mongoModel as any);
|
|
80
|
+
}
|
|
81
|
+
let store = this.stores.get(name);
|
|
82
|
+
if (!store) {
|
|
83
|
+
store = this.openStore(source, name);
|
|
84
|
+
this.stores.set(name, store);
|
|
85
|
+
}
|
|
86
|
+
return store;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
defaultSqlSource(): undefined | { engine?: string; ownerUrl?: string; url: string } {
|
|
90
|
+
const name = this.resolved.defaultSource;
|
|
91
|
+
const source = name ? this.resolved.sources[name] : undefined;
|
|
92
|
+
return source && source.driver !== 'mongodb'
|
|
93
|
+
? { engine: source.sqlEngine, ownerUrl: source.ownerUrl, url: source.url }
|
|
94
|
+
: undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
describe(): PersistenceOverview {
|
|
98
|
+
return {
|
|
99
|
+
defaultSource: this.resolved.defaultSource,
|
|
100
|
+
models: getPersistedModels().map((model) => ({
|
|
101
|
+
name: model.name,
|
|
102
|
+
source: resolveModelSource(this.resolved, model.name, model.declaredSource),
|
|
103
|
+
})),
|
|
104
|
+
sources: Object.values(this.resolved.sources).map((source) => ({
|
|
105
|
+
driver: source.driver,
|
|
106
|
+
engine: source.sqlEngine,
|
|
107
|
+
host: redactUrl(source.url),
|
|
108
|
+
name: source.name,
|
|
109
|
+
ownerConfigured: !!source.ownerUrl,
|
|
110
|
+
})),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async iamStorage(): Promise<IamSqlStorage | undefined> {
|
|
115
|
+
const registration = getPersistedModels().find((model) => {
|
|
116
|
+
const descriptor = getModelDescriptor(model.name);
|
|
117
|
+
return !!descriptor && tableName(descriptor) === USERS_TABLE;
|
|
118
|
+
});
|
|
119
|
+
const source = registration ? this.sourceOf(registration.name) : undefined;
|
|
120
|
+
if (!registration || !source || source.driver === 'mongodb') {
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
if (source.driver !== 'postgres') {
|
|
124
|
+
throw new Error(
|
|
125
|
+
`Better-Auth cannot run on ${this.dialect(source).label} yet: the user model (table ${USERS_TABLE}) is routed to ` +
|
|
126
|
+
`the source "${source.name}". Keep the user model on PostgreSQL or MongoDB (database.models), or switch Better-Auth off.`,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
// Opening the store brings `users` up to date first: Better-Auth only adds its columns to the table the user model
|
|
130
|
+
// defines, never the other way round
|
|
131
|
+
const users = await this.resolveStore(registration.name, undefined);
|
|
132
|
+
const app = await this.executor(source);
|
|
133
|
+
return {
|
|
134
|
+
applySchema: (migrate, tables) => this.applyForeignSchema(source, app, migrate, tables),
|
|
135
|
+
pool: app.driverPool,
|
|
136
|
+
source: source.name,
|
|
137
|
+
users,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async sweepExpired(now = new Date()): Promise<{ deleted: number; model: string }[]> {
|
|
142
|
+
const swept: { deleted: number; model: string }[] = [];
|
|
143
|
+
const failed: string[] = [];
|
|
144
|
+
for (const [name, pending] of this.stores) {
|
|
145
|
+
const descriptor = getModelDescriptor(name);
|
|
146
|
+
const expired = descriptor ? expiredRecordsFilter(descriptor, now) : undefined;
|
|
147
|
+
if (!expired) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
const store = await pending;
|
|
152
|
+
// System work across every tenant, as the TTL monitor is: row-level security admits the system scope
|
|
153
|
+
const deleted = await RequestContext.runWithBypassTenantGuard(() => store.deleteMany(expired as any));
|
|
154
|
+
swept.push({ deleted, model: name });
|
|
155
|
+
} catch (error) {
|
|
156
|
+
// One table that cannot be swept must not keep the others full
|
|
157
|
+
failed.push(`${name}: ${(error as Error)?.message ?? error}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (failed.length) {
|
|
161
|
+
throw new Error(failed.join('; '));
|
|
162
|
+
}
|
|
163
|
+
return swept;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
sqlSources(): { name: string; ping: () => Promise<void> }[] {
|
|
167
|
+
return Object.values(this.resolved.sources)
|
|
168
|
+
.filter((source) => source.driver !== 'mongodb')
|
|
169
|
+
.map((source) => ({
|
|
170
|
+
name: source.name,
|
|
171
|
+
ping: async () => {
|
|
172
|
+
await (await this.executor(source)).run({ params: [], text: 'SELECT 1' }, { kind: 'system' });
|
|
173
|
+
},
|
|
174
|
+
}));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Whether a model's rows are tenant-isolated: tenant-scoped, multi-tenancy on, not excluded. On PostgreSQL the table
|
|
179
|
+
* carries row-level security; MySQL / MariaDB have none, and the store alone isolates
|
|
180
|
+
*/
|
|
181
|
+
isTenantIsolated(model: ModelDescriptor): boolean {
|
|
182
|
+
const multiTenancy = ConfigService.configFastButReadOnly?.multiTenancy;
|
|
183
|
+
return (
|
|
184
|
+
!!multiTenancy &&
|
|
185
|
+
multiTenancy.enabled !== false &&
|
|
186
|
+
model.tenantScoped &&
|
|
187
|
+
!multiTenancy.excludeSchemas?.includes(model.name)
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
protected sourceOf(name: string): ResolvedDataSource | undefined {
|
|
192
|
+
const registration = getPersistedModels().find((model) => model.name === name);
|
|
193
|
+
const source = resolveModelSource(this.resolved, name, registration?.declaredSource);
|
|
194
|
+
return source ? this.resolved.sources[source] : undefined;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private async openStore(source: ResolvedDataSource, name: string): Promise<DataStore<any>> {
|
|
198
|
+
const model = getModelDescriptor(name);
|
|
199
|
+
if (!model) {
|
|
200
|
+
throw new Error(`No descriptor for model "${name}" — register it with CorePersistenceModule.forFeature()`);
|
|
201
|
+
}
|
|
202
|
+
const app = await this.executor(source);
|
|
203
|
+
const dialect = this.dialect(source);
|
|
204
|
+
let changes: SchemaChange[];
|
|
205
|
+
if (source.ownerUrl) {
|
|
206
|
+
changes = await dialect.applySchema(await this.owner(source), app, [model]);
|
|
207
|
+
} else {
|
|
208
|
+
changes = await dialect.verifySchema(app, [model]);
|
|
209
|
+
}
|
|
210
|
+
for (const change of changes) {
|
|
211
|
+
if (change.kind === 'automatic') {
|
|
212
|
+
this.logger.log(`[${source.name}] ${change.description}`);
|
|
213
|
+
} else if (change.kind === 'notice') {
|
|
214
|
+
this.logger.warn(`[${source.name}] ${change.description}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
await dialect.assertSafeApplication(app, [tableName(model), tableName(LOCK_MODEL)]);
|
|
218
|
+
const isolated = this.isTenantIsolated(model);
|
|
219
|
+
if (isolated && source.driver !== 'postgres' && !this.isolationReported.has(source.name)) {
|
|
220
|
+
this.isolationReported.add(source.name);
|
|
221
|
+
this.logger.warn(
|
|
222
|
+
`[${source.name}] ${dialect.label} has no row-level security: tenant isolation of ${source.name} is enforced by ` +
|
|
223
|
+
'the application only. A query on a tenant table through store.native(), store.raw() or a connection of your ' +
|
|
224
|
+
'own must filter by tenantId itself — nothing in the database stops it.',
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
this.logger.log(
|
|
228
|
+
`${name} → ${source.name} → ${source.driver} ${redactUrl(source.url)} → ${source.sqlEngine}${dialect.isolation(isolated)}`,
|
|
229
|
+
);
|
|
230
|
+
const registration = getPersistedModels().find((entry) => entry.name === name);
|
|
231
|
+
return dialect.createStore(app, model, { schema: registration?.schema as any });
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
private async openLockStore(): Promise<LockStore | undefined> {
|
|
235
|
+
const name = this.resolved.defaultSource;
|
|
236
|
+
const source = name ? this.resolved.sources[name] : undefined;
|
|
237
|
+
if (!source || source.driver === 'mongodb') {
|
|
238
|
+
return undefined;
|
|
239
|
+
}
|
|
240
|
+
const app = await this.executor(source);
|
|
241
|
+
const dialect = this.dialect(source);
|
|
242
|
+
if (!source.ownerUrl) {
|
|
243
|
+
await dialect.verifySchema(app, []);
|
|
244
|
+
} else if (this.bootReleased) {
|
|
245
|
+
// First asked after the boot released its owner connections: a connection of its own, closed at once
|
|
246
|
+
const owner = await createSqlExecutor({
|
|
247
|
+
applicationName: 'nest-server-schema',
|
|
248
|
+
engine: source.sqlEngine,
|
|
249
|
+
maxConnections: 1,
|
|
250
|
+
url: source.ownerUrl,
|
|
251
|
+
});
|
|
252
|
+
try {
|
|
253
|
+
await dialect.applySchema(owner, app, []);
|
|
254
|
+
} finally {
|
|
255
|
+
await owner.close();
|
|
256
|
+
}
|
|
257
|
+
} else {
|
|
258
|
+
await dialect.applySchema(await this.owner(source), app, []);
|
|
259
|
+
}
|
|
260
|
+
await dialect.assertSafeApplication(app, [tableName(LOCK_MODEL)]);
|
|
261
|
+
return dialect.createLockStore(app);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Tables another library creates in a SQL source (Better-Auth's): its migration runs as the owner under the schema lock
|
|
266
|
+
* the framework's own evolution takes, so replicas booting together apply it once; the application role is granted
|
|
267
|
+
* and checked as on the framework's tables. Two owner connections: one holds the lock, the library migrates on the
|
|
268
|
+
* other — it brings its own client and cannot run inside the locking transaction.
|
|
269
|
+
*/
|
|
270
|
+
private async applyForeignSchema(
|
|
271
|
+
source: ResolvedDataSource,
|
|
272
|
+
app: SqlExecutor,
|
|
273
|
+
migrate: (pool: unknown, asOwner: boolean) => Promise<void>,
|
|
274
|
+
tables: string[],
|
|
275
|
+
): Promise<void> {
|
|
276
|
+
if (!source.ownerUrl) {
|
|
277
|
+
await migrate(app.driverPool, false);
|
|
278
|
+
} else {
|
|
279
|
+
const connect = (maxConnections: number) =>
|
|
280
|
+
createSqlExecutor({
|
|
281
|
+
applicationName: 'nest-server-schema',
|
|
282
|
+
engine: source.sqlEngine,
|
|
283
|
+
maxConnections,
|
|
284
|
+
url: source.ownerUrl,
|
|
285
|
+
});
|
|
286
|
+
const [lock, migrator] = await Promise.all([connect(1), connect(2)]);
|
|
287
|
+
const appRole = decodeURIComponent(new URL(source.url).username);
|
|
288
|
+
try {
|
|
289
|
+
await lock.transaction(async (tx) => {
|
|
290
|
+
await tx.run({ params: [], text: SCHEMA_LOCK_SQL }, SYSTEM);
|
|
291
|
+
await migrate(migrator.driverPool, true);
|
|
292
|
+
for (const table of tables) {
|
|
293
|
+
await tx.run({ params: [], text: grantStatement(table, appRole) }, SYSTEM);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
} finally {
|
|
297
|
+
await Promise.all([lock.close(), migrator.close()]);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
await assertSafeApplicationRole(app, tables);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
private dialect(source: ResolvedDataSource): SqlDialect {
|
|
304
|
+
let dialect = this.dialects.get(source.name);
|
|
305
|
+
if (!dialect) {
|
|
306
|
+
dialect = sqlDialect({
|
|
307
|
+
appRole: decodeURIComponent(new URL(source.url).username),
|
|
308
|
+
driver: source.driver as 'mariadb' | 'mysql' | 'postgres',
|
|
309
|
+
isTenantIsolated: (candidate) => this.isTenantIsolated(candidate),
|
|
310
|
+
});
|
|
311
|
+
this.dialects.set(source.name, dialect);
|
|
312
|
+
}
|
|
313
|
+
return dialect;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* One owner connection per source for the whole boot, released once every store exists: one per model opened and
|
|
318
|
+
* closed a privileged connection for every table, and the stores are created concurrently
|
|
319
|
+
*/
|
|
320
|
+
private owner(source: ResolvedDataSource): Promise<SqlExecutor> {
|
|
321
|
+
let pending = this.owners.get(source.name);
|
|
322
|
+
if (!pending) {
|
|
323
|
+
pending = createSqlExecutor({
|
|
324
|
+
applicationName: 'nest-server-schema',
|
|
325
|
+
engine: source.sqlEngine,
|
|
326
|
+
maxConnections: 1,
|
|
327
|
+
url: source.ownerUrl,
|
|
328
|
+
});
|
|
329
|
+
this.owners.set(source.name, pending);
|
|
330
|
+
}
|
|
331
|
+
return pending;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
private executor(source: ResolvedDataSource): Promise<SqlExecutor> {
|
|
335
|
+
let pending = this.executors.get(source.name);
|
|
336
|
+
if (!pending) {
|
|
337
|
+
pending = createSqlExecutor({ applicationName: 'nest-server', engine: source.sqlEngine, url: source.url });
|
|
338
|
+
this.executors.set(source.name, pending);
|
|
339
|
+
}
|
|
340
|
+
return pending;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/** The table the user model and Better-Auth share */
|
|
345
|
+
const USERS_TABLE = 'users';
|
|
346
|
+
|
|
347
|
+
const SYSTEM = { kind: 'system' } as const;
|
|
348
|
+
|
|
349
|
+
async function closeExecutor(pending: Promise<SqlExecutor>): Promise<void> {
|
|
350
|
+
await (await pending.catch(() => undefined))?.close();
|
|
351
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The data store port: the neutral operations every database implements.
|
|
3
|
+
*
|
|
4
|
+
* Import-free on purpose (types only), like every leaf the persistence layer and its DI tokens depend on.
|
|
5
|
+
*
|
|
6
|
+
* Records are plain objects with the id as a string under `id` (never `_id`, never a driver type). Filters
|
|
7
|
+
* and updates use the Mongo-operator subset the codebase already speaks: MongoDB executes them as they
|
|
8
|
+
* are, a SQL store compiles them and refuses what it cannot express — it never drops a condition.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Neutral filter: implicit equality, dot paths into embedded objects, and the operators
|
|
13
|
+
* `$eq $ne $gt $gte $lt $lte $in $nin $exists $regex/$options $and $or $nor $not $all $size $elemMatch`.
|
|
14
|
+
* The id is addressed as `id`.
|
|
15
|
+
*/
|
|
16
|
+
export type Where<T = Record<string, any>> = { [K in keyof T]?: any } & {
|
|
17
|
+
[path: string]: any;
|
|
18
|
+
$and?: Where<T>[];
|
|
19
|
+
$nor?: Where<T>[];
|
|
20
|
+
$or?: Where<T>[];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Neutral update: a partial record (equivalent to `$set`), or the operators `$set $unset $inc $push
|
|
25
|
+
* $pull $addToSet $setOnInsert`.
|
|
26
|
+
*/
|
|
27
|
+
export type DataStoreUpdate<T = Record<string, any>> =
|
|
28
|
+
| Partial<T>
|
|
29
|
+
| {
|
|
30
|
+
$addToSet?: Record<string, unknown>;
|
|
31
|
+
$inc?: Record<string, number>;
|
|
32
|
+
$pull?: Record<string, unknown>;
|
|
33
|
+
$push?: Record<string, unknown>;
|
|
34
|
+
$set?: Partial<T> & Record<string, unknown>;
|
|
35
|
+
$setOnInsert?: Partial<T> & Record<string, unknown>;
|
|
36
|
+
$unset?: Record<string, '' | 1 | true>;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/** A stored record: the model's fields plus its id as a string. */
|
|
40
|
+
export type StoredRecord<T = Record<string, any>> = T & { id: string };
|
|
41
|
+
|
|
42
|
+
export interface FindOptions {
|
|
43
|
+
/** Language-specific string comparison (MongoDB collation; mapped by SQL stores where supported) */
|
|
44
|
+
collation?: Record<string, unknown>;
|
|
45
|
+
limit?: number;
|
|
46
|
+
/**
|
|
47
|
+
* Return this many records chosen at random from the page — after filter, sort, skip and limit, as MongoDB's `$sample`
|
|
48
|
+
* stage follows them in `CrudService.findAndCount()`. A positive integer; a sample larger than the page returns the
|
|
49
|
+
* whole page. The order of the result is random too.
|
|
50
|
+
*/
|
|
51
|
+
sample?: number;
|
|
52
|
+
/**
|
|
53
|
+
* Field names to leave out (MongoDB's `-name` projection); everything else the model returns by default is read.
|
|
54
|
+
* Not combinable with `select`, as MongoDB does not combine them either.
|
|
55
|
+
*/
|
|
56
|
+
exclude?: readonly string[];
|
|
57
|
+
/** Field names to return; the id is always returned */
|
|
58
|
+
select?: readonly string[];
|
|
59
|
+
skip?: number;
|
|
60
|
+
/** Field → 1 (ascending) or -1 (descending), applied in declaration order */
|
|
61
|
+
sort?: Record<string, -1 | 1>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface UpdateOptions {
|
|
65
|
+
/**
|
|
66
|
+
* `false` leaves the update time (`updatedAt`) as it is — for bookkeeping writes such as a last-used stamp, which
|
|
67
|
+
* must not read as a change of the record (Mongoose's `timestamps: false`). @default true
|
|
68
|
+
*/
|
|
69
|
+
timestamps?: boolean;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface PushOptions {
|
|
73
|
+
/** Insert position (MongoDB `$position`) */
|
|
74
|
+
position?: number;
|
|
75
|
+
/** Keep only this many items after the push: negative = the last n, positive = the first n */
|
|
76
|
+
slice?: number;
|
|
77
|
+
/** Sort the array after the push */
|
|
78
|
+
sort?: Record<string, -1 | 1>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* What a store can do. Features ask the store, never the driver name; an unavailable capability fails at
|
|
83
|
+
* boot when it is configured and at the call with a named error otherwise.
|
|
84
|
+
*/
|
|
85
|
+
export interface StoreCapabilities {
|
|
86
|
+
/** `aggregate()` with a MongoDB pipeline */
|
|
87
|
+
aggregatePipeline: boolean;
|
|
88
|
+
/** Change streams / logical replication feed */
|
|
89
|
+
changeFeed: boolean;
|
|
90
|
+
/** GridFS file storage */
|
|
91
|
+
gridfs: boolean;
|
|
92
|
+
/** Mongoose documents and populate — the CrudService pipeline's MongoDB path */
|
|
93
|
+
mongoosePopulate: boolean;
|
|
94
|
+
/** A raw driver filter is passed through untouched */
|
|
95
|
+
rawFilterArm: boolean;
|
|
96
|
+
/** Row-level security enforced by the database */
|
|
97
|
+
rls: boolean;
|
|
98
|
+
/** Full-text search */
|
|
99
|
+
textSearch: boolean;
|
|
100
|
+
/** Multi-statement transactions */
|
|
101
|
+
transactions: boolean;
|
|
102
|
+
/** Format of generated ids */
|
|
103
|
+
idType: 'objectId';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface DataStore<T = Record<string, any>> {
|
|
107
|
+
readonly capabilities: StoreCapabilities;
|
|
108
|
+
/** The database behind the store (`mongodb`, `postgres`, …) */
|
|
109
|
+
readonly driver: string;
|
|
110
|
+
readonly modelName: string;
|
|
111
|
+
|
|
112
|
+
count(where?: Where<T>): Promise<number>;
|
|
113
|
+
deleteById(id: string): Promise<null | StoredRecord<T>>;
|
|
114
|
+
/** Remove every record matching `where`; returns how many were removed. */
|
|
115
|
+
deleteMany(where: Where<T>): Promise<number>;
|
|
116
|
+
/**
|
|
117
|
+
* Remove the first record matching `where`, atomically (MongoDB's `findOneAndDelete`), and return it; `null` when
|
|
118
|
+
* nothing matches.
|
|
119
|
+
*/
|
|
120
|
+
deleteOne(where: Where<T>): Promise<null | StoredRecord<T>>;
|
|
121
|
+
distinct(field: string, where?: Where<T>): Promise<unknown[]>;
|
|
122
|
+
find(where?: Where<T>, options?: FindOptions): Promise<StoredRecord<T>[]>;
|
|
123
|
+
findById(id: string, options?: Pick<FindOptions, 'exclude' | 'select'>): Promise<null | StoredRecord<T>>;
|
|
124
|
+
findOne(where: Where<T>, options?: Omit<FindOptions, 'limit' | 'skip'>): Promise<null | StoredRecord<T>>;
|
|
125
|
+
insert(record: Partial<T>): Promise<StoredRecord<T>>;
|
|
126
|
+
/**
|
|
127
|
+
* Tier 2 access: the database library's own API for this model (the Mongoose model on MongoDB), bound
|
|
128
|
+
* to the current unit of work. The reason is mandatory and logged.
|
|
129
|
+
*/
|
|
130
|
+
native<N = unknown>(reason: string): N;
|
|
131
|
+
/** Remove every array item matching `condition` from `field`, atomically. */
|
|
132
|
+
pullFromArray(id: string, field: string, condition: unknown): Promise<void>;
|
|
133
|
+
/** Append `items` to the array `field`, atomically. */
|
|
134
|
+
pushToArray(id: string, field: string, items: readonly unknown[], options?: PushOptions): Promise<void>;
|
|
135
|
+
/**
|
|
136
|
+
* Tier 3 access: the raw driver (the MongoDB collection on MongoDB). Bypasses every policy of the
|
|
137
|
+
* store; the reason is mandatory and logged as a security event.
|
|
138
|
+
*/
|
|
139
|
+
raw<N = unknown>(reason: string): N;
|
|
140
|
+
/**
|
|
141
|
+
* How many records match `where`, and the total of the numeric `field` over them (a missing value adds 0) — counted
|
|
142
|
+
* by the database, so only the result crosses the wire (MongoDB's `$group`, SQL's `count`/`sum`).
|
|
143
|
+
*/
|
|
144
|
+
sum(field: string, where?: Where<T>): Promise<{ count: number; sum: number }>;
|
|
145
|
+
/** Apply `update` to the record and return it as stored afterwards; `null` when there is none. */
|
|
146
|
+
updateById(id: string, update: DataStoreUpdate<T>, options?: UpdateOptions): Promise<null | StoredRecord<T>>;
|
|
147
|
+
/** Apply `update` to every record matching `where`; returns how many records matched. */
|
|
148
|
+
updateMany(where: Where<T>, update: DataStoreUpdate<T>): Promise<number>;
|
|
149
|
+
/**
|
|
150
|
+
* Apply `update` to the first record matching `where`, atomically: the record still matches when it is changed, and
|
|
151
|
+
* concurrent callers never change the same record on the strength of one match (MongoDB's `findOneAndUpdate`).
|
|
152
|
+
* Returns the record as stored afterwards; `null` when nothing matches.
|
|
153
|
+
*/
|
|
154
|
+
updateOne(where: Where<T>, update: DataStoreUpdate<T>): Promise<null | StoredRecord<T>>;
|
|
155
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { Logger } from '@nestjs/common';
|
|
2
|
+
|
|
3
|
+
import { defaultSqlLockStore } from './infrastructure-locks.js';
|
|
4
|
+
import type { ModelDescriptor } from './model-descriptor.interface.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* How often expired rows are removed from SQL tables. MongoDB's TTL monitor does this for its collections about once a
|
|
8
|
+
* minute; on SQL nothing does it unless the framework does. Nothing depends on it for correctness — every reader checks
|
|
9
|
+
* the expiry itself — so the interval only bounds how long expired rows take up space.
|
|
10
|
+
*/
|
|
11
|
+
export const EXPIRY_SWEEP_INTERVAL_MS = 15 * 60_000;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The records of a model whose TTL has passed, decided as MongoDB's TTL monitor decides it: a non-array date field plus
|
|
15
|
+
* its TTL lies before `now` — a record without that date never expires — and, for a PARTIAL TTL index, the record also
|
|
16
|
+
* matches the index's `partialFilterExpression`, because MongoDB expires nothing outside it. `undefined` when the model
|
|
17
|
+
* declares no TTL.
|
|
18
|
+
*
|
|
19
|
+
* The partial filter is not decoration: `{ expireAfterSeconds, partialFilterExpression: { verified: false } }` expires
|
|
20
|
+
* unverified accounts only, and a sweep that dropped the filter deleted every verified one as well. A filter the SQL
|
|
21
|
+
* where compiler cannot express makes the delete fail, so nothing is deleted rather than too much.
|
|
22
|
+
*/
|
|
23
|
+
export function expiredRecordsFilter(model: ModelDescriptor, now: Date): Record<string, unknown> | undefined {
|
|
24
|
+
const conditions = Object.values(model.fields)
|
|
25
|
+
.filter(
|
|
26
|
+
(field) =>
|
|
27
|
+
field.kind === 'date' &&
|
|
28
|
+
!field.isArray &&
|
|
29
|
+
Number.isFinite(field.expiresAfterSeconds) &&
|
|
30
|
+
field.expiresAfterSeconds >= 0,
|
|
31
|
+
)
|
|
32
|
+
.map((field) => {
|
|
33
|
+
const expired = { [field.name]: { $lt: new Date(now.getTime() - field.expiresAfterSeconds * 1000) } };
|
|
34
|
+
const ttlIndex = model.indexes.find(
|
|
35
|
+
(index) =>
|
|
36
|
+
Object.keys(index.fields).length === 1 && field.name in index.fields && 'expireAfterSeconds' in index.options,
|
|
37
|
+
);
|
|
38
|
+
const partial = ttlIndex?.options.partialFilterExpression as Record<string, unknown> | undefined;
|
|
39
|
+
return partial ? { $and: [expired, partial] } : expired;
|
|
40
|
+
});
|
|
41
|
+
if (!conditions.length) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
return conditions.length === 1 ? conditions[0] : { $or: conditions };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** A scheduled sweep */
|
|
48
|
+
export interface ExpirySweep {
|
|
49
|
+
/** Sweep at once, without the lease — after the sweep that may be running has finished */
|
|
50
|
+
runNow(): Promise<void>;
|
|
51
|
+
/** End the timer and wait for a sweep that is still running */
|
|
52
|
+
stop(): Promise<void>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Run `sweep` now and then every `intervalMs` — on one replica per interval when the default source is a SQL database
|
|
57
|
+
* (the lease `expiry-sweep:<key>` in its lease table, never released, so the next replica takes it once it expired),
|
|
58
|
+
* otherwise on every replica. Running it on several is harmless: a sweep only deletes what has already expired.
|
|
59
|
+
*
|
|
60
|
+
* Fails open: a sweep or a lease that fails is logged and tried again at the next interval, it never stops the
|
|
61
|
+
* application.
|
|
62
|
+
*/
|
|
63
|
+
export function scheduleExpirySweep(
|
|
64
|
+
key: string,
|
|
65
|
+
sweep: () => Promise<void>,
|
|
66
|
+
options: { intervalMs?: number; logger: Logger },
|
|
67
|
+
): ExpirySweep {
|
|
68
|
+
const intervalMs = options.intervalMs ?? EXPIRY_SWEEP_INTERVAL_MS;
|
|
69
|
+
const owner = `expiry-sweep:${process.pid}:${Math.random().toString(36).slice(2)}`;
|
|
70
|
+
let running: Promise<void> | undefined;
|
|
71
|
+
|
|
72
|
+
const tick = async (): Promise<void> => {
|
|
73
|
+
try {
|
|
74
|
+
const locks = await defaultSqlLockStore('expiry-sweep');
|
|
75
|
+
if (locks && !(await locks.tryAcquire(key, owner, Math.max(1, Math.floor(intervalMs / 1000))))) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
await sweep();
|
|
79
|
+
} catch (error) {
|
|
80
|
+
options.logger.warn(
|
|
81
|
+
`Expiry sweep "${key}" failed, trying again in ${Math.max(1, Math.round(intervalMs / 60_000))} min: ${(error as Error)?.message ?? error}`,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const run = () => {
|
|
86
|
+
// One sweep at a time: an interval that elapses during a slow sweep is skipped, not queued
|
|
87
|
+
running ??= tick().finally(() => {
|
|
88
|
+
running = undefined;
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
run();
|
|
93
|
+
const timer = setInterval(run, intervalMs);
|
|
94
|
+
timer.unref();
|
|
95
|
+
return {
|
|
96
|
+
runNow: async () => {
|
|
97
|
+
await running;
|
|
98
|
+
await sweep();
|
|
99
|
+
},
|
|
100
|
+
stop: async () => {
|
|
101
|
+
clearInterval(timer);
|
|
102
|
+
await running;
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
import { isObjectIdLike } from '../helpers/id.helper.js';
|
|
4
|
+
|
|
5
|
+
export { isObjectIdLike } from '../helpers/id.helper.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The stored id format, the same on every database: 24 hex characters — an ObjectId's hex string.
|
|
9
|
+
* On MongoDB that IS the ObjectId; on SQL the application generates it (plan §3.5), so ids, URLs and
|
|
10
|
+
* validators stay identical across the fleet and survive a data move between databases.
|
|
11
|
+
*/
|
|
12
|
+
const STORED_ID = /^[0-9a-f]{24}$/i;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Whether a value is a well-formed stored id: an ObjectId (from any bson copy) or its 24-character hex
|
|
16
|
+
* string.
|
|
17
|
+
*
|
|
18
|
+
* Deliberately stricter than `Types.ObjectId.isValid()`, which also accepts any 12-character string
|
|
19
|
+
* (a 12-byte ObjectId seed) — no stored id ever has that form. For strings this is exactly the rule
|
|
20
|
+
* `checkStringIds()` and `CoreFileService.isMalformedFileId()` applied as `length === 24 && isValid()`.
|
|
21
|
+
*/
|
|
22
|
+
export function isWellFormedId(value: unknown): boolean {
|
|
23
|
+
if (typeof value === 'string') {
|
|
24
|
+
return STORED_ID.test(value);
|
|
25
|
+
}
|
|
26
|
+
return isObjectIdLike(value);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const PROCESS_UNIQUE = randomBytes(5);
|
|
30
|
+
let counter = randomBytes(3).readUIntBE(0, 3);
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* A new id in the stored format, generated by the application (plan §3.5): an ObjectId — 4 bytes of seconds,
|
|
34
|
+
* 5 random bytes per process, a 3-byte counter — so ids stay sortable by creation time like MongoDB's.
|
|
35
|
+
*/
|
|
36
|
+
export function generateId(): string {
|
|
37
|
+
const bytes = Buffer.alloc(12);
|
|
38
|
+
bytes.writeUInt32BE(Math.floor(Date.now() / 1000) >>> 0, 0);
|
|
39
|
+
PROCESS_UNIQUE.copy(bytes, 4);
|
|
40
|
+
counter = (counter + 1) % 0x1000000;
|
|
41
|
+
bytes.writeUIntBE(counter, 9, 3);
|
|
42
|
+
return bytes.toString('hex');
|
|
43
|
+
}
|