@pattern-stack/codegen 0.10.1 → 0.12.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/CHANGELOG.md +122 -0
- package/README.md +5 -5
- package/consumer-skills/codegen/SKILL.md +2 -2
- package/consumer-skills/{sync → integration}/SKILL.md +29 -29
- package/consumer-skills/{sync → integration}/audit-and-detection.md +22 -22
- package/consumer-skills/{sync → integration}/change-sources-and-sinks.md +60 -60
- package/consumer-skills/subsystems/SKILL.md +8 -8
- package/consumer-skills/subsystems/wiring-and-order.md +7 -7
- package/dist/runtime/base-classes/index.d.ts +4 -4
- package/dist/runtime/base-classes/index.js +35 -35
- package/dist/runtime/base-classes/index.js.map +1 -1
- package/dist/runtime/base-classes/{synced-entity-repository.d.ts → integrated-entity-repository.d.ts} +15 -15
- package/dist/runtime/base-classes/{synced-entity-repository.js → integrated-entity-repository.js} +21 -21
- package/dist/runtime/base-classes/integrated-entity-repository.js.map +1 -0
- package/dist/runtime/base-classes/{synced-entity-service.d.ts → integrated-entity-service.d.ts} +6 -6
- package/dist/runtime/base-classes/{synced-entity-service.js → integrated-entity-service.js} +4 -4
- package/dist/runtime/base-classes/integrated-entity-service.js.map +1 -0
- package/dist/runtime/base-classes/{sync-upsert-config.d.ts → integration-upsert-config.d.ts} +13 -13
- package/dist/runtime/base-classes/integration-upsert-config.js +1 -0
- package/dist/runtime/base-classes/{junction-sync-repository.d.ts → junction-integration-repository.d.ts} +11 -11
- package/dist/runtime/base-classes/{junction-sync-repository.js → junction-integration-repository.js} +15 -15
- package/dist/runtime/base-classes/junction-integration-repository.js.map +1 -0
- package/dist/runtime/subsystems/auth/auth-oauth-state.schema.js.map +1 -1
- package/dist/runtime/subsystems/auth/auth.module.d.ts +4 -4
- package/dist/runtime/subsystems/auth/auth.module.js +3 -3
- package/dist/runtime/subsystems/auth/auth.module.js.map +1 -1
- package/dist/runtime/subsystems/auth/auth.tokens.d.ts +8 -8
- package/dist/runtime/subsystems/auth/auth.tokens.js +6 -6
- package/dist/runtime/subsystems/auth/auth.tokens.js.map +1 -1
- package/dist/runtime/subsystems/auth/backends/state-store.drizzle-backend.js.map +1 -1
- package/dist/runtime/subsystems/auth/controllers/auth.controller.d.ts +2 -2
- package/dist/runtime/subsystems/auth/controllers/auth.controller.js +3 -3
- package/dist/runtime/subsystems/auth/controllers/auth.controller.js.map +1 -1
- package/dist/runtime/subsystems/auth/index.d.ts +3 -3
- package/dist/runtime/subsystems/auth/index.js +40 -40
- package/dist/runtime/subsystems/auth/index.js.map +1 -1
- package/dist/runtime/subsystems/auth/middleware/requester-context.js.map +1 -1
- package/dist/runtime/subsystems/auth/protocols/auth-strategy.d.ts +3 -3
- package/dist/runtime/subsystems/auth/protocols/{integration-store.d.ts → connection-store.d.ts} +20 -20
- package/dist/runtime/subsystems/auth/protocols/connection-store.js +1 -0
- package/dist/runtime/subsystems/auth/protocols/provider-strategy.d.ts +3 -3
- package/dist/runtime/subsystems/auth/runtime/{integration-broken.error.d.ts → connection-broken.error.d.ts} +5 -5
- package/dist/runtime/subsystems/auth/runtime/connection-broken.error.js +19 -0
- package/dist/runtime/subsystems/auth/runtime/connection-broken.error.js.map +1 -0
- package/dist/runtime/subsystems/auth/runtime/oauth2-refresh.strategy.d.ts +10 -10
- package/dist/runtime/subsystems/auth/runtime/oauth2-refresh.strategy.js +28 -28
- package/dist/runtime/subsystems/auth/runtime/oauth2-refresh.strategy.js.map +1 -1
- package/dist/runtime/subsystems/auth/runtime/with-auth-retry.d.ts +1 -1
- package/dist/runtime/subsystems/auth/runtime/with-auth-retry.js +3 -3
- package/dist/runtime/subsystems/auth/runtime/with-auth-retry.js.map +1 -1
- package/dist/runtime/subsystems/index.d.ts +11 -7
- package/dist/runtime/subsystems/index.js +1041 -67
- package/dist/runtime/subsystems/index.js.map +1 -1
- package/dist/runtime/subsystems/{sync → integration}/build-change-source.d.ts +3 -3
- package/dist/runtime/subsystems/{sync → integration}/build-change-source.js +3 -3
- package/dist/runtime/subsystems/integration/build-change-source.js.map +1 -0
- package/dist/runtime/subsystems/{sync → integration}/deep-equal.differ.d.ts +2 -2
- package/dist/runtime/subsystems/{sync → integration}/deep-equal.differ.js +1 -1
- package/dist/runtime/subsystems/integration/deep-equal.differ.js.map +1 -0
- package/dist/runtime/subsystems/{sync → integration}/detection-config.schema.d.ts +3 -3
- package/dist/runtime/subsystems/{sync → integration}/detection-config.schema.js +1 -1
- package/dist/runtime/subsystems/integration/detection-config.schema.js.map +1 -0
- package/dist/runtime/subsystems/integration/entity-change-source-registry.memory.d.ts +25 -0
- package/dist/runtime/subsystems/integration/entity-change-source-registry.memory.js +34 -0
- package/dist/runtime/subsystems/integration/entity-change-source-registry.memory.js.map +1 -0
- package/dist/runtime/subsystems/integration/entity-change-source-registry.protocol.d.ts +53 -0
- package/dist/runtime/subsystems/integration/entity-change-source-registry.protocol.js +13 -0
- package/dist/runtime/subsystems/integration/entity-change-source-registry.protocol.js.map +1 -0
- package/dist/runtime/subsystems/{sync/execute-sync.use-case.d.ts → integration/execute-integration.use-case.d.ts} +13 -13
- package/dist/runtime/subsystems/{sync/execute-sync.use-case.js → integration/execute-integration.use-case.js} +30 -30
- package/dist/runtime/subsystems/integration/execute-integration.use-case.js.map +1 -0
- package/dist/runtime/subsystems/integration/index.d.ts +30 -0
- package/dist/runtime/subsystems/{sync → integration}/index.js +206 -171
- package/dist/runtime/subsystems/integration/index.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-audit.schema.d.ts → integration/integration-audit.schema.d.ts} +64 -64
- package/dist/runtime/subsystems/{sync/sync-audit.schema.js → integration/integration-audit.schema.js} +47 -47
- package/dist/runtime/subsystems/integration/integration-audit.schema.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-change-source.protocol.d.ts → integration/integration-change-source.protocol.d.ts} +10 -10
- package/dist/runtime/subsystems/integration/integration-change-source.protocol.js +1 -0
- package/dist/runtime/subsystems/{sync/sync-cursor-store.drizzle-backend.d.ts → integration/integration-cursor-store.drizzle-backend.d.ts} +1 -1
- package/dist/runtime/subsystems/{sync/sync-cursor-store.drizzle-backend.js → integration/integration-cursor-store.drizzle-backend.js} +65 -65
- package/dist/runtime/subsystems/integration/integration-cursor-store.drizzle-backend.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-cursor-store.memory-backend.d.ts → integration/integration-cursor-store.memory-backend.d.ts} +6 -6
- package/dist/runtime/subsystems/{sync/sync-cursor-store.memory-backend.js → integration/integration-cursor-store.memory-backend.js} +5 -5
- package/dist/runtime/subsystems/integration/integration-cursor-store.memory-backend.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-cursor-store.protocol.d.ts → integration/integration-cursor-store.protocol.d.ts} +13 -13
- package/dist/runtime/subsystems/integration/integration-cursor-store.protocol.js +1 -0
- package/dist/runtime/subsystems/{sync/sync-errors.d.ts → integration/integration-errors.d.ts} +2 -2
- package/dist/runtime/subsystems/{sync/sync-errors.js → integration/integration-errors.js} +3 -3
- package/dist/runtime/subsystems/integration/integration-errors.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-field-diff.protocol.d.ts → integration/integration-field-diff.protocol.d.ts} +2 -2
- package/dist/runtime/subsystems/{sync/sync-field-diff.protocol.js → integration/integration-field-diff.protocol.js} +2 -2
- package/dist/runtime/subsystems/integration/integration-field-diff.protocol.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-loopback.protocol.d.ts → integration/integration-loopback.protocol.d.ts} +2 -2
- package/dist/runtime/subsystems/integration/integration-loopback.protocol.js +1 -0
- package/dist/runtime/subsystems/{sync/sync-middleware.protocol.d.ts → integration/integration-middleware.protocol.d.ts} +5 -5
- package/dist/runtime/subsystems/integration/integration-middleware.protocol.js +1 -0
- package/dist/runtime/subsystems/{sync/sync-run-recorder.drizzle-backend.d.ts → integration/integration-run-recorder.drizzle-backend.d.ts} +5 -5
- package/dist/runtime/subsystems/{sync/sync-run-recorder.drizzle-backend.js → integration/integration-run-recorder.drizzle-backend.js} +73 -73
- package/dist/runtime/subsystems/integration/integration-run-recorder.drizzle-backend.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-run-recorder.memory-backend.d.ts → integration/integration-run-recorder.memory-backend.d.ts} +15 -15
- package/dist/runtime/subsystems/{sync/sync-run-recorder.memory-backend.js → integration/integration-run-recorder.memory-backend.js} +11 -11
- package/dist/runtime/subsystems/integration/integration-run-recorder.memory-backend.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-run-recorder.protocol.d.ts → integration/integration-run-recorder.protocol.d.ts} +25 -25
- package/dist/runtime/subsystems/integration/integration-run-recorder.protocol.js +1 -0
- package/dist/runtime/subsystems/{sync/sync-sink.protocol.d.ts → integration/integration-sink.protocol.d.ts} +5 -5
- package/dist/runtime/subsystems/integration/integration-sink.protocol.js +1 -0
- package/dist/runtime/subsystems/{sync/sync.module.d.ts → integration/integration.module.d.ts} +24 -24
- package/dist/runtime/subsystems/{sync/sync.module.js → integration/integration.module.js} +132 -132
- package/dist/runtime/subsystems/integration/integration.module.js.map +1 -0
- package/dist/runtime/subsystems/integration/integration.tokens.d.ts +60 -0
- package/dist/runtime/subsystems/integration/integration.tokens.js +20 -0
- package/dist/runtime/subsystems/integration/integration.tokens.js.map +1 -0
- package/dist/runtime/subsystems/{sync → integration}/loopback.middleware.d.ts +5 -5
- package/dist/runtime/subsystems/{sync → integration}/loopback.middleware.js +1 -1
- package/dist/runtime/subsystems/integration/loopback.middleware.js.map +1 -0
- package/dist/runtime/subsystems/{sync → integration}/poll-change-source.d.ts +5 -5
- package/dist/runtime/subsystems/{sync → integration}/poll-change-source.js +1 -1
- package/dist/runtime/subsystems/integration/poll-change-source.js.map +1 -0
- package/dist/runtime/subsystems/{sync → integration}/webhook-change-source.d.ts +5 -5
- package/dist/runtime/subsystems/{sync → integration}/webhook-change-source.js +1 -1
- package/dist/runtime/subsystems/integration/webhook-change-source.js.map +1 -0
- package/dist/runtime/subsystems/jobs/job-worker.module.d.ts +1 -1
- package/dist/runtime/subsystems/observability/index.d.ts +4 -4
- package/dist/runtime/subsystems/observability/index.js +11 -11
- package/dist/runtime/subsystems/observability/index.js.map +1 -1
- package/dist/runtime/subsystems/observability/observability.module.d.ts +2 -2
- package/dist/runtime/subsystems/observability/observability.module.js +11 -11
- package/dist/runtime/subsystems/observability/observability.module.js.map +1 -1
- package/dist/runtime/subsystems/observability/observability.protocol.d.ts +11 -11
- package/dist/runtime/subsystems/observability/observability.service.d.ts +6 -6
- package/dist/runtime/subsystems/observability/observability.service.js +11 -11
- package/dist/runtime/subsystems/observability/observability.service.js.map +1 -1
- package/dist/runtime/subsystems/observability/observability.tokens.d.ts +1 -1
- package/dist/runtime/subsystems/observability/observability.tokens.js.map +1 -1
- package/dist/runtime/subsystems/observability/reporters/bridge-metrics.reporter.d.ts +3 -3
- package/dist/runtime/subsystems/observability/reporters/bridge-metrics.reporter.js.map +1 -1
- package/dist/runtime/subsystems/observability/reporters/index.d.ts +3 -3
- package/dist/runtime/subsystems/observability/reporters/index.js.map +1 -1
- package/dist/src/cli/index.js +1336 -376
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/index.d.ts +70 -22
- package/dist/src/index.js +290 -194
- package/dist/src/index.js.map +1 -1
- package/examples/auth-integrations/README.md +32 -32
- package/examples/auth-integrations/definitions/entities/{integration.yaml → connection.yaml} +10 -10
- package/examples/auth-integrations/runtime/{integrations/adapters/integration-grant-sink.adapter.ts → connections/adapters/connection-grant-sink.adapter.ts} +7 -7
- package/examples/auth-integrations/runtime/{integrations/adapters/integration-reader.adapter.ts → connections/adapters/connection-reader.adapter.ts} +10 -10
- package/examples/auth-integrations/runtime/{integrations/adapters/integration-token-writer.adapter.ts → connections/adapters/connection-token-writer.adapter.ts} +11 -11
- package/examples/auth-integrations/runtime/connections/connections-auth.module.ts +81 -0
- package/examples/auth-integrations/runtime/{integrations/facade/integrations.service.ts → connections/facade/connections.service.ts} +35 -35
- package/examples/auth-integrations/runtime/{integrations → connections}/oauth/use-cases/create-or-update-from-oauth-grant.use-case.ts +11 -11
- package/examples/auth-integrations/runtime/{integrations/oauth/use-cases/disconnect-integration.use-case.ts → connections/oauth/use-cases/disconnect-connection.use-case.ts} +6 -6
- package/examples/auth-integrations/runtime/connections/oauth/use-cases/list-user-connections.use-case.ts +21 -0
- package/examples/auth-integrations/runtime/connections/oauth/use-cases/mark-connection-requires-reauth.use-case.ts +21 -0
- package/package.json +9 -1
- package/runtime/base-classes/index.ts +8 -8
- package/runtime/base-classes/{synced-entity-repository.ts → integrated-entity-repository.ts} +36 -36
- package/runtime/base-classes/{synced-entity-service.ts → integrated-entity-service.ts} +6 -6
- package/runtime/base-classes/{sync-upsert-config.ts → integration-upsert-config.ts} +12 -12
- package/runtime/base-classes/{junction-sync-repository.ts → junction-integration-repository.ts} +28 -28
- package/runtime/subsystems/auth/auth-oauth-state.schema.ts +1 -1
- package/runtime/subsystems/auth/auth.module.ts +4 -4
- package/runtime/subsystems/auth/auth.tokens.ts +7 -7
- package/runtime/subsystems/auth/controllers/auth.controller.ts +7 -7
- package/runtime/subsystems/auth/index.ts +19 -19
- package/runtime/subsystems/auth/protocols/auth-strategy.ts +3 -3
- package/runtime/subsystems/auth/protocols/{integration-store.ts → connection-store.ts} +19 -19
- package/runtime/subsystems/auth/protocols/provider-strategy.ts +2 -2
- package/runtime/subsystems/auth/runtime/{integration-broken.error.ts → connection-broken.error.ts} +5 -5
- package/runtime/subsystems/auth/runtime/oauth2-refresh.strategy.ts +35 -35
- package/runtime/subsystems/auth/runtime/with-auth-retry.ts +3 -3
- package/runtime/subsystems/index.ts +26 -11
- package/runtime/subsystems/{sync → integration}/build-change-source.ts +3 -3
- package/runtime/subsystems/{sync → integration}/deep-equal.differ.ts +7 -7
- package/runtime/subsystems/{sync → integration}/detection-config.schema.ts +3 -3
- package/runtime/subsystems/integration/entity-change-source-registry.memory.ts +40 -0
- package/runtime/subsystems/integration/entity-change-source-registry.protocol.ts +59 -0
- package/runtime/subsystems/{sync/execute-sync.use-case.ts → integration/execute-integration.use-case.ts} +40 -40
- package/runtime/subsystems/{sync → integration}/index.ts +56 -47
- package/runtime/subsystems/{sync/sync-audit.schema.ts → integration/integration-audit.schema.ts} +61 -61
- package/runtime/subsystems/{sync/sync-change-source.protocol.ts → integration/integration-change-source.protocol.ts} +9 -9
- package/runtime/subsystems/{sync/sync-cursor-store.drizzle-backend.ts → integration/integration-cursor-store.drizzle-backend.ts} +30 -30
- package/runtime/subsystems/{sync/sync-cursor-store.memory-backend.ts → integration/integration-cursor-store.memory-backend.ts} +9 -9
- package/runtime/subsystems/{sync/sync-cursor-store.protocol.ts → integration/integration-cursor-store.protocol.ts} +13 -13
- package/runtime/subsystems/{sync/sync-errors.ts → integration/integration-errors.ts} +3 -3
- package/runtime/subsystems/{sync/sync-field-diff.protocol.ts → integration/integration-field-diff.protocol.ts} +2 -2
- package/runtime/subsystems/{sync/sync-loopback.protocol.ts → integration/integration-loopback.protocol.ts} +2 -2
- package/runtime/subsystems/{sync/sync-middleware.protocol.ts → integration/integration-middleware.protocol.ts} +6 -6
- package/runtime/subsystems/{sync/sync-run-recorder.drizzle-backend.ts → integration/integration-run-recorder.drizzle-backend.ts} +39 -39
- package/runtime/subsystems/{sync/sync-run-recorder.memory-backend.ts → integration/integration-run-recorder.memory-backend.ts} +23 -23
- package/runtime/subsystems/{sync/sync-run-recorder.protocol.ts → integration/integration-run-recorder.protocol.ts} +25 -25
- package/runtime/subsystems/{sync/sync-sink.protocol.ts → integration/integration-sink.protocol.ts} +4 -4
- package/runtime/subsystems/{sync/sync.module.ts → integration/integration.module.ts} +48 -48
- package/runtime/subsystems/integration/integration.tokens.ts +63 -0
- package/runtime/subsystems/{sync → integration}/loopback.middleware.ts +5 -5
- package/runtime/subsystems/{sync → integration}/poll-change-source.ts +7 -7
- package/runtime/subsystems/{sync → integration}/webhook-change-source.ts +7 -7
- package/runtime/subsystems/observability/index.ts +1 -1
- package/runtime/subsystems/observability/observability.module.ts +2 -2
- package/runtime/subsystems/observability/observability.protocol.ts +11 -11
- package/runtime/subsystems/observability/observability.service.ts +13 -13
- package/runtime/subsystems/observability/observability.tokens.ts +1 -1
- package/src/patterns/library/index.ts +4 -4
- package/src/patterns/library/{synced.pattern.ts → integrated.pattern.ts} +12 -12
- package/src/patterns/library/junction.pattern.ts +1 -1
- package/src/patterns/pattern-definition.ts +3 -3
- package/templates/entity/new/backend/modules/core/{sync-source.ejs.t → integration-source.ejs.t} +6 -6
- package/templates/entity/new/clean-lite-ps/entity.ejs.t +12 -3
- package/templates/entity/new/clean-lite-ps/module.ejs.t +1 -1
- package/templates/entity/new/clean-lite-ps/prompt-extension.js +243 -60
- package/templates/entity/new/clean-lite-ps/repository.ejs.t +27 -27
- package/templates/entity/new/frontend/collections/collection.ejs.t +26 -1
- package/templates/entity/new/frontend/collections/collections-base.ejs.t +11 -0
- package/templates/entity/new/frontend/entity/combined.ejs.t +31 -1
- package/templates/entity/new/prompt.js +27 -15
- package/templates/junction/new/entity.ejs.t +1 -1
- package/templates/junction/new/prompt.js +24 -24
- package/templates/junction/new/repository.ejs.t +19 -19
- package/templates/subsystem/auth/auth-oauth-state.schema.ejs.t +2 -2
- package/templates/subsystem/auth-config/prompt.js +1 -1
- package/templates/subsystem/auth-integrations/app-module-hook.ejs.t +5 -5
- package/templates/subsystem/bridge/prompt.js +1 -1
- package/templates/subsystem/integration/integration-audit.schema.ejs.t +192 -0
- package/templates/subsystem/{sync → integration}/prompt.js +12 -12
- package/templates/subsystem/{sync-config/codegen-config-sync-block.ejs.t → integration-config/codegen-config-integration-block.ejs.t} +7 -7
- package/templates/subsystem/integration-config/prompt.js +22 -0
- package/templates/subsystem/jobs/worker.ejs.t +2 -2
- package/templates/subsystem/observability/main-hook.ejs.t +1 -1
- package/templates/subsystem/observability/prompt.js +1 -1
- package/templates/subsystem/openapi-config/prompt.js +1 -1
- package/dist/runtime/base-classes/junction-sync-repository.js.map +0 -1
- package/dist/runtime/base-classes/sync-upsert-config.js +0 -1
- package/dist/runtime/base-classes/synced-entity-repository.js.map +0 -1
- package/dist/runtime/base-classes/synced-entity-service.js.map +0 -1
- package/dist/runtime/subsystems/auth/protocols/integration-store.js +0 -1
- package/dist/runtime/subsystems/auth/runtime/integration-broken.error.js +0 -19
- package/dist/runtime/subsystems/auth/runtime/integration-broken.error.js.map +0 -1
- package/dist/runtime/subsystems/sync/build-change-source.js.map +0 -1
- package/dist/runtime/subsystems/sync/deep-equal.differ.js.map +0 -1
- package/dist/runtime/subsystems/sync/detection-config.schema.js.map +0 -1
- package/dist/runtime/subsystems/sync/execute-sync.use-case.js.map +0 -1
- package/dist/runtime/subsystems/sync/index.d.ts +0 -28
- package/dist/runtime/subsystems/sync/index.js.map +0 -1
- package/dist/runtime/subsystems/sync/loopback.middleware.js.map +0 -1
- package/dist/runtime/subsystems/sync/poll-change-source.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-audit.schema.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-change-source.protocol.js +0 -1
- package/dist/runtime/subsystems/sync/sync-cursor-store.drizzle-backend.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-cursor-store.memory-backend.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-cursor-store.protocol.js +0 -1
- package/dist/runtime/subsystems/sync/sync-errors.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-field-diff.protocol.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-loopback.protocol.js +0 -1
- package/dist/runtime/subsystems/sync/sync-middleware.protocol.js +0 -1
- package/dist/runtime/subsystems/sync/sync-run-recorder.drizzle-backend.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-run-recorder.memory-backend.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-run-recorder.protocol.js +0 -1
- package/dist/runtime/subsystems/sync/sync-sink.protocol.js +0 -1
- package/dist/runtime/subsystems/sync/sync.module.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync.tokens.d.ts +0 -47
- package/dist/runtime/subsystems/sync/sync.tokens.js +0 -18
- package/dist/runtime/subsystems/sync/sync.tokens.js.map +0 -1
- package/dist/runtime/subsystems/sync/webhook-change-source.js.map +0 -1
- package/examples/auth-integrations/runtime/integrations/integrations-auth.module.ts +0 -81
- package/examples/auth-integrations/runtime/integrations/oauth/use-cases/list-user-integrations.use-case.ts +0 -21
- package/examples/auth-integrations/runtime/integrations/oauth/use-cases/mark-integration-requires-reauth.use-case.ts +0 -21
- package/runtime/subsystems/sync/sync.tokens.ts +0 -49
- package/templates/entity/new/backend/modules/core/sync-source.providers.ejs.t +0 -18
- package/templates/subsystem/sync/sync-audit.schema.ejs.t +0 -192
- package/templates/subsystem/sync-config/prompt.js +0 -22
- /package/dist/runtime/base-classes/{sync-upsert-config.js.map → integration-upsert-config.js.map} +0 -0
- /package/dist/runtime/subsystems/auth/protocols/{integration-store.js.map → connection-store.js.map} +0 -0
- /package/dist/runtime/subsystems/{sync/sync-change-source.protocol.js.map → integration/integration-change-source.protocol.js.map} +0 -0
- /package/dist/runtime/subsystems/{sync/sync-cursor-store.protocol.js.map → integration/integration-cursor-store.protocol.js.map} +0 -0
- /package/dist/runtime/subsystems/{sync/sync-loopback.protocol.js.map → integration/integration-loopback.protocol.js.map} +0 -0
- /package/dist/runtime/subsystems/{sync/sync-middleware.protocol.js.map → integration/integration-middleware.protocol.js.map} +0 -0
- /package/dist/runtime/subsystems/{sync/sync-run-recorder.protocol.js.map → integration/integration-run-recorder.protocol.js.map} +0 -0
- /package/dist/runtime/subsystems/{sync/sync-sink.protocol.js.map → integration/integration-sink.protocol.js.map} +0 -0
package/dist/src/index.js
CHANGED
|
@@ -43,7 +43,7 @@ import { parse as parseYaml } from "yaml";
|
|
|
43
43
|
// src/schema/entity-definition.schema.ts
|
|
44
44
|
import { z as z3 } from "zod";
|
|
45
45
|
|
|
46
|
-
// runtime/subsystems/
|
|
46
|
+
// runtime/subsystems/integration/integration-field-diff.protocol.ts
|
|
47
47
|
import { z } from "zod";
|
|
48
48
|
var FieldDiffValueSchema = z.object({
|
|
49
49
|
from: z.unknown(),
|
|
@@ -51,7 +51,7 @@ var FieldDiffValueSchema = z.object({
|
|
|
51
51
|
});
|
|
52
52
|
var FieldDiffSchema = z.record(z.string(), FieldDiffValueSchema);
|
|
53
53
|
|
|
54
|
-
// runtime/subsystems/
|
|
54
|
+
// runtime/subsystems/integration/detection-config.schema.ts
|
|
55
55
|
import { z as z2 } from "zod";
|
|
56
56
|
var FieldMappingSchema = z2.object({
|
|
57
57
|
source: z2.string().min(1),
|
|
@@ -109,21 +109,21 @@ var DetectionConfigSchema = z2.discriminatedUnion("mode", [
|
|
|
109
109
|
WebhookModeSchema
|
|
110
110
|
]);
|
|
111
111
|
|
|
112
|
-
// runtime/subsystems/
|
|
113
|
-
var
|
|
114
|
-
var
|
|
115
|
-
var
|
|
116
|
-
var
|
|
117
|
-
var
|
|
118
|
-
var
|
|
119
|
-
var
|
|
112
|
+
// runtime/subsystems/integration/integration.tokens.ts
|
|
113
|
+
var INTEGRATION_CHANGE_SOURCE = "INTEGRATION_CHANGE_SOURCE";
|
|
114
|
+
var INTEGRATION_CURSOR_STORE = "INTEGRATION_CURSOR_STORE";
|
|
115
|
+
var INTEGRATION_FIELD_DIFFER = "INTEGRATION_FIELD_DIFFER";
|
|
116
|
+
var INTEGRATION_SINK = "INTEGRATION_SINK";
|
|
117
|
+
var INTEGRATION_RUN_RECORDER = "INTEGRATION_RUN_RECORDER";
|
|
118
|
+
var INTEGRATION_MODULE_OPTIONS = "INTEGRATION_MODULE_OPTIONS";
|
|
119
|
+
var INTEGRATION_MULTI_TENANT = "INTEGRATION_MULTI_TENANT";
|
|
120
120
|
|
|
121
|
-
// runtime/subsystems/
|
|
121
|
+
// runtime/subsystems/integration/integration-errors.ts
|
|
122
122
|
var MissingTenantIdError = class extends Error {
|
|
123
123
|
name = "MissingTenantIdError";
|
|
124
124
|
constructor(operation) {
|
|
125
125
|
super(
|
|
126
|
-
`Missing tenantId for
|
|
126
|
+
`Missing tenantId for integration operation '${operation}'. IntegrationModule is configured with multiTenant: true \u2014 every call must include a non-null tenantId. Either pass the tenantId or disable multi-tenancy on the module.`
|
|
127
127
|
);
|
|
128
128
|
}
|
|
129
129
|
};
|
|
@@ -134,7 +134,7 @@ function assertTenantId(tenantId, options) {
|
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
// runtime/subsystems/
|
|
137
|
+
// runtime/subsystems/integration/integration-audit.schema.ts
|
|
138
138
|
import {
|
|
139
139
|
pgEnum,
|
|
140
140
|
pgTable,
|
|
@@ -147,39 +147,39 @@ import {
|
|
|
147
147
|
index,
|
|
148
148
|
uniqueIndex
|
|
149
149
|
} from "drizzle-orm/pg-core";
|
|
150
|
-
var
|
|
150
|
+
var integrationRunDirectionEnum = pgEnum("integration_run_direction", [
|
|
151
151
|
"inbound",
|
|
152
152
|
"outbound"
|
|
153
153
|
]);
|
|
154
|
-
var
|
|
154
|
+
var integrationRunActionEnum = pgEnum("integration_run_action", [
|
|
155
155
|
"poll",
|
|
156
156
|
"cdc",
|
|
157
157
|
"webhook",
|
|
158
158
|
"manual",
|
|
159
159
|
"writeback"
|
|
160
160
|
]);
|
|
161
|
-
var
|
|
161
|
+
var integrationRunStatusEnum = pgEnum("integration_run_status", [
|
|
162
162
|
"running",
|
|
163
163
|
"success",
|
|
164
164
|
"no_changes",
|
|
165
165
|
"failed"
|
|
166
166
|
]);
|
|
167
|
-
var
|
|
167
|
+
var integrationRunItemOperationEnum = pgEnum("integration_run_item_operation", [
|
|
168
168
|
"created",
|
|
169
169
|
"updated",
|
|
170
170
|
"deleted",
|
|
171
171
|
"noop"
|
|
172
172
|
]);
|
|
173
|
-
var
|
|
173
|
+
var integrationRunItemStatusEnum = pgEnum("integration_run_item_status", [
|
|
174
174
|
"success",
|
|
175
175
|
"failed",
|
|
176
176
|
"skipped"
|
|
177
177
|
]);
|
|
178
|
-
var
|
|
179
|
-
"
|
|
178
|
+
var integrationSubscriptions = pgTable(
|
|
179
|
+
"integration_subscriptions",
|
|
180
180
|
{
|
|
181
181
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
182
|
-
|
|
182
|
+
connectionId: text("connection_id").notNull(),
|
|
183
183
|
adapter: text("adapter").notNull(),
|
|
184
184
|
domain: text("domain").notNull(),
|
|
185
185
|
externalRef: text("external_ref"),
|
|
@@ -194,8 +194,8 @@ var syncSubscriptions = pgTable(
|
|
|
194
194
|
* successful run advances it.
|
|
195
195
|
*/
|
|
196
196
|
cursor: jsonb("cursor").$type(),
|
|
197
|
-
|
|
198
|
-
/** Runtime-enforced when `
|
|
197
|
+
lastIntegrationAt: timestamp("last_integration_at", { withTimezone: true }),
|
|
198
|
+
/** Runtime-enforced when `INTEGRATION_MULTI_TENANT` is true; see SYNC-6. */
|
|
199
199
|
tenantId: text("tenant_id"),
|
|
200
200
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
201
201
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
@@ -204,31 +204,31 @@ var syncSubscriptions = pgTable(
|
|
|
204
204
|
/**
|
|
205
205
|
* Composite uniqueness per the epic shape. `external_ref` is nullable;
|
|
206
206
|
* Postgres treats NULLs as distinct in a UNIQUE constraint, which means
|
|
207
|
-
* two rows with the same `(
|
|
207
|
+
* two rows with the same `(connection_id, adapter, domain)` and NULL
|
|
208
208
|
* external_ref are allowed. That's intentional — a subscription with
|
|
209
209
|
* NULL external_ref covers the full domain, and duplicates there would
|
|
210
210
|
* be a consumer-layer modeling issue, not a schema concern.
|
|
211
211
|
*/
|
|
212
|
-
|
|
213
|
-
t.
|
|
212
|
+
uqIntegrationSubscriptionTuple: uniqueIndex("uq_integration_subscriptions_tuple").on(
|
|
213
|
+
t.connectionId,
|
|
214
214
|
t.adapter,
|
|
215
215
|
t.domain,
|
|
216
216
|
t.externalRef
|
|
217
217
|
),
|
|
218
218
|
/** Scheduling query: list enabled subscriptions ordered by staleness. */
|
|
219
|
-
|
|
220
|
-
"
|
|
221
|
-
).on(t.enabled, t.
|
|
219
|
+
idxIntegrationSubscriptionsEnabledLastIntegration: index(
|
|
220
|
+
"idx_integration_subscriptions_enabled_last_integration"
|
|
221
|
+
).on(t.enabled, t.lastIntegrationAt)
|
|
222
222
|
})
|
|
223
223
|
);
|
|
224
|
-
var
|
|
225
|
-
"
|
|
224
|
+
var integrationRuns = pgTable(
|
|
225
|
+
"integration_runs",
|
|
226
226
|
{
|
|
227
227
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
228
|
-
subscriptionId: uuid("subscription_id").notNull().references(() =>
|
|
229
|
-
direction:
|
|
230
|
-
action:
|
|
231
|
-
status:
|
|
228
|
+
subscriptionId: uuid("subscription_id").notNull().references(() => integrationSubscriptions.id, { onDelete: "cascade" }),
|
|
229
|
+
direction: integrationRunDirectionEnum("direction").notNull(),
|
|
230
|
+
action: integrationRunActionEnum("action").notNull(),
|
|
231
|
+
status: integrationRunStatusEnum("status").notNull().default("running"),
|
|
232
232
|
recordsFound: integer("records_found").notNull().default(0),
|
|
233
233
|
recordsProcessed: integer("records_processed").notNull().default(0),
|
|
234
234
|
cursorBefore: jsonb("cursor_before").$type(),
|
|
@@ -237,31 +237,31 @@ var syncRuns = pgTable(
|
|
|
237
237
|
error: text("error"),
|
|
238
238
|
startedAt: timestamp("started_at", { withTimezone: true }).notNull().defaultNow(),
|
|
239
239
|
completedAt: timestamp("completed_at", { withTimezone: true }),
|
|
240
|
-
/** Runtime-enforced when `
|
|
240
|
+
/** Runtime-enforced when `INTEGRATION_MULTI_TENANT` is true; see SYNC-6. */
|
|
241
241
|
tenantId: text("tenant_id")
|
|
242
242
|
},
|
|
243
243
|
(t) => ({
|
|
244
244
|
/** Timeline read: "most recent runs for this subscription". */
|
|
245
|
-
|
|
246
|
-
"
|
|
245
|
+
idxIntegrationRunsSubscriptionStartedAt: index(
|
|
246
|
+
"idx_integration_runs_subscription_started_at"
|
|
247
247
|
).on(t.subscriptionId, t.startedAt),
|
|
248
248
|
/** Stale-run sweeper: "runs that started > N minutes ago and are still running". */
|
|
249
|
-
|
|
249
|
+
idxIntegrationRunsStatusStartedAt: index("idx_integration_runs_status_started_at").on(
|
|
250
250
|
t.status,
|
|
251
251
|
t.startedAt
|
|
252
252
|
)
|
|
253
253
|
})
|
|
254
254
|
);
|
|
255
|
-
var
|
|
256
|
-
"
|
|
255
|
+
var integrationRunItems = pgTable(
|
|
256
|
+
"integration_run_items",
|
|
257
257
|
{
|
|
258
258
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
259
|
-
|
|
259
|
+
integrationRunId: uuid("integration_run_id").notNull().references(() => integrationRuns.id, { onDelete: "cascade" }),
|
|
260
260
|
entityType: text("entity_type").notNull(),
|
|
261
261
|
externalId: text("external_id").notNull(),
|
|
262
262
|
localId: text("local_id"),
|
|
263
|
-
operation:
|
|
264
|
-
status:
|
|
263
|
+
operation: integrationRunItemOperationEnum("operation").notNull(),
|
|
264
|
+
status: integrationRunItemStatusEnum("status").notNull(),
|
|
265
265
|
/**
|
|
266
266
|
* Structured per-field diff — ADR-0003 shape enforced by
|
|
267
267
|
* `FieldDiffSchema.parse` at the recorder service layer.
|
|
@@ -275,23 +275,23 @@ var syncRunItems = pgTable(
|
|
|
275
275
|
title: text("title"),
|
|
276
276
|
error: text("error"),
|
|
277
277
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
278
|
-
/** Runtime-enforced when `
|
|
278
|
+
/** Runtime-enforced when `INTEGRATION_MULTI_TENANT` is true; see SYNC-6. */
|
|
279
279
|
tenantId: text("tenant_id")
|
|
280
280
|
},
|
|
281
281
|
(t) => ({
|
|
282
282
|
/** Ordered timeline within a run. */
|
|
283
|
-
|
|
284
|
-
t.
|
|
283
|
+
idxIntegrationRunItemsRunCreatedAt: index("idx_integration_run_items_run_created_at").on(
|
|
284
|
+
t.integrationRunId,
|
|
285
285
|
t.createdAt
|
|
286
286
|
),
|
|
287
|
-
/** Per-record history: "every
|
|
288
|
-
|
|
289
|
-
"
|
|
287
|
+
/** Per-record history: "every integration that touched opportunity/$extId". */
|
|
288
|
+
idxIntegrationRunItemsEntityExternal: index(
|
|
289
|
+
"idx_integration_run_items_entity_external"
|
|
290
290
|
).on(t.entityType, t.externalId)
|
|
291
291
|
})
|
|
292
292
|
);
|
|
293
293
|
|
|
294
|
-
// runtime/subsystems/
|
|
294
|
+
// runtime/subsystems/integration/integration-cursor-store.memory-backend.ts
|
|
295
295
|
import { Injectable } from "@nestjs/common";
|
|
296
296
|
var MemoryCursorStore = class {
|
|
297
297
|
/**
|
|
@@ -302,7 +302,7 @@ var MemoryCursorStore = class {
|
|
|
302
302
|
/**
|
|
303
303
|
* Seedable subscription metadata for `listAll` — the memory backend
|
|
304
304
|
* stores only `subscriptionId → cursor` in its write path, so the
|
|
305
|
-
* snapshot shape (`
|
|
305
|
+
* snapshot shape (`connectionId`, `adapter`, `domain`, `externalRef`,
|
|
306
306
|
* timestamps) has no natural source without test seeding. Tests populate
|
|
307
307
|
* this map; unseeded entries get empty-string metadata and `new Date(0)`
|
|
308
308
|
* timestamps so the shape stays stable. Production paths go through the
|
|
@@ -322,12 +322,12 @@ var MemoryCursorStore = class {
|
|
|
322
322
|
const meta = this.subscriptions.get(subscriptionId);
|
|
323
323
|
snapshots.push({
|
|
324
324
|
subscriptionId,
|
|
325
|
-
|
|
325
|
+
connectionId: meta?.connectionId ?? "",
|
|
326
326
|
adapter: meta?.adapter ?? "",
|
|
327
327
|
domain: meta?.domain ?? "",
|
|
328
328
|
externalRef: meta?.externalRef ?? null,
|
|
329
329
|
cursor: cursor ?? null,
|
|
330
|
-
|
|
330
|
+
lastIntegrationAt: meta?.lastIntegrationAt ?? null,
|
|
331
331
|
updatedAt: meta?.updatedAt ?? /* @__PURE__ */ new Date(0),
|
|
332
332
|
tenantId: null
|
|
333
333
|
});
|
|
@@ -346,7 +346,7 @@ MemoryCursorStore = __decorateClass([
|
|
|
346
346
|
Injectable()
|
|
347
347
|
], MemoryCursorStore);
|
|
348
348
|
|
|
349
|
-
// runtime/subsystems/
|
|
349
|
+
// runtime/subsystems/integration/integration-run-recorder.memory-backend.ts
|
|
350
350
|
import { Injectable as Injectable2 } from "@nestjs/common";
|
|
351
351
|
var MemoryRunRecorder = class {
|
|
352
352
|
/**
|
|
@@ -355,14 +355,14 @@ var MemoryRunRecorder = class {
|
|
|
355
355
|
*/
|
|
356
356
|
runs = /* @__PURE__ */ new Map();
|
|
357
357
|
/**
|
|
358
|
-
* Items keyed by `
|
|
359
|
-
* mirrors the timeline the `(
|
|
358
|
+
* Items keyed by `integration_run_id`, array order matches insertion order —
|
|
359
|
+
* mirrors the timeline the `(integration_run_id, created_at)` index produces
|
|
360
360
|
* in Postgres.
|
|
361
361
|
*/
|
|
362
362
|
items = /* @__PURE__ */ new Map();
|
|
363
363
|
/**
|
|
364
364
|
* Seedable subscription metadata — tests populate this to make
|
|
365
|
-
* `listRecent` return meaningful `
|
|
365
|
+
* `listRecent` return meaningful `connectionId` values. The memory
|
|
366
366
|
* backend doesn't track subscriptions on its own (only runs + items), so
|
|
367
367
|
* this map is the intentional extension point: tests write entries for
|
|
368
368
|
* the subscription ids they use, production code never touches it.
|
|
@@ -391,10 +391,10 @@ var MemoryRunRecorder = class {
|
|
|
391
391
|
}
|
|
392
392
|
async recordItem(input) {
|
|
393
393
|
FieldDiffSchema.parse(input.changedFields);
|
|
394
|
-
const bucket = this.items.get(input.
|
|
394
|
+
const bucket = this.items.get(input.integrationRunId);
|
|
395
395
|
if (!bucket) {
|
|
396
396
|
throw new Error(
|
|
397
|
-
`MemoryRunRecorder.recordItem: no run started for id '${input.
|
|
397
|
+
`MemoryRunRecorder.recordItem: no run started for id '${input.integrationRunId}'. Call startRun(...) first.`
|
|
398
398
|
);
|
|
399
399
|
}
|
|
400
400
|
bucket.push(input);
|
|
@@ -420,10 +420,10 @@ var MemoryRunRecorder = class {
|
|
|
420
420
|
return filtered.sort((a, b) => b.startedAt.getTime() - a.startedAt.getTime()).slice(0, limit).map((r) => ({
|
|
421
421
|
id: r.id,
|
|
422
422
|
subscriptionId: r.subscriptionId,
|
|
423
|
-
//
|
|
423
|
+
// connectionId is only knowable if the test seeded subscriptions
|
|
424
424
|
// metadata; empty string otherwise. The Drizzle backend resolves
|
|
425
425
|
// it via JOIN, which is the production path.
|
|
426
|
-
|
|
426
|
+
connectionId: this.subscriptions.get(r.subscriptionId)?.connectionId ?? "",
|
|
427
427
|
status: r.status,
|
|
428
428
|
startedAt: r.startedAt,
|
|
429
429
|
completedAt: r.completedAt,
|
|
@@ -451,7 +451,7 @@ MemoryRunRecorder = __decorateClass([
|
|
|
451
451
|
Injectable2()
|
|
452
452
|
], MemoryRunRecorder);
|
|
453
453
|
|
|
454
|
-
// runtime/subsystems/
|
|
454
|
+
// runtime/subsystems/integration/deep-equal.differ.ts
|
|
455
455
|
import { Injectable as Injectable3 } from "@nestjs/common";
|
|
456
456
|
var DEFAULT_IGNORE_FIELDS = /* @__PURE__ */ new Set([
|
|
457
457
|
"id",
|
|
@@ -550,9 +550,9 @@ function deepEqualObject(a, b) {
|
|
|
550
550
|
return true;
|
|
551
551
|
}
|
|
552
552
|
|
|
553
|
-
// runtime/subsystems/
|
|
553
|
+
// runtime/subsystems/integration/execute-integration.use-case.ts
|
|
554
554
|
import { Inject, Injectable as Injectable4, Logger, Optional } from "@nestjs/common";
|
|
555
|
-
var
|
|
555
|
+
var ExecuteIntegrationUseCase = class {
|
|
556
556
|
constructor(source, cursors, differ, sink, recorder, multiTenant = false) {
|
|
557
557
|
this.source = source;
|
|
558
558
|
this.cursors = cursors;
|
|
@@ -567,7 +567,7 @@ var ExecuteSyncUseCase = class {
|
|
|
567
567
|
sink;
|
|
568
568
|
recorder;
|
|
569
569
|
multiTenant;
|
|
570
|
-
logger = new Logger(
|
|
570
|
+
logger = new Logger(ExecuteIntegrationUseCase.name);
|
|
571
571
|
async execute(input) {
|
|
572
572
|
assertTenantId(input.tenantId, {
|
|
573
573
|
multiTenant: this.multiTenant,
|
|
@@ -602,10 +602,10 @@ var ExecuteSyncUseCase = class {
|
|
|
602
602
|
recordsFailed++;
|
|
603
603
|
const message = err instanceof Error ? err.message : String(err);
|
|
604
604
|
this.logger.warn(
|
|
605
|
-
`
|
|
605
|
+
`integration item failed: subscription=${input.subscription.id} externalId=${change.externalId}: ${message}`
|
|
606
606
|
);
|
|
607
607
|
await this.recorder.recordItem({
|
|
608
|
-
|
|
608
|
+
integrationRunId: runId,
|
|
609
609
|
entityType: input.subscription.domain,
|
|
610
610
|
externalId: change.externalId,
|
|
611
611
|
operation: change.operation === "deleted" ? "deleted" : "updated",
|
|
@@ -628,7 +628,7 @@ var ExecuteSyncUseCase = class {
|
|
|
628
628
|
status = "failed";
|
|
629
629
|
runError = err instanceof Error ? err.message : String(err);
|
|
630
630
|
this.logger.error(
|
|
631
|
-
`
|
|
631
|
+
`integration source failed: subscription=${input.subscription.id}: ${runError}`
|
|
632
632
|
);
|
|
633
633
|
}
|
|
634
634
|
if (cursorAdvanced && latestCursor !== null && latestCursor !== void 0) {
|
|
@@ -673,7 +673,7 @@ var ExecuteSyncUseCase = class {
|
|
|
673
673
|
change.externalId
|
|
674
674
|
);
|
|
675
675
|
await this.recorder.recordItem({
|
|
676
|
-
|
|
676
|
+
integrationRunId: runId,
|
|
677
677
|
entityType: input.subscription.domain,
|
|
678
678
|
externalId: change.externalId,
|
|
679
679
|
localId: result?.id ?? null,
|
|
@@ -696,7 +696,7 @@ var ExecuteSyncUseCase = class {
|
|
|
696
696
|
if (diff === "noop") {
|
|
697
697
|
if (!this.sink.reprojectsOnNoop) {
|
|
698
698
|
await this.recorder.recordItem({
|
|
699
|
-
|
|
699
|
+
integrationRunId: runId,
|
|
700
700
|
entityType: input.subscription.domain,
|
|
701
701
|
externalId: change.externalId,
|
|
702
702
|
localId: null,
|
|
@@ -713,7 +713,7 @@ var ExecuteSyncUseCase = class {
|
|
|
713
713
|
input.provider
|
|
714
714
|
);
|
|
715
715
|
await this.recorder.recordItem({
|
|
716
|
-
|
|
716
|
+
integrationRunId: runId,
|
|
717
717
|
entityType: input.subscription.domain,
|
|
718
718
|
externalId: change.externalId,
|
|
719
719
|
localId: noopLocalId,
|
|
@@ -730,7 +730,7 @@ var ExecuteSyncUseCase = class {
|
|
|
730
730
|
input.provider
|
|
731
731
|
);
|
|
732
732
|
await this.recorder.recordItem({
|
|
733
|
-
|
|
733
|
+
integrationRunId: runId,
|
|
734
734
|
entityType: input.subscription.domain,
|
|
735
735
|
externalId: change.externalId,
|
|
736
736
|
localId,
|
|
@@ -741,25 +741,25 @@ var ExecuteSyncUseCase = class {
|
|
|
741
741
|
});
|
|
742
742
|
}
|
|
743
743
|
};
|
|
744
|
-
|
|
744
|
+
ExecuteIntegrationUseCase = __decorateClass([
|
|
745
745
|
Injectable4(),
|
|
746
|
-
__decorateParam(0, Inject(
|
|
747
|
-
__decorateParam(1, Inject(
|
|
748
|
-
__decorateParam(2, Inject(
|
|
749
|
-
__decorateParam(3, Inject(
|
|
750
|
-
__decorateParam(4, Inject(
|
|
746
|
+
__decorateParam(0, Inject(INTEGRATION_CHANGE_SOURCE)),
|
|
747
|
+
__decorateParam(1, Inject(INTEGRATION_CURSOR_STORE)),
|
|
748
|
+
__decorateParam(2, Inject(INTEGRATION_FIELD_DIFFER)),
|
|
749
|
+
__decorateParam(3, Inject(INTEGRATION_SINK)),
|
|
750
|
+
__decorateParam(4, Inject(INTEGRATION_RUN_RECORDER)),
|
|
751
751
|
__decorateParam(5, Optional()),
|
|
752
|
-
__decorateParam(5, Inject(
|
|
753
|
-
],
|
|
752
|
+
__decorateParam(5, Inject(INTEGRATION_MULTI_TENANT))
|
|
753
|
+
], ExecuteIntegrationUseCase);
|
|
754
754
|
|
|
755
|
-
// runtime/subsystems/
|
|
755
|
+
// runtime/subsystems/integration/integration-cursor-store.drizzle-backend.ts
|
|
756
756
|
import { Inject as Inject2, Injectable as Injectable5, Optional as Optional2 } from "@nestjs/common";
|
|
757
757
|
import { and, desc, eq } from "drizzle-orm";
|
|
758
758
|
|
|
759
759
|
// runtime/constants/tokens.ts
|
|
760
760
|
var DRIZZLE = "DRIZZLE";
|
|
761
761
|
|
|
762
|
-
// runtime/subsystems/
|
|
762
|
+
// runtime/subsystems/integration/integration-cursor-store.drizzle-backend.ts
|
|
763
763
|
var PostgresCursorStore = class {
|
|
764
764
|
constructor(db, multiTenant) {
|
|
765
765
|
this.db = db;
|
|
@@ -769,15 +769,15 @@ var PostgresCursorStore = class {
|
|
|
769
769
|
multiTenant;
|
|
770
770
|
async get(subscriptionId, tenantId) {
|
|
771
771
|
const where = this.buildWhere(subscriptionId, tenantId, "cursor.get");
|
|
772
|
-
const rows = await this.db.select({ cursor:
|
|
772
|
+
const rows = await this.db.select({ cursor: integrationSubscriptions.cursor }).from(integrationSubscriptions).where(where).limit(1);
|
|
773
773
|
if (rows.length === 0) return null;
|
|
774
774
|
return rows[0]?.cursor ?? null;
|
|
775
775
|
}
|
|
776
776
|
async put(subscriptionId, cursor, tenantId) {
|
|
777
777
|
const where = this.buildWhere(subscriptionId, tenantId, "cursor.put");
|
|
778
|
-
await this.db.update(
|
|
778
|
+
await this.db.update(integrationSubscriptions).set({
|
|
779
779
|
cursor,
|
|
780
|
-
|
|
780
|
+
lastIntegrationAt: /* @__PURE__ */ new Date(),
|
|
781
781
|
updatedAt: /* @__PURE__ */ new Date()
|
|
782
782
|
}).where(where);
|
|
783
783
|
}
|
|
@@ -786,26 +786,26 @@ var PostgresCursorStore = class {
|
|
|
786
786
|
multiTenant: this.multiTenant,
|
|
787
787
|
operation: "cursor.listAll"
|
|
788
788
|
});
|
|
789
|
-
const where = this.multiTenant ? eq(
|
|
789
|
+
const where = this.multiTenant ? eq(integrationSubscriptions.tenantId, tenantId) : void 0;
|
|
790
790
|
const rows = await this.db.select({
|
|
791
|
-
id:
|
|
792
|
-
|
|
793
|
-
adapter:
|
|
794
|
-
domain:
|
|
795
|
-
externalRef:
|
|
796
|
-
cursor:
|
|
797
|
-
|
|
798
|
-
updatedAt:
|
|
799
|
-
tenantId:
|
|
800
|
-
}).from(
|
|
791
|
+
id: integrationSubscriptions.id,
|
|
792
|
+
connectionId: integrationSubscriptions.connectionId,
|
|
793
|
+
adapter: integrationSubscriptions.adapter,
|
|
794
|
+
domain: integrationSubscriptions.domain,
|
|
795
|
+
externalRef: integrationSubscriptions.externalRef,
|
|
796
|
+
cursor: integrationSubscriptions.cursor,
|
|
797
|
+
lastIntegrationAt: integrationSubscriptions.lastIntegrationAt,
|
|
798
|
+
updatedAt: integrationSubscriptions.updatedAt,
|
|
799
|
+
tenantId: integrationSubscriptions.tenantId
|
|
800
|
+
}).from(integrationSubscriptions).where(where).orderBy(desc(integrationSubscriptions.updatedAt));
|
|
801
801
|
return rows.map((row) => ({
|
|
802
802
|
subscriptionId: row.id,
|
|
803
|
-
|
|
803
|
+
connectionId: row.connectionId,
|
|
804
804
|
adapter: row.adapter,
|
|
805
805
|
domain: row.domain,
|
|
806
806
|
externalRef: row.externalRef,
|
|
807
807
|
cursor: row.cursor ?? null,
|
|
808
|
-
|
|
808
|
+
lastIntegrationAt: row.lastIntegrationAt,
|
|
809
809
|
updatedAt: row.updatedAt,
|
|
810
810
|
tenantId: row.tenantId
|
|
811
811
|
}));
|
|
@@ -822,24 +822,24 @@ var PostgresCursorStore = class {
|
|
|
822
822
|
});
|
|
823
823
|
if (this.multiTenant) {
|
|
824
824
|
return and(
|
|
825
|
-
eq(
|
|
826
|
-
eq(
|
|
825
|
+
eq(integrationSubscriptions.id, subscriptionId),
|
|
826
|
+
eq(integrationSubscriptions.tenantId, tenantId)
|
|
827
827
|
);
|
|
828
828
|
}
|
|
829
|
-
return eq(
|
|
829
|
+
return eq(integrationSubscriptions.id, subscriptionId);
|
|
830
830
|
}
|
|
831
831
|
};
|
|
832
832
|
PostgresCursorStore = __decorateClass([
|
|
833
833
|
Injectable5(),
|
|
834
834
|
__decorateParam(0, Inject2(DRIZZLE)),
|
|
835
835
|
__decorateParam(1, Optional2()),
|
|
836
|
-
__decorateParam(1, Inject2(
|
|
836
|
+
__decorateParam(1, Inject2(INTEGRATION_MULTI_TENANT))
|
|
837
837
|
], PostgresCursorStore);
|
|
838
838
|
|
|
839
|
-
// runtime/subsystems/
|
|
839
|
+
// runtime/subsystems/integration/integration-run-recorder.drizzle-backend.ts
|
|
840
840
|
import { Inject as Inject3, Injectable as Injectable6, Optional as Optional3 } from "@nestjs/common";
|
|
841
841
|
import { and as and2, desc as desc2, eq as eq2 } from "drizzle-orm";
|
|
842
|
-
var
|
|
842
|
+
var DrizzleIntegrationRunRecorder = class {
|
|
843
843
|
constructor(db, multiTenant) {
|
|
844
844
|
this.db = db;
|
|
845
845
|
this.multiTenant = multiTenant ?? false;
|
|
@@ -851,17 +851,17 @@ var DrizzleSyncRunRecorder = class {
|
|
|
851
851
|
multiTenant: this.multiTenant,
|
|
852
852
|
operation: "startRun"
|
|
853
853
|
});
|
|
854
|
-
const rows = await this.db.insert(
|
|
854
|
+
const rows = await this.db.insert(integrationRuns).values({
|
|
855
855
|
subscriptionId: input.subscriptionId,
|
|
856
856
|
direction: input.direction,
|
|
857
857
|
action: input.action,
|
|
858
858
|
status: "running",
|
|
859
859
|
cursorBefore: input.cursorBefore ?? null,
|
|
860
860
|
tenantId: input.tenantId ?? null
|
|
861
|
-
}).returning({ id:
|
|
861
|
+
}).returning({ id: integrationRuns.id });
|
|
862
862
|
const id = rows[0]?.id;
|
|
863
863
|
if (!id) {
|
|
864
|
-
throw new Error("
|
|
864
|
+
throw new Error("DrizzleIntegrationRunRecorder: INSERT RETURNING produced no id");
|
|
865
865
|
}
|
|
866
866
|
return { id };
|
|
867
867
|
}
|
|
@@ -871,8 +871,8 @@ var DrizzleSyncRunRecorder = class {
|
|
|
871
871
|
operation: "recordItem"
|
|
872
872
|
});
|
|
873
873
|
FieldDiffSchema.parse(input.changedFields);
|
|
874
|
-
await this.db.insert(
|
|
875
|
-
|
|
874
|
+
await this.db.insert(integrationRunItems).values({
|
|
875
|
+
integrationRunId: input.integrationRunId,
|
|
876
876
|
entityType: input.entityType,
|
|
877
877
|
externalId: input.externalId,
|
|
878
878
|
localId: input.localId ?? null,
|
|
@@ -891,29 +891,29 @@ var DrizzleSyncRunRecorder = class {
|
|
|
891
891
|
});
|
|
892
892
|
const conditions = [];
|
|
893
893
|
if (subscriptionId !== void 0) {
|
|
894
|
-
conditions.push(eq2(
|
|
894
|
+
conditions.push(eq2(integrationRuns.subscriptionId, subscriptionId));
|
|
895
895
|
}
|
|
896
896
|
if (this.multiTenant) {
|
|
897
|
-
conditions.push(eq2(
|
|
897
|
+
conditions.push(eq2(integrationRuns.tenantId, tenantId));
|
|
898
898
|
}
|
|
899
899
|
const where = conditions.length === 0 ? void 0 : conditions.length === 1 ? conditions[0] : and2(...conditions);
|
|
900
900
|
const rows = await this.db.select({
|
|
901
|
-
id:
|
|
902
|
-
subscriptionId:
|
|
903
|
-
|
|
904
|
-
status:
|
|
905
|
-
startedAt:
|
|
906
|
-
completedAt:
|
|
907
|
-
recordsProcessed:
|
|
908
|
-
tenantId:
|
|
909
|
-
}).from(
|
|
910
|
-
|
|
911
|
-
eq2(
|
|
912
|
-
).where(where).orderBy(desc2(
|
|
901
|
+
id: integrationRuns.id,
|
|
902
|
+
subscriptionId: integrationRuns.subscriptionId,
|
|
903
|
+
connectionId: integrationSubscriptions.connectionId,
|
|
904
|
+
status: integrationRuns.status,
|
|
905
|
+
startedAt: integrationRuns.startedAt,
|
|
906
|
+
completedAt: integrationRuns.completedAt,
|
|
907
|
+
recordsProcessed: integrationRuns.recordsProcessed,
|
|
908
|
+
tenantId: integrationRuns.tenantId
|
|
909
|
+
}).from(integrationRuns).innerJoin(
|
|
910
|
+
integrationSubscriptions,
|
|
911
|
+
eq2(integrationRuns.subscriptionId, integrationSubscriptions.id)
|
|
912
|
+
).where(where).orderBy(desc2(integrationRuns.startedAt)).limit(limit);
|
|
913
913
|
return rows.map((row) => ({
|
|
914
914
|
id: row.id,
|
|
915
915
|
subscriptionId: row.subscriptionId,
|
|
916
|
-
|
|
916
|
+
connectionId: row.connectionId,
|
|
917
917
|
status: row.status,
|
|
918
918
|
startedAt: row.startedAt,
|
|
919
919
|
completedAt: row.completedAt,
|
|
@@ -922,7 +922,7 @@ var DrizzleSyncRunRecorder = class {
|
|
|
922
922
|
}));
|
|
923
923
|
}
|
|
924
924
|
async completeRun(runId, input) {
|
|
925
|
-
await this.db.update(
|
|
925
|
+
await this.db.update(integrationRuns).set({
|
|
926
926
|
status: input.status,
|
|
927
927
|
recordsFound: input.recordsFound,
|
|
928
928
|
recordsProcessed: input.recordsProcessed,
|
|
@@ -930,27 +930,27 @@ var DrizzleSyncRunRecorder = class {
|
|
|
930
930
|
durationMs: input.durationMs,
|
|
931
931
|
error: input.error ?? null,
|
|
932
932
|
completedAt: /* @__PURE__ */ new Date()
|
|
933
|
-
}).where(eq2(
|
|
933
|
+
}).where(eq2(integrationRuns.id, runId));
|
|
934
934
|
}
|
|
935
935
|
};
|
|
936
|
-
|
|
936
|
+
DrizzleIntegrationRunRecorder = __decorateClass([
|
|
937
937
|
Injectable6(),
|
|
938
938
|
__decorateParam(0, Inject3(DRIZZLE)),
|
|
939
939
|
__decorateParam(1, Optional3()),
|
|
940
|
-
__decorateParam(1, Inject3(
|
|
941
|
-
],
|
|
940
|
+
__decorateParam(1, Inject3(INTEGRATION_MULTI_TENANT))
|
|
941
|
+
], DrizzleIntegrationRunRecorder);
|
|
942
942
|
|
|
943
|
-
// runtime/subsystems/
|
|
943
|
+
// runtime/subsystems/integration/integration.module.ts
|
|
944
944
|
import { Module } from "@nestjs/common";
|
|
945
|
-
var
|
|
945
|
+
var IntegrationModule = class {
|
|
946
946
|
static forRoot(options) {
|
|
947
947
|
const multiTenant = options.multiTenant ?? false;
|
|
948
948
|
const sharedProviders = [
|
|
949
|
-
{ provide:
|
|
950
|
-
{ provide:
|
|
949
|
+
{ provide: INTEGRATION_MODULE_OPTIONS, useValue: options },
|
|
950
|
+
{ provide: INTEGRATION_MULTI_TENANT, useValue: multiTenant },
|
|
951
951
|
// Default differ — consumers can override by binding a different
|
|
952
|
-
// `IFieldDiffer<T>` to `
|
|
953
|
-
{ provide:
|
|
952
|
+
// `IFieldDiffer<T>` to `INTEGRATION_FIELD_DIFFER` in their feature module.
|
|
953
|
+
{ provide: INTEGRATION_FIELD_DIFFER, useValue: new DeepEqualDiffer() }
|
|
954
954
|
];
|
|
955
955
|
const backendProviders = options.backend === "memory" ? [
|
|
956
956
|
// Wired as singletons via `useValue` so tests can pull
|
|
@@ -958,38 +958,38 @@ var SyncModule = class {
|
|
|
958
958
|
// direct assertions. Matches JOB-4 / MemoryJobStore shape.
|
|
959
959
|
{ provide: MemoryCursorStore, useValue: new MemoryCursorStore() },
|
|
960
960
|
{
|
|
961
|
-
provide:
|
|
961
|
+
provide: INTEGRATION_CURSOR_STORE,
|
|
962
962
|
useExisting: MemoryCursorStore
|
|
963
963
|
},
|
|
964
964
|
{ provide: MemoryRunRecorder, useValue: new MemoryRunRecorder() },
|
|
965
965
|
{
|
|
966
|
-
provide:
|
|
966
|
+
provide: INTEGRATION_RUN_RECORDER,
|
|
967
967
|
useExisting: MemoryRunRecorder
|
|
968
968
|
}
|
|
969
969
|
] : [
|
|
970
970
|
// Drizzle backends — injected with DRIZZLE (provided by the
|
|
971
|
-
// consumer's DrizzleModule) + the
|
|
971
|
+
// consumer's DrizzleModule) + the INTEGRATION_MULTI_TENANT flag
|
|
972
972
|
// we bound above.
|
|
973
|
-
{ provide:
|
|
974
|
-
{ provide:
|
|
973
|
+
{ provide: INTEGRATION_CURSOR_STORE, useClass: PostgresCursorStore },
|
|
974
|
+
{ provide: INTEGRATION_RUN_RECORDER, useClass: DrizzleIntegrationRunRecorder }
|
|
975
975
|
];
|
|
976
976
|
return {
|
|
977
|
-
module:
|
|
977
|
+
module: IntegrationModule,
|
|
978
978
|
global: true,
|
|
979
979
|
providers: [...sharedProviders, ...backendProviders],
|
|
980
980
|
exports: [
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
981
|
+
INTEGRATION_MODULE_OPTIONS,
|
|
982
|
+
INTEGRATION_MULTI_TENANT,
|
|
983
|
+
INTEGRATION_FIELD_DIFFER,
|
|
984
|
+
INTEGRATION_CURSOR_STORE,
|
|
985
|
+
INTEGRATION_RUN_RECORDER
|
|
986
986
|
]
|
|
987
987
|
};
|
|
988
988
|
}
|
|
989
989
|
};
|
|
990
|
-
|
|
990
|
+
IntegrationModule = __decorateClass([
|
|
991
991
|
Module({})
|
|
992
|
-
],
|
|
992
|
+
], IntegrationModule);
|
|
993
993
|
|
|
994
994
|
// src/schema/entity-definition.schema.ts
|
|
995
995
|
var FieldTypeSchema = z3.enum([
|
|
@@ -1297,21 +1297,21 @@ var AnyQueryDeclarationSchema = z3.union([
|
|
|
1297
1297
|
SearchQueryDeclarationSchema,
|
|
1298
1298
|
QueryDeclarationSchema
|
|
1299
1299
|
]);
|
|
1300
|
-
var
|
|
1300
|
+
var IntegrationDirectionSchema = z3.enum([
|
|
1301
1301
|
"inbound",
|
|
1302
1302
|
"outbound",
|
|
1303
1303
|
"bidirectional"
|
|
1304
1304
|
]);
|
|
1305
|
-
var
|
|
1305
|
+
var ProviderIntegrationSchema = z3.object({
|
|
1306
1306
|
remote_entity: z3.string(),
|
|
1307
|
-
direction:
|
|
1307
|
+
direction: IntegrationDirectionSchema,
|
|
1308
1308
|
cdc: z3.boolean().optional().default(false),
|
|
1309
1309
|
field_mapping: z3.record(z3.string(), z3.string()).optional(),
|
|
1310
1310
|
read_only_fields: z3.array(z3.string()).optional()
|
|
1311
1311
|
});
|
|
1312
|
-
var
|
|
1312
|
+
var IntegrationConfigSchema = z3.object({
|
|
1313
1313
|
electric: z3.boolean().optional().default(false),
|
|
1314
|
-
providers: z3.record(z3.string(),
|
|
1314
|
+
providers: z3.record(z3.string(), ProviderIntegrationSchema).optional()
|
|
1315
1315
|
});
|
|
1316
1316
|
var EventDeclarationSchema = z3.object({
|
|
1317
1317
|
name: z3.string().regex(/^[a-z][a-z0-9_]*$/, "Event name must be snake_case"),
|
|
@@ -1411,22 +1411,52 @@ var EntityDefinitionSchema = z3.object({
|
|
|
1411
1411
|
// v2: Declarative query generation (ADR-005)
|
|
1412
1412
|
// Generates repository + service + use case methods from declarations
|
|
1413
1413
|
queries: z3.array(AnyQueryDeclarationSchema).optional(),
|
|
1414
|
-
// v2: Integration
|
|
1415
|
-
// Electric SQL + provider
|
|
1416
|
-
|
|
1414
|
+
// v2: Integration integration configuration (CODEGEN-EVOLUTION-PLAN Phase 2)
|
|
1415
|
+
// Electric SQL + provider integration (Salesforce, HubSpot, etc.)
|
|
1416
|
+
integration: IntegrationConfigSchema.optional(),
|
|
1417
1417
|
// ADR-033.1: Provider-keyed change-source detection.
|
|
1418
1418
|
//
|
|
1419
1419
|
// Map of provider name → DetectionConfig. Single-provider entities use
|
|
1420
1420
|
// a one-key map; multi-provider entities list each provider as a
|
|
1421
1421
|
// separate key. Each value is an independent `DetectionConfig` (its
|
|
1422
1422
|
// own mode/cursor/mapping/filters) sourced from the canonical schema
|
|
1423
|
-
// in `runtime/subsystems/
|
|
1423
|
+
// in `runtime/subsystems/integration` so this validator and the runtime
|
|
1424
1424
|
// parser stay in lockstep.
|
|
1425
1425
|
//
|
|
1426
1426
|
// Within-file cross-check (ADR-033.1 §6): every key here must also
|
|
1427
|
-
// appear in `
|
|
1427
|
+
// appear in `integration.providers` — see the superRefine on
|
|
1428
1428
|
// `EntityDefinitionSchema` below.
|
|
1429
1429
|
detection: z3.record(z3.string(), DetectionConfigSchema).optional(),
|
|
1430
|
+
// RFC-0001 §1/§8: the integration *surface* this entity belongs to
|
|
1431
|
+
// (e.g. 'calendar', 'mail', 'crm'). Surfaces span provider contexts
|
|
1432
|
+
// (ADR-0006) — one Google OAuth feeds calendar+mail+transcript. The union
|
|
1433
|
+
// of `surface:` values across all entity YAML is the closed set that a
|
|
1434
|
+
// provider's `surfaces:` must be a subset of (cross-checked in
|
|
1435
|
+
// src/parser/validate-providers.ts). Optional: entities without an
|
|
1436
|
+
// integration surface omit it. The surface-package *emission* convention
|
|
1437
|
+
// is Track C (#329); this field is only the declarative input both tracks
|
|
1438
|
+
// read.
|
|
1439
|
+
surface: z3.string().optional(),
|
|
1440
|
+
// Bounded-context declaration (ADR-0004) — "which bounded context this
|
|
1441
|
+
// entity belongs to". This is the DURABLE decision; it is a plain
|
|
1442
|
+
// bounded-context slug, NOT a folder knob. Different features consume it:
|
|
1443
|
+
//
|
|
1444
|
+
// - #403 (this PR, the FIRST consumer): drives the generated code's
|
|
1445
|
+
// module output folder. clean-lite-ps nests the entity's module under
|
|
1446
|
+
// `<modules>/<context>/<entity>/` so same-context entities group
|
|
1447
|
+
// together; untagged entities stay flat (`<modules>/<entity>/`).
|
|
1448
|
+
// - ADR-0004 (deferred): a later `naming: prefix | schema` knob reads
|
|
1449
|
+
// this SAME field to drive the Postgres physical layout —
|
|
1450
|
+
// `prefix` → `pgTable('<context>__<table>')`, then the flip to
|
|
1451
|
+
// `schema` → `pgSchema('<context>').table('<table>')`. NOT wired here;
|
|
1452
|
+
// #403 makes no table/column/schema changes.
|
|
1453
|
+
//
|
|
1454
|
+
// Sibling to `surface:` and orthogonal to it (ADR-0006): context = model
|
|
1455
|
+
// cohesion (which domain), surface = vendor composition (which integration).
|
|
1456
|
+
context: z3.string().regex(
|
|
1457
|
+
/^[a-z][a-z0-9_]*$/,
|
|
1458
|
+
"context must be lowercase snake_case (e.g. 'integration')"
|
|
1459
|
+
).optional(),
|
|
1430
1460
|
// v2: Domain event declarations (CODEGEN-EVOLUTION-PLAN Phase 2)
|
|
1431
1461
|
// Generates typed event classes, handlers, and queue registration
|
|
1432
1462
|
events: z3.array(EventDeclarationSchema).optional(),
|
|
@@ -1445,7 +1475,18 @@ var EntityDefinitionSchema = z3.object({
|
|
|
1445
1475
|
).optional(),
|
|
1446
1476
|
// v2: Analytics / semantic layer configuration
|
|
1447
1477
|
// Cube.js measure packs, custom cube name, and metric definitions
|
|
1448
|
-
analytics: AnalyticsBlockSchema.optional()
|
|
1478
|
+
analytics: AnalyticsBlockSchema.optional(),
|
|
1479
|
+
// Composite (multi-column) unique indexes (#356). Single-column uniqueness
|
|
1480
|
+
// is `unique: true` on the field itself; this declares constraints that
|
|
1481
|
+
// span 2+ columns, e.g. UNIQUE (conversation_id, sequence). Emitted as a
|
|
1482
|
+
// `uniqueIndex(...).on(...)` entry in the generated entity's pgTable
|
|
1483
|
+
// extra-config callback. `name` defaults to <table>_<col1>_<col2>_uniq.
|
|
1484
|
+
unique_indexes: z3.array(
|
|
1485
|
+
z3.object({
|
|
1486
|
+
fields: z3.array(z3.string()).min(2, "unique_indexes entries span 2+ columns \u2014 use `unique: true` on the field for single-column uniqueness"),
|
|
1487
|
+
name: z3.string().optional()
|
|
1488
|
+
}).strict()
|
|
1489
|
+
).optional()
|
|
1449
1490
|
}).strict().refine(
|
|
1450
1491
|
(data) => !data.eav_value_table || typeof data.eav_definition_table === "string",
|
|
1451
1492
|
{
|
|
@@ -1454,13 +1495,13 @@ var EntityDefinitionSchema = z3.object({
|
|
|
1454
1495
|
}
|
|
1455
1496
|
).superRefine((entity, ctx) => {
|
|
1456
1497
|
if (!entity.detection) return;
|
|
1457
|
-
const declared = new Set(Object.keys(entity.
|
|
1498
|
+
const declared = new Set(Object.keys(entity.integration?.providers ?? {}));
|
|
1458
1499
|
for (const provider of Object.keys(entity.detection)) {
|
|
1459
1500
|
if (!declared.has(provider)) {
|
|
1460
1501
|
ctx.addIssue({
|
|
1461
1502
|
code: "custom",
|
|
1462
1503
|
path: ["detection", provider],
|
|
1463
|
-
message: `Provider '${provider}' used in detection: but not declared in
|
|
1504
|
+
message: `Provider '${provider}' used in detection: but not declared in integration.providers. Known providers: ${[...declared].join(", ")}`
|
|
1464
1505
|
});
|
|
1465
1506
|
}
|
|
1466
1507
|
}
|
|
@@ -1882,6 +1923,56 @@ function safeValidateJunctionDefinition(data) {
|
|
|
1882
1923
|
return { success: false, error: result.error };
|
|
1883
1924
|
}
|
|
1884
1925
|
|
|
1926
|
+
// src/schema/provider-definition.schema.ts
|
|
1927
|
+
import { z as z7 } from "zod";
|
|
1928
|
+
var IMPORT_REF_RE = /^[^#\s]+#[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
1929
|
+
var ImportRefSchema = z7.string().regex(
|
|
1930
|
+
IMPORT_REF_RE,
|
|
1931
|
+
"must be an 'import-path#Export' reference (e.g. '@app/foo/bar.strategy#BarStrategy')"
|
|
1932
|
+
);
|
|
1933
|
+
var AuthTypeSchema = z7.enum(["oauth2", "api-key", "app-password"]);
|
|
1934
|
+
var AuthSchema = z7.object({
|
|
1935
|
+
type: AuthTypeSchema,
|
|
1936
|
+
// Class implementing the auth subsystem's strategy contract (ADR-031).
|
|
1937
|
+
// Pre-flight verified against a real export at codegen time.
|
|
1938
|
+
strategy: ImportRefSchema,
|
|
1939
|
+
// Required (and non-empty) iff type === 'oauth2'; see refine below.
|
|
1940
|
+
scopes: z7.array(z7.string()).optional()
|
|
1941
|
+
}).strict().refine(
|
|
1942
|
+
(a) => a.type !== "oauth2" || a.scopes !== void 0 && a.scopes.length > 0,
|
|
1943
|
+
{
|
|
1944
|
+
message: "auth.scopes is required and must be non-empty when auth.type is 'oauth2'",
|
|
1945
|
+
path: ["scopes"]
|
|
1946
|
+
}
|
|
1947
|
+
);
|
|
1948
|
+
var ClientSchema = z7.object({
|
|
1949
|
+
// API client class. Pre-flight verified against a real export.
|
|
1950
|
+
class: ImportRefSchema,
|
|
1951
|
+
base_url: z7.string().url("client.base_url must be an absolute URL")
|
|
1952
|
+
}).strict();
|
|
1953
|
+
var ProviderDefinitionSchema = z7.object({
|
|
1954
|
+
// Provider id — the canonical string used as detection: keys, audit rows,
|
|
1955
|
+
// subscription rows. kebab/lower; unique across definitions/providers/
|
|
1956
|
+
// (uniqueness is a cross-file check in validate-providers.ts).
|
|
1957
|
+
slug: z7.string().regex(
|
|
1958
|
+
/^[a-z][a-z0-9-]*$/,
|
|
1959
|
+
"slug must be kebab-case lower (e.g. 'google', 'hubspot')"
|
|
1960
|
+
),
|
|
1961
|
+
display_name: z7.string().optional(),
|
|
1962
|
+
auth: AuthSchema,
|
|
1963
|
+
client: ClientSchema,
|
|
1964
|
+
// Surfaces this provider serves (ADR-0006: surfaces span contexts — one
|
|
1965
|
+
// Google OAuth feeds calendar+mail+transcript). Each must reference a real
|
|
1966
|
+
// `surface:` declared on some entity; that cross-check is in
|
|
1967
|
+
// validate-providers.ts. Non-empty enforced here.
|
|
1968
|
+
surfaces: z7.array(z7.string()).min(1, "surfaces must list at least one surface"),
|
|
1969
|
+
// Optional auth lifecycle hints consumed by provider-module emission (D2).
|
|
1970
|
+
// `refresh_behavior` is left as a free string in D1 — its domain firms up
|
|
1971
|
+
// when D2 consumes it; carrying it now keeps the YAML lossless.
|
|
1972
|
+
token_lifetime: z7.number().int().positive().optional(),
|
|
1973
|
+
refresh_behavior: z7.string().optional()
|
|
1974
|
+
}).strict();
|
|
1975
|
+
|
|
1885
1976
|
// src/utils/yaml-loader.ts
|
|
1886
1977
|
function loadEntityFromYaml(filePath) {
|
|
1887
1978
|
if (!existsSync(filePath)) {
|
|
@@ -2094,14 +2185,14 @@ function transformToEntity(result) {
|
|
|
2094
2185
|
entity.relationships.set(name, relationship);
|
|
2095
2186
|
}
|
|
2096
2187
|
}
|
|
2097
|
-
if (definition.
|
|
2098
|
-
const
|
|
2099
|
-
const
|
|
2100
|
-
electric:
|
|
2188
|
+
if (definition.integration) {
|
|
2189
|
+
const integrationDef = definition.integration;
|
|
2190
|
+
const parsedIntegration = {
|
|
2191
|
+
electric: integrationDef.electric ?? false
|
|
2101
2192
|
};
|
|
2102
|
-
if (
|
|
2103
|
-
|
|
2104
|
-
for (const [providerName, providerDef] of Object.entries(
|
|
2193
|
+
if (integrationDef.providers) {
|
|
2194
|
+
parsedIntegration.providers = {};
|
|
2195
|
+
for (const [providerName, providerDef] of Object.entries(integrationDef.providers)) {
|
|
2105
2196
|
const parsedProvider = {
|
|
2106
2197
|
remoteEntity: providerDef.remote_entity,
|
|
2107
2198
|
direction: providerDef.direction,
|
|
@@ -2113,10 +2204,10 @@ function transformToEntity(result) {
|
|
|
2113
2204
|
if (providerDef.read_only_fields) {
|
|
2114
2205
|
parsedProvider.readOnlyFields = providerDef.read_only_fields;
|
|
2115
2206
|
}
|
|
2116
|
-
|
|
2207
|
+
parsedIntegration.providers[providerName] = parsedProvider;
|
|
2117
2208
|
}
|
|
2118
2209
|
}
|
|
2119
|
-
entity.
|
|
2210
|
+
entity.integration = parsedIntegration;
|
|
2120
2211
|
}
|
|
2121
2212
|
if (definition.events) {
|
|
2122
2213
|
entity.events = definition.events.map((ev) => ({
|
|
@@ -2389,6 +2480,11 @@ function resolveRelationshipReferences(relationshipDefs, entities) {
|
|
|
2389
2480
|
return issues;
|
|
2390
2481
|
}
|
|
2391
2482
|
|
|
2483
|
+
// src/parser/validate-providers.ts
|
|
2484
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, statSync } from "fs";
|
|
2485
|
+
import { isAbsolute, join as join2, resolve as resolve3 } from "path";
|
|
2486
|
+
import ts from "typescript";
|
|
2487
|
+
|
|
2392
2488
|
// src/analyzer/graph-builder.ts
|
|
2393
2489
|
function inferCardinality(type) {
|
|
2394
2490
|
switch (type) {
|
|
@@ -2770,8 +2866,8 @@ function checkConsistency(graph) {
|
|
|
2770
2866
|
if (entity.queries !== void 0) {
|
|
2771
2867
|
issues.push(...checkQueryFieldReferences(entity));
|
|
2772
2868
|
}
|
|
2773
|
-
if (entity.
|
|
2774
|
-
issues.push(...
|
|
2869
|
+
if (entity.integration !== void 0) {
|
|
2870
|
+
issues.push(...checkIntegrationFieldMappingReferences(entity));
|
|
2775
2871
|
issues.push(...checkExternalIdTrackingCollision(entity));
|
|
2776
2872
|
}
|
|
2777
2873
|
}
|
|
@@ -3007,19 +3103,19 @@ function checkQueryFieldReferences(entity) {
|
|
|
3007
3103
|
}
|
|
3008
3104
|
return issues;
|
|
3009
3105
|
}
|
|
3010
|
-
function
|
|
3106
|
+
function checkIntegrationFieldMappingReferences(entity) {
|
|
3011
3107
|
const issues = [];
|
|
3012
3108
|
const availableFields = getAvailableFieldNames(entity);
|
|
3013
3109
|
const availableSet = new Set(availableFields);
|
|
3014
|
-
for (const [providerName, provider] of Object.entries(entity.
|
|
3110
|
+
for (const [providerName, provider] of Object.entries(entity.integration?.providers ?? {})) {
|
|
3015
3111
|
for (const fieldName of Object.keys(provider.fieldMapping ?? {})) {
|
|
3016
3112
|
if (!availableSet.has(fieldName)) {
|
|
3017
3113
|
issues.push({
|
|
3018
3114
|
severity: "warning",
|
|
3019
|
-
type: "
|
|
3115
|
+
type: "unknown_integration_field_mapping",
|
|
3020
3116
|
entity: entity.name,
|
|
3021
3117
|
field: fieldName,
|
|
3022
|
-
message: `
|
|
3118
|
+
message: `Integration field mapping references unknown field "${fieldName}" for provider "${providerName}" in entity "${entity.name}"`
|
|
3023
3119
|
});
|
|
3024
3120
|
}
|
|
3025
3121
|
}
|
|
@@ -3027,10 +3123,10 @@ function checkSyncFieldMappingReferences(entity) {
|
|
|
3027
3123
|
if (!availableSet.has(fieldName)) {
|
|
3028
3124
|
issues.push({
|
|
3029
3125
|
severity: "warning",
|
|
3030
|
-
type: "
|
|
3126
|
+
type: "unknown_integration_field_mapping",
|
|
3031
3127
|
entity: entity.name,
|
|
3032
3128
|
field: fieldName,
|
|
3033
|
-
message: `
|
|
3129
|
+
message: `Integration field mapping references unknown field "${fieldName}" for provider "${providerName}" in entity "${entity.name}"`
|
|
3034
3130
|
});
|
|
3035
3131
|
}
|
|
3036
3132
|
}
|
|
@@ -3041,14 +3137,14 @@ function checkExternalIdTrackingCollision(entity) {
|
|
|
3041
3137
|
const issues = [];
|
|
3042
3138
|
const hasExternalIdTracking = entity.behaviors.includes("external_id_tracking");
|
|
3043
3139
|
if (!hasExternalIdTracking) return issues;
|
|
3044
|
-
for (const [providerName, provider] of Object.entries(entity.
|
|
3140
|
+
for (const [providerName, provider] of Object.entries(entity.integration?.providers ?? {})) {
|
|
3045
3141
|
if (provider.fieldMapping && "external_id" in provider.fieldMapping) {
|
|
3046
3142
|
issues.push({
|
|
3047
3143
|
severity: "warning",
|
|
3048
3144
|
type: "external_id_tracking_collision",
|
|
3049
3145
|
entity: entity.name,
|
|
3050
3146
|
field: "external_id",
|
|
3051
|
-
message: `Entity "${entity.name}" has external_id_tracking behavior and also maps "external_id" in
|
|
3147
|
+
message: `Entity "${entity.name}" has external_id_tracking behavior and also maps "external_id" in integration field_mapping for provider "${providerName}". The behavior-added field may collide with the mapped field.`
|
|
3052
3148
|
});
|
|
3053
3149
|
}
|
|
3054
3150
|
}
|
|
@@ -3928,8 +4024,8 @@ var BasePattern = definePattern({
|
|
|
3928
4024
|
});
|
|
3929
4025
|
|
|
3930
4026
|
// src/patterns/library/junction.pattern.ts
|
|
3931
|
-
import { z as
|
|
3932
|
-
var JunctionPatternConfigSchema =
|
|
4027
|
+
import { z as z8 } from "zod";
|
|
4028
|
+
var JunctionPatternConfigSchema = z8.object({}).strict();
|
|
3933
4029
|
var JunctionPattern = definePattern({
|
|
3934
4030
|
name: "Junction",
|
|
3935
4031
|
description: "Explicit many-to-many junction with role + temporal + sourcing metadata",
|
|
@@ -3975,30 +4071,30 @@ var MetadataPattern = definePattern({
|
|
|
3975
4071
|
description: "History-tracked metadata rows \u2014 entity-id + type scoped lookups"
|
|
3976
4072
|
});
|
|
3977
4073
|
|
|
3978
|
-
// src/patterns/library/
|
|
3979
|
-
var
|
|
3980
|
-
name: "
|
|
4074
|
+
// src/patterns/library/integrated.pattern.ts
|
|
4075
|
+
var IntegratedPattern = definePattern({
|
|
4076
|
+
name: "Integrated",
|
|
3981
4077
|
extends: ["Base"],
|
|
3982
|
-
repositoryClass: "
|
|
3983
|
-
serviceClass: "
|
|
3984
|
-
repositoryImport: "@shared/base-classes/
|
|
3985
|
-
serviceImport: "@shared/base-classes/
|
|
4078
|
+
repositoryClass: "IntegratedEntityRepository",
|
|
4079
|
+
serviceClass: "IntegratedEntityService",
|
|
4080
|
+
repositoryImport: "@shared/base-classes/integrated-entity-repository",
|
|
4081
|
+
serviceImport: "@shared/base-classes/integrated-entity-service",
|
|
3986
4082
|
repositoryInheritedMethods: [
|
|
3987
4083
|
"findById, findByIds, list, count, exists, create, update, delete, upsertMany",
|
|
3988
4084
|
"findByExternalId, findManyByExternalIds, findAllByUserId, findVisibleByUserId",
|
|
3989
|
-
"
|
|
4085
|
+
"integrationUpsertOne, findByExternalIdProjected, softDeleteByExternalId, integrationUpsert"
|
|
3990
4086
|
],
|
|
3991
4087
|
serviceInheritedMethods: [
|
|
3992
4088
|
"findById, findByIds, list, count, exists, create, update, delete",
|
|
3993
4089
|
"findByExternalId, findAllByUserId, findVisibleByUserId"
|
|
3994
4090
|
],
|
|
3995
4091
|
impliedBehaviors: ["external_id_tracking"],
|
|
3996
|
-
description: "External CRM/system
|
|
4092
|
+
description: "External CRM/system integration columns and integrationUpsert methods"
|
|
3997
4093
|
});
|
|
3998
4094
|
|
|
3999
4095
|
// src/patterns/library/index.ts
|
|
4000
4096
|
registerLibraryPattern(BasePattern);
|
|
4001
|
-
registerLibraryPattern(
|
|
4097
|
+
registerLibraryPattern(IntegratedPattern);
|
|
4002
4098
|
registerLibraryPattern(ActivityPattern);
|
|
4003
4099
|
registerLibraryPattern(KnowledgePattern);
|
|
4004
4100
|
registerLibraryPattern(MetadataPattern);
|
|
@@ -4060,11 +4156,11 @@ export {
|
|
|
4060
4156
|
BASE_JUNCTION_FIELD_NAMES,
|
|
4061
4157
|
BaseJunctionFields,
|
|
4062
4158
|
BasePattern,
|
|
4159
|
+
IntegratedPattern,
|
|
4063
4160
|
JunctionDefinitionSchema,
|
|
4064
4161
|
JunctionPattern,
|
|
4065
4162
|
KnowledgePattern,
|
|
4066
4163
|
MetadataPattern,
|
|
4067
|
-
SyncedPattern,
|
|
4068
4164
|
analyzeDomain,
|
|
4069
4165
|
buildDomainGraph,
|
|
4070
4166
|
checkConsistency,
|