@pattern-stack/codegen 0.10.1 → 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 +62 -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 +7 -7
- package/dist/runtime/subsystems/index.js +51 -51
- 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/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 +262 -269
- 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/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/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/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
|
@@ -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
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Typed errors + shared boundary helpers for the
|
|
2
|
+
* Typed errors + shared boundary helpers for the integration subsystem.
|
|
3
3
|
*
|
|
4
4
|
* Classes (not bare Error) so consumers can `instanceof` them in catch
|
|
5
5
|
* blocks and exception filters can map them to HTTP codes.
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Thrown by the Drizzle cursor-store / run-recorder backends AND by the
|
|
12
|
-
* orchestrator entry point when `
|
|
12
|
+
* orchestrator entry point when `INTEGRATION_MULTI_TENANT` is enabled but the
|
|
13
13
|
* caller did not supply a non-null `tenantId`. Strict enforcement at the
|
|
14
14
|
* boundary — explicit `null` still throws.
|
|
15
15
|
*
|
|
@@ -24,7 +24,7 @@ export class MissingTenantIdError extends Error {
|
|
|
24
24
|
override readonly name = 'MissingTenantIdError';
|
|
25
25
|
constructor(operation: string) {
|
|
26
26
|
super(
|
|
27
|
-
`Missing tenantId for
|
|
27
|
+
`Missing tenantId for integration operation '${operation}'. IntegrationModule is ` +
|
|
28
28
|
`configured with multiTenant: true — every call must include a ` +
|
|
29
29
|
`non-null tenantId. Either pass the tenantId or disable multi-` +
|
|
30
30
|
`tenancy on the module.`,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Integration subsystem — field-diff protocol (port)
|
|
3
3
|
*
|
|
4
4
|
* `IFieldDiffer<T>` is the pluggable differ seam. The default implementation
|
|
5
5
|
* (`DeepEqualDiffer`, ships in SYNC-5) walks every field except an ignore
|
|
@@ -17,7 +17,7 @@ import { z } from 'zod';
|
|
|
17
17
|
// ============================================================================
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* Structured per-field change. Enforced shape for `
|
|
20
|
+
* Structured per-field change. Enforced shape for `integration_run_items.changed_fields`.
|
|
21
21
|
*
|
|
22
22
|
* `created` items set `from: null, to: <value>` for every non-null field.
|
|
23
23
|
* `deleted` items set `from: <value>, to: null`.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Integration subsystem — loopback-fingerprint protocol (port)
|
|
3
3
|
*
|
|
4
4
|
* Optional port. When the local system writes to an upstream provider via an
|
|
5
5
|
* outbound path, the same change typically echoes back on the next inbound
|
|
6
|
-
* poll/CDC/webhook. A fingerprint store lets `
|
|
6
|
+
* poll/CDC/webhook. A fingerprint store lets `ExecuteIntegrationUseCase` skip
|
|
7
7
|
* records it already wrote, avoiding a diff-noop round trip and a spurious
|
|
8
8
|
* audit row.
|
|
9
9
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Integration subsystem — change-source middleware protocol (#226-1)
|
|
3
3
|
*
|
|
4
4
|
* `ChangeMiddleware<T>` lets consumers compose cross-cutting concerns
|
|
5
5
|
* (loopback suppression, redaction, throttling) onto an `IChangeSource<T>`
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*
|
|
12
12
|
* type ChangeMiddleware<T> =
|
|
13
13
|
* (next: ChangeIterator<T>) =>
|
|
14
|
-
* (subscription:
|
|
14
|
+
* (subscription: IntegrationSubscriptionView, cursor: unknown | null) =>
|
|
15
15
|
* AsyncIterable<Change<T>>;
|
|
16
16
|
*
|
|
17
17
|
* The middleware wraps the *next* iterator factory rather than the
|
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
* Loopback shipping as middleware (#226-5) is the canonical example —
|
|
27
27
|
* `createLoopbackMiddleware(store)` filters echoes of local writes
|
|
28
28
|
* before they reach the orchestrator's diff stage, replacing the prior
|
|
29
|
-
* `@Optional()
|
|
29
|
+
* `@Optional() INTEGRATION_LOOPBACK_FINGERPRINT_STORE` orchestrator branch.
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
32
|
import type {
|
|
33
33
|
Change,
|
|
34
|
-
|
|
35
|
-
} from './
|
|
34
|
+
IntegrationSubscriptionView,
|
|
35
|
+
} from './integration-change-source.protocol';
|
|
36
36
|
|
|
37
37
|
// ============================================================================
|
|
38
38
|
// ChangeIterator — the inner shape middleware wraps
|
|
@@ -44,7 +44,7 @@ import type {
|
|
|
44
44
|
* `IChangeSource<T>` instance method binds 1:1 to this signature.
|
|
45
45
|
*/
|
|
46
46
|
export type ChangeIterator<T> = (
|
|
47
|
-
subscription:
|
|
47
|
+
subscription: IntegrationSubscriptionView,
|
|
48
48
|
cursor: unknown | null,
|
|
49
49
|
) => AsyncIterable<Change<T>>;
|
|
50
50
|
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* DrizzleIntegrationRunRecorder — Drizzle-backed `IIntegrationRunRecorder` (SYNC-4).
|
|
3
3
|
*
|
|
4
4
|
* Generic write path only — extracted from the source app's
|
|
5
|
-
* `
|
|
5
|
+
* `IntegrationRunRecorderService`, minus CRM-specific convenience methods. Those
|
|
6
6
|
* stay consumer-owned; the subsystem ships the substrate.
|
|
7
7
|
*
|
|
8
8
|
* ## Responsibilities
|
|
9
9
|
*
|
|
10
|
-
* - `startRun` — INSERT
|
|
10
|
+
* - `startRun` — INSERT integration_runs row in status='running', returns id.
|
|
11
11
|
* - `recordItem` — validates `changedFields` via `FieldDiffSchema.parse`
|
|
12
12
|
* BEFORE the INSERT; a malformed shape throws before
|
|
13
13
|
* any DB call fires. Enforces the ADR-0003 contract at
|
|
14
14
|
* the write boundary.
|
|
15
|
-
* - `completeRun` — UPDATE
|
|
15
|
+
* - `completeRun` — UPDATE integration_runs with terminal status, counts,
|
|
16
16
|
* cursor_after, duration_ms, completed_at.
|
|
17
17
|
*
|
|
18
18
|
* ## Multi-tenancy
|
|
19
19
|
*
|
|
20
|
-
* When `
|
|
20
|
+
* When `INTEGRATION_MULTI_TENANT` is true (SYNC-6):
|
|
21
21
|
* - `startRun` and `recordItem` require non-null `tenantId` on input.
|
|
22
22
|
* Enforcement goes through the shared `assertTenantId` helper so the
|
|
23
23
|
* error message shape matches the orchestrator entry point + the
|
|
@@ -33,23 +33,23 @@ import type { DrizzleClient } from '../../types/drizzle';
|
|
|
33
33
|
import { DRIZZLE } from '../../constants/tokens';
|
|
34
34
|
import type {
|
|
35
35
|
CompleteRunInput,
|
|
36
|
-
|
|
36
|
+
IIntegrationRunRecorder,
|
|
37
37
|
RecordItemInput,
|
|
38
38
|
StartRunInput,
|
|
39
|
-
|
|
40
|
-
} from './
|
|
41
|
-
import {
|
|
42
|
-
import { FieldDiffSchema } from './
|
|
43
|
-
import {
|
|
44
|
-
import { assertTenantId } from './
|
|
39
|
+
IntegrationRunSummary,
|
|
40
|
+
} from './integration-run-recorder.protocol';
|
|
41
|
+
import { integrationRuns, integrationRunItems, integrationSubscriptions } from './integration-audit.schema';
|
|
42
|
+
import { FieldDiffSchema } from './integration-field-diff.protocol';
|
|
43
|
+
import { INTEGRATION_MULTI_TENANT } from './integration.tokens';
|
|
44
|
+
import { assertTenantId } from './integration-errors';
|
|
45
45
|
|
|
46
46
|
@Injectable()
|
|
47
|
-
export class
|
|
47
|
+
export class DrizzleIntegrationRunRecorder implements IIntegrationRunRecorder {
|
|
48
48
|
private readonly multiTenant: boolean;
|
|
49
49
|
|
|
50
50
|
constructor(
|
|
51
51
|
@Inject(DRIZZLE) private readonly db: DrizzleClient,
|
|
52
|
-
@Optional() @Inject(
|
|
52
|
+
@Optional() @Inject(INTEGRATION_MULTI_TENANT) multiTenant?: boolean,
|
|
53
53
|
) {
|
|
54
54
|
this.multiTenant = multiTenant ?? false;
|
|
55
55
|
}
|
|
@@ -61,7 +61,7 @@ export class DrizzleSyncRunRecorder implements ISyncRunRecorder {
|
|
|
61
61
|
});
|
|
62
62
|
|
|
63
63
|
const rows = await this.db
|
|
64
|
-
.insert(
|
|
64
|
+
.insert(integrationRuns)
|
|
65
65
|
.values({
|
|
66
66
|
subscriptionId: input.subscriptionId,
|
|
67
67
|
direction: input.direction,
|
|
@@ -70,14 +70,14 @@ export class DrizzleSyncRunRecorder implements ISyncRunRecorder {
|
|
|
70
70
|
cursorBefore: input.cursorBefore ?? null,
|
|
71
71
|
tenantId: input.tenantId ?? null,
|
|
72
72
|
})
|
|
73
|
-
.returning({ id:
|
|
73
|
+
.returning({ id: integrationRuns.id });
|
|
74
74
|
|
|
75
75
|
const id = rows[0]?.id;
|
|
76
76
|
if (!id) {
|
|
77
77
|
// Drizzle's insert().returning() contract: at least one row is
|
|
78
78
|
// returned for every successful INSERT. A missing id would indicate
|
|
79
79
|
// a driver misbehavior; throw loudly rather than return bogus data.
|
|
80
|
-
throw new Error('
|
|
80
|
+
throw new Error('DrizzleIntegrationRunRecorder: INSERT RETURNING produced no id');
|
|
81
81
|
}
|
|
82
82
|
return { id };
|
|
83
83
|
}
|
|
@@ -93,8 +93,8 @@ export class DrizzleSyncRunRecorder implements ISyncRunRecorder {
|
|
|
93
93
|
// the validation failure, not a DB constraint error.
|
|
94
94
|
FieldDiffSchema.parse(input.changedFields);
|
|
95
95
|
|
|
96
|
-
await this.db.insert(
|
|
97
|
-
|
|
96
|
+
await this.db.insert(integrationRunItems).values({
|
|
97
|
+
integrationRunId: input.integrationRunId,
|
|
98
98
|
entityType: input.entityType,
|
|
99
99
|
externalId: input.externalId,
|
|
100
100
|
localId: input.localId ?? null,
|
|
@@ -111,21 +111,21 @@ export class DrizzleSyncRunRecorder implements ISyncRunRecorder {
|
|
|
111
111
|
limit: number,
|
|
112
112
|
subscriptionId?: string,
|
|
113
113
|
tenantId?: string | null,
|
|
114
|
-
): Promise<
|
|
114
|
+
): Promise<IntegrationRunSummary[]> {
|
|
115
115
|
assertTenantId(tenantId, {
|
|
116
116
|
multiTenant: this.multiTenant,
|
|
117
117
|
operation: 'listRecent',
|
|
118
118
|
});
|
|
119
119
|
|
|
120
|
-
// JOIN against
|
|
121
|
-
// `
|
|
120
|
+
// JOIN against integration_subscriptions to resolve `connection_id` per run.
|
|
121
|
+
// `integration_runs.subscription_id` is a non-null FK so INNER JOIN is correct;
|
|
122
122
|
// there should be no orphaned runs.
|
|
123
123
|
const conditions: SQL[] = [];
|
|
124
124
|
if (subscriptionId !== undefined) {
|
|
125
|
-
conditions.push(eq(
|
|
125
|
+
conditions.push(eq(integrationRuns.subscriptionId, subscriptionId));
|
|
126
126
|
}
|
|
127
127
|
if (this.multiTenant) {
|
|
128
|
-
conditions.push(eq(
|
|
128
|
+
conditions.push(eq(integrationRuns.tenantId, tenantId as string));
|
|
129
129
|
}
|
|
130
130
|
const where =
|
|
131
131
|
conditions.length === 0
|
|
@@ -136,28 +136,28 @@ export class DrizzleSyncRunRecorder implements ISyncRunRecorder {
|
|
|
136
136
|
|
|
137
137
|
const rows = await this.db
|
|
138
138
|
.select({
|
|
139
|
-
id:
|
|
140
|
-
subscriptionId:
|
|
141
|
-
|
|
142
|
-
status:
|
|
143
|
-
startedAt:
|
|
144
|
-
completedAt:
|
|
145
|
-
recordsProcessed:
|
|
146
|
-
tenantId:
|
|
139
|
+
id: integrationRuns.id,
|
|
140
|
+
subscriptionId: integrationRuns.subscriptionId,
|
|
141
|
+
connectionId: integrationSubscriptions.connectionId,
|
|
142
|
+
status: integrationRuns.status,
|
|
143
|
+
startedAt: integrationRuns.startedAt,
|
|
144
|
+
completedAt: integrationRuns.completedAt,
|
|
145
|
+
recordsProcessed: integrationRuns.recordsProcessed,
|
|
146
|
+
tenantId: integrationRuns.tenantId,
|
|
147
147
|
})
|
|
148
|
-
.from(
|
|
148
|
+
.from(integrationRuns)
|
|
149
149
|
.innerJoin(
|
|
150
|
-
|
|
151
|
-
eq(
|
|
150
|
+
integrationSubscriptions,
|
|
151
|
+
eq(integrationRuns.subscriptionId, integrationSubscriptions.id),
|
|
152
152
|
)
|
|
153
153
|
.where(where)
|
|
154
|
-
.orderBy(desc(
|
|
154
|
+
.orderBy(desc(integrationRuns.startedAt))
|
|
155
155
|
.limit(limit);
|
|
156
156
|
|
|
157
157
|
return rows.map((row) => ({
|
|
158
158
|
id: row.id,
|
|
159
159
|
subscriptionId: row.subscriptionId,
|
|
160
|
-
|
|
160
|
+
connectionId: row.connectionId,
|
|
161
161
|
status: row.status,
|
|
162
162
|
startedAt: row.startedAt,
|
|
163
163
|
completedAt: row.completedAt,
|
|
@@ -168,7 +168,7 @@ export class DrizzleSyncRunRecorder implements ISyncRunRecorder {
|
|
|
168
168
|
|
|
169
169
|
async completeRun(runId: string, input: CompleteRunInput): Promise<void> {
|
|
170
170
|
await this.db
|
|
171
|
-
.update(
|
|
171
|
+
.update(integrationRuns)
|
|
172
172
|
.set({
|
|
173
173
|
status: input.status,
|
|
174
174
|
recordsFound: input.recordsFound,
|
|
@@ -178,6 +178,6 @@ export class DrizzleSyncRunRecorder implements ISyncRunRecorder {
|
|
|
178
178
|
error: input.error ?? null,
|
|
179
179
|
completedAt: new Date(),
|
|
180
180
|
})
|
|
181
|
-
.where(eq(
|
|
181
|
+
.where(eq(integrationRuns.id, runId));
|
|
182
182
|
}
|
|
183
183
|
}
|