@pattern-stack/codegen 0.10.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +122 -0
- package/README.md +5 -5
- package/consumer-skills/codegen/SKILL.md +2 -2
- package/consumer-skills/{sync → integration}/SKILL.md +29 -29
- package/consumer-skills/{sync → integration}/audit-and-detection.md +22 -22
- package/consumer-skills/{sync → integration}/change-sources-and-sinks.md +60 -60
- package/consumer-skills/subsystems/SKILL.md +8 -8
- package/consumer-skills/subsystems/wiring-and-order.md +7 -7
- package/dist/runtime/base-classes/index.d.ts +4 -4
- package/dist/runtime/base-classes/index.js +35 -35
- package/dist/runtime/base-classes/index.js.map +1 -1
- package/dist/runtime/base-classes/{synced-entity-repository.d.ts → integrated-entity-repository.d.ts} +15 -15
- package/dist/runtime/base-classes/{synced-entity-repository.js → integrated-entity-repository.js} +21 -21
- package/dist/runtime/base-classes/integrated-entity-repository.js.map +1 -0
- package/dist/runtime/base-classes/{synced-entity-service.d.ts → integrated-entity-service.d.ts} +6 -6
- package/dist/runtime/base-classes/{synced-entity-service.js → integrated-entity-service.js} +4 -4
- package/dist/runtime/base-classes/integrated-entity-service.js.map +1 -0
- package/dist/runtime/base-classes/{sync-upsert-config.d.ts → integration-upsert-config.d.ts} +13 -13
- package/dist/runtime/base-classes/integration-upsert-config.js +1 -0
- package/dist/runtime/base-classes/{junction-sync-repository.d.ts → junction-integration-repository.d.ts} +11 -11
- package/dist/runtime/base-classes/{junction-sync-repository.js → junction-integration-repository.js} +15 -15
- package/dist/runtime/base-classes/junction-integration-repository.js.map +1 -0
- package/dist/runtime/subsystems/auth/auth-oauth-state.schema.js.map +1 -1
- package/dist/runtime/subsystems/auth/auth.module.d.ts +4 -4
- package/dist/runtime/subsystems/auth/auth.module.js +3 -3
- package/dist/runtime/subsystems/auth/auth.module.js.map +1 -1
- package/dist/runtime/subsystems/auth/auth.tokens.d.ts +8 -8
- package/dist/runtime/subsystems/auth/auth.tokens.js +6 -6
- package/dist/runtime/subsystems/auth/auth.tokens.js.map +1 -1
- package/dist/runtime/subsystems/auth/backends/state-store.drizzle-backend.js.map +1 -1
- package/dist/runtime/subsystems/auth/controllers/auth.controller.d.ts +2 -2
- package/dist/runtime/subsystems/auth/controllers/auth.controller.js +3 -3
- package/dist/runtime/subsystems/auth/controllers/auth.controller.js.map +1 -1
- package/dist/runtime/subsystems/auth/index.d.ts +3 -3
- package/dist/runtime/subsystems/auth/index.js +40 -40
- package/dist/runtime/subsystems/auth/index.js.map +1 -1
- package/dist/runtime/subsystems/auth/middleware/requester-context.js.map +1 -1
- package/dist/runtime/subsystems/auth/protocols/auth-strategy.d.ts +3 -3
- package/dist/runtime/subsystems/auth/protocols/{integration-store.d.ts → connection-store.d.ts} +20 -20
- package/dist/runtime/subsystems/auth/protocols/connection-store.js +1 -0
- package/dist/runtime/subsystems/auth/protocols/provider-strategy.d.ts +3 -3
- package/dist/runtime/subsystems/auth/runtime/{integration-broken.error.d.ts → connection-broken.error.d.ts} +5 -5
- package/dist/runtime/subsystems/auth/runtime/connection-broken.error.js +19 -0
- package/dist/runtime/subsystems/auth/runtime/connection-broken.error.js.map +1 -0
- package/dist/runtime/subsystems/auth/runtime/oauth2-refresh.strategy.d.ts +10 -10
- package/dist/runtime/subsystems/auth/runtime/oauth2-refresh.strategy.js +28 -28
- package/dist/runtime/subsystems/auth/runtime/oauth2-refresh.strategy.js.map +1 -1
- package/dist/runtime/subsystems/auth/runtime/with-auth-retry.d.ts +1 -1
- package/dist/runtime/subsystems/auth/runtime/with-auth-retry.js +3 -3
- package/dist/runtime/subsystems/auth/runtime/with-auth-retry.js.map +1 -1
- package/dist/runtime/subsystems/index.d.ts +11 -7
- package/dist/runtime/subsystems/index.js +1041 -67
- package/dist/runtime/subsystems/index.js.map +1 -1
- package/dist/runtime/subsystems/{sync → integration}/build-change-source.d.ts +3 -3
- package/dist/runtime/subsystems/{sync → integration}/build-change-source.js +3 -3
- package/dist/runtime/subsystems/integration/build-change-source.js.map +1 -0
- package/dist/runtime/subsystems/{sync → integration}/deep-equal.differ.d.ts +2 -2
- package/dist/runtime/subsystems/{sync → integration}/deep-equal.differ.js +1 -1
- package/dist/runtime/subsystems/integration/deep-equal.differ.js.map +1 -0
- package/dist/runtime/subsystems/{sync → integration}/detection-config.schema.d.ts +3 -3
- package/dist/runtime/subsystems/{sync → integration}/detection-config.schema.js +1 -1
- package/dist/runtime/subsystems/integration/detection-config.schema.js.map +1 -0
- package/dist/runtime/subsystems/integration/entity-change-source-registry.memory.d.ts +25 -0
- package/dist/runtime/subsystems/integration/entity-change-source-registry.memory.js +34 -0
- package/dist/runtime/subsystems/integration/entity-change-source-registry.memory.js.map +1 -0
- package/dist/runtime/subsystems/integration/entity-change-source-registry.protocol.d.ts +53 -0
- package/dist/runtime/subsystems/integration/entity-change-source-registry.protocol.js +13 -0
- package/dist/runtime/subsystems/integration/entity-change-source-registry.protocol.js.map +1 -0
- package/dist/runtime/subsystems/{sync/execute-sync.use-case.d.ts → integration/execute-integration.use-case.d.ts} +13 -13
- package/dist/runtime/subsystems/{sync/execute-sync.use-case.js → integration/execute-integration.use-case.js} +30 -30
- package/dist/runtime/subsystems/integration/execute-integration.use-case.js.map +1 -0
- package/dist/runtime/subsystems/integration/index.d.ts +30 -0
- package/dist/runtime/subsystems/{sync → integration}/index.js +206 -171
- package/dist/runtime/subsystems/integration/index.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-audit.schema.d.ts → integration/integration-audit.schema.d.ts} +64 -64
- package/dist/runtime/subsystems/{sync/sync-audit.schema.js → integration/integration-audit.schema.js} +47 -47
- package/dist/runtime/subsystems/integration/integration-audit.schema.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-change-source.protocol.d.ts → integration/integration-change-source.protocol.d.ts} +10 -10
- package/dist/runtime/subsystems/integration/integration-change-source.protocol.js +1 -0
- package/dist/runtime/subsystems/{sync/sync-cursor-store.drizzle-backend.d.ts → integration/integration-cursor-store.drizzle-backend.d.ts} +1 -1
- package/dist/runtime/subsystems/{sync/sync-cursor-store.drizzle-backend.js → integration/integration-cursor-store.drizzle-backend.js} +65 -65
- package/dist/runtime/subsystems/integration/integration-cursor-store.drizzle-backend.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-cursor-store.memory-backend.d.ts → integration/integration-cursor-store.memory-backend.d.ts} +6 -6
- package/dist/runtime/subsystems/{sync/sync-cursor-store.memory-backend.js → integration/integration-cursor-store.memory-backend.js} +5 -5
- package/dist/runtime/subsystems/integration/integration-cursor-store.memory-backend.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-cursor-store.protocol.d.ts → integration/integration-cursor-store.protocol.d.ts} +13 -13
- package/dist/runtime/subsystems/integration/integration-cursor-store.protocol.js +1 -0
- package/dist/runtime/subsystems/{sync/sync-errors.d.ts → integration/integration-errors.d.ts} +2 -2
- package/dist/runtime/subsystems/{sync/sync-errors.js → integration/integration-errors.js} +3 -3
- package/dist/runtime/subsystems/integration/integration-errors.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-field-diff.protocol.d.ts → integration/integration-field-diff.protocol.d.ts} +2 -2
- package/dist/runtime/subsystems/{sync/sync-field-diff.protocol.js → integration/integration-field-diff.protocol.js} +2 -2
- package/dist/runtime/subsystems/integration/integration-field-diff.protocol.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-loopback.protocol.d.ts → integration/integration-loopback.protocol.d.ts} +2 -2
- package/dist/runtime/subsystems/integration/integration-loopback.protocol.js +1 -0
- package/dist/runtime/subsystems/{sync/sync-middleware.protocol.d.ts → integration/integration-middleware.protocol.d.ts} +5 -5
- package/dist/runtime/subsystems/integration/integration-middleware.protocol.js +1 -0
- package/dist/runtime/subsystems/{sync/sync-run-recorder.drizzle-backend.d.ts → integration/integration-run-recorder.drizzle-backend.d.ts} +5 -5
- package/dist/runtime/subsystems/{sync/sync-run-recorder.drizzle-backend.js → integration/integration-run-recorder.drizzle-backend.js} +73 -73
- package/dist/runtime/subsystems/integration/integration-run-recorder.drizzle-backend.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-run-recorder.memory-backend.d.ts → integration/integration-run-recorder.memory-backend.d.ts} +15 -15
- package/dist/runtime/subsystems/{sync/sync-run-recorder.memory-backend.js → integration/integration-run-recorder.memory-backend.js} +11 -11
- package/dist/runtime/subsystems/integration/integration-run-recorder.memory-backend.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-run-recorder.protocol.d.ts → integration/integration-run-recorder.protocol.d.ts} +25 -25
- package/dist/runtime/subsystems/integration/integration-run-recorder.protocol.js +1 -0
- package/dist/runtime/subsystems/{sync/sync-sink.protocol.d.ts → integration/integration-sink.protocol.d.ts} +5 -5
- package/dist/runtime/subsystems/integration/integration-sink.protocol.js +1 -0
- package/dist/runtime/subsystems/{sync/sync.module.d.ts → integration/integration.module.d.ts} +24 -24
- package/dist/runtime/subsystems/{sync/sync.module.js → integration/integration.module.js} +132 -132
- package/dist/runtime/subsystems/integration/integration.module.js.map +1 -0
- package/dist/runtime/subsystems/integration/integration.tokens.d.ts +60 -0
- package/dist/runtime/subsystems/integration/integration.tokens.js +20 -0
- package/dist/runtime/subsystems/integration/integration.tokens.js.map +1 -0
- package/dist/runtime/subsystems/{sync → integration}/loopback.middleware.d.ts +5 -5
- package/dist/runtime/subsystems/{sync → integration}/loopback.middleware.js +1 -1
- package/dist/runtime/subsystems/integration/loopback.middleware.js.map +1 -0
- package/dist/runtime/subsystems/{sync → integration}/poll-change-source.d.ts +5 -5
- package/dist/runtime/subsystems/{sync → integration}/poll-change-source.js +1 -1
- package/dist/runtime/subsystems/integration/poll-change-source.js.map +1 -0
- package/dist/runtime/subsystems/{sync → integration}/webhook-change-source.d.ts +5 -5
- package/dist/runtime/subsystems/{sync → integration}/webhook-change-source.js +1 -1
- package/dist/runtime/subsystems/integration/webhook-change-source.js.map +1 -0
- package/dist/runtime/subsystems/jobs/job-worker.module.d.ts +1 -1
- package/dist/runtime/subsystems/observability/index.d.ts +4 -4
- package/dist/runtime/subsystems/observability/index.js +11 -11
- package/dist/runtime/subsystems/observability/index.js.map +1 -1
- package/dist/runtime/subsystems/observability/observability.module.d.ts +2 -2
- package/dist/runtime/subsystems/observability/observability.module.js +11 -11
- package/dist/runtime/subsystems/observability/observability.module.js.map +1 -1
- package/dist/runtime/subsystems/observability/observability.protocol.d.ts +11 -11
- package/dist/runtime/subsystems/observability/observability.service.d.ts +6 -6
- package/dist/runtime/subsystems/observability/observability.service.js +11 -11
- package/dist/runtime/subsystems/observability/observability.service.js.map +1 -1
- package/dist/runtime/subsystems/observability/observability.tokens.d.ts +1 -1
- package/dist/runtime/subsystems/observability/observability.tokens.js.map +1 -1
- package/dist/runtime/subsystems/observability/reporters/bridge-metrics.reporter.d.ts +3 -3
- package/dist/runtime/subsystems/observability/reporters/bridge-metrics.reporter.js.map +1 -1
- package/dist/runtime/subsystems/observability/reporters/index.d.ts +3 -3
- package/dist/runtime/subsystems/observability/reporters/index.js.map +1 -1
- package/dist/src/cli/index.js +1336 -376
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/index.d.ts +70 -22
- package/dist/src/index.js +290 -194
- package/dist/src/index.js.map +1 -1
- package/examples/auth-integrations/README.md +32 -32
- package/examples/auth-integrations/definitions/entities/{integration.yaml → connection.yaml} +10 -10
- package/examples/auth-integrations/runtime/{integrations/adapters/integration-grant-sink.adapter.ts → connections/adapters/connection-grant-sink.adapter.ts} +7 -7
- package/examples/auth-integrations/runtime/{integrations/adapters/integration-reader.adapter.ts → connections/adapters/connection-reader.adapter.ts} +10 -10
- package/examples/auth-integrations/runtime/{integrations/adapters/integration-token-writer.adapter.ts → connections/adapters/connection-token-writer.adapter.ts} +11 -11
- package/examples/auth-integrations/runtime/connections/connections-auth.module.ts +81 -0
- package/examples/auth-integrations/runtime/{integrations/facade/integrations.service.ts → connections/facade/connections.service.ts} +35 -35
- package/examples/auth-integrations/runtime/{integrations → connections}/oauth/use-cases/create-or-update-from-oauth-grant.use-case.ts +11 -11
- package/examples/auth-integrations/runtime/{integrations/oauth/use-cases/disconnect-integration.use-case.ts → connections/oauth/use-cases/disconnect-connection.use-case.ts} +6 -6
- package/examples/auth-integrations/runtime/connections/oauth/use-cases/list-user-connections.use-case.ts +21 -0
- package/examples/auth-integrations/runtime/connections/oauth/use-cases/mark-connection-requires-reauth.use-case.ts +21 -0
- package/package.json +9 -1
- package/runtime/base-classes/index.ts +8 -8
- package/runtime/base-classes/{synced-entity-repository.ts → integrated-entity-repository.ts} +36 -36
- package/runtime/base-classes/{synced-entity-service.ts → integrated-entity-service.ts} +6 -6
- package/runtime/base-classes/{sync-upsert-config.ts → integration-upsert-config.ts} +12 -12
- package/runtime/base-classes/{junction-sync-repository.ts → junction-integration-repository.ts} +28 -28
- package/runtime/subsystems/auth/auth-oauth-state.schema.ts +1 -1
- package/runtime/subsystems/auth/auth.module.ts +4 -4
- package/runtime/subsystems/auth/auth.tokens.ts +7 -7
- package/runtime/subsystems/auth/controllers/auth.controller.ts +7 -7
- package/runtime/subsystems/auth/index.ts +19 -19
- package/runtime/subsystems/auth/protocols/auth-strategy.ts +3 -3
- package/runtime/subsystems/auth/protocols/{integration-store.ts → connection-store.ts} +19 -19
- package/runtime/subsystems/auth/protocols/provider-strategy.ts +2 -2
- package/runtime/subsystems/auth/runtime/{integration-broken.error.ts → connection-broken.error.ts} +5 -5
- package/runtime/subsystems/auth/runtime/oauth2-refresh.strategy.ts +35 -35
- package/runtime/subsystems/auth/runtime/with-auth-retry.ts +3 -3
- package/runtime/subsystems/index.ts +26 -11
- package/runtime/subsystems/{sync → integration}/build-change-source.ts +3 -3
- package/runtime/subsystems/{sync → integration}/deep-equal.differ.ts +7 -7
- package/runtime/subsystems/{sync → integration}/detection-config.schema.ts +3 -3
- package/runtime/subsystems/integration/entity-change-source-registry.memory.ts +40 -0
- package/runtime/subsystems/integration/entity-change-source-registry.protocol.ts +59 -0
- package/runtime/subsystems/{sync/execute-sync.use-case.ts → integration/execute-integration.use-case.ts} +40 -40
- package/runtime/subsystems/{sync → integration}/index.ts +56 -47
- package/runtime/subsystems/{sync/sync-audit.schema.ts → integration/integration-audit.schema.ts} +61 -61
- package/runtime/subsystems/{sync/sync-change-source.protocol.ts → integration/integration-change-source.protocol.ts} +9 -9
- package/runtime/subsystems/{sync/sync-cursor-store.drizzle-backend.ts → integration/integration-cursor-store.drizzle-backend.ts} +30 -30
- package/runtime/subsystems/{sync/sync-cursor-store.memory-backend.ts → integration/integration-cursor-store.memory-backend.ts} +9 -9
- package/runtime/subsystems/{sync/sync-cursor-store.protocol.ts → integration/integration-cursor-store.protocol.ts} +13 -13
- package/runtime/subsystems/{sync/sync-errors.ts → integration/integration-errors.ts} +3 -3
- package/runtime/subsystems/{sync/sync-field-diff.protocol.ts → integration/integration-field-diff.protocol.ts} +2 -2
- package/runtime/subsystems/{sync/sync-loopback.protocol.ts → integration/integration-loopback.protocol.ts} +2 -2
- package/runtime/subsystems/{sync/sync-middleware.protocol.ts → integration/integration-middleware.protocol.ts} +6 -6
- package/runtime/subsystems/{sync/sync-run-recorder.drizzle-backend.ts → integration/integration-run-recorder.drizzle-backend.ts} +39 -39
- package/runtime/subsystems/{sync/sync-run-recorder.memory-backend.ts → integration/integration-run-recorder.memory-backend.ts} +23 -23
- package/runtime/subsystems/{sync/sync-run-recorder.protocol.ts → integration/integration-run-recorder.protocol.ts} +25 -25
- package/runtime/subsystems/{sync/sync-sink.protocol.ts → integration/integration-sink.protocol.ts} +4 -4
- package/runtime/subsystems/{sync/sync.module.ts → integration/integration.module.ts} +48 -48
- package/runtime/subsystems/integration/integration.tokens.ts +63 -0
- package/runtime/subsystems/{sync → integration}/loopback.middleware.ts +5 -5
- package/runtime/subsystems/{sync → integration}/poll-change-source.ts +7 -7
- package/runtime/subsystems/{sync → integration}/webhook-change-source.ts +7 -7
- package/runtime/subsystems/observability/index.ts +1 -1
- package/runtime/subsystems/observability/observability.module.ts +2 -2
- package/runtime/subsystems/observability/observability.protocol.ts +11 -11
- package/runtime/subsystems/observability/observability.service.ts +13 -13
- package/runtime/subsystems/observability/observability.tokens.ts +1 -1
- package/src/patterns/library/index.ts +4 -4
- package/src/patterns/library/{synced.pattern.ts → integrated.pattern.ts} +12 -12
- package/src/patterns/library/junction.pattern.ts +1 -1
- package/src/patterns/pattern-definition.ts +3 -3
- package/templates/entity/new/backend/modules/core/{sync-source.ejs.t → integration-source.ejs.t} +6 -6
- package/templates/entity/new/clean-lite-ps/entity.ejs.t +12 -3
- package/templates/entity/new/clean-lite-ps/module.ejs.t +1 -1
- package/templates/entity/new/clean-lite-ps/prompt-extension.js +243 -60
- package/templates/entity/new/clean-lite-ps/repository.ejs.t +27 -27
- package/templates/entity/new/frontend/collections/collection.ejs.t +26 -1
- package/templates/entity/new/frontend/collections/collections-base.ejs.t +11 -0
- package/templates/entity/new/frontend/entity/combined.ejs.t +31 -1
- package/templates/entity/new/prompt.js +27 -15
- package/templates/junction/new/entity.ejs.t +1 -1
- package/templates/junction/new/prompt.js +24 -24
- package/templates/junction/new/repository.ejs.t +19 -19
- package/templates/subsystem/auth/auth-oauth-state.schema.ejs.t +2 -2
- package/templates/subsystem/auth-config/prompt.js +1 -1
- package/templates/subsystem/auth-integrations/app-module-hook.ejs.t +5 -5
- package/templates/subsystem/bridge/prompt.js +1 -1
- package/templates/subsystem/integration/integration-audit.schema.ejs.t +192 -0
- package/templates/subsystem/{sync → integration}/prompt.js +12 -12
- package/templates/subsystem/{sync-config/codegen-config-sync-block.ejs.t → integration-config/codegen-config-integration-block.ejs.t} +7 -7
- package/templates/subsystem/integration-config/prompt.js +22 -0
- package/templates/subsystem/jobs/worker.ejs.t +2 -2
- package/templates/subsystem/observability/main-hook.ejs.t +1 -1
- package/templates/subsystem/observability/prompt.js +1 -1
- package/templates/subsystem/openapi-config/prompt.js +1 -1
- package/dist/runtime/base-classes/junction-sync-repository.js.map +0 -1
- package/dist/runtime/base-classes/sync-upsert-config.js +0 -1
- package/dist/runtime/base-classes/synced-entity-repository.js.map +0 -1
- package/dist/runtime/base-classes/synced-entity-service.js.map +0 -1
- package/dist/runtime/subsystems/auth/protocols/integration-store.js +0 -1
- package/dist/runtime/subsystems/auth/runtime/integration-broken.error.js +0 -19
- package/dist/runtime/subsystems/auth/runtime/integration-broken.error.js.map +0 -1
- package/dist/runtime/subsystems/sync/build-change-source.js.map +0 -1
- package/dist/runtime/subsystems/sync/deep-equal.differ.js.map +0 -1
- package/dist/runtime/subsystems/sync/detection-config.schema.js.map +0 -1
- package/dist/runtime/subsystems/sync/execute-sync.use-case.js.map +0 -1
- package/dist/runtime/subsystems/sync/index.d.ts +0 -28
- package/dist/runtime/subsystems/sync/index.js.map +0 -1
- package/dist/runtime/subsystems/sync/loopback.middleware.js.map +0 -1
- package/dist/runtime/subsystems/sync/poll-change-source.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-audit.schema.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-change-source.protocol.js +0 -1
- package/dist/runtime/subsystems/sync/sync-cursor-store.drizzle-backend.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-cursor-store.memory-backend.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-cursor-store.protocol.js +0 -1
- package/dist/runtime/subsystems/sync/sync-errors.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-field-diff.protocol.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-loopback.protocol.js +0 -1
- package/dist/runtime/subsystems/sync/sync-middleware.protocol.js +0 -1
- package/dist/runtime/subsystems/sync/sync-run-recorder.drizzle-backend.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-run-recorder.memory-backend.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-run-recorder.protocol.js +0 -1
- package/dist/runtime/subsystems/sync/sync-sink.protocol.js +0 -1
- package/dist/runtime/subsystems/sync/sync.module.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync.tokens.d.ts +0 -47
- package/dist/runtime/subsystems/sync/sync.tokens.js +0 -18
- package/dist/runtime/subsystems/sync/sync.tokens.js.map +0 -1
- package/dist/runtime/subsystems/sync/webhook-change-source.js.map +0 -1
- package/examples/auth-integrations/runtime/integrations/integrations-auth.module.ts +0 -81
- package/examples/auth-integrations/runtime/integrations/oauth/use-cases/list-user-integrations.use-case.ts +0 -21
- package/examples/auth-integrations/runtime/integrations/oauth/use-cases/mark-integration-requires-reauth.use-case.ts +0 -21
- package/runtime/subsystems/sync/sync.tokens.ts +0 -49
- package/templates/entity/new/backend/modules/core/sync-source.providers.ejs.t +0 -18
- package/templates/subsystem/sync/sync-audit.schema.ejs.t +0 -192
- package/templates/subsystem/sync-config/prompt.js +0 -22
- /package/dist/runtime/base-classes/{sync-upsert-config.js.map → integration-upsert-config.js.map} +0 -0
- /package/dist/runtime/subsystems/auth/protocols/{integration-store.js.map → connection-store.js.map} +0 -0
- /package/dist/runtime/subsystems/{sync/sync-change-source.protocol.js.map → integration/integration-change-source.protocol.js.map} +0 -0
- /package/dist/runtime/subsystems/{sync/sync-cursor-store.protocol.js.map → integration/integration-cursor-store.protocol.js.map} +0 -0
- /package/dist/runtime/subsystems/{sync/sync-loopback.protocol.js.map → integration/integration-loopback.protocol.js.map} +0 -0
- /package/dist/runtime/subsystems/{sync/sync-middleware.protocol.js.map → integration/integration-middleware.protocol.js.map} +0 -0
- /package/dist/runtime/subsystems/{sync/sync-run-recorder.protocol.js.map → integration/integration-run-recorder.protocol.js.map} +0 -0
- /package/dist/runtime/subsystems/{sync/sync-sink.protocol.js.map → integration/integration-sink.protocol.js.map} +0 -0
|
@@ -2,71 +2,71 @@
|
|
|
2
2
|
|
|
3
3
|
# Change sources, sinks, and feature-module wiring
|
|
4
4
|
|
|
5
|
-
How you wire a
|
|
6
|
-
`IChangeSource<T>` adapter, the `
|
|
5
|
+
How you wire a integration integration end to end: the per-entity feature module, the
|
|
6
|
+
`IChangeSource<T>` adapter, the `IIntegrationSink<T>` write surface, the entity-YAML
|
|
7
7
|
`detection:` block, triggering runs, multi-tenancy, loopback, and testing.
|
|
8
8
|
|
|
9
|
-
Everything imports from `@shared/subsystems/
|
|
9
|
+
Everything imports from `@shared/subsystems/integration`.
|
|
10
10
|
|
|
11
11
|
## The per-entity feature module
|
|
12
12
|
|
|
13
|
-
`
|
|
13
|
+
`IntegrationModule.forRoot(...)` in `AppModule` wires the substrate (cursor store, run
|
|
14
14
|
recorder, field differ, multi-tenant flag). For **each canonical entity you
|
|
15
|
-
|
|
15
|
+
integration**, write a feature module that binds:
|
|
16
16
|
|
|
17
|
-
- `
|
|
18
|
-
- `
|
|
19
|
-
- `
|
|
20
|
-
- optionally `
|
|
21
|
-
`
|
|
17
|
+
- `INTEGRATION_CHANGE_SOURCE` — your adapter (one per `(provider, detection-mode, entity)`)
|
|
18
|
+
- `INTEGRATION_SINK` — your sink (one per canonical entity)
|
|
19
|
+
- `ExecuteIntegrationUseCase` — the orchestrator class itself
|
|
20
|
+
- optionally `INTEGRATION_FIELD_DIFFER` (custom diff rules) and/or
|
|
21
|
+
`INTEGRATION_LOOPBACK_FINGERPRINT_STORE`
|
|
22
22
|
|
|
23
23
|
```ts
|
|
24
24
|
import { Module } from '@nestjs/common';
|
|
25
25
|
import {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
ExecuteIntegrationUseCase,
|
|
27
|
+
INTEGRATION_CHANGE_SOURCE,
|
|
28
|
+
INTEGRATION_SINK,
|
|
29
|
+
INTEGRATION_FIELD_DIFFER,
|
|
30
30
|
DeepEqualDiffer,
|
|
31
|
-
} from '@shared/subsystems/
|
|
31
|
+
} from '@shared/subsystems/integration';
|
|
32
32
|
|
|
33
33
|
@Module({
|
|
34
34
|
providers: [
|
|
35
|
-
{ provide:
|
|
36
|
-
{ provide:
|
|
35
|
+
{ provide: INTEGRATION_CHANGE_SOURCE, useClass: SalesforceOpportunityChangeSource },
|
|
36
|
+
{ provide: INTEGRATION_SINK, useClass: OpportunityIntegrationSink },
|
|
37
37
|
// Override the differ per-entity when you need a wider ignore list:
|
|
38
38
|
{
|
|
39
|
-
provide:
|
|
40
|
-
useValue: new DeepEqualDiffer({ ignore: ['
|
|
39
|
+
provide: INTEGRATION_FIELD_DIFFER,
|
|
40
|
+
useValue: new DeepEqualDiffer({ ignore: ['integration_version', 'internal_notes'] }),
|
|
41
41
|
},
|
|
42
|
-
|
|
42
|
+
ExecuteIntegrationUseCase,
|
|
43
43
|
],
|
|
44
|
-
exports: [
|
|
44
|
+
exports: [ExecuteIntegrationUseCase],
|
|
45
45
|
})
|
|
46
|
-
export class
|
|
46
|
+
export class OpportunityIntegrationModule {}
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
**Why `
|
|
50
|
-
orchestrator depends on `
|
|
49
|
+
**Why `ExecuteIntegrationUseCase` lives here and not in `IntegrationModule`:** the
|
|
50
|
+
orchestrator depends on `INTEGRATION_CHANGE_SOURCE` + `INTEGRATION_SINK`, which are
|
|
51
51
|
per-feature. Nest resolves providers at module compile time; putting the
|
|
52
|
-
orchestrator in the global `
|
|
52
|
+
orchestrator in the global `IntegrationModule` would require those tokens globally,
|
|
53
53
|
which fails until your feature module is imported.
|
|
54
54
|
|
|
55
|
-
Inject `
|
|
55
|
+
Inject `ExecuteIntegrationUseCase<CanonicalOpportunity>` wherever you trigger a run —
|
|
56
56
|
a scheduled job, a CLI command, a webhook handler, an operator UI button.
|
|
57
57
|
|
|
58
58
|
## Writing an `IChangeSource<T>`
|
|
59
59
|
|
|
60
60
|
The one port every adapter implements. The signature is
|
|
61
|
-
`listChanges(subscription, cursor):
|
|
61
|
+
`listChanges(subscription, cursor): AintegrationIterable<Change<T>>`:
|
|
62
62
|
|
|
63
63
|
```ts
|
|
64
64
|
interface IChangeSource<T> {
|
|
65
65
|
readonly label: string; // e.g. 'salesforce-poll-opportunity'
|
|
66
66
|
listChanges(
|
|
67
|
-
subscription:
|
|
67
|
+
subscription: IntegrationSubscriptionView,
|
|
68
68
|
cursor: unknown | null,
|
|
69
|
-
):
|
|
69
|
+
): AintegrationIterable<Change<T>>;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
interface Change<T> {
|
|
@@ -84,7 +84,7 @@ A worked poll adapter:
|
|
|
84
84
|
|
|
85
85
|
```ts
|
|
86
86
|
import { Injectable } from '@nestjs/common';
|
|
87
|
-
import type { IChangeSource, Change,
|
|
87
|
+
import type { IChangeSource, Change, IntegrationSubscriptionView } from '@shared/subsystems/integration';
|
|
88
88
|
|
|
89
89
|
@Injectable()
|
|
90
90
|
export class SalesforceOpportunityChangeSource
|
|
@@ -98,9 +98,9 @@ export class SalesforceOpportunityChangeSource
|
|
|
98
98
|
) {}
|
|
99
99
|
|
|
100
100
|
async *listChanges(
|
|
101
|
-
sub:
|
|
101
|
+
sub: IntegrationSubscriptionView,
|
|
102
102
|
cursor: unknown | null,
|
|
103
|
-
):
|
|
103
|
+
): AintegrationIterable<Change<CanonicalOpportunity>> {
|
|
104
104
|
const typed = cursor as { systemModstamp?: string } | null;
|
|
105
105
|
const since = typed?.systemModstamp ?? '1970-01-01T00:00:00Z';
|
|
106
106
|
|
|
@@ -151,14 +151,14 @@ export class SalesforceOpportunityChangeSource
|
|
|
151
151
|
methods** to `IChangeSource`; if a new mode emerges, add a value to the
|
|
152
152
|
`source` union and a metadata field, not a new port.
|
|
153
153
|
|
|
154
|
-
## Writing an `
|
|
154
|
+
## Writing an `IIntegrationSink<T>`
|
|
155
155
|
|
|
156
156
|
One sink per canonical entity. It speaks the *canonical* shape externally;
|
|
157
157
|
internal mapping (canonical → local columns, EAV dual-write, FK resolution)
|
|
158
158
|
stays inside:
|
|
159
159
|
|
|
160
160
|
```ts
|
|
161
|
-
interface
|
|
161
|
+
interface IIntegrationSink<TCanonical> {
|
|
162
162
|
findByExternalId(userId: string, externalId: string): Promise<TCanonical | null>;
|
|
163
163
|
upsertByExternalId(userId: string, record: TCanonical, provider: string): Promise<{ id: string; saved: TCanonical }>;
|
|
164
164
|
softDeleteByExternalId(userId: string, externalId: string): Promise<{ id: string } | null>;
|
|
@@ -167,7 +167,7 @@ interface ISyncSink<TCanonical> {
|
|
|
167
167
|
|
|
168
168
|
```ts
|
|
169
169
|
@Injectable()
|
|
170
|
-
export class
|
|
170
|
+
export class OpportunityIntegrationSink implements IIntegrationSink<CanonicalOpportunity> {
|
|
171
171
|
constructor(
|
|
172
172
|
@Inject(DRIZZLE) private readonly db: DrizzleClient,
|
|
173
173
|
private readonly opportunities: OpportunityService,
|
|
@@ -208,7 +208,7 @@ export class OpportunitySyncSink implements ISyncSink<CanonicalOpportunity> {
|
|
|
208
208
|
dual-write (canonical columns + custom-field rows), `user_id` + `provider`
|
|
209
209
|
stamping all happen inside its transaction. The subsystem never reaches around
|
|
210
210
|
the sink to write local tables. **Return the local id** so the orchestrator
|
|
211
|
-
can record it on `
|
|
211
|
+
can record it on `integration_run_items.local_id`.
|
|
212
212
|
- **Re-entry tolerance is the sink's job.** A webhook retry or polling overlap
|
|
213
213
|
can deliver the same record twice — make the upsert idempotent (typically
|
|
214
214
|
`ON CONFLICT (external_id) DO UPDATE` with no-op semantics when nothing
|
|
@@ -222,7 +222,7 @@ integration provider:
|
|
|
222
222
|
|
|
223
223
|
```yaml
|
|
224
224
|
# entities/opportunity.yaml
|
|
225
|
-
|
|
225
|
+
integration:
|
|
226
226
|
providers:
|
|
227
227
|
hubspot-crm: { remote_entity: deal, direction: inbound }
|
|
228
228
|
salesforce-crm: { remote_entity: Opportunity, direction: inbound }
|
|
@@ -240,7 +240,7 @@ detection:
|
|
|
240
240
|
filters: [ ... ]
|
|
241
241
|
```
|
|
242
242
|
|
|
243
|
-
Codegen emits exactly one `<entity>-
|
|
243
|
+
Codegen emits exactly one `<entity>-integration-source.module.ts` per entity,
|
|
244
244
|
regardless of provider count. It exports two runtime symbols (plus the module
|
|
245
245
|
class):
|
|
246
246
|
|
|
@@ -260,13 +260,13 @@ Wire your fetch callbacks in a feature module:
|
|
|
260
260
|
import {
|
|
261
261
|
OPPORTUNITY_POLL_FETCH_REGISTRY,
|
|
262
262
|
OPPORTUNITY_CHANGE_SOURCES,
|
|
263
|
-
|
|
264
|
-
} from '@modules/opportunity-
|
|
265
|
-
import type { OpportunityProvider } from '@modules/opportunity-
|
|
263
|
+
OpportunityIntegrationSourceModule,
|
|
264
|
+
} from '@modules/opportunity-integration-source.module';
|
|
265
|
+
import type { OpportunityProvider } from '@modules/opportunity-integration-source.providers';
|
|
266
266
|
import { hubspotFetchOpportunities, salesforceFetchOpportunities } from './my-fetches';
|
|
267
267
|
|
|
268
268
|
@Module({
|
|
269
|
-
imports: [
|
|
269
|
+
imports: [OpportunityIntegrationSourceModule],
|
|
270
270
|
providers: [
|
|
271
271
|
{
|
|
272
272
|
provide: OPPORTUNITY_POLL_FETCH_REGISTRY,
|
|
@@ -277,10 +277,10 @@ import { hubspotFetchOpportunities, salesforceFetchOpportunities } from './my-fe
|
|
|
277
277
|
},
|
|
278
278
|
],
|
|
279
279
|
})
|
|
280
|
-
export class
|
|
280
|
+
export class OpportunityIntegrationWiringModule {}
|
|
281
281
|
```
|
|
282
282
|
|
|
283
|
-
The sibling `<entity>-
|
|
283
|
+
The sibling `<entity>-integration-source.providers.ts` artifact exports the
|
|
284
284
|
`<EntityName>Provider` literal-union type — using `Record<OpportunityProvider, …>`
|
|
285
285
|
(or the `satisfies` form above) turns a provider-key typo into a compile error.
|
|
286
286
|
|
|
@@ -291,7 +291,7 @@ services.
|
|
|
291
291
|
|
|
292
292
|
## Triggering a run
|
|
293
293
|
|
|
294
|
-
`
|
|
294
|
+
`ExecuteIntegrationUseCase` does not schedule itself. Common triggers:
|
|
295
295
|
|
|
296
296
|
**Scheduled job (typical polling)** — wrap the use case in a normal background
|
|
297
297
|
job on one of *your own* pools (never a reserved `events_*` pool — those belong
|
|
@@ -299,14 +299,14 @@ to the event/bridge machinery and throw at boot) and give it a cron trigger
|
|
|
299
299
|
(see the `jobs` skill for the handler shape + scheduling):
|
|
300
300
|
|
|
301
301
|
```ts
|
|
302
|
-
@JobHandler<{ subscriptionId: string; tenantId?: string }>('
|
|
302
|
+
@JobHandler<{ subscriptionId: string; tenantId?: string }>('integration_opportunity_poll', {
|
|
303
303
|
pool: 'batch',
|
|
304
304
|
})
|
|
305
|
-
export class
|
|
305
|
+
export class IntegrationOpportunityPollHandler extends JobHandlerBase<{
|
|
306
306
|
subscriptionId: string;
|
|
307
307
|
tenantId?: string;
|
|
308
308
|
}> {
|
|
309
|
-
constructor(private readonly execute:
|
|
309
|
+
constructor(private readonly execute: ExecuteIntegrationUseCase<CanonicalOpportunity>) {
|
|
310
310
|
super();
|
|
311
311
|
}
|
|
312
312
|
|
|
@@ -347,7 +347,7 @@ await this.execute.execute({
|
|
|
347
347
|
});
|
|
348
348
|
```
|
|
349
349
|
|
|
350
|
-
## Emitting events on successful
|
|
350
|
+
## Emitting events on successful integration
|
|
351
351
|
|
|
352
352
|
The orchestrator does not emit events — wire `TypedEventBus.publish(...)`
|
|
353
353
|
inside your sink's `upsertByExternalId` transaction, after the row is saved, so
|
|
@@ -372,18 +372,18 @@ consumers subscribe via their own handlers. See the `events` skill.
|
|
|
372
372
|
|
|
373
373
|
Three things change when `multi_tenant: true`:
|
|
374
374
|
|
|
375
|
-
1. **`
|
|
375
|
+
1. **`IntegrationModule.forRoot({ backend: 'drizzle', multiTenant: true })`** in
|
|
376
376
|
`AppModule` — binds the multi-tenant flag the orchestrator and Drizzle
|
|
377
377
|
backends inject.
|
|
378
378
|
2. **Every `execute()` call passes `tenantId`.** Missing/null throws
|
|
379
|
-
`MissingTenantIdError` at entry, *before* a `
|
|
379
|
+
`MissingTenantIdError` at entry, *before* a `integration_runs` row is opened (no
|
|
380
380
|
dangling `status=running` rows). The Drizzle backends re-validate at their
|
|
381
381
|
write boundary (defense in depth); all sites share one helper so error
|
|
382
382
|
messages match. Explicit `null` is allowed only for deliberate cross-tenant
|
|
383
383
|
work.
|
|
384
|
-
3. **Schema gains `tenant_id` columns** on all three
|
|
385
|
-
`
|
|
386
|
-
`subsystem install
|
|
384
|
+
3. **Schema gains `tenant_id` columns** on all three integration tables. Flip
|
|
385
|
+
`integration.multi_tenant: true` in `codegen.config.yaml`, re-run
|
|
386
|
+
`subsystem install integration --force --force-config` to re-emit the schema, then
|
|
387
387
|
apply the migration **before** flipping the module flag — otherwise the
|
|
388
388
|
Drizzle backends throw `column "tenant_id" does not exist` on every write.
|
|
389
389
|
|
|
@@ -402,7 +402,7 @@ interface ILoopbackFingerprintStore<T = unknown> {
|
|
|
402
402
|
isEchoOfOwnWrite(entityType: string, externalId: string, record: T): Promise<boolean>;
|
|
403
403
|
}
|
|
404
404
|
|
|
405
|
-
{ provide:
|
|
405
|
+
{ provide: INTEGRATION_LOOPBACK_FINGERPRINT_STORE, useClass: RedisLoopbackStore }
|
|
406
406
|
```
|
|
407
407
|
|
|
408
408
|
Record a fingerprint (hash of the canonicalized record, TTL **shorter than the
|
|
@@ -414,20 +414,20 @@ verify suppression in the audit log; the sink is never called.
|
|
|
414
414
|
|
|
415
415
|
## Testing
|
|
416
416
|
|
|
417
|
-
`
|
|
417
|
+
`IntegrationModule.forRoot({ backend: 'memory' })` plus memory feature-module fakes
|
|
418
418
|
gives an end-to-end test with no Postgres:
|
|
419
419
|
|
|
420
420
|
```ts
|
|
421
|
-
import {
|
|
421
|
+
import { IntegrationModule, MemoryRunRecorder } from '@shared/subsystems/integration';
|
|
422
422
|
|
|
423
423
|
const moduleRef = await Test.createTestingModule({
|
|
424
424
|
imports: [
|
|
425
|
-
|
|
426
|
-
|
|
425
|
+
IntegrationModule.forRoot({ backend: 'memory' }),
|
|
426
|
+
OpportunityIntegrationTestModule, // same shape as the real feature module, with fakes
|
|
427
427
|
],
|
|
428
428
|
}).compile();
|
|
429
429
|
|
|
430
|
-
const orch = moduleRef.get(
|
|
430
|
+
const orch = moduleRef.get(ExecuteIntegrationUseCase);
|
|
431
431
|
const recorder = moduleRef.get(MemoryRunRecorder);
|
|
432
432
|
|
|
433
433
|
await orch.execute({ /* ... */ });
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: subsystems
|
|
3
3
|
description: >-
|
|
4
4
|
Load when installing or wiring an infrastructure subsystem in a project that
|
|
5
|
-
uses @pattern-stack/codegen — events, jobs, cache, storage,
|
|
5
|
+
uses @pattern-stack/codegen — events, jobs, cache, storage, integration, bridge,
|
|
6
6
|
observability, auth, or the OpenAPI config. Covers `codegen subsystem
|
|
7
7
|
install`, the `forRoot` registration ORDER in app.module.ts, which subsystems
|
|
8
8
|
depend on which, and multi-tenancy opt-in. Get the order wrong and the bridge
|
|
@@ -17,7 +17,7 @@ user-invocable: false
|
|
|
17
17
|
# Infrastructure subsystems
|
|
18
18
|
|
|
19
19
|
Subsystems are the generated infrastructure your use cases call: an event bus,
|
|
20
|
-
a job queue, a cache, file storage, an external-
|
|
20
|
+
a job queue, a cache, file storage, an external-integration engine, the event-to-job
|
|
21
21
|
bridge, a read-only observability facade, and OAuth auth. Each follows one
|
|
22
22
|
pattern — **Protocol (port) → Backend (adapter) → Factory (`DynamicModule.
|
|
23
23
|
forRoot`)** — and each is `global: true`, so you register it once in
|
|
@@ -33,7 +33,7 @@ forRoot`)** — and each is `global: true`, so you register it once in
|
|
|
33
33
|
(Postgres) production backend and a memory backend for tests. Swap via the
|
|
34
34
|
`forRoot({ backend })` arg — app code is unchanged.
|
|
35
35
|
- **Order matters.** Some subsystems consume others. The bridge consumes events
|
|
36
|
-
+ jobs; observability composes events/jobs/bridge/
|
|
36
|
+
+ jobs; observability composes events/jobs/bridge/integration read ports via
|
|
37
37
|
optional DI. Registering them in the wrong order means a silently idle bridge
|
|
38
38
|
or an observability facade that reports nothing. See `wiring-and-order.md`.
|
|
39
39
|
|
|
@@ -45,11 +45,11 @@ forRoot`)** — and each is `global: true`, so you register it once in
|
|
|
45
45
|
| jobs | `JobsDomainModule` + `JobWorkerModule` | `subsystem install jobs` | — |
|
|
46
46
|
| cache | `CacheModule` | `subsystem install cache` | jobs (optional, for cleanup) |
|
|
47
47
|
| storage | `StorageModule` | `subsystem install storage` | — |
|
|
48
|
-
|
|
|
48
|
+
| integration | `IntegrationModule` | `subsystem install integration` | — |
|
|
49
49
|
| bridge | `BridgeModule` | `subsystem install bridge` | **events + jobs** |
|
|
50
|
-
| observability | `ObservabilityModule` | `subsystem install observability` | composes events/jobs/bridge/
|
|
50
|
+
| observability | `ObservabilityModule` | `subsystem install observability` | composes events/jobs/bridge/integration (optional) |
|
|
51
51
|
| auth | `AuthModule` | `subsystem install auth` | — |
|
|
52
|
-
| auth-integrations | `
|
|
52
|
+
| auth-integrations | `ConnectionsAuthModule` | `subsystem install auth-integrations` | **auth** |
|
|
53
53
|
| openapi | (config only) | `subsystem install openapi-config` | registry vendored at init |
|
|
54
54
|
|
|
55
55
|
## Registration order (authoritative)
|
|
@@ -60,13 +60,13 @@ In `app.module.ts`, import in this order (omit what you haven't installed):
|
|
|
60
60
|
2. `OpenApiModule` — the registry singleton (vendored at init).
|
|
61
61
|
3. `EventsModule.forRoot(...)`
|
|
62
62
|
4. `JobsDomainModule.forRoot(...)` **and** `JobWorkerModule.forRoot(...)`
|
|
63
|
-
5. `CacheModule` / `StorageModule` / `
|
|
63
|
+
5. `CacheModule` / `StorageModule` / `IntegrationModule.forRoot(...)`
|
|
64
64
|
6. `BridgeModule.forRoot(...)` — **after** events + jobs.
|
|
65
65
|
7. `ObservabilityModule.forRoot(...)` — **last** of the subsystems (composes the
|
|
66
66
|
ones above via optional DI).
|
|
67
67
|
8. `...GENERATED_MODULES` — your entity modules.
|
|
68
68
|
|
|
69
|
-
For auth: register `AuthModule.forRoot(...)` before the `
|
|
69
|
+
For auth: register `AuthModule.forRoot(...)` before the `ConnectionsAuthModule`
|
|
70
70
|
that depends on it. Full per-subsystem `forRoot` signatures, the bridge reserved
|
|
71
71
|
pools, and multi-tenancy are in `wiring-and-order.md`.
|
|
72
72
|
|
|
@@ -17,7 +17,7 @@ import { EventsModule } from '@shared/subsystems/events';
|
|
|
17
17
|
import { JobsDomainModule, JobWorkerModule } from '@shared/subsystems/jobs';
|
|
18
18
|
import { CacheModule } from '@shared/subsystems/cache';
|
|
19
19
|
import { StorageModule } from '@shared/subsystems/storage';
|
|
20
|
-
import {
|
|
20
|
+
import { IntegrationModule } from '@shared/subsystems/integration';
|
|
21
21
|
import { BridgeModule, BRIDGE_RESERVED_POOLS } from '@shared/subsystems/bridge';
|
|
22
22
|
import { ObservabilityModule } from '@shared/subsystems/observability';
|
|
23
23
|
|
|
@@ -44,10 +44,10 @@ class OpenApiModule {}
|
|
|
44
44
|
// include the bridge's reserved pools so wrappers actually drain:
|
|
45
45
|
pools: ['interactive', 'batch', ...BRIDGE_RESERVED_POOLS],
|
|
46
46
|
}),
|
|
47
|
-
// 5. cache / storage /
|
|
47
|
+
// 5. cache / storage / integration
|
|
48
48
|
CacheModule.forRoot({ backend: 'drizzle' }),
|
|
49
49
|
StorageModule.forRoot({ backend: 'local' }),
|
|
50
|
-
|
|
50
|
+
IntegrationModule.forRoot({ backend: 'drizzle' }),
|
|
51
51
|
// 6. bridge — AFTER events + jobs
|
|
52
52
|
BridgeModule.forRoot({ backend: 'drizzle', multiTenant: false }),
|
|
53
53
|
// 7. observability — LAST (composes the siblings above)
|
|
@@ -68,10 +68,10 @@ export class AppModule {}
|
|
|
68
68
|
| `JobWorkerModule` | `forRoot({ mode, backend?, pools?, allPools?, shutdownTimeoutMs? })` | `mode: 'embedded' \| 'standalone'`. `pools` = active pool names this process drains (defaults to all non-reserved). `allPools: true` drains every pool incl. reserved. |
|
|
69
69
|
| `CacheModule` | `forRoot({ backend })` | optionally registers a cleanup job when jobs is present. |
|
|
70
70
|
| `StorageModule` | `forRoot({ backend })` | `backend: 'local' \| 'memory'`. Implement S3/GCS by implementing the storage protocol. |
|
|
71
|
-
| `
|
|
71
|
+
| `IntegrationModule` | `forRoot({ backend, multiTenant? })` | wires the cursor store / run recorder / differ ports — NOT the orchestrator (that's per-entity; see the `integration` skill). |
|
|
72
72
|
| `BridgeModule` | `forRoot({ backend, multiTenant? })` | must come after events + jobs; fails fast at boot if reserved pools aren't polled. |
|
|
73
73
|
| `ObservabilityModule` | `forRoot({ reporters? })` | read-only facade; `reporters.bridgeMetrics: true` opts into the 60s bridge sampler. Register last. |
|
|
74
|
-
| `AuthModule` | `forRoot({ encryptionKey, oauthStateStore })` | `global: true`; provides `ENCRYPTION_KEY` + `OAUTH_STATE_STORE`. Register before `
|
|
74
|
+
| `AuthModule` | `forRoot({ encryptionKey, oauthStateStore })` | `global: true`; provides `ENCRYPTION_KEY` + `OAUTH_STATE_STORE`. Register before `ConnectionsAuthModule`. |
|
|
75
75
|
|
|
76
76
|
## Pool configuration (jobs)
|
|
77
77
|
|
|
@@ -90,7 +90,7 @@ jobs:
|
|
|
90
90
|
`JobWorkerModule.forRoot({ pools })` then names which of those a given worker
|
|
91
91
|
process drains — scale horizontally by running one worker per pool subset.
|
|
92
92
|
|
|
93
|
-
## Multi-tenancy opt-in (events / jobs /
|
|
93
|
+
## Multi-tenancy opt-in (events / jobs / integration / bridge)
|
|
94
94
|
|
|
95
95
|
Three coordinated steps — never a runtime-only toggle:
|
|
96
96
|
|
|
@@ -110,7 +110,7 @@ tenant-less / cross-tenant work).
|
|
|
110
110
|
- **bridge → events + jobs.** The bridge claims `domain_events` rows and starts
|
|
111
111
|
wrapper job runs in the reserved pools. Without events + jobs registered (and
|
|
112
112
|
the reserved pools polled) it has nothing to consume and nowhere to write.
|
|
113
|
-
- **observability → events/jobs/bridge/
|
|
113
|
+
- **observability → events/jobs/bridge/integration (optional).** It composes the read
|
|
114
114
|
ports of whatever siblings exist, via optional DI. Register it last so those
|
|
115
115
|
ports are already bound; missing siblings are simply omitted from its output.
|
|
116
116
|
- **auth-integrations → auth.** The integration adapters need the encryption key
|
|
@@ -3,13 +3,13 @@ export { RequesterContext, RequesterScope, requireRequester, requireRequesterSco
|
|
|
3
3
|
export { BaseService, IBaseRepository } from './base-service.js';
|
|
4
4
|
export { EventCategory, buildChangeEvents, buildLifecycleEvent, diffSnapshots, emitSafely, entitySnapshot } from './lifecycle-events.js';
|
|
5
5
|
export { BaseFindByIdUseCase, BaseListUseCase, IFindByIdService, IListService } from './base-read-use-cases.js';
|
|
6
|
-
export {
|
|
7
|
-
export {
|
|
8
|
-
export {
|
|
6
|
+
export { IntegrationFkResolver, IntegrationUpsertConfig } from './integration-upsert-config.js';
|
|
7
|
+
export { IntegratedEntityRepository } from './integrated-entity-repository.js';
|
|
8
|
+
export { JunctionIntegrationConfig, JunctionIntegrationRepository, buildCompositeExternalId, parseCompositeExternalId } from './junction-integration-repository.js';
|
|
9
9
|
export { ActivityEntityRepository } from './activity-entity-repository.js';
|
|
10
10
|
export { MetadataEntityRepository } from './metadata-entity-repository.js';
|
|
11
11
|
export { KnowledgeEntityRepository } from './knowledge-entity-repository.js';
|
|
12
|
-
export {
|
|
12
|
+
export { IIntegratedEntityRepository, IntegratedEntityService } from './integrated-entity-service.js';
|
|
13
13
|
export { ActivityEntityService, IActivityEntityRepository } from './activity-entity-service.js';
|
|
14
14
|
export { IMetadataEntityRepository, MetadataEntityService } from './metadata-entity-service.js';
|
|
15
15
|
export { KnowledgeEntityService } from './knowledge-entity-service.js';
|
|
@@ -510,9 +510,9 @@ var BaseListUseCase = class {
|
|
|
510
510
|
}
|
|
511
511
|
};
|
|
512
512
|
|
|
513
|
-
// runtime/base-classes/
|
|
513
|
+
// runtime/base-classes/integrated-entity-repository.ts
|
|
514
514
|
import { and as and2, eq as eq2, inArray as inArray2 } from "drizzle-orm";
|
|
515
|
-
var
|
|
515
|
+
var IntegratedEntityRepository = class extends BaseRepository {
|
|
516
516
|
/**
|
|
517
517
|
* Find a single entity by its external CRM identifier.
|
|
518
518
|
*/
|
|
@@ -536,20 +536,20 @@ var SyncedEntityRepository = class extends BaseRepository {
|
|
|
536
536
|
return rows;
|
|
537
537
|
}
|
|
538
538
|
// ==========================================================================
|
|
539
|
-
// Inbound
|
|
539
|
+
// Inbound integration (#374) — canonical→Drizzle write + provider-scoped FK
|
|
540
540
|
// resolution + EAV dual-write seam, all inside a SINGLE transaction.
|
|
541
|
-
// Driven entirely by `this.
|
|
541
|
+
// Driven entirely by `this.integrationConfig`; the per-entity shape lives there.
|
|
542
542
|
// ==========================================================================
|
|
543
543
|
/**
|
|
544
544
|
* Upsert ONE entity by its `(provider, externalId)` identity, in a single
|
|
545
545
|
* transaction:
|
|
546
|
-
* 1. resolve each `
|
|
546
|
+
* 1. resolve each `integrationConfig.fkResolvers` FK (provider-scoped). Strict
|
|
547
547
|
* resolvers throw on unresolved; non-strict leave the column null.
|
|
548
548
|
* 2. insert-or-update the canonical columns via `onConflictDoUpdate` on the
|
|
549
549
|
* `conflictTarget`. Resolved FKs are only written into `set` when
|
|
550
550
|
* non-null this run (no-clobber).
|
|
551
551
|
* 3. EAV dual-write of `write.fields` via `writeCustomFields` when
|
|
552
|
-
* `
|
|
552
|
+
* `integrationConfig.eav` and the bag is non-empty (same tx).
|
|
553
553
|
*
|
|
554
554
|
* Idempotent: a second call with the same identity updates in place. Returns
|
|
555
555
|
* the canonical projection (so the orchestrator records `local_id`).
|
|
@@ -558,8 +558,8 @@ var SyncedEntityRepository = class extends BaseRepository {
|
|
|
558
558
|
* @param provider adapter/provider label persisted + used to scope lookups
|
|
559
559
|
* @param tx optional outer transaction; when omitted we open our own
|
|
560
560
|
*/
|
|
561
|
-
async
|
|
562
|
-
const cfg = this.
|
|
561
|
+
async integrationUpsertOne(write, provider, tx) {
|
|
562
|
+
const cfg = this.integrationConfig;
|
|
563
563
|
const w = write;
|
|
564
564
|
const run = async (db) => {
|
|
565
565
|
const resolvedFks = {};
|
|
@@ -617,14 +617,14 @@ var SyncedEntityRepository = class extends BaseRepository {
|
|
|
617
617
|
return row ? this.toProjection(row) : null;
|
|
618
618
|
}
|
|
619
619
|
/**
|
|
620
|
-
*
|
|
620
|
+
* Integration "delete" by external id, provider-scoped. When `softDelete: true`,
|
|
621
621
|
* sets `deletedAt`. When `softDelete: false`, tombstone-by-clearing: null out
|
|
622
622
|
* `external_id`/`provider` so the row no longer matches future inbound
|
|
623
623
|
* changes while preserving local-id references. Returns `{ id }` or `null`.
|
|
624
624
|
*/
|
|
625
625
|
async softDeleteByExternalId(externalId, provider, tx) {
|
|
626
626
|
const db = this.runner(tx);
|
|
627
|
-
const set = this.
|
|
627
|
+
const set = this.integrationConfig.softDelete ? { deletedAt: /* @__PURE__ */ new Date(), updatedAt: /* @__PURE__ */ new Date() } : { externalId: null, provider: null, updatedAt: /* @__PURE__ */ new Date() };
|
|
628
628
|
const rows = await db.update(this.table).set(set).where(
|
|
629
629
|
and2(
|
|
630
630
|
eq2(this.table["provider"], provider),
|
|
@@ -634,19 +634,19 @@ var SyncedEntityRepository = class extends BaseRepository {
|
|
|
634
634
|
return rows[0] ? { id: rows[0].id } : null;
|
|
635
635
|
}
|
|
636
636
|
/**
|
|
637
|
-
* Batch
|
|
638
|
-
* `
|
|
637
|
+
* Batch integration upsert — concretizes the former abstract stub. Delegates to
|
|
638
|
+
* `integrationUpsertOne` per input inside one transaction. Inputs are raw partial
|
|
639
639
|
* rows: provider is read from each input's own `provider` column; rows
|
|
640
640
|
* missing `externalId`/`provider` are skipped.
|
|
641
641
|
*/
|
|
642
|
-
async
|
|
642
|
+
async integrationUpsert(inputs) {
|
|
643
643
|
if (inputs.length === 0) return [];
|
|
644
644
|
return this.db.transaction(async (tx) => {
|
|
645
645
|
const out = [];
|
|
646
646
|
for (const input of inputs) {
|
|
647
647
|
const rec = input;
|
|
648
648
|
if (!rec["externalId"] || !rec["provider"]) continue;
|
|
649
|
-
const proj = await this.
|
|
649
|
+
const proj = await this.integrationUpsertOne(
|
|
650
650
|
input,
|
|
651
651
|
rec["provider"],
|
|
652
652
|
tx
|
|
@@ -660,13 +660,13 @@ var SyncedEntityRepository = class extends BaseRepository {
|
|
|
660
660
|
}
|
|
661
661
|
/**
|
|
662
662
|
* Project a raw row to the canonical differ shape — a generic pick over
|
|
663
|
-
* `
|
|
663
|
+
* `integrationConfig.projectionColumns`. Override only for synthesized projections
|
|
664
664
|
* (e.g. junctions); entities use this verbatim.
|
|
665
665
|
*/
|
|
666
666
|
toProjection(row) {
|
|
667
667
|
const r = row;
|
|
668
668
|
const out = {};
|
|
669
|
-
for (const col of this.
|
|
669
|
+
for (const col of this.integrationConfig.projectionColumns) out[col] = r[col];
|
|
670
670
|
return out;
|
|
671
671
|
}
|
|
672
672
|
/**
|
|
@@ -688,7 +688,7 @@ var SyncedEntityRepository = class extends BaseRepository {
|
|
|
688
688
|
if (!parentExternalId) {
|
|
689
689
|
if (fk.strict) {
|
|
690
690
|
throw new Error(
|
|
691
|
-
`${this.constructor.name}.
|
|
691
|
+
`${this.constructor.name}.integrationUpsertOne: missing required parent external id for '${fk.column}' (writeKey '${fk.writeKey}')`
|
|
692
692
|
);
|
|
693
693
|
}
|
|
694
694
|
return null;
|
|
@@ -703,7 +703,7 @@ var SyncedEntityRepository = class extends BaseRepository {
|
|
|
703
703
|
const id = rows[0]?.id ?? null;
|
|
704
704
|
if (id === null && fk.strict) {
|
|
705
705
|
throw new Error(
|
|
706
|
-
`${this.constructor.name}.
|
|
706
|
+
`${this.constructor.name}.integrationUpsertOne: unresolved parent '${parentExternalId}' (provider '${provider}') for '${fk.column}' \u2014 parent not integrated yet`
|
|
707
707
|
);
|
|
708
708
|
}
|
|
709
709
|
return id;
|
|
@@ -717,9 +717,9 @@ var SyncedEntityRepository = class extends BaseRepository {
|
|
|
717
717
|
}
|
|
718
718
|
};
|
|
719
719
|
|
|
720
|
-
// runtime/base-classes/junction-
|
|
720
|
+
// runtime/base-classes/junction-integration-repository.ts
|
|
721
721
|
import { and as and3, eq as eq3 } from "drizzle-orm";
|
|
722
|
-
var
|
|
722
|
+
var JunctionIntegrationRepository = class extends BaseRepository {
|
|
723
723
|
/**
|
|
724
724
|
* Upsert ONE junction row by its composite identity, in a single transaction:
|
|
725
725
|
* 1. resolve the REQUIRED left FK (provider-scoped) — STRICT: missing → throws;
|
|
@@ -733,8 +733,8 @@ var JunctionSyncRepository = class extends BaseRepository {
|
|
|
733
733
|
* @param provider adapter/provider label used to scope the parent lookups
|
|
734
734
|
* @param tx optional outer transaction; when omitted we open our own
|
|
735
735
|
*/
|
|
736
|
-
async
|
|
737
|
-
const cfg = this.
|
|
736
|
+
async integrationUpsertOne(write, provider, tx) {
|
|
737
|
+
const cfg = this.integrationConfig;
|
|
738
738
|
const w = write;
|
|
739
739
|
const leftWriteKey = `${cfg.left.column.replace(/Id$/, "")}ExternalId`;
|
|
740
740
|
const rightWriteKey = `${cfg.right.column.replace(/Id$/, "")}ExternalId`;
|
|
@@ -778,7 +778,7 @@ var JunctionSyncRepository = class extends BaseRepository {
|
|
|
778
778
|
* parent / no row (a missing "before" side is a create from the differ's view).
|
|
779
779
|
*/
|
|
780
780
|
async findByExternalIdProjected(externalId, provider) {
|
|
781
|
-
const cfg = this.
|
|
781
|
+
const cfg = this.integrationConfig;
|
|
782
782
|
const parsed = parseCompositeExternalId(externalId, cfg.roleColumn !== null);
|
|
783
783
|
if (!parsed) return null;
|
|
784
784
|
const leftId = await this.resolveLoose(this.db, cfg.left.refTable, parsed.left, provider);
|
|
@@ -798,12 +798,12 @@ var JunctionSyncRepository = class extends BaseRepository {
|
|
|
798
798
|
}
|
|
799
799
|
/**
|
|
800
800
|
* Hard-delete the junction by composite externalId. Junctions have no
|
|
801
|
-
* `deleted_at` and no external-linkage columns to clear, so a
|
|
801
|
+
* `deleted_at` and no external-linkage columns to clear, so a integration "delete"
|
|
802
802
|
* removes the row. Resolves both parents NON-throwing, then deletes by the
|
|
803
803
|
* identity tuple. Returns the composite id, or `null` when nothing matched.
|
|
804
804
|
*/
|
|
805
805
|
async softDeleteByExternalId(externalId, provider, tx) {
|
|
806
|
-
const cfg = this.
|
|
806
|
+
const cfg = this.integrationConfig;
|
|
807
807
|
const parsed = parseCompositeExternalId(externalId, cfg.roleColumn !== null);
|
|
808
808
|
if (!parsed) return null;
|
|
809
809
|
const db = this.runner(tx);
|
|
@@ -818,12 +818,12 @@ var JunctionSyncRepository = class extends BaseRepository {
|
|
|
818
818
|
* Project a raw junction row to the differ shape: the COMPOSITE externalId
|
|
819
819
|
* `id` (junctions have no surrogate id) plus the local FK columns, role (when
|
|
820
820
|
* role-bearing) and timestamps — matching the emitted
|
|
821
|
-
* `<Junction>
|
|
821
|
+
* `<Junction>IntegrationProjection` interface (id + leftId + rightId + role? +
|
|
822
822
|
* createdAt + updatedAt). Junction projections are purely structural, so this
|
|
823
|
-
* is fully generic over `
|
|
823
|
+
* is fully generic over `integrationConfig` — no per-junction override is emitted.
|
|
824
824
|
*/
|
|
825
825
|
toProjection(row, write, _provider) {
|
|
826
|
-
const cfg = this.
|
|
826
|
+
const cfg = this.integrationConfig;
|
|
827
827
|
const r = row;
|
|
828
828
|
const leftExt = write[`${cfg.left.column.replace(/Id$/, "")}ExternalId`];
|
|
829
829
|
const rightExt = write[`${cfg.right.column.replace(/Id$/, "")}ExternalId`];
|
|
@@ -840,7 +840,7 @@ var JunctionSyncRepository = class extends BaseRepository {
|
|
|
840
840
|
}
|
|
841
841
|
/** Build the identity WHERE clause `(left, right[, role])`. */
|
|
842
842
|
identityWhere(leftId, rightId, role) {
|
|
843
|
-
const cfg = this.
|
|
843
|
+
const cfg = this.integrationConfig;
|
|
844
844
|
const conds = [
|
|
845
845
|
eq3(this.table[cfg.left.column], leftId),
|
|
846
846
|
eq3(this.table[cfg.right.column], rightId)
|
|
@@ -855,7 +855,7 @@ var JunctionSyncRepository = class extends BaseRepository {
|
|
|
855
855
|
const id = await this.resolveLoose(db, refTable, parentExternalId, provider);
|
|
856
856
|
if (!id) {
|
|
857
857
|
throw new Error(
|
|
858
|
-
`${this.constructor.name}.
|
|
858
|
+
`${this.constructor.name}.integrationUpsertOne: unresolved parent '${parentExternalId}' (provider '${provider}') for '${column}' \u2014 parent not integrated yet`
|
|
859
859
|
);
|
|
860
860
|
}
|
|
861
861
|
return id;
|
|
@@ -976,8 +976,8 @@ var KnowledgeEntityRepository = class extends BaseRepository {
|
|
|
976
976
|
// semanticSearch, findPendingByOpportunityId, updateStatus, updateStatusBatch
|
|
977
977
|
};
|
|
978
978
|
|
|
979
|
-
// runtime/base-classes/
|
|
980
|
-
var
|
|
979
|
+
// runtime/base-classes/integrated-entity-service.ts
|
|
980
|
+
var IntegratedEntityService = class extends BaseService {
|
|
981
981
|
/**
|
|
982
982
|
* Find a single entity by its external CRM identifier.
|
|
983
983
|
*/
|
|
@@ -1081,13 +1081,13 @@ export {
|
|
|
1081
1081
|
BaseListUseCase,
|
|
1082
1082
|
BaseRepository,
|
|
1083
1083
|
BaseService,
|
|
1084
|
-
|
|
1084
|
+
IntegratedEntityRepository,
|
|
1085
|
+
IntegratedEntityService,
|
|
1086
|
+
JunctionIntegrationRepository,
|
|
1085
1087
|
KnowledgeEntityRepository,
|
|
1086
1088
|
KnowledgeEntityService,
|
|
1087
1089
|
MetadataEntityRepository,
|
|
1088
1090
|
MetadataEntityService,
|
|
1089
|
-
SyncedEntityRepository,
|
|
1090
|
-
SyncedEntityService,
|
|
1091
1091
|
WithAnalytics,
|
|
1092
1092
|
buildChangeEvents,
|
|
1093
1093
|
buildCompositeExternalId,
|