@lenne.tech/nest-server 12.1.1 → 12.3.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 +1 -0
- package/.claude/rules/better-auth.md +31 -4
- package/.claude/rules/configurable-features.md +8 -6
- package/.claude/rules/package-management.md +29 -2
- package/.claude/rules/persistence.md +27 -1
- package/.claude/rules/testing.md +80 -15
- package/.claude/rules/versioning.md +13 -2
- package/CLAUDE.md +7 -1
- package/FRAMEWORK-API.md +20 -4
- package/README.md +20 -2
- package/dist/config.env.js +2 -2
- package/dist/config.env.js.map +1 -1
- package/dist/core/common/helpers/graphql-ws-connection.helper.d.ts +4 -0
- package/dist/core/common/helpers/graphql-ws-connection.helper.js +17 -3
- package/dist/core/common/helpers/graphql-ws-connection.helper.js.map +1 -1
- package/dist/core/common/interfaces/server-options.interface.d.ts +49 -0
- package/dist/core/common/interfaces/service-options.interface.d.ts +3 -0
- package/dist/core/common/persistence/core-persistence.module.js +12 -2
- package/dist/core/common/persistence/core-persistence.module.js.map +1 -1
- package/dist/core/common/persistence/data-source.runtime.js +4 -2
- package/dist/core/common/persistence/data-source.runtime.js.map +1 -1
- package/dist/core/common/persistence/data-store.interface.d.ts +24 -9
- package/dist/core/common/persistence/id-format.d.ts +2 -0
- package/dist/core/common/persistence/id-format.js +14 -0
- package/dist/core/common/persistence/id-format.js.map +1 -1
- package/dist/core/common/persistence/model-descriptor.interface.d.ts +6 -0
- package/dist/core/common/persistence/model-metadata.registry.d.ts +2 -1
- package/dist/core/common/persistence/model-metadata.registry.js +20 -2
- package/dist/core/common/persistence/model-metadata.registry.js.map +1 -1
- package/dist/core/common/persistence/mongo/sync-schema.plugin.d.ts +18 -0
- package/dist/core/common/persistence/mongo/sync-schema.plugin.js +368 -0
- package/dist/core/common/persistence/mongo/sync-schema.plugin.js.map +1 -0
- package/dist/core/common/persistence/mongoose-data-store.d.ts +19 -9
- package/dist/core/common/persistence/mongoose-data-store.js +107 -31
- package/dist/core/common/persistence/mongoose-data-store.js.map +1 -1
- package/dist/core/common/persistence/persisted-model.decorator.d.ts +3 -0
- package/dist/core/common/persistence/persisted-model.decorator.js +9 -0
- package/dist/core/common/persistence/persisted-model.decorator.js.map +1 -1
- package/dist/core/common/persistence/policies/sync.policy.d.ts +17 -0
- package/dist/core/common/persistence/policies/sync.policy.js +77 -0
- package/dist/core/common/persistence/policies/sync.policy.js.map +1 -0
- package/dist/core/common/persistence/sql/mysql/mysql-data-store.d.ts +12 -8
- package/dist/core/common/persistence/sql/mysql/mysql-data-store.js +82 -29
- package/dist/core/common/persistence/sql/mysql/mysql-data-store.js.map +1 -1
- package/dist/core/common/persistence/sql/mysql/mysql-schema.compiler.d.ts +10 -0
- package/dist/core/common/persistence/sql/mysql/mysql-schema.compiler.js +61 -4
- package/dist/core/common/persistence/sql/mysql/mysql-schema.compiler.js.map +1 -1
- package/dist/core/common/persistence/sql/mysql/mysql-schema.migrator.js +101 -4
- package/dist/core/common/persistence/sql/mysql/mysql-schema.migrator.js.map +1 -1
- package/dist/core/common/persistence/sql/postgres/postgres-schema.compiler.d.ts +10 -0
- package/dist/core/common/persistence/sql/postgres/postgres-schema.compiler.js +41 -2
- package/dist/core/common/persistence/sql/postgres/postgres-schema.compiler.js.map +1 -1
- package/dist/core/common/persistence/sql/postgres/postgres-schema.migrator.js +98 -10
- package/dist/core/common/persistence/sql/postgres/postgres-schema.migrator.js.map +1 -1
- package/dist/core/common/persistence/sql/sql-data-store.d.ts +38 -14
- package/dist/core/common/persistence/sql/sql-data-store.js +212 -38
- package/dist/core/common/persistence/sql/sql-data-store.js.map +1 -1
- package/dist/core/common/persistence/sql/sql-errors.d.ts +2 -1
- package/dist/core/common/persistence/sql/sql-errors.js +3 -1
- package/dist/core/common/persistence/sql/sql-errors.js.map +1 -1
- package/dist/core/common/persistence/sync-access.registry.d.ts +17 -0
- package/dist/core/common/persistence/sync-access.registry.js +8 -0
- package/dist/core/common/persistence/sync-access.registry.js.map +1 -0
- package/dist/core/common/persistence/sync-model.d.ts +29 -0
- package/dist/core/common/persistence/sync-model.js +138 -0
- package/dist/core/common/persistence/sync-model.js.map +1 -0
- package/dist/core/common/services/crud.service.d.ts +13 -1
- package/dist/core/common/services/crud.service.js +174 -16
- package/dist/core/common/services/crud.service.js.map +1 -1
- package/dist/core/modules/ai/core-ai-mcp.controller.d.ts +3 -0
- package/dist/core/modules/ai/core-ai-mcp.controller.js +29 -0
- package/dist/core/modules/ai/core-ai-mcp.controller.js.map +1 -1
- package/dist/core/modules/ai/core-ai.constants.d.ts +2 -0
- package/dist/core/modules/ai/core-ai.constants.js +2 -0
- package/dist/core/modules/ai/core-ai.constants.js.map +1 -1
- package/dist/core/modules/ai/core-ai.controller.d.ts +6 -0
- package/dist/core/modules/ai/core-ai.controller.js +31 -1
- package/dist/core/modules/ai/core-ai.controller.js.map +1 -1
- package/dist/core/modules/ai/core-ai.module.d.ts +2 -0
- package/dist/core/modules/ai/core-ai.module.js +8 -1
- package/dist/core/modules/ai/core-ai.module.js.map +1 -1
- package/dist/core/modules/ai/core-ai.resolver.d.ts +6 -0
- package/dist/core/modules/ai/core-ai.resolver.js +32 -0
- package/dist/core/modules/ai/core-ai.resolver.js.map +1 -1
- package/dist/core/modules/ai/helpers/ai-mcp-oauth-consent.helper.d.ts +65 -1
- package/dist/core/modules/ai/helpers/ai-mcp-oauth-consent.helper.js +6 -3
- package/dist/core/modules/ai/helpers/ai-mcp-oauth-consent.helper.js.map +1 -1
- package/dist/core/modules/ai/helpers/ai-mcp-oauth.helper.js +48 -1
- package/dist/core/modules/ai/helpers/ai-mcp-oauth.helper.js.map +1 -1
- package/dist/core/modules/ai/helpers/ai-tool-authorization.helper.d.ts +9 -0
- package/dist/core/modules/ai/helpers/ai-tool-authorization.helper.js +54 -0
- package/dist/core/modules/ai/helpers/ai-tool-authorization.helper.js.map +1 -0
- package/dist/core/modules/ai/index.d.ts +7 -0
- package/dist/core/modules/ai/index.js +6 -0
- package/dist/core/modules/ai/index.js.map +1 -1
- package/dist/core/modules/ai/inputs/core-ai-settings.input.d.ts +3 -0
- package/dist/core/modules/ai/inputs/core-ai-settings.input.js +31 -0
- package/dist/core/modules/ai/inputs/core-ai-settings.input.js.map +1 -0
- package/dist/core/modules/ai/interfaces/ai-tool.interface.d.ts +2 -0
- package/dist/core/modules/ai/models/core-ai-settings.model.d.ts +4 -0
- package/dist/core/modules/ai/models/core-ai-settings.model.js +30 -0
- package/dist/core/modules/ai/models/core-ai-settings.model.js.map +1 -0
- package/dist/core/modules/ai/models/core-ai-user-setting.model.d.ts +8 -0
- package/dist/core/modules/ai/models/core-ai-user-setting.model.js +45 -0
- package/dist/core/modules/ai/models/core-ai-user-setting.model.js.map +1 -0
- package/dist/core/modules/ai/services/core-ai-conversation.service.d.ts +4 -1
- package/dist/core/modules/ai/services/core-ai-conversation.service.js +16 -4
- package/dist/core/modules/ai/services/core-ai-conversation.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-mcp-oauth.service.js +6 -6
- package/dist/core/modules/ai/services/core-ai-mcp-oauth.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-mcp.service.d.ts +27 -2
- package/dist/core/modules/ai/services/core-ai-mcp.service.js +118 -11
- package/dist/core/modules/ai/services/core-ai-mcp.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-prompt-builder.service.js +9 -6
- package/dist/core/modules/ai/services/core-ai-prompt-builder.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-user-setting.service.d.ts +17 -0
- package/dist/core/modules/ai/services/core-ai-user-setting.service.js +72 -0
- package/dist/core/modules/ai/services/core-ai-user-setting.service.js.map +1 -0
- package/dist/core/modules/ai/services/core-ai.service.d.ts +8 -3
- package/dist/core/modules/ai/services/core-ai.service.js +68 -29
- package/dist/core/modules/ai/services/core-ai.service.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.config.js +3 -1
- package/dist/core/modules/better-auth/better-auth.config.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth-email-verification.service.js +2 -2
- package/dist/core/modules/better-auth/core-better-auth-email-verification.service.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth-path-guard.middleware.d.ts +8 -0
- package/dist/core/modules/better-auth/core-better-auth-path-guard.middleware.js +64 -0
- package/dist/core/modules/better-auth/core-better-auth-path-guard.middleware.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth-rate-limit.middleware.js +2 -1
- package/dist/core/modules/better-auth/core-better-auth-rate-limit.middleware.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth-rate-limiter.service.d.ts +1 -1
- package/dist/core/modules/better-auth/core-better-auth-rate-limiter.service.js +5 -3
- package/dist/core/modules/better-auth/core-better-auth-rate-limiter.service.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth.module.js +2 -0
- package/dist/core/modules/better-auth/core-better-auth.module.js.map +1 -1
- package/dist/core/modules/better-auth/index.d.ts +1 -0
- package/dist/core/modules/better-auth/index.js +1 -0
- package/dist/core/modules/better-auth/index.js.map +1 -1
- package/dist/core/modules/error-code/error-codes.d.ts +72 -0
- package/dist/core/modules/error-code/error-codes.js +64 -0
- package/dist/core/modules/error-code/error-codes.js.map +1 -1
- package/dist/core/modules/migrate/sql-migration.helper.d.ts +15 -1
- package/dist/core/modules/migrate/sql-migration.helper.js +90 -5
- package/dist/core/modules/migrate/sql-migration.helper.js.map +1 -1
- package/dist/core/modules/sync/core-sync-base.controller.d.ts +7 -0
- package/dist/core/modules/sync/core-sync-base.controller.js +44 -0
- package/dist/core/modules/sync/core-sync-base.controller.js.map +1 -0
- package/dist/core/modules/sync/core-sync-cursor.d.ts +19 -0
- package/dist/core/modules/sync/core-sync-cursor.js +77 -0
- package/dist/core/modules/sync/core-sync-cursor.js.map +1 -0
- package/dist/core/modules/sync/core-sync-hints.controller.d.ts +4 -0
- package/dist/core/modules/sync/core-sync-hints.controller.js +55 -0
- package/dist/core/modules/sync/core-sync-hints.controller.js.map +1 -0
- package/dist/core/modules/sync/core-sync-hints.service.d.ts +28 -0
- package/dist/core/modules/sync/core-sync-hints.service.js +155 -0
- package/dist/core/modules/sync/core-sync-hints.service.js.map +1 -0
- package/dist/core/modules/sync/core-sync-infrastructure.models.d.ts +2 -0
- package/dist/core/modules/sync/core-sync-infrastructure.models.js +17 -0
- package/dist/core/modules/sync/core-sync-infrastructure.models.js.map +1 -0
- package/dist/core/modules/sync/core-sync-mutation.store.d.ts +43 -0
- package/dist/core/modules/sync/core-sync-mutation.store.js +88 -0
- package/dist/core/modules/sync/core-sync-mutation.store.js.map +1 -0
- package/dist/core/modules/sync/core-sync-state.store.d.ts +18 -0
- package/dist/core/modules/sync/core-sync-state.store.js +64 -0
- package/dist/core/modules/sync/core-sync-state.store.js.map +1 -0
- package/dist/core/modules/sync/core-sync.constants.d.ts +28 -0
- package/dist/core/modules/sync/core-sync.constants.js +23 -0
- package/dist/core/modules/sync/core-sync.constants.js.map +1 -0
- package/dist/core/modules/sync/core-sync.controller.d.ts +8 -0
- package/dist/core/modules/sync/core-sync.controller.js +79 -0
- package/dist/core/modules/sync/core-sync.controller.js.map +1 -0
- package/dist/core/modules/sync/core-sync.decorators.d.ts +12 -0
- package/dist/core/modules/sync/core-sync.decorators.js +28 -0
- package/dist/core/modules/sync/core-sync.decorators.js.map +1 -0
- package/dist/core/modules/sync/core-sync.module.d.ts +16 -0
- package/dist/core/modules/sync/core-sync.module.js +50 -0
- package/dist/core/modules/sync/core-sync.module.js.map +1 -0
- package/dist/core/modules/sync/core-sync.resolver.d.ts +9 -0
- package/dist/core/modules/sync/core-sync.resolver.js +46 -0
- package/dist/core/modules/sync/core-sync.resolver.js.map +1 -0
- package/dist/core/modules/sync/core-sync.service.d.ts +143 -0
- package/dist/core/modules/sync/core-sync.service.js +1237 -0
- package/dist/core/modules/sync/core-sync.service.js.map +1 -0
- package/dist/core/modules/sync/interfaces/core-sync.interfaces.d.ts +56 -0
- package/dist/core/modules/sync/interfaces/core-sync.interfaces.js +2 -0
- package/dist/core/modules/sync/interfaces/core-sync.interfaces.js.map +1 -0
- package/dist/core/modules/sync/models/core-sync-hint.model.d.ts +4 -0
- package/dist/core/modules/sync/models/core-sync-hint.model.js +30 -0
- package/dist/core/modules/sync/models/core-sync-hint.model.js.map +1 -0
- package/dist/core/modules/tenant/core-tenant.guard.js +14 -0
- package/dist/core/modules/tenant/core-tenant.guard.js.map +1 -1
- package/dist/core/modules/tenant/core-tenant.service.d.ts +21 -1
- package/dist/core/modules/tenant/core-tenant.service.js +153 -1
- package/dist/core/modules/tenant/core-tenant.service.js.map +1 -1
- package/dist/core.module.js +14 -0
- package/dist/core.module.js.map +1 -1
- package/dist/index.d.ts +16 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -1
- package/dist/server/modules/error-code/error-codes.d.ts +8 -0
- package/dist/server/modules/sync-note/inputs/sync-note-create.input.d.ts +4 -0
- package/dist/server/modules/sync-note/inputs/sync-note-create.input.js +27 -0
- package/dist/server/modules/sync-note/inputs/sync-note-create.input.js.map +1 -0
- package/dist/server/modules/sync-note/inputs/sync-note.input.d.ts +6 -0
- package/dist/server/modules/sync-note/inputs/sync-note.input.js +37 -0
- package/dist/server/modules/sync-note/inputs/sync-note.input.js.map +1 -0
- package/dist/server/modules/sync-note/sync-note.model.d.ts +10 -0
- package/dist/server/modules/sync-note/sync-note.model.js +73 -0
- package/dist/server/modules/sync-note/sync-note.model.js.map +1 -0
- package/dist/server/modules/sync-note/sync-note.module.d.ts +2 -0
- package/dist/server/modules/sync-note/sync-note.module.js +21 -0
- package/dist/server/modules/sync-note/sync-note.module.js.map +1 -0
- package/dist/server/modules/sync-note/sync-note.service.d.ts +10 -0
- package/dist/server/modules/sync-note/sync-note.service.js +35 -0
- package/dist/server/modules/sync-note/sync-note.service.js.map +1 -0
- package/dist/test/test.helper.d.ts +1 -0
- package/dist/test/test.helper.js +9 -2
- package/dist/test/test.helper.js.map +1 -1
- package/docs/REQUEST-LIFECYCLE.md +4 -2
- package/docs/error-codes.md +14 -0
- package/docs/offline-sync-protocol.md +111 -68
- package/docs/security-overrides.md +14 -0
- package/migration-guides/12.0.x-to-12.1.x.md +0 -1
- package/migration-guides/12.1.x-to-12.2.x.md +154 -0
- package/migration-guides/12.2.x-to-12.3.x.md +440 -0
- package/package.json +23 -21
- package/src/config.env.ts +6 -2
- package/src/core/common/helpers/graphql-ws-connection.helper.ts +32 -4
- package/src/core/common/interfaces/server-options.interface.ts +229 -8
- package/src/core/common/interfaces/service-options.interface.ts +15 -0
- package/src/core/common/persistence/core-persistence.module.ts +21 -2
- package/src/core/common/persistence/data-source.runtime.ts +5 -2
- package/src/core/common/persistence/data-store.interface.ts +65 -12
- package/src/core/common/persistence/id-format.ts +26 -0
- package/src/core/common/persistence/model-descriptor.interface.ts +12 -0
- package/src/core/common/persistence/model-metadata.registry.ts +31 -2
- package/src/core/common/persistence/mongo/sync-schema.plugin.ts +508 -0
- package/src/core/common/persistence/mongoose-data-store.ts +171 -35
- package/src/core/common/persistence/persisted-model.decorator.ts +26 -0
- package/src/core/common/persistence/policies/sync.policy.ts +121 -0
- package/src/core/common/persistence/sql/mysql/mysql-data-store.ts +108 -30
- package/src/core/common/persistence/sql/mysql/mysql-schema.compiler.ts +107 -4
- package/src/core/common/persistence/sql/mysql/mysql-schema.migrator.ts +129 -3
- package/src/core/common/persistence/sql/postgres/postgres-schema.compiler.ts +74 -2
- package/src/core/common/persistence/sql/postgres/postgres-schema.migrator.ts +139 -7
- package/src/core/common/persistence/sql/sql-data-store.ts +325 -44
- package/src/core/common/persistence/sql/sql-errors.ts +5 -1
- package/src/core/common/persistence/sync-access.registry.ts +50 -0
- package/src/core/common/persistence/sync-model.ts +270 -0
- package/src/core/common/services/crud.service.ts +257 -19
- package/src/core/modules/ai/INTEGRATION-CHECKLIST.md +36 -15
- package/src/core/modules/ai/README.md +163 -25
- package/src/core/modules/ai/core-ai-mcp.controller.ts +48 -1
- package/src/core/modules/ai/core-ai.constants.ts +6 -0
- package/src/core/modules/ai/core-ai.controller.ts +36 -1
- package/src/core/modules/ai/core-ai.module.ts +12 -0
- package/src/core/modules/ai/core-ai.resolver.ts +36 -0
- package/src/core/modules/ai/helpers/ai-mcp-oauth-consent.helper.ts +35 -4
- package/src/core/modules/ai/helpers/ai-mcp-oauth.helper.ts +68 -1
- package/src/core/modules/ai/helpers/ai-tool-authorization.helper.ts +99 -0
- package/src/core/modules/ai/index.ts +18 -0
- package/src/core/modules/ai/inputs/core-ai-settings.input.ts +24 -0
- package/src/core/modules/ai/interfaces/ai-hook.interface.ts +1 -1
- package/src/core/modules/ai/interfaces/ai-tool.interface.ts +38 -26
- package/src/core/modules/ai/models/core-ai-mode.model.ts +1 -1
- package/src/core/modules/ai/models/core-ai-settings.model.ts +23 -0
- package/src/core/modules/ai/models/core-ai-tool-policy.model.ts +1 -1
- package/src/core/modules/ai/models/core-ai-user-setting.model.ts +46 -0
- package/src/core/modules/ai/services/core-ai-conversation.service.ts +31 -6
- package/src/core/modules/ai/services/core-ai-mcp-oauth.service.ts +12 -9
- package/src/core/modules/ai/services/core-ai-mcp.service.ts +206 -13
- package/src/core/modules/ai/services/core-ai-prompt-builder.service.ts +13 -6
- package/src/core/modules/ai/services/core-ai-user-setting.service.ts +85 -0
- package/src/core/modules/ai/services/core-ai.service.ts +134 -35
- package/src/core/modules/api-token/README.md +4 -2
- package/src/core/modules/better-auth/CUSTOMIZATION.md +1 -1
- package/src/core/modules/better-auth/INTEGRATION-CHECKLIST.md +7 -0
- package/src/core/modules/better-auth/README.md +32 -1
- package/src/core/modules/better-auth/better-auth.config.ts +12 -1
- package/src/core/modules/better-auth/core-better-auth-email-verification.service.ts +5 -2
- package/src/core/modules/better-auth/core-better-auth-path-guard.middleware.ts +85 -0
- package/src/core/modules/better-auth/core-better-auth-rate-limit.middleware.ts +5 -2
- package/src/core/modules/better-auth/core-better-auth-rate-limiter.service.ts +14 -7
- package/src/core/modules/better-auth/core-better-auth.module.ts +12 -2
- package/src/core/modules/better-auth/index.ts +1 -0
- package/src/core/modules/error-code/error-codes.ts +77 -0
- package/src/core/modules/migrate/sql-migration.helper.ts +141 -7
- package/src/core/modules/sync/INTEGRATION-CHECKLIST.md +95 -0
- package/src/core/modules/sync/README.md +198 -0
- package/src/core/modules/sync/SECURITY.md +65 -0
- package/src/core/modules/sync/core-sync-base.controller.ts +43 -0
- package/src/core/modules/sync/core-sync-cursor.ts +117 -0
- package/src/core/modules/sync/core-sync-hints.controller.ts +52 -0
- package/src/core/modules/sync/core-sync-hints.service.ts +178 -0
- package/src/core/modules/sync/core-sync-infrastructure.models.ts +40 -0
- package/src/core/modules/sync/core-sync-mutation.store.ts +146 -0
- package/src/core/modules/sync/core-sync-state.store.ts +89 -0
- package/src/core/modules/sync/core-sync.constants.ts +51 -0
- package/src/core/modules/sync/core-sync.controller.ts +68 -0
- package/src/core/modules/sync/core-sync.decorators.ts +84 -0
- package/src/core/modules/sync/core-sync.module.ts +75 -0
- package/src/core/modules/sync/core-sync.resolver.ts +35 -0
- package/src/core/modules/sync/core-sync.service.ts +1773 -0
- package/src/core/modules/sync/interfaces/core-sync.interfaces.ts +85 -0
- package/src/core/modules/sync/models/core-sync-hint.model.ts +18 -0
- package/src/core/modules/tenant/INTEGRATION-CHECKLIST.md +12 -1
- package/src/core/modules/tenant/README.md +49 -3
- package/src/core/modules/tenant/core-tenant.guard.ts +26 -2
- package/src/core/modules/tenant/core-tenant.service.ts +287 -2
- package/src/core.module.ts +25 -0
- package/src/index.ts +21 -0
- package/src/server/modules/sync-note/inputs/sync-note-create.input.ts +14 -0
- package/src/server/modules/sync-note/inputs/sync-note.input.ts +23 -0
- package/src/server/modules/sync-note/sync-note.model.ts +77 -0
- package/src/server/modules/sync-note/sync-note.module.ts +16 -0
- package/src/server/modules/sync-note/sync-note.service.ts +25 -0
- package/src/test/test.helper.ts +17 -3
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lenne.tech/nest-server",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.3.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "Server framework in TypeScript on top of NestJS: GraphQL and REST APIs on MongoDB, PostgreSQL, MySQL or MariaDB, with authentication, field-level permissions, multi-tenancy and an AI assistant.",
|
|
5
|
+
"description": "Server framework in TypeScript on top of NestJS: GraphQL and REST APIs on MongoDB, PostgreSQL, MySQL or MariaDB, with authentication, field-level permissions, multi-tenancy, offline sync and an AI assistant.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"node",
|
|
8
8
|
"nest",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"graphql",
|
|
16
16
|
"rest",
|
|
17
17
|
"multi-tenancy",
|
|
18
|
+
"offline-sync",
|
|
18
19
|
"typescript"
|
|
19
20
|
],
|
|
20
21
|
"author": "lenne.Tech <info@lenne.tech> (https://lenne.tech)",
|
|
@@ -103,18 +104,18 @@
|
|
|
103
104
|
"@as-integrations/express5": "1.1.2",
|
|
104
105
|
"@getbrevo/brevo": "6.2.0",
|
|
105
106
|
"@modelcontextprotocol/sdk": "1.32.2",
|
|
106
|
-
"@nestjs/apollo": "14.1.
|
|
107
|
+
"@nestjs/apollo": "14.1.2",
|
|
107
108
|
"@nestjs/common": "12.3.2",
|
|
108
109
|
"@nestjs/core": "12.3.2",
|
|
109
|
-
"@nestjs/graphql": "14.1.
|
|
110
|
+
"@nestjs/graphql": "14.1.2",
|
|
110
111
|
"@nestjs/jwt": "12.0.3",
|
|
111
112
|
"@nestjs/passport": "12.0.0",
|
|
112
113
|
"@nestjs/platform-express": "12.3.2",
|
|
113
114
|
"@nestjs/schedule": "12.0.2",
|
|
114
|
-
"@nestjs/swagger": "12.0
|
|
115
|
+
"@nestjs/swagger": "12.1.0",
|
|
115
116
|
"@nestjs/terminus": "12.1.0",
|
|
116
|
-
"@tus/file-store": "2.1.
|
|
117
|
-
"@tus/server": "2.4.
|
|
117
|
+
"@tus/file-store": "2.1.2",
|
|
118
|
+
"@tus/server": "2.4.7",
|
|
118
119
|
"@types/supertest": "7.2.1",
|
|
119
120
|
"bcrypt": "6.0.0",
|
|
120
121
|
"class-transformer": "0.5.1",
|
|
@@ -130,13 +131,13 @@
|
|
|
130
131
|
"graphql-subscriptions": "3.0.0",
|
|
131
132
|
"graphql-upload": "17.1.0",
|
|
132
133
|
"graphql-ws": "6.3.0",
|
|
133
|
-
"jose": "6.2.
|
|
134
|
+
"jose": "6.2.15",
|
|
134
135
|
"js-sha256": "1.0.0",
|
|
135
136
|
"json-to-graphql-query": "2.3.0",
|
|
136
137
|
"lodash": "4.18.1",
|
|
137
138
|
"multer": "2.5.0",
|
|
138
139
|
"node-mailjet": "6.0.11",
|
|
139
|
-
"nodemailer": "10.3.
|
|
140
|
+
"nodemailer": "10.3.1",
|
|
140
141
|
"passport": "0.7.0",
|
|
141
142
|
"passport-jwt": "4.0.1",
|
|
142
143
|
"reflect-metadata": "0.2.2",
|
|
@@ -225,9 +226,9 @@
|
|
|
225
226
|
}
|
|
226
227
|
},
|
|
227
228
|
"devDependencies": {
|
|
228
|
-
"@aws-sdk/client-s3": "3.
|
|
229
|
-
"@aws-sdk/lib-storage": "3.
|
|
230
|
-
"@aws-sdk/s3-request-presigner": "3.
|
|
229
|
+
"@aws-sdk/client-s3": "3.1161.0",
|
|
230
|
+
"@aws-sdk/lib-storage": "3.1161.0",
|
|
231
|
+
"@aws-sdk/s3-request-presigner": "3.1161.0",
|
|
231
232
|
"@better-auth/core": "1.7.14",
|
|
232
233
|
"@better-auth/passkey": "1.7.14",
|
|
233
234
|
"@compodoc/compodoc": "2.0.0",
|
|
@@ -236,8 +237,8 @@
|
|
|
236
237
|
"@nestjs/schematics": "12.0.12",
|
|
237
238
|
"@nestjs/testing": "12.3.2",
|
|
238
239
|
"@swc/cli": "0.8.2",
|
|
239
|
-
"@swc/core": "1.16.
|
|
240
|
-
"@tus/s3-store": "2.0
|
|
240
|
+
"@swc/core": "1.16.21",
|
|
241
|
+
"@tus/s3-store": "2.1.0",
|
|
241
242
|
"@types/compression": "1.8.1",
|
|
242
243
|
"@types/cookie-parser": "1.4.10",
|
|
243
244
|
"@types/ejs": "3.1.5",
|
|
@@ -247,31 +248,32 @@
|
|
|
247
248
|
"@types/node": "26.6.6",
|
|
248
249
|
"@types/passport": "1.0.17",
|
|
249
250
|
"@types/pg": "8.23.1",
|
|
250
|
-
"@vitest/coverage-v8": "5.0
|
|
251
|
+
"@vitest/coverage-v8": "5.1.0",
|
|
251
252
|
"better-auth": "1.7.14",
|
|
252
|
-
"bullmq": "6.5.
|
|
253
|
+
"bullmq": "6.5.4",
|
|
253
254
|
"cross-env": "10.1.0",
|
|
254
255
|
"drizzle-orm": "1.0.0-rc.5",
|
|
255
256
|
"ioredis": "6.0.0",
|
|
256
|
-
"kysely": "0.29.
|
|
257
|
+
"kysely": "0.29.8",
|
|
257
258
|
"mongodb": "7.7.0",
|
|
258
259
|
"mongoose": "9.11.4",
|
|
259
260
|
"mysql2": "3.24.6",
|
|
260
261
|
"nodemon": "3.1.14",
|
|
261
|
-
"otpauth": "9.
|
|
262
|
+
"otpauth": "9.6.0",
|
|
262
263
|
"oxfmt": "0.75.0",
|
|
263
264
|
"oxlint": "1.90.0",
|
|
264
265
|
"oxlint-tsgolint": "7.0.2005",
|
|
265
266
|
"pg": "8.23.1",
|
|
267
|
+
"playwright-core": "1.64.1",
|
|
266
268
|
"rimraf": "6.1.3",
|
|
267
|
-
"smtp-server": "3.21.
|
|
269
|
+
"smtp-server": "3.21.3",
|
|
268
270
|
"tsx": "4.23.15",
|
|
269
271
|
"tus-js-client": "4.3.1",
|
|
270
272
|
"typeorm": "1.1.2",
|
|
271
273
|
"typescript": "6.0.3",
|
|
272
274
|
"unplugin-swc": "2.0.0",
|
|
273
|
-
"vite": "8.3.
|
|
274
|
-
"vitest": "5.0
|
|
275
|
+
"vite": "8.3.5",
|
|
276
|
+
"vitest": "5.1.0"
|
|
275
277
|
},
|
|
276
278
|
"main": "dist/index.js",
|
|
277
279
|
"types": "dist/index.d.ts",
|
package/src/config.env.ts
CHANGED
|
@@ -59,7 +59,9 @@ const config: { [env: string]: IServerOptions } = {
|
|
|
59
59
|
rpId: 'localhost',
|
|
60
60
|
rpName: 'Nest Server Local',
|
|
61
61
|
},
|
|
62
|
-
|
|
62
|
+
// 200: sign-in and sign-up get the strict half (100), the headroom the suites had while the limiter
|
|
63
|
+
// counted every IAM request of a client on one shared counter of 100
|
|
64
|
+
rateLimit: { enabled: true, max: 200, windowSeconds: 60 },
|
|
63
65
|
secret: process.env.BETTER_AUTH_SECRET || 'BETTER_AUTH_SECRET_LOCAL_32_CHARS_M',
|
|
64
66
|
// Social providers disabled in local environment (no credentials)
|
|
65
67
|
socialProviders: {
|
|
@@ -367,7 +369,9 @@ const config: { [env: string]: IServerOptions } = {
|
|
|
367
369
|
rpId: 'localhost',
|
|
368
370
|
rpName: 'Nest Server Local',
|
|
369
371
|
},
|
|
370
|
-
|
|
372
|
+
// 200: sign-in and sign-up get the strict half (100), the headroom the suites had while the limiter
|
|
373
|
+
// counted every IAM request of a client on one shared counter of 100
|
|
374
|
+
rateLimit: { enabled: true, max: 200, windowSeconds: 60 },
|
|
371
375
|
secret: process.env.BETTER_AUTH_SECRET || 'BETTER_AUTH_SECRET_LOCAL_32_CHARS_M',
|
|
372
376
|
// Social providers disabled in local environment (no credentials)
|
|
373
377
|
socialProviders: {
|
|
@@ -82,6 +82,17 @@ export interface IWsConnectionRegistration {
|
|
|
82
82
|
*/
|
|
83
83
|
carrier: object;
|
|
84
84
|
|
|
85
|
+
/** How the log names the connection when it is closed. Default `GraphQL WebSocket`. */
|
|
86
|
+
channel?: string;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* The connection cannot be re-checked in place — an HTTP stream has no handshake to repeat — so every re-check ends
|
|
90
|
+
* it, and the client's reconnect passes the guards again. The end the re-check interval brings is planned and logged
|
|
91
|
+
* at debug; one an invalidation brings (sign-out, role or tenant change) is logged like every withdrawn
|
|
92
|
+
* authorization. `reauthenticate` is not called.
|
|
93
|
+
*/
|
|
94
|
+
endsOnRecheck?: boolean;
|
|
95
|
+
|
|
85
96
|
/**
|
|
86
97
|
* Re-run the handshake's authentication. Resolves to the user the credential authenticates as NOW,
|
|
87
98
|
* or to a falsy value when it no longer does. A rejection counts as falsy. Without it only the
|
|
@@ -105,7 +116,9 @@ export interface IWsConnectionIdentity {
|
|
|
105
116
|
|
|
106
117
|
/** @internal The registered state of one connection. */
|
|
107
118
|
export interface WsConnection {
|
|
119
|
+
readonly channel: string;
|
|
108
120
|
readonly email?: string;
|
|
121
|
+
readonly endsOnRecheck: boolean;
|
|
109
122
|
readonly iamId?: string;
|
|
110
123
|
readonly reauthenticate?: () => any;
|
|
111
124
|
readonly socket?: IWsConnectionSocket;
|
|
@@ -206,13 +219,15 @@ export function readWsTenantHeader(contextValue: any): string | undefined {
|
|
|
206
219
|
* the handshake recorded on the carrier.
|
|
207
220
|
*/
|
|
208
221
|
export function registerWsConnection(registration: IWsConnectionRegistration): void {
|
|
209
|
-
const { carrier, reauthenticate, socket, user } = registration;
|
|
222
|
+
const { carrier, channel, endsOnRecheck, reauthenticate, socket, user } = registration;
|
|
210
223
|
if (!carrier || typeof carrier !== 'object') {
|
|
211
224
|
return;
|
|
212
225
|
}
|
|
213
226
|
const userId = user?.id ?? user?._id;
|
|
214
227
|
const connection: WsConnection = {
|
|
228
|
+
channel: channel || 'GraphQL WebSocket',
|
|
215
229
|
email: typeof user?.email === 'string' ? user.email.toLowerCase() : undefined,
|
|
230
|
+
endsOnRecheck: endsOnRecheck === true,
|
|
216
231
|
iamId: typeof user?.iamId === 'string' ? user.iamId : undefined,
|
|
217
232
|
reauthenticate,
|
|
218
233
|
socket,
|
|
@@ -246,15 +261,20 @@ export function getWsConnection(contextValue: any): undefined | WsConnection {
|
|
|
246
261
|
|
|
247
262
|
/**
|
|
248
263
|
* Close a connection whose authorization no longer holds. Idempotent; always answers `false` so a
|
|
249
|
-
* caller can `return terminate(...)`.
|
|
264
|
+
* caller can `return terminate(...)`. `debug` is for a planned end, which says nothing about the user.
|
|
250
265
|
*/
|
|
251
|
-
function terminate(connection: WsConnection, reason: string): false {
|
|
266
|
+
function terminate(connection: WsConnection, reason: string, level: 'debug' | 'log' = 'log'): false {
|
|
252
267
|
if (connection.terminated) {
|
|
253
268
|
return false;
|
|
254
269
|
}
|
|
255
270
|
connection.terminated = true;
|
|
256
271
|
liveConnections.delete(connection);
|
|
257
|
-
|
|
272
|
+
const message = `Closing ${connection.channel} of user ${connection.userId ?? '(unknown)'}: ${reason}`;
|
|
273
|
+
if (level === 'debug') {
|
|
274
|
+
logger.debug(message);
|
|
275
|
+
} else {
|
|
276
|
+
logger.log(message);
|
|
277
|
+
}
|
|
258
278
|
try {
|
|
259
279
|
// A close reason is limited to 123 bytes on the wire; these are short and ASCII.
|
|
260
280
|
connection.socket?.close(WS_AUTHORIZATION_CHANGED_CLOSE_CODE, reason);
|
|
@@ -265,7 +285,15 @@ function terminate(connection: WsConnection, reason: string): false {
|
|
|
265
285
|
}
|
|
266
286
|
|
|
267
287
|
async function revalidate(connection: WsConnection): Promise<boolean> {
|
|
288
|
+
const invalidated = connection.stale;
|
|
268
289
|
connection.stale = false;
|
|
290
|
+
if (connection.endsOnRecheck) {
|
|
291
|
+
// Nothing to check in place: the end IS the re-check, and the reconnect is authorized afresh. Only an invalidation
|
|
292
|
+
// says that the rights may have changed; the interval is the planned beat and must not read like a lost session.
|
|
293
|
+
return invalidated
|
|
294
|
+
? terminate(connection, 'authorization invalidated, client reconnects')
|
|
295
|
+
: terminate(connection, 're-check interval reached, client reconnects', 'debug');
|
|
296
|
+
}
|
|
269
297
|
try {
|
|
270
298
|
let user = connection.user;
|
|
271
299
|
if (connection.reauthenticate) {
|
|
@@ -510,8 +510,14 @@ export type IBetterAuth = IBetterAuthWithoutPasskey | IBetterAuthWithPasskey;
|
|
|
510
510
|
*/
|
|
511
511
|
export interface IBetterAuthEmailVerificationConfig {
|
|
512
512
|
/**
|
|
513
|
-
* Whether
|
|
514
|
-
*
|
|
513
|
+
* Whether `GET /iam/verify-email` signs in the browser that opens the verification link.
|
|
514
|
+
*
|
|
515
|
+
* Off by default, as in Better-Auth (12.3.0+; before, the framework defaulted to `true`). Switching it on signs in
|
|
516
|
+
* WHOEVER opens the link, to the account the link belongs to: someone who signs up with their own address and
|
|
517
|
+
* forwards the unopened link has the recipient's browser signed in to their account, and what the recipient then
|
|
518
|
+
* enters lands there (login CSRF). Only switch it on where verification links reach nobody but their owner.
|
|
519
|
+
*
|
|
520
|
+
* @default false
|
|
515
521
|
*/
|
|
516
522
|
autoSignInAfterVerification?: boolean;
|
|
517
523
|
|
|
@@ -975,6 +981,17 @@ export interface IBetterAuthSocialProvider {
|
|
|
975
981
|
*/
|
|
976
982
|
clientSecret: string;
|
|
977
983
|
|
|
984
|
+
/**
|
|
985
|
+
* Whether a first sign-in through this provider may create an account.
|
|
986
|
+
*
|
|
987
|
+
* Unset, the provider follows `betterAuth.emailAndPassword.disableSignUp`: with that set to `true` (since 12.3.0)
|
|
988
|
+
* nobody gets a new account through any provider, while existing users keep signing in. `false` opens sign-up
|
|
989
|
+
* through this provider although `disableSignUp` closes it otherwise; `true` closes it for this provider alone.
|
|
990
|
+
* Only a boolean counts: any other value follows `emailAndPassword.disableSignUp`.
|
|
991
|
+
* @default emailAndPassword.disableSignUp
|
|
992
|
+
*/
|
|
993
|
+
disableSignUp?: boolean;
|
|
994
|
+
|
|
978
995
|
/**
|
|
979
996
|
* Whether this provider is enabled.
|
|
980
997
|
* Defaults to true when clientId and clientSecret are provided.
|
|
@@ -2004,7 +2021,13 @@ export interface IAi {
|
|
|
2004
2021
|
* Fallback total context window (input + output tokens) used to budget the
|
|
2005
2022
|
* assembled session messages when a connection has no `contextWindow` set. When the
|
|
2006
2023
|
* conversation (per user/session) would overflow, the oldest history turns are
|
|
2007
|
-
* dropped and oversized payloads truncated before the LLM call.
|
|
2024
|
+
* dropped and oversized payloads truncated before the LLM call. Native tool
|
|
2025
|
+
* definitions count against that budget too (12.3.0+), and when the system prompt and
|
|
2026
|
+
* tools take more than half of it, a warning names the connection once per process.
|
|
2027
|
+
*
|
|
2028
|
+
* A test that guards the prompt size against this value guards the connections WITHOUT
|
|
2029
|
+
* a window of their own — the worst case — not the window a pinned or detected
|
|
2030
|
+
* connection actually has. Name it that way; raising its limit removes that guard.
|
|
2008
2031
|
* @default 8192
|
|
2009
2032
|
*/
|
|
2010
2033
|
contextWindow?: number;
|
|
@@ -2061,14 +2084,14 @@ export interface IAi {
|
|
|
2061
2084
|
* a caller sees and may run is decided server-side by the registry's role filter
|
|
2062
2085
|
* (`AiToolRegistry.forUser()`), re-checked at execution time, and the
|
|
2063
2086
|
* confirmation gate reads the `mutating`/`destructive` FLAGS, never the
|
|
2064
|
-
* description text. (`AiTool.authorize()` runs
|
|
2065
|
-
* and
|
|
2087
|
+
* description text. (`AiTool.authorize()` runs before every execution — auto
|
|
2088
|
+
* mode, plan mode and MCP — on the call's arguments, never its description.)
|
|
2066
2089
|
*
|
|
2067
2090
|
* Applies to the EMULATED tool protocol, not to a provider as such. In auto mode
|
|
2068
2091
|
* a connection with native tool calling receives every full description and schema
|
|
2069
|
-
* through `buildToolSchemas()` regardless,
|
|
2070
|
-
* `
|
|
2071
|
-
*
|
|
2092
|
+
* through `buildToolSchemas()` regardless, and its catalog lists names only since
|
|
2093
|
+
* 12.3.0 (`nativeToolCatalog`), so this cap has no effect there and no `search_tools`
|
|
2094
|
+
* banner is emitted.
|
|
2072
2095
|
*
|
|
2073
2096
|
* **Plan mode always uses the emulated protocol** (it sends no native schemas), so a
|
|
2074
2097
|
* native connection IS truncated there. Because plan mode returns a complete plan in
|
|
@@ -2091,6 +2114,17 @@ export interface IAi {
|
|
|
2091
2114
|
*/
|
|
2092
2115
|
deferToolSummaryChars?: number;
|
|
2093
2116
|
|
|
2117
|
+
/**
|
|
2118
|
+
* What the system prompt's tool catalog holds for a connection with NATIVE tool calling in auto mode (12.3.0+).
|
|
2119
|
+
* Such a connection receives every description and parameter schema in the tool payload, so by default the catalog
|
|
2120
|
+
* lists names only — repeating the descriptions (and, without `deferToolSchemas`, the schemas) paid for every tool
|
|
2121
|
+
* twice. `'full'` restores the full catalog, for a model that picks tools better with the descriptions in the prompt.
|
|
2122
|
+
*
|
|
2123
|
+
* Emulated connections and plan mode always get the full catalog: there it is the model's only source.
|
|
2124
|
+
* @default 'names'
|
|
2125
|
+
*/
|
|
2126
|
+
nativeToolCatalog?: 'full' | 'names';
|
|
2127
|
+
|
|
2094
2128
|
/** Maximum number of agent-loop iterations (tool round-trips). @default 5 */
|
|
2095
2129
|
maxIterations?: number;
|
|
2096
2130
|
|
|
@@ -2142,11 +2176,32 @@ export interface IAi {
|
|
|
2142
2176
|
* answers with a built-in consent page: sign-in through Better-Auth (password + 2FA),
|
|
2143
2177
|
* then Allow / Deny. Replace it via `CoreModule.forRoot(env, { ai: { mcpOAuthService } })`.
|
|
2144
2178
|
* Without `oauth`, MCP authenticates via the existing Bearer/session token.
|
|
2179
|
+
*
|
|
2180
|
+
* `tools/call` applies the admin's tool policies and a tool's `authorize()`, and asks the user before a
|
|
2181
|
+
* tool that needs confirmation (`destructive`, or `mutating` under `ai.confirmation.mutating`) — see
|
|
2182
|
+
* `confirmation`.
|
|
2145
2183
|
* @default false
|
|
2146
2184
|
*/
|
|
2147
2185
|
mcp?:
|
|
2148
2186
|
| boolean
|
|
2149
2187
|
| {
|
|
2188
|
+
/**
|
|
2189
|
+
* Whether MCP asks the user — through MCP elicitation, a yes/no form the client shows — before a
|
|
2190
|
+
* `destructive` tool, or a `mutating` one under `ai.confirmation.mutating` (12.3.0+). The tool runs only
|
|
2191
|
+
* on an explicit yes; a client without the `elicitation` capability gets a refusal. `authorize()` runs
|
|
2192
|
+
* either way, and an admin's `ask` tool policy always asks.
|
|
2193
|
+
*
|
|
2194
|
+
* - `true` / `{}` / unset: MCP asks, unless a user switched it off for themselves
|
|
2195
|
+
* (`PUT /ai/settings`, `updateAiSettings` — like a "bypass permissions" mode).
|
|
2196
|
+
* - `false`: MCP asks nobody, unless a user switched it on for themselves.
|
|
2197
|
+
* - `{ default, enforced }`: `default` as above; `enforced: true` ignores every user's choice and
|
|
2198
|
+
* refuses a change with 403.
|
|
2199
|
+
*
|
|
2200
|
+
* Only `false` / `default: false` switch the default off and only `enforced: true` enforces, so a
|
|
2201
|
+
* misspelt value never relaxes the gate.
|
|
2202
|
+
* @default { default: true, enforced: false }
|
|
2203
|
+
*/
|
|
2204
|
+
confirmation?: boolean | { default?: boolean; enforced?: boolean };
|
|
2150
2205
|
enabled?: boolean;
|
|
2151
2206
|
oauth?: boolean;
|
|
2152
2207
|
/**
|
|
@@ -2164,6 +2219,17 @@ export interface IAi {
|
|
|
2164
2219
|
*/
|
|
2165
2220
|
oauthLoginUrl?: string;
|
|
2166
2221
|
oauthSecret?: string;
|
|
2222
|
+
/**
|
|
2223
|
+
* Serve `/ai/mcp` without sessions: every POST builds its own server and transport for the
|
|
2224
|
+
* authenticated user and ends with the response, no `mcp-session-id` is issued, and GET /
|
|
2225
|
+
* DELETE answer 405. Any replica can answer any request, so no sticky sessions are needed
|
|
2226
|
+
* behind a load balancer. The price is what a session carries: server-initiated messages
|
|
2227
|
+
* (notifications, SSE streams) and server-initiated requests — MCP elicitation among them, so a
|
|
2228
|
+
* tool that needs confirmation (`confirmation`) is refused unless the user or `confirmation`
|
|
2229
|
+
* switched the question off. A server that only offers read tools uses none of it (12.3.0+).
|
|
2230
|
+
* @default false
|
|
2231
|
+
*/
|
|
2232
|
+
stateless?: boolean;
|
|
2167
2233
|
};
|
|
2168
2234
|
|
|
2169
2235
|
/** Rate limiting for prompts. */
|
|
@@ -3509,6 +3575,16 @@ export interface IServerOptions {
|
|
|
3509
3575
|
path?: string;
|
|
3510
3576
|
};
|
|
3511
3577
|
|
|
3578
|
+
/**
|
|
3579
|
+
* Offline sync (`docs/offline-sync-protocol.md`): `GET/POST /sync/{model}` for the models whose service carries
|
|
3580
|
+
* `@SyncService()` and whose model is declared with `@PersistedModel({ sync })`. Presence implies enabled — `true`
|
|
3581
|
+
* or `{}` enables it with the defaults, `{ enabled: false }` pre-configures without enabling. Absent: no route, no
|
|
3582
|
+
* store, no code path.
|
|
3583
|
+
*
|
|
3584
|
+
* @since 12.2.0
|
|
3585
|
+
*/
|
|
3586
|
+
sync?: boolean | ISync;
|
|
3587
|
+
|
|
3512
3588
|
/**
|
|
3513
3589
|
* System setup configuration for initial admin creation.
|
|
3514
3590
|
*
|
|
@@ -3831,6 +3907,125 @@ export interface IS3Config {
|
|
|
3831
3907
|
stagingBucket?: string;
|
|
3832
3908
|
}
|
|
3833
3909
|
|
|
3910
|
+
/**
|
|
3911
|
+
* Offline sync (`sync`, presence implies enabled). See `src/core/modules/sync/README.md`.
|
|
3912
|
+
*
|
|
3913
|
+
* @since 12.2.0
|
|
3914
|
+
*/
|
|
3915
|
+
export interface ISync {
|
|
3916
|
+
/**
|
|
3917
|
+
* Secret the pull cursors are signed with (HMAC; the key is derived from it). Falls back to the
|
|
3918
|
+
* `SECRETS_ENCRYPTION_KEY` environment variable. REQUIRED in `production` / `staging` — the boot fails without it;
|
|
3919
|
+
* elsewhere an insecure development default is used with a warning. Changing it answers every client's next pull
|
|
3920
|
+
* with a full resync (`410 invalid`).
|
|
3921
|
+
*/
|
|
3922
|
+
cursorSecret?: string;
|
|
3923
|
+
|
|
3924
|
+
/**
|
|
3925
|
+
* Pre-configure without enabling.
|
|
3926
|
+
* @default true (when the object is present)
|
|
3927
|
+
*/
|
|
3928
|
+
enabled?: boolean;
|
|
3929
|
+
|
|
3930
|
+
/**
|
|
3931
|
+
* Hints that a synchronized model changed (protocol §9): SSE `GET /sync/hints` and the GraphQL subscription
|
|
3932
|
+
* `syncHint`, carrying `{ model, at }` only, to callers with the model's pull roles and — on a tenant-scoped model —
|
|
3933
|
+
* for changes in their own tenants. A hint only means "pull now": a client works without them. `false` switches both
|
|
3934
|
+
* channels off; with `redis` a hint reaches the subscribers of every replica.
|
|
3935
|
+
* @default on (both channels)
|
|
3936
|
+
*/
|
|
3937
|
+
hints?:
|
|
3938
|
+
| boolean
|
|
3939
|
+
| {
|
|
3940
|
+
/**
|
|
3941
|
+
* At most one hint per model and tenant within this many milliseconds — a burst of writes is one hint.
|
|
3942
|
+
* @default 250
|
|
3943
|
+
*/
|
|
3944
|
+
debounceMs?: number;
|
|
3945
|
+
/** @default true (when the object is present) */
|
|
3946
|
+
enabled?: boolean;
|
|
3947
|
+
/**
|
|
3948
|
+
* The GraphQL subscription `syncHint` (only where GraphQL is enabled).
|
|
3949
|
+
* @default true
|
|
3950
|
+
*/
|
|
3951
|
+
graphql?: boolean;
|
|
3952
|
+
/**
|
|
3953
|
+
* The SSE stream `GET /sync/hints`.
|
|
3954
|
+
* @default true
|
|
3955
|
+
*/
|
|
3956
|
+
sse?: boolean;
|
|
3957
|
+
};
|
|
3958
|
+
|
|
3959
|
+
/**
|
|
3960
|
+
* Route prefix of the sync endpoints.
|
|
3961
|
+
* @default 'sync'
|
|
3962
|
+
*/
|
|
3963
|
+
path?: string;
|
|
3964
|
+
|
|
3965
|
+
pull?: {
|
|
3966
|
+
/**
|
|
3967
|
+
* Records per page when the client sends no `limit`.
|
|
3968
|
+
* @default 200
|
|
3969
|
+
*/
|
|
3970
|
+
defaultLimit?: number;
|
|
3971
|
+
/**
|
|
3972
|
+
* Upper bound of the serialized size of one page; a page ends before it would exceed it (it always holds at least
|
|
3973
|
+
* one item when one is pending).
|
|
3974
|
+
* @default 1048576 (1 MiB)
|
|
3975
|
+
*/
|
|
3976
|
+
maxBytes?: number;
|
|
3977
|
+
/**
|
|
3978
|
+
* Highest `limit` a client may ask for (at most 1000).
|
|
3979
|
+
* @default 1000
|
|
3980
|
+
*/
|
|
3981
|
+
maxLimit?: number;
|
|
3982
|
+
/**
|
|
3983
|
+
* MongoDB only: the window W a pull run re-reads below its previous start (plan §3.4). Changes stamped within W
|
|
3984
|
+
* before a run started may be repeated; a write that takes longer than W from its stamp until it is visible would
|
|
3985
|
+
* be missed, so W must cover the longest such write.
|
|
3986
|
+
* @default replica set / sharded: the server's `transactionLifetimeLimitSeconds` (60 when unreadable) + 30;
|
|
3987
|
+
* standalone: 30
|
|
3988
|
+
*/
|
|
3989
|
+
windowSeconds?: number;
|
|
3990
|
+
};
|
|
3991
|
+
|
|
3992
|
+
push?: {
|
|
3993
|
+
/**
|
|
3994
|
+
* Items per push request; more answer 400.
|
|
3995
|
+
* @default 100
|
|
3996
|
+
*/
|
|
3997
|
+
maxItems?: number;
|
|
3998
|
+
};
|
|
3999
|
+
|
|
4000
|
+
/**
|
|
4001
|
+
* Per-user rate limits of pull and push (fixed window; exact across replicas when `redis` is configured). `false`
|
|
4002
|
+
* switches both off, `{ pull: false }` one of them.
|
|
4003
|
+
* @default pull 120 / 60 s, push 60 / 60 s
|
|
4004
|
+
*/
|
|
4005
|
+
rateLimit?:
|
|
4006
|
+
| false
|
|
4007
|
+
| {
|
|
4008
|
+
pull?: false | { max?: number; windowSeconds?: number };
|
|
4009
|
+
push?: false | { max?: number; windowSeconds?: number };
|
|
4010
|
+
};
|
|
4011
|
+
|
|
4012
|
+
retention?: {
|
|
4013
|
+
/**
|
|
4014
|
+
* How long the outcome of a pushed mutation is kept, so a repeat is answered with it (protocol §6.5).
|
|
4015
|
+
* @default 30
|
|
4016
|
+
*/
|
|
4017
|
+
mutationsDays?: number;
|
|
4018
|
+
/**
|
|
4019
|
+
* How long a tombstone is kept. Every hour (on one replica, under a lease) the purge first moves the model's
|
|
4020
|
+
* horizon to this age, then deletes the older tombstones for good; a client whose cursor is older than the horizon
|
|
4021
|
+
* answers its next pull with a full resync (`410 horizon`). `false` keeps tombstones for ever; an invalid value
|
|
4022
|
+
* (zero, negative, not a number) falls back to the default — never to "purge everything".
|
|
4023
|
+
* @default 90
|
|
4024
|
+
*/
|
|
4025
|
+
tombstonesDays?: false | number;
|
|
4026
|
+
};
|
|
4027
|
+
}
|
|
4028
|
+
|
|
3834
4029
|
export interface ISystemSetup {
|
|
3835
4030
|
/**
|
|
3836
4031
|
* Whether system setup is enabled.
|
|
@@ -4258,6 +4453,10 @@ interface IBetterAuthBase {
|
|
|
4258
4453
|
* Disable user registration (sign-up) via BetterAuth.
|
|
4259
4454
|
* Passed through to better-auth's native emailAndPassword.disableSignUp.
|
|
4260
4455
|
* Custom endpoints (GraphQL + REST) also check this flag early.
|
|
4456
|
+
* Since 12.3.0 it closes social sign-up too: every configured social provider receives it as its own
|
|
4457
|
+
* `disableSignUp`, so a first sign-in through Google, GitHub etc. creates no account, while existing users keep
|
|
4458
|
+
* signing in. `socialProviders.<name>.disableSignUp: false` opens one provider deliberately. Plugins a project
|
|
4459
|
+
* registers itself through `options.plugins` are not covered.
|
|
4261
4460
|
* The Swagger document built by `setupSwagger()` then leaves out `POST /iam/sign-up/email`,
|
|
4262
4461
|
* which would answer `SIGNUP_DISABLED` to every caller.
|
|
4263
4462
|
* @default false
|
|
@@ -4831,6 +5030,26 @@ export interface ICoreModuleOverrides {
|
|
|
4831
5030
|
service?: Type<any>;
|
|
4832
5031
|
};
|
|
4833
5032
|
|
|
5033
|
+
/**
|
|
5034
|
+
* Override the offline sync endpoints, the hint subscription and their service (`sync` config).
|
|
5035
|
+
*
|
|
5036
|
+
* - `controller` must extend `CoreSyncController` (e.g. to release a sync scope to API tokens)
|
|
5037
|
+
* - `hintsController` must extend `CoreSyncHintsController` (`GET /sync/hints`, e.g. for API tokens there too)
|
|
5038
|
+
* - `resolver` must extend `CoreSyncResolver` (the `syncHint` subscription)
|
|
5039
|
+
* - `service` must extend `CoreSyncService`
|
|
5040
|
+
*
|
|
5041
|
+
* @example
|
|
5042
|
+
* ```typescript
|
|
5043
|
+
* { sync: { controller: SyncController } }
|
|
5044
|
+
* ```
|
|
5045
|
+
*/
|
|
5046
|
+
sync?: {
|
|
5047
|
+
controller?: Type<any>;
|
|
5048
|
+
hintsController?: Type<any>;
|
|
5049
|
+
resolver?: Type<any>;
|
|
5050
|
+
service?: Type<any>;
|
|
5051
|
+
};
|
|
5052
|
+
|
|
4834
5053
|
/**
|
|
4835
5054
|
* Override AI module collaborators with project-specific subclasses.
|
|
4836
5055
|
*
|
|
@@ -4853,6 +5072,7 @@ export interface ICoreModuleOverrides {
|
|
|
4853
5072
|
* - `slotService` must extend `CoreAiSlotService`
|
|
4854
5073
|
* - `toolGrantService` must extend `CoreAiToolGrantService`
|
|
4855
5074
|
* - `toolPolicyService` must extend `CoreAiToolPolicyService`
|
|
5075
|
+
* - `userSettingService` must extend `CoreAiUserSettingService`
|
|
4856
5076
|
*
|
|
4857
5077
|
* @example
|
|
4858
5078
|
* ```typescript
|
|
@@ -4886,6 +5106,7 @@ export interface ICoreModuleOverrides {
|
|
|
4886
5106
|
slotService?: Type<any>;
|
|
4887
5107
|
toolGrantService?: Type<any>;
|
|
4888
5108
|
toolPolicyService?: Type<any>;
|
|
5109
|
+
userSettingService?: Type<any>;
|
|
4889
5110
|
};
|
|
4890
5111
|
|
|
4891
5112
|
/**
|
|
@@ -32,6 +32,11 @@ export interface ServiceOptions {
|
|
|
32
32
|
// If truly: create mode is activated
|
|
33
33
|
create?: boolean;
|
|
34
34
|
|
|
35
|
+
// The id `create()` gives the new record: on a synchronized model the id a client chose offline, in the model's
|
|
36
|
+
// format (refused otherwise), on any other model a 24-character hex id. Passed beside the input because an input
|
|
37
|
+
// class keeps only the fields it declares: an `id` inside the input is lost the moment `inputType` maps it
|
|
38
|
+
createId?: string;
|
|
39
|
+
|
|
35
40
|
// Current user to set ownership, check rights and other things
|
|
36
41
|
currentUser?: {
|
|
37
42
|
[key: string]: any;
|
|
@@ -42,9 +47,19 @@ export interface ServiceOptions {
|
|
|
42
47
|
// Field selection for results (will be overwritten by populate if populate was set)
|
|
43
48
|
fieldSelection?: FieldSelection;
|
|
44
49
|
|
|
50
|
+
// Conditional change of a synchronized model (`@PersistedModel({ sync })`): `update()` and `delete()` apply only
|
|
51
|
+
// when the stored record still has this version, in one atomic statement, and throw a 409 (`VersionConflictError`)
|
|
52
|
+
// otherwise. Refused on a model that is not synchronized
|
|
53
|
+
expectedVersion?: number;
|
|
54
|
+
|
|
45
55
|
// Determines whether all restrictions are ignored
|
|
46
56
|
force?: boolean;
|
|
47
57
|
|
|
58
|
+
// Read a synchronized model's tombstones too — its deleted records, which every other read leaves out (get, find,
|
|
59
|
+
// findOne, findAndCount, distinct). Reads only: update() and delete() never touch a tombstone and answer 404 for one.
|
|
60
|
+
// Applies to the main model only: a populated reference to a tombstone stays empty
|
|
61
|
+
includeTombstones?: boolean;
|
|
62
|
+
|
|
48
63
|
// Overwrites type of input (array items)
|
|
49
64
|
inputType?: new (...params: any[]) => any;
|
|
50
65
|
|
|
@@ -4,8 +4,15 @@ import { getConnectionToken, getModelToken } from './mongo/mongoose-compat.js';
|
|
|
4
4
|
import type { Model } from 'mongoose';
|
|
5
5
|
|
|
6
6
|
import { registerModelSchema } from './model-metadata.registry.js';
|
|
7
|
+
import { isDeclaredSchema } from './mongo/model-declarations.js';
|
|
8
|
+
import { applySyncSchema, assertSyncCollectionReady } from './mongo/sync-schema.plugin.js';
|
|
7
9
|
import { MongooseDataStore } from './mongoose-data-store.js';
|
|
8
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
getPersistedModelOptions,
|
|
12
|
+
getPersistedModelOptionsByName,
|
|
13
|
+
getPersistedSyncByName,
|
|
14
|
+
} from './persisted-model.decorator.js';
|
|
15
|
+
import { resolveSyncOptions } from './sync-model.js';
|
|
9
16
|
import {
|
|
10
17
|
bindStoreToModel,
|
|
11
18
|
getPersistenceRuntime,
|
|
@@ -46,7 +53,15 @@ export interface PersistenceModelDefinition extends ModelDefinition {
|
|
|
46
53
|
export class CorePersistenceModule {
|
|
47
54
|
static forFeature(models: PersistenceModelDefinition[]): DynamicModule {
|
|
48
55
|
for (const model of models) {
|
|
49
|
-
|
|
56
|
+
const sync = resolveSyncOptions(
|
|
57
|
+
model.model ? getPersistedModelOptions(model.model)?.sync : getPersistedSyncByName(model.name),
|
|
58
|
+
model.name,
|
|
59
|
+
);
|
|
60
|
+
// Described as declared first, then prepared: the descriptor must not see the Mongoose sync paths
|
|
61
|
+
registerModelSchema(model.name, model.schema, sync);
|
|
62
|
+
if (sync && !isDeclaredSchema(model.schema)) {
|
|
63
|
+
applySyncSchema(model.schema, sync);
|
|
64
|
+
}
|
|
50
65
|
registerPersistedModel({
|
|
51
66
|
declaredSource:
|
|
52
67
|
model.dataSource ??
|
|
@@ -88,6 +103,10 @@ export class CorePersistenceModule {
|
|
|
88
103
|
? // No SQL source and no MongoDB connection: the project runs without a database (`database: false`)
|
|
89
104
|
unavailableStore(model.name, 'the project runs without a database (`database: false`)')
|
|
90
105
|
: new MongooseDataStore(mongooseModel);
|
|
106
|
+
// A synchronized model on MongoDB: the collection must hold nothing from before it was synchronized
|
|
107
|
+
if (store.capabilities.mongoosePopulate && store.capabilities.changeOrder) {
|
|
108
|
+
await assertSyncCollectionReady(mongooseModel);
|
|
109
|
+
}
|
|
91
110
|
registerDataStore(model.name, store);
|
|
92
111
|
bindStoreToModel(mongooseModel, store);
|
|
93
112
|
// A stand-in already refuses, naming the cause; a real Mongoose model of a model routed elsewhere is made to
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from './data-source.config.js';
|
|
14
14
|
import type { ModelDescriptor } from './model-descriptor.interface.js';
|
|
15
15
|
import { getModelDescriptor } from './model-metadata.registry.js';
|
|
16
|
+
import { withSequenceField } from './sync-model.js';
|
|
16
17
|
import { MongooseDataStore } from './mongoose-data-store.js';
|
|
17
18
|
import {
|
|
18
19
|
getPersistedModels,
|
|
@@ -190,10 +191,12 @@ export class DataSourceRuntime implements PersistenceRuntime {
|
|
|
190
191
|
}
|
|
191
192
|
|
|
192
193
|
private async openStore(source: ResolvedDataSource, name: string): Promise<DataStore<any>> {
|
|
193
|
-
const
|
|
194
|
-
if (!
|
|
194
|
+
const declared = getModelDescriptor(name);
|
|
195
|
+
if (!declared) {
|
|
195
196
|
throw new Error(`No descriptor for model "${name}" — register it with CorePersistenceModule.forFeature()`);
|
|
196
197
|
}
|
|
198
|
+
// A synchronized model's table also carries its commit-ordered change position (offline-sync plan §3.4)
|
|
199
|
+
const model = withSequenceField(declared);
|
|
197
200
|
const app = await this.executor(source);
|
|
198
201
|
const dialect = this.dialect(source);
|
|
199
202
|
let changes: SchemaChange[];
|