@pattern-stack/codegen 0.10.0 → 0.11.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 +135 -0
- package/README.md +5 -5
- package/consumer-skills/codegen/SKILL.md +2 -2
- package/consumer-skills/events/typed-bus-and-outbox.md +1 -1
- 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 +64 -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/bridge/bridge.module.d.ts +0 -1
- package/dist/runtime/subsystems/bridge/bridge.module.js +294 -710
- package/dist/runtime/subsystems/bridge/bridge.module.js.map +1 -1
- package/dist/runtime/subsystems/bridge/index.d.ts +0 -1
- package/dist/runtime/subsystems/bridge/index.js +248 -664
- package/dist/runtime/subsystems/bridge/index.js.map +1 -1
- package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js +18 -10
- package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js.map +1 -1
- package/dist/runtime/subsystems/events/events.module.js +43 -244
- package/dist/runtime/subsystems/events/events.module.js.map +1 -1
- package/dist/runtime/subsystems/events/index.d.ts +0 -1
- package/dist/runtime/subsystems/events/index.js +39 -241
- package/dist/runtime/subsystems/events/index.js.map +1 -1
- package/dist/runtime/subsystems/index.d.ts +7 -7
- package/dist/runtime/subsystems/index.js +222 -839
- 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/{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 +28 -0
- package/dist/runtime/subsystems/{sync → integration}/index.js +171 -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 +47 -0
- package/dist/runtime/subsystems/integration/integration.tokens.js +18 -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/bullmq.config.d.ts +22 -3
- package/dist/runtime/subsystems/jobs/bullmq.config.js.map +1 -1
- package/dist/runtime/subsystems/jobs/index.d.ts +1 -4
- package/dist/runtime/subsystems/jobs/index.js +87 -506
- package/dist/runtime/subsystems/jobs/index.js.map +1 -1
- package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.js.map +1 -1
- package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.js +3 -0
- package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.js.map +1 -1
- package/dist/runtime/subsystems/jobs/job-worker.module.d.ts +10 -3
- package/dist/runtime/subsystems/jobs/job-worker.module.js +248 -664
- package/dist/runtime/subsystems/jobs/job-worker.module.js.map +1 -1
- package/dist/runtime/subsystems/jobs/jobs-domain.module.d.ts +0 -1
- package/dist/runtime/subsystems/jobs/jobs-domain.module.js +89 -391
- package/dist/runtime/subsystems/jobs/jobs-domain.module.js.map +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 +412 -302
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/index.d.ts +22 -22
- package/dist/src/index.js +191 -191
- 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 +1 -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/events/event-bus.drizzle-backend.ts +32 -10
- package/runtime/subsystems/events/events.module.ts +38 -6
- package/runtime/subsystems/events/index.ts +7 -1
- package/runtime/subsystems/index.ts +11 -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/{sync/execute-sync.use-case.ts → integration/execute-integration.use-case.ts} +40 -40
- package/runtime/subsystems/{sync → integration}/index.ts +47 -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 +49 -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/jobs/bullmq.config.ts +23 -3
- package/runtime/subsystems/jobs/index.ts +13 -8
- package/runtime/subsystems/jobs/job-worker.bullmq-backend.ts +5 -2
- package/runtime/subsystems/jobs/job-worker.module.ts +27 -7
- package/runtime/subsystems/jobs/jobs-domain.module.ts +27 -2
- 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/backend/modules/core/{sync-source.providers.ejs.t → integration-source.providers.ejs.t} +2 -2
- package/templates/entity/new/clean-lite-ps/entity.ejs.t +1 -1
- package/templates/entity/new/clean-lite-ps/module.ejs.t +1 -1
- package/templates/entity/new/clean-lite-ps/prompt-extension.js +33 -33
- 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/events/domain-events.schema.ejs.t +43 -2
- 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/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/runtime/subsystems/{sync/sync-audit.schema.ts → integration/integration-audit.schema.ts}
RENAMED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Drizzle schema for the
|
|
2
|
+
* Drizzle schema for the integration subsystem audit/observability tables (SYNC-1).
|
|
3
3
|
*
|
|
4
|
-
* Three tables model end-to-end
|
|
5
|
-
* every
|
|
4
|
+
* Three tables model end-to-end integration observability, keyed by the single port
|
|
5
|
+
* every integration adapter implements (`IChangeSource<T>` from SYNC-2):
|
|
6
6
|
*
|
|
7
|
-
* - `
|
|
8
|
-
* `(
|
|
7
|
+
* - `integration_subscriptions` — owns the cursor per
|
|
8
|
+
* `(connection_id, adapter, domain, external_ref)` tuple. Addressed
|
|
9
9
|
* by id by `ICursorStore` (SYNC-3/SYNC-4).
|
|
10
|
-
* - `
|
|
10
|
+
* - `integration_runs` — per-run audit log: start/complete, status,
|
|
11
11
|
* cursor before/after, counts, direction (inbound|outbound),
|
|
12
12
|
* action (poll|cdc|webhook|manual|writeback).
|
|
13
|
-
* - `
|
|
13
|
+
* - `integration_run_items` — per-record change log with structured
|
|
14
14
|
* `changed_fields` jsonb enforced by the Zod `FieldDiffSchema`
|
|
15
15
|
* contract (ADR-0003; protocol lives in SYNC-2's
|
|
16
|
-
*
|
|
16
|
+
* integration-field-diff.protocol.ts).
|
|
17
17
|
*
|
|
18
18
|
* Design calls (vs. issue #126 open questions):
|
|
19
19
|
*
|
|
20
|
-
* - `
|
|
20
|
+
* - `integration_subscriptions` ships in the subsystem (not consumer-owned).
|
|
21
21
|
* Rationale: SYNC-4's `PostgresCursorStore` needs to read/write this
|
|
22
22
|
* table directly; making it consumer-owned would require consumers to
|
|
23
23
|
* hand-wire a shape the backend already knows. The row is addressable
|
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
* owned while remaining consumer-queryable.
|
|
27
27
|
*
|
|
28
28
|
* - `tenant_id` is always emitted on the three tables as nullable text.
|
|
29
|
-
* The `
|
|
29
|
+
* The `INTEGRATION_MULTI_TENANT` DI flag (SYNC-6) is what enforces the
|
|
30
30
|
* non-null + cross-tenant-isolation contract at the service/orchestrator
|
|
31
31
|
* boundary. This mirrors JOB-1/JOB-8's final shape — runtime guard, not
|
|
32
32
|
* a scaffold-time conditional column. Keeps the schema file uniform
|
|
33
33
|
* across single-tenant and multi-tenant deployments.
|
|
34
34
|
*
|
|
35
|
-
* - `changed_fields` on `
|
|
35
|
+
* - `changed_fields` on `integration_run_items` is typed via the Zod-inferred
|
|
36
36
|
* `FieldDiff` shape from SYNC-2 (`{ [fieldName]: { from, to } }`). The
|
|
37
37
|
* recorder service (SYNC-5) validates every write against
|
|
38
38
|
* `FieldDiffSchema.parse` so consumers can rely on the shape.
|
|
@@ -51,28 +51,28 @@ import {
|
|
|
51
51
|
} from 'drizzle-orm/pg-core';
|
|
52
52
|
import type { InferSelectModel } from 'drizzle-orm';
|
|
53
53
|
|
|
54
|
-
import type { FieldDiff } from './
|
|
54
|
+
import type { FieldDiff } from './integration-field-diff.protocol';
|
|
55
55
|
|
|
56
56
|
// ─── Enums ──────────────────────────────────────────────────────────────────
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
|
-
* Direction of a
|
|
59
|
+
* Direction of a integration run relative to local state.
|
|
60
60
|
*
|
|
61
61
|
* - `inbound` — external → local (the common case: SFDC poll → local DB).
|
|
62
62
|
* - `outbound` — local → external (writeback; deferred per epic but the
|
|
63
63
|
* column shape is reserved so future writeback runs share the audit log).
|
|
64
64
|
*/
|
|
65
|
-
export const
|
|
65
|
+
export const integrationRunDirectionEnum = pgEnum('integration_run_direction', [
|
|
66
66
|
'inbound',
|
|
67
67
|
'outbound',
|
|
68
68
|
]);
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
71
|
* How the run detected upstream changes. Maps 1:1 to the `Change.source`
|
|
72
|
-
* provenance on inbound runs; `manual` captures operator-triggered re-
|
|
72
|
+
* provenance on inbound runs; `manual` captures operator-triggered re-integrations
|
|
73
73
|
* and `writeback` captures outbound runs.
|
|
74
74
|
*/
|
|
75
|
-
export const
|
|
75
|
+
export const integrationRunActionEnum = pgEnum('integration_run_action', [
|
|
76
76
|
'poll',
|
|
77
77
|
'cdc',
|
|
78
78
|
'webhook',
|
|
@@ -81,7 +81,7 @@ export const syncRunActionEnum = pgEnum('sync_run_action', [
|
|
|
81
81
|
]);
|
|
82
82
|
|
|
83
83
|
/**
|
|
84
|
-
* Lifecycle status of a
|
|
84
|
+
* Lifecycle status of a integration run.
|
|
85
85
|
*
|
|
86
86
|
* - `running` — in-flight; recorder has started but not completed.
|
|
87
87
|
* - `success` — completed with at least one change processed.
|
|
@@ -89,7 +89,7 @@ export const syncRunActionEnum = pgEnum('sync_run_action', [
|
|
|
89
89
|
* - `failed` — errored before completion; `error` column carries the
|
|
90
90
|
* message. `records_processed` may be non-zero (partial progress).
|
|
91
91
|
*/
|
|
92
|
-
export const
|
|
92
|
+
export const integrationRunStatusEnum = pgEnum('integration_run_status', [
|
|
93
93
|
'running',
|
|
94
94
|
'success',
|
|
95
95
|
'no_changes',
|
|
@@ -100,7 +100,7 @@ export const syncRunStatusEnum = pgEnum('sync_run_status', [
|
|
|
100
100
|
* Operation applied per record. Mirrors `Change<T>.operation` from SYNC-2,
|
|
101
101
|
* plus the recorder's own `'noop'` for changes that matched existing state.
|
|
102
102
|
*/
|
|
103
|
-
export const
|
|
103
|
+
export const integrationRunItemOperationEnum = pgEnum('integration_run_item_operation', [
|
|
104
104
|
'created',
|
|
105
105
|
'updated',
|
|
106
106
|
'deleted',
|
|
@@ -113,18 +113,18 @@ export const syncRunItemOperationEnum = pgEnum('sync_run_item_operation', [
|
|
|
113
113
|
* epic), which record the external_id but intentionally do not touch local
|
|
114
114
|
* state.
|
|
115
115
|
*/
|
|
116
|
-
export const
|
|
116
|
+
export const integrationRunItemStatusEnum = pgEnum('integration_run_item_status', [
|
|
117
117
|
'success',
|
|
118
118
|
'failed',
|
|
119
119
|
'skipped',
|
|
120
120
|
]);
|
|
121
121
|
|
|
122
|
-
// ───
|
|
122
|
+
// ─── integration_subscriptions ─────────────────────────────────────────────────────
|
|
123
123
|
|
|
124
124
|
/**
|
|
125
125
|
* One cursor owner per (integration, adapter, domain, external_ref).
|
|
126
126
|
*
|
|
127
|
-
* - `
|
|
127
|
+
* - `connection_id` — opaque id of the connected account/instance. E.g.
|
|
128
128
|
* the SFDC org id for polling strategies, the GitHub installation id
|
|
129
129
|
* for webhook strategies.
|
|
130
130
|
* - `adapter` — short adapter label, e.g. `'salesforce'`, `'hubspot'`.
|
|
@@ -138,11 +138,11 @@ export const syncRunItemStatusEnum = pgEnum('sync_run_item_status', [
|
|
|
138
138
|
* `{ systemModstamp }`, cdc: `{ replayId }`, webhook: `{ ts }`). Overwritten
|
|
139
139
|
* by `ICursorStore.put(id, cursor)`.
|
|
140
140
|
*/
|
|
141
|
-
export const
|
|
142
|
-
'
|
|
141
|
+
export const integrationSubscriptions = pgTable(
|
|
142
|
+
'integration_subscriptions',
|
|
143
143
|
{
|
|
144
144
|
id: uuid('id').primaryKey().defaultRandom(),
|
|
145
|
-
|
|
145
|
+
connectionId: text('connection_id').notNull(),
|
|
146
146
|
adapter: text('adapter').notNull(),
|
|
147
147
|
domain: text('domain').notNull(),
|
|
148
148
|
externalRef: text('external_ref'),
|
|
@@ -157,8 +157,8 @@ export const syncSubscriptions = pgTable(
|
|
|
157
157
|
* successful run advances it.
|
|
158
158
|
*/
|
|
159
159
|
cursor: jsonb('cursor').$type<unknown>(),
|
|
160
|
-
|
|
161
|
-
/** Runtime-enforced when `
|
|
160
|
+
lastIntegrationAt: timestamp('last_integration_at', { withTimezone: true }),
|
|
161
|
+
/** Runtime-enforced when `INTEGRATION_MULTI_TENANT` is true; see SYNC-6. */
|
|
162
162
|
tenantId: text('tenant_id'),
|
|
163
163
|
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
|
164
164
|
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
|
@@ -167,30 +167,30 @@ export const syncSubscriptions = pgTable(
|
|
|
167
167
|
/**
|
|
168
168
|
* Composite uniqueness per the epic shape. `external_ref` is nullable;
|
|
169
169
|
* Postgres treats NULLs as distinct in a UNIQUE constraint, which means
|
|
170
|
-
* two rows with the same `(
|
|
170
|
+
* two rows with the same `(connection_id, adapter, domain)` and NULL
|
|
171
171
|
* external_ref are allowed. That's intentional — a subscription with
|
|
172
172
|
* NULL external_ref covers the full domain, and duplicates there would
|
|
173
173
|
* be a consumer-layer modeling issue, not a schema concern.
|
|
174
174
|
*/
|
|
175
|
-
|
|
176
|
-
t.
|
|
175
|
+
uqIntegrationSubscriptionTuple: uniqueIndex('uq_integration_subscriptions_tuple').on(
|
|
176
|
+
t.connectionId,
|
|
177
177
|
t.adapter,
|
|
178
178
|
t.domain,
|
|
179
179
|
t.externalRef,
|
|
180
180
|
),
|
|
181
181
|
/** Scheduling query: list enabled subscriptions ordered by staleness. */
|
|
182
|
-
|
|
183
|
-
'
|
|
184
|
-
).on(t.enabled, t.
|
|
182
|
+
idxIntegrationSubscriptionsEnabledLastIntegration: index(
|
|
183
|
+
'idx_integration_subscriptions_enabled_last_integration',
|
|
184
|
+
).on(t.enabled, t.lastIntegrationAt),
|
|
185
185
|
}),
|
|
186
186
|
);
|
|
187
187
|
|
|
188
|
-
export type
|
|
188
|
+
export type IntegrationSubscriptionRow = InferSelectModel<typeof integrationSubscriptions>;
|
|
189
189
|
|
|
190
|
-
// ───
|
|
190
|
+
// ─── integration_runs ──────────────────────────────────────────────────────────────
|
|
191
191
|
|
|
192
192
|
/**
|
|
193
|
-
* One row per invocation of `
|
|
193
|
+
* One row per invocation of `ExecuteIntegrationUseCase`. `started_at` is set when
|
|
194
194
|
* the recorder opens the run; `completed_at`, `status`, `records_*`,
|
|
195
195
|
* `cursor_after`, and `duration_ms` are filled on completion.
|
|
196
196
|
*
|
|
@@ -199,16 +199,16 @@ export type SyncSubscriptionRow = InferSelectModel<typeof syncSubscriptions>;
|
|
|
199
199
|
* about exactly what window was scanned without cross-referencing another
|
|
200
200
|
* table.
|
|
201
201
|
*/
|
|
202
|
-
export const
|
|
203
|
-
'
|
|
202
|
+
export const integrationRuns = pgTable(
|
|
203
|
+
'integration_runs',
|
|
204
204
|
{
|
|
205
205
|
id: uuid('id').primaryKey().defaultRandom(),
|
|
206
206
|
subscriptionId: uuid('subscription_id')
|
|
207
207
|
.notNull()
|
|
208
|
-
.references(() =>
|
|
209
|
-
direction:
|
|
210
|
-
action:
|
|
211
|
-
status:
|
|
208
|
+
.references(() => integrationSubscriptions.id, { onDelete: 'cascade' }),
|
|
209
|
+
direction: integrationRunDirectionEnum('direction').notNull(),
|
|
210
|
+
action: integrationRunActionEnum('action').notNull(),
|
|
211
|
+
status: integrationRunStatusEnum('status').notNull().default('running'),
|
|
212
212
|
recordsFound: integer('records_found').notNull().default(0),
|
|
213
213
|
recordsProcessed: integer('records_processed').notNull().default(0),
|
|
214
214
|
cursorBefore: jsonb('cursor_before').$type<unknown>(),
|
|
@@ -219,25 +219,25 @@ export const syncRuns = pgTable(
|
|
|
219
219
|
.notNull()
|
|
220
220
|
.defaultNow(),
|
|
221
221
|
completedAt: timestamp('completed_at', { withTimezone: true }),
|
|
222
|
-
/** Runtime-enforced when `
|
|
222
|
+
/** Runtime-enforced when `INTEGRATION_MULTI_TENANT` is true; see SYNC-6. */
|
|
223
223
|
tenantId: text('tenant_id'),
|
|
224
224
|
},
|
|
225
225
|
(t) => ({
|
|
226
226
|
/** Timeline read: "most recent runs for this subscription". */
|
|
227
|
-
|
|
228
|
-
'
|
|
227
|
+
idxIntegrationRunsSubscriptionStartedAt: index(
|
|
228
|
+
'idx_integration_runs_subscription_started_at',
|
|
229
229
|
).on(t.subscriptionId, t.startedAt),
|
|
230
230
|
/** Stale-run sweeper: "runs that started > N minutes ago and are still running". */
|
|
231
|
-
|
|
231
|
+
idxIntegrationRunsStatusStartedAt: index('idx_integration_runs_status_started_at').on(
|
|
232
232
|
t.status,
|
|
233
233
|
t.startedAt,
|
|
234
234
|
),
|
|
235
235
|
}),
|
|
236
236
|
);
|
|
237
237
|
|
|
238
|
-
export type
|
|
238
|
+
export type IntegrationRunRow = InferSelectModel<typeof integrationRuns>;
|
|
239
239
|
|
|
240
|
-
// ───
|
|
240
|
+
// ─── integration_run_items ─────────────────────────────────────────────────────────
|
|
241
241
|
|
|
242
242
|
/**
|
|
243
243
|
* One row per upstream change processed within a run. Captures the canonical
|
|
@@ -254,18 +254,18 @@ export type SyncRunRow = InferSelectModel<typeof syncRuns>;
|
|
|
254
254
|
* `"Pinnacle opportunity"`) so run-log UIs don't need to re-hydrate the
|
|
255
255
|
* canonical record.
|
|
256
256
|
*/
|
|
257
|
-
export const
|
|
258
|
-
'
|
|
257
|
+
export const integrationRunItems = pgTable(
|
|
258
|
+
'integration_run_items',
|
|
259
259
|
{
|
|
260
260
|
id: uuid('id').primaryKey().defaultRandom(),
|
|
261
|
-
|
|
261
|
+
integrationRunId: uuid('integration_run_id')
|
|
262
262
|
.notNull()
|
|
263
|
-
.references(() =>
|
|
263
|
+
.references(() => integrationRuns.id, { onDelete: 'cascade' }),
|
|
264
264
|
entityType: text('entity_type').notNull(),
|
|
265
265
|
externalId: text('external_id').notNull(),
|
|
266
266
|
localId: text('local_id'),
|
|
267
|
-
operation:
|
|
268
|
-
status:
|
|
267
|
+
operation: integrationRunItemOperationEnum('operation').notNull(),
|
|
268
|
+
status: integrationRunItemStatusEnum('status').notNull(),
|
|
269
269
|
/**
|
|
270
270
|
* Structured per-field diff — ADR-0003 shape enforced by
|
|
271
271
|
* `FieldDiffSchema.parse` at the recorder service layer.
|
|
@@ -281,20 +281,20 @@ export const syncRunItems = pgTable(
|
|
|
281
281
|
createdAt: timestamp('created_at', { withTimezone: true })
|
|
282
282
|
.notNull()
|
|
283
283
|
.defaultNow(),
|
|
284
|
-
/** Runtime-enforced when `
|
|
284
|
+
/** Runtime-enforced when `INTEGRATION_MULTI_TENANT` is true; see SYNC-6. */
|
|
285
285
|
tenantId: text('tenant_id'),
|
|
286
286
|
},
|
|
287
287
|
(t) => ({
|
|
288
288
|
/** Ordered timeline within a run. */
|
|
289
|
-
|
|
290
|
-
t.
|
|
289
|
+
idxIntegrationRunItemsRunCreatedAt: index('idx_integration_run_items_run_created_at').on(
|
|
290
|
+
t.integrationRunId,
|
|
291
291
|
t.createdAt,
|
|
292
292
|
),
|
|
293
|
-
/** Per-record history: "every
|
|
294
|
-
|
|
295
|
-
'
|
|
293
|
+
/** Per-record history: "every integration that touched opportunity/$extId". */
|
|
294
|
+
idxIntegrationRunItemsEntityExternal: index(
|
|
295
|
+
'idx_integration_run_items_entity_external',
|
|
296
296
|
).on(t.entityType, t.externalId),
|
|
297
297
|
}),
|
|
298
298
|
);
|
|
299
299
|
|
|
300
|
-
export type
|
|
300
|
+
export type IntegrationRunItemRow = InferSelectModel<typeof integrationRunItems>;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Integration subsystem — change-source protocol (port)
|
|
3
3
|
*
|
|
4
|
-
* `IChangeSource<T>` is the hexagonal port every
|
|
5
|
-
* Use cases inject this interface via `
|
|
4
|
+
* `IChangeSource<T>` is the hexagonal port every integration adapter implements.
|
|
5
|
+
* Use cases inject this interface via `INTEGRATION_CHANGE_SOURCE` token. They never
|
|
6
6
|
* depend on a specific backend implementation.
|
|
7
7
|
*
|
|
8
8
|
* Three detection modes (poll / cdc / webhook) converge on this single port
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
// ============================================================================
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
|
-
* Provenance of a change record. Maps 1:1 to `
|
|
29
|
+
* Provenance of a change record. Maps 1:1 to `integration_runs.action` so run logs
|
|
30
30
|
* self-identify.
|
|
31
31
|
*/
|
|
32
32
|
export type ChangeSource = 'poll' | 'cdc' | 'webhook';
|
|
@@ -65,13 +65,13 @@ export interface Change<T> {
|
|
|
65
65
|
// ============================================================================
|
|
66
66
|
|
|
67
67
|
/**
|
|
68
|
-
* Minimal structural view of a
|
|
68
|
+
* Minimal structural view of a integration-subscription row the port needs.
|
|
69
69
|
*
|
|
70
|
-
* The consumer owns the concrete `
|
|
70
|
+
* The consumer owns the concrete `integration_subscriptions` table (schema lands in
|
|
71
71
|
* SYNC-1). This interface captures only the fields the port itself reads, so
|
|
72
72
|
* adapters can be typed without depending on the consumer's ORM row type.
|
|
73
73
|
*/
|
|
74
|
-
export interface
|
|
74
|
+
export interface IntegrationSubscriptionView {
|
|
75
75
|
/** Primary key — addresses the cursor in `ICursorStore`. */
|
|
76
76
|
readonly id: string;
|
|
77
77
|
/** Canonical entity domain, e.g. `'opportunity'`, `'contact'`. */
|
|
@@ -85,7 +85,7 @@ export interface SyncSubscriptionView {
|
|
|
85
85
|
// ============================================================================
|
|
86
86
|
|
|
87
87
|
/**
|
|
88
|
-
* The one port every
|
|
88
|
+
* The one port every integration adapter implements. Mode-specific concerns
|
|
89
89
|
* (scheduling, rate-limiting, ack contracts, credential refresh) stay in the
|
|
90
90
|
* strategy class that implements this interface — this seam is deliberately
|
|
91
91
|
* minimal.
|
|
@@ -108,7 +108,7 @@ export interface IChangeSource<T> {
|
|
|
108
108
|
* it internally. `null` means "first run, no cursor yet."
|
|
109
109
|
*/
|
|
110
110
|
listChanges(
|
|
111
|
-
subscription:
|
|
111
|
+
subscription: IntegrationSubscriptionView,
|
|
112
112
|
cursor: unknown | null,
|
|
113
113
|
): AsyncIterable<Change<T>>;
|
|
114
114
|
}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PostgresCursorStore — Drizzle-backed `ICursorStore` (SYNC-4).
|
|
3
3
|
*
|
|
4
|
-
* Reads/writes `
|
|
4
|
+
* Reads/writes `integration_subscriptions.cursor` directly — no service
|
|
5
5
|
* composition. Consumers that want a service layer around subscriptions
|
|
6
6
|
* wire it themselves; the port's contract is just cursor persistence.
|
|
7
7
|
*
|
|
8
8
|
* ## What `put` stamps
|
|
9
9
|
*
|
|
10
|
-
* `put` writes three columns in one statement: `cursor`, `
|
|
10
|
+
* `put` writes three columns in one statement: `cursor`, `last_integration_at`,
|
|
11
11
|
* and `updated_at`. Rationale: SYNC-1's scheduling index
|
|
12
|
-
* `(enabled,
|
|
12
|
+
* `(enabled, last_integration_at)` is useless if `last_integration_at` doesn't advance
|
|
13
13
|
* with every cursor put. Every real consumer needs this stamped, so
|
|
14
14
|
* bundling it here avoids every consumer wrapping the port in a service
|
|
15
15
|
* layer just to stamp a timestamp.
|
|
16
16
|
*
|
|
17
17
|
* ## Multi-tenancy
|
|
18
18
|
*
|
|
19
|
-
* When `
|
|
19
|
+
* When `INTEGRATION_MULTI_TENANT` is true (SYNC-6):
|
|
20
20
|
* - every read/write is scoped by `AND tenant_id = $tenantId`
|
|
21
21
|
* - a null/missing `tenantId` throws `MissingTenantIdError` via the
|
|
22
22
|
* shared `assertTenantId` helper (one message shape across the
|
|
@@ -33,10 +33,10 @@ import { DRIZZLE } from '../../constants/tokens';
|
|
|
33
33
|
import type {
|
|
34
34
|
CursorSnapshot,
|
|
35
35
|
ICursorStore,
|
|
36
|
-
} from './
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import { assertTenantId } from './
|
|
36
|
+
} from './integration-cursor-store.protocol';
|
|
37
|
+
import { integrationSubscriptions } from './integration-audit.schema';
|
|
38
|
+
import { INTEGRATION_MULTI_TENANT } from './integration.tokens';
|
|
39
|
+
import { assertTenantId } from './integration-errors';
|
|
40
40
|
|
|
41
41
|
@Injectable()
|
|
42
42
|
export class PostgresCursorStore implements ICursorStore {
|
|
@@ -44,7 +44,7 @@ export class PostgresCursorStore implements ICursorStore {
|
|
|
44
44
|
|
|
45
45
|
constructor(
|
|
46
46
|
@Inject(DRIZZLE) private readonly db: DrizzleClient,
|
|
47
|
-
@Optional() @Inject(
|
|
47
|
+
@Optional() @Inject(INTEGRATION_MULTI_TENANT) multiTenant?: boolean,
|
|
48
48
|
) {
|
|
49
49
|
this.multiTenant = multiTenant ?? false;
|
|
50
50
|
}
|
|
@@ -56,8 +56,8 @@ export class PostgresCursorStore implements ICursorStore {
|
|
|
56
56
|
const where = this.buildWhere(subscriptionId, tenantId, 'cursor.get');
|
|
57
57
|
|
|
58
58
|
const rows = await this.db
|
|
59
|
-
.select({ cursor:
|
|
60
|
-
.from(
|
|
59
|
+
.select({ cursor: integrationSubscriptions.cursor })
|
|
60
|
+
.from(integrationSubscriptions)
|
|
61
61
|
.where(where)
|
|
62
62
|
.limit(1);
|
|
63
63
|
|
|
@@ -73,10 +73,10 @@ export class PostgresCursorStore implements ICursorStore {
|
|
|
73
73
|
const where = this.buildWhere(subscriptionId, tenantId, 'cursor.put');
|
|
74
74
|
|
|
75
75
|
await this.db
|
|
76
|
-
.update(
|
|
76
|
+
.update(integrationSubscriptions)
|
|
77
77
|
.set({
|
|
78
78
|
cursor,
|
|
79
|
-
|
|
79
|
+
lastIntegrationAt: new Date(),
|
|
80
80
|
updatedAt: new Date(),
|
|
81
81
|
})
|
|
82
82
|
.where(where);
|
|
@@ -89,33 +89,33 @@ export class PostgresCursorStore implements ICursorStore {
|
|
|
89
89
|
});
|
|
90
90
|
|
|
91
91
|
const where = this.multiTenant
|
|
92
|
-
? eq(
|
|
92
|
+
? eq(integrationSubscriptions.tenantId, tenantId as string)
|
|
93
93
|
: undefined;
|
|
94
94
|
|
|
95
95
|
const rows = await this.db
|
|
96
96
|
.select({
|
|
97
|
-
id:
|
|
98
|
-
|
|
99
|
-
adapter:
|
|
100
|
-
domain:
|
|
101
|
-
externalRef:
|
|
102
|
-
cursor:
|
|
103
|
-
|
|
104
|
-
updatedAt:
|
|
105
|
-
tenantId:
|
|
97
|
+
id: integrationSubscriptions.id,
|
|
98
|
+
connectionId: integrationSubscriptions.connectionId,
|
|
99
|
+
adapter: integrationSubscriptions.adapter,
|
|
100
|
+
domain: integrationSubscriptions.domain,
|
|
101
|
+
externalRef: integrationSubscriptions.externalRef,
|
|
102
|
+
cursor: integrationSubscriptions.cursor,
|
|
103
|
+
lastIntegrationAt: integrationSubscriptions.lastIntegrationAt,
|
|
104
|
+
updatedAt: integrationSubscriptions.updatedAt,
|
|
105
|
+
tenantId: integrationSubscriptions.tenantId,
|
|
106
106
|
})
|
|
107
|
-
.from(
|
|
107
|
+
.from(integrationSubscriptions)
|
|
108
108
|
.where(where)
|
|
109
|
-
.orderBy(desc(
|
|
109
|
+
.orderBy(desc(integrationSubscriptions.updatedAt));
|
|
110
110
|
|
|
111
111
|
return rows.map((row) => ({
|
|
112
112
|
subscriptionId: row.id,
|
|
113
|
-
|
|
113
|
+
connectionId: row.connectionId,
|
|
114
114
|
adapter: row.adapter,
|
|
115
115
|
domain: row.domain,
|
|
116
116
|
externalRef: row.externalRef,
|
|
117
117
|
cursor: row.cursor ?? null,
|
|
118
|
-
|
|
118
|
+
lastIntegrationAt: row.lastIntegrationAt,
|
|
119
119
|
updatedAt: row.updatedAt,
|
|
120
120
|
tenantId: row.tenantId,
|
|
121
121
|
}));
|
|
@@ -137,10 +137,10 @@ export class PostgresCursorStore implements ICursorStore {
|
|
|
137
137
|
});
|
|
138
138
|
if (this.multiTenant) {
|
|
139
139
|
return and(
|
|
140
|
-
eq(
|
|
141
|
-
eq(
|
|
140
|
+
eq(integrationSubscriptions.id, subscriptionId),
|
|
141
|
+
eq(integrationSubscriptions.tenantId, tenantId as string),
|
|
142
142
|
);
|
|
143
143
|
}
|
|
144
|
-
return eq(
|
|
144
|
+
return eq(integrationSubscriptions.id, subscriptionId);
|
|
145
145
|
}
|
|
146
146
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MemoryCursorStore — in-memory backend for `ICursorStore` (SYNC-3).
|
|
3
3
|
*
|
|
4
|
-
* Test double that lets consumers exercise `
|
|
4
|
+
* Test double that lets consumers exercise `ExecuteIntegrationUseCase` (SYNC-5) and
|
|
5
5
|
* other cursor-consuming code paths without Postgres. Mirrors the role of
|
|
6
6
|
* `MemoryEventBus` and `MemoryJobStore`: plain keyed state, tests take a
|
|
7
7
|
* direct reference for `beforeEach` resets.
|
|
@@ -23,15 +23,15 @@
|
|
|
23
23
|
*
|
|
24
24
|
* Not shipped in the upstream consumer; this is a subsystem-first addition for the
|
|
25
25
|
* test surface. Consumed by:
|
|
26
|
-
* - SYNC-5 unit tests (`
|
|
27
|
-
* - SYNC-6 module tests (`
|
|
26
|
+
* - SYNC-5 unit tests (`ExecuteIntegrationUseCase` against synthetic sources)
|
|
27
|
+
* - SYNC-6 module tests (`IntegrationModule.forRoot({ backend: 'memory' })`)
|
|
28
28
|
*/
|
|
29
29
|
import { Injectable } from '@nestjs/common';
|
|
30
30
|
import type {
|
|
31
31
|
CursorSnapshot,
|
|
32
32
|
ICursorStore,
|
|
33
|
-
} from './
|
|
34
|
-
import type {
|
|
33
|
+
} from './integration-cursor-store.protocol';
|
|
34
|
+
import type { MemoryIntegrationSubscription } from './integration-run-recorder.memory-backend';
|
|
35
35
|
|
|
36
36
|
@Injectable()
|
|
37
37
|
export class MemoryCursorStore implements ICursorStore {
|
|
@@ -44,13 +44,13 @@ export class MemoryCursorStore implements ICursorStore {
|
|
|
44
44
|
/**
|
|
45
45
|
* Seedable subscription metadata for `listAll` — the memory backend
|
|
46
46
|
* stores only `subscriptionId → cursor` in its write path, so the
|
|
47
|
-
* snapshot shape (`
|
|
47
|
+
* snapshot shape (`connectionId`, `adapter`, `domain`, `externalRef`,
|
|
48
48
|
* timestamps) has no natural source without test seeding. Tests populate
|
|
49
49
|
* this map; unseeded entries get empty-string metadata and `new Date(0)`
|
|
50
50
|
* timestamps so the shape stays stable. Production paths go through the
|
|
51
51
|
* Drizzle backend.
|
|
52
52
|
*/
|
|
53
|
-
readonly subscriptions: Map<string,
|
|
53
|
+
readonly subscriptions: Map<string, MemoryIntegrationSubscription> = new Map();
|
|
54
54
|
|
|
55
55
|
async get(
|
|
56
56
|
subscriptionId: string,
|
|
@@ -80,12 +80,12 @@ export class MemoryCursorStore implements ICursorStore {
|
|
|
80
80
|
const meta = this.subscriptions.get(subscriptionId);
|
|
81
81
|
snapshots.push({
|
|
82
82
|
subscriptionId,
|
|
83
|
-
|
|
83
|
+
connectionId: meta?.connectionId ?? '',
|
|
84
84
|
adapter: meta?.adapter ?? '',
|
|
85
85
|
domain: meta?.domain ?? '',
|
|
86
86
|
externalRef: meta?.externalRef ?? null,
|
|
87
87
|
cursor: cursor ?? null,
|
|
88
|
-
|
|
88
|
+
lastIntegrationAt: meta?.lastIntegrationAt ?? null,
|
|
89
89
|
updatedAt: meta?.updatedAt ?? new Date(0),
|
|
90
90
|
tenantId: null,
|
|
91
91
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Integration subsystem — cursor-store protocol (port)
|
|
3
3
|
*
|
|
4
4
|
* Subscription-addressed cursor persistence. The subscription row IS the
|
|
5
5
|
* cursor owner — addressable by id, scoped by
|
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Cursor shape is opaque at this seam; strategies type it internally
|
|
9
9
|
* (polling: `{ systemModstamp }`, CDC: `{ replayId }`, webhook: `{ ts }`).
|
|
10
|
-
* The Drizzle backend stores this as `
|
|
10
|
+
* The Drizzle backend stores this as `integration_subscriptions.cursor` jsonb.
|
|
11
11
|
*
|
|
12
12
|
* ## Multi-tenancy (SYNC-4)
|
|
13
13
|
*
|
|
14
|
-
* Both methods accept an optional `tenantId`. When `
|
|
14
|
+
* Both methods accept an optional `tenantId`. When `INTEGRATION_MULTI_TENANT` is
|
|
15
15
|
* enabled (SYNC-6), the Drizzle backend MUST scope every read/write by
|
|
16
16
|
* `tenant_id`, and a `null`/missing value throws `MissingTenantIdError` at
|
|
17
17
|
* the module boundary. When the flag is off, `tenantId` is ignored.
|
|
@@ -26,23 +26,23 @@
|
|
|
26
26
|
* through input shapes, not through wrapper layers.
|
|
27
27
|
*/
|
|
28
28
|
/**
|
|
29
|
-
* Denormalized snapshot of one `
|
|
29
|
+
* Denormalized snapshot of one `integration_subscriptions` row for the OBS-5
|
|
30
30
|
* observability composer (epic #195). `cursor` is opaque (the port's
|
|
31
31
|
* contract); the rest is subscription metadata needed to label the snapshot
|
|
32
32
|
* in a dashboard/API surface.
|
|
33
33
|
*
|
|
34
|
-
* The Drizzle backend reads this directly from `
|
|
34
|
+
* The Drizzle backend reads this directly from `integration_subscriptions`. Memory
|
|
35
35
|
* backends derive it from the seedable `subscriptions` side-map — tests
|
|
36
36
|
* that want meaningful snapshots must seed first.
|
|
37
37
|
*/
|
|
38
38
|
export interface CursorSnapshot {
|
|
39
39
|
readonly subscriptionId: string;
|
|
40
|
-
readonly
|
|
40
|
+
readonly connectionId: string;
|
|
41
41
|
readonly adapter: string;
|
|
42
42
|
readonly domain: string;
|
|
43
43
|
readonly externalRef: string | null;
|
|
44
44
|
readonly cursor: unknown | null;
|
|
45
|
-
readonly
|
|
45
|
+
readonly lastIntegrationAt: Date | null;
|
|
46
46
|
readonly updatedAt: Date;
|
|
47
47
|
readonly tenantId: string | null;
|
|
48
48
|
}
|
|
@@ -51,7 +51,7 @@ export interface ICursorStore {
|
|
|
51
51
|
/**
|
|
52
52
|
* Return the last persisted cursor for `subscriptionId`, or `null`.
|
|
53
53
|
*
|
|
54
|
-
* @param tenantId required when `
|
|
54
|
+
* @param tenantId required when `INTEGRATION_MULTI_TENANT` is on (backend
|
|
55
55
|
* scopes the SELECT by tenant); ignored otherwise.
|
|
56
56
|
*/
|
|
57
57
|
get(subscriptionId: string, tenantId?: string | null): Promise<unknown | null>;
|
|
@@ -59,12 +59,12 @@ export interface ICursorStore {
|
|
|
59
59
|
/**
|
|
60
60
|
* Persist `cursor` for `subscriptionId`. Overwrites.
|
|
61
61
|
*
|
|
62
|
-
* The Drizzle backend also stamps `
|
|
63
|
-
* same row so the scheduling index `(enabled,
|
|
62
|
+
* The Drizzle backend also stamps `last_integration_at` + `updated_at` on the
|
|
63
|
+
* same row so the scheduling index `(enabled, last_integration_at)` stays
|
|
64
64
|
* accurate without consumers wrapping the port. The memory backend
|
|
65
65
|
* ignores timestamps.
|
|
66
66
|
*
|
|
67
|
-
* @param tenantId required when `
|
|
67
|
+
* @param tenantId required when `INTEGRATION_MULTI_TENANT` is on (backend
|
|
68
68
|
* scopes the UPDATE by tenant); ignored otherwise.
|
|
69
69
|
*/
|
|
70
70
|
put(
|
|
@@ -74,11 +74,11 @@ export interface ICursorStore {
|
|
|
74
74
|
): Promise<void>;
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
|
-
* Return one `CursorSnapshot` per `
|
|
77
|
+
* Return one `CursorSnapshot` per `integration_subscriptions` row, ordered by
|
|
78
78
|
* `updated_at DESC`. Consumed by the OBS-5 observability composer to
|
|
79
79
|
* surface current cursor state per subscription.
|
|
80
80
|
*
|
|
81
|
-
* @param tenantId required by Drizzle backend when `
|
|
81
|
+
* @param tenantId required by Drizzle backend when `INTEGRATION_MULTI_TENANT`
|
|
82
82
|
* is on (throws `MissingTenantIdError` otherwise); memory
|
|
83
83
|
* backend accepts but ignores.
|
|
84
84
|
*/
|