@pattern-stack/codegen 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +135 -0
- package/README.md +5 -5
- package/consumer-skills/codegen/SKILL.md +2 -2
- package/consumer-skills/events/typed-bus-and-outbox.md +1 -1
- package/consumer-skills/{sync → integration}/SKILL.md +29 -29
- package/consumer-skills/{sync → integration}/audit-and-detection.md +22 -22
- package/consumer-skills/{sync → integration}/change-sources-and-sinks.md +60 -60
- package/consumer-skills/subsystems/SKILL.md +64 -8
- package/consumer-skills/subsystems/wiring-and-order.md +7 -7
- package/dist/runtime/base-classes/index.d.ts +4 -4
- package/dist/runtime/base-classes/index.js +35 -35
- package/dist/runtime/base-classes/index.js.map +1 -1
- package/dist/runtime/base-classes/{synced-entity-repository.d.ts → integrated-entity-repository.d.ts} +15 -15
- package/dist/runtime/base-classes/{synced-entity-repository.js → integrated-entity-repository.js} +21 -21
- package/dist/runtime/base-classes/integrated-entity-repository.js.map +1 -0
- package/dist/runtime/base-classes/{synced-entity-service.d.ts → integrated-entity-service.d.ts} +6 -6
- package/dist/runtime/base-classes/{synced-entity-service.js → integrated-entity-service.js} +4 -4
- package/dist/runtime/base-classes/integrated-entity-service.js.map +1 -0
- package/dist/runtime/base-classes/{sync-upsert-config.d.ts → integration-upsert-config.d.ts} +13 -13
- package/dist/runtime/base-classes/integration-upsert-config.js +1 -0
- package/dist/runtime/base-classes/{junction-sync-repository.d.ts → junction-integration-repository.d.ts} +11 -11
- package/dist/runtime/base-classes/{junction-sync-repository.js → junction-integration-repository.js} +15 -15
- package/dist/runtime/base-classes/junction-integration-repository.js.map +1 -0
- package/dist/runtime/subsystems/auth/auth-oauth-state.schema.js.map +1 -1
- package/dist/runtime/subsystems/auth/auth.module.d.ts +4 -4
- package/dist/runtime/subsystems/auth/auth.module.js +3 -3
- package/dist/runtime/subsystems/auth/auth.module.js.map +1 -1
- package/dist/runtime/subsystems/auth/auth.tokens.d.ts +8 -8
- package/dist/runtime/subsystems/auth/auth.tokens.js +6 -6
- package/dist/runtime/subsystems/auth/auth.tokens.js.map +1 -1
- package/dist/runtime/subsystems/auth/backends/state-store.drizzle-backend.js.map +1 -1
- package/dist/runtime/subsystems/auth/controllers/auth.controller.d.ts +2 -2
- package/dist/runtime/subsystems/auth/controllers/auth.controller.js +3 -3
- package/dist/runtime/subsystems/auth/controllers/auth.controller.js.map +1 -1
- package/dist/runtime/subsystems/auth/index.d.ts +3 -3
- package/dist/runtime/subsystems/auth/index.js +40 -40
- package/dist/runtime/subsystems/auth/index.js.map +1 -1
- package/dist/runtime/subsystems/auth/middleware/requester-context.js.map +1 -1
- package/dist/runtime/subsystems/auth/protocols/auth-strategy.d.ts +3 -3
- package/dist/runtime/subsystems/auth/protocols/{integration-store.d.ts → connection-store.d.ts} +20 -20
- package/dist/runtime/subsystems/auth/protocols/connection-store.js +1 -0
- package/dist/runtime/subsystems/auth/protocols/provider-strategy.d.ts +3 -3
- package/dist/runtime/subsystems/auth/runtime/{integration-broken.error.d.ts → connection-broken.error.d.ts} +5 -5
- package/dist/runtime/subsystems/auth/runtime/connection-broken.error.js +19 -0
- package/dist/runtime/subsystems/auth/runtime/connection-broken.error.js.map +1 -0
- package/dist/runtime/subsystems/auth/runtime/oauth2-refresh.strategy.d.ts +10 -10
- package/dist/runtime/subsystems/auth/runtime/oauth2-refresh.strategy.js +28 -28
- package/dist/runtime/subsystems/auth/runtime/oauth2-refresh.strategy.js.map +1 -1
- package/dist/runtime/subsystems/auth/runtime/with-auth-retry.d.ts +1 -1
- package/dist/runtime/subsystems/auth/runtime/with-auth-retry.js +3 -3
- package/dist/runtime/subsystems/auth/runtime/with-auth-retry.js.map +1 -1
- package/dist/runtime/subsystems/bridge/bridge.module.d.ts +0 -1
- package/dist/runtime/subsystems/bridge/bridge.module.js +294 -710
- package/dist/runtime/subsystems/bridge/bridge.module.js.map +1 -1
- package/dist/runtime/subsystems/bridge/index.d.ts +0 -1
- package/dist/runtime/subsystems/bridge/index.js +248 -664
- package/dist/runtime/subsystems/bridge/index.js.map +1 -1
- package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js +18 -10
- package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js.map +1 -1
- package/dist/runtime/subsystems/events/events.module.js +43 -244
- package/dist/runtime/subsystems/events/events.module.js.map +1 -1
- package/dist/runtime/subsystems/events/index.d.ts +0 -1
- package/dist/runtime/subsystems/events/index.js +39 -241
- package/dist/runtime/subsystems/events/index.js.map +1 -1
- package/dist/runtime/subsystems/index.d.ts +7 -7
- package/dist/runtime/subsystems/index.js +222 -839
- package/dist/runtime/subsystems/index.js.map +1 -1
- package/dist/runtime/subsystems/{sync → integration}/build-change-source.d.ts +3 -3
- package/dist/runtime/subsystems/{sync → integration}/build-change-source.js +3 -3
- package/dist/runtime/subsystems/integration/build-change-source.js.map +1 -0
- package/dist/runtime/subsystems/{sync → integration}/deep-equal.differ.d.ts +2 -2
- package/dist/runtime/subsystems/{sync → integration}/deep-equal.differ.js +1 -1
- package/dist/runtime/subsystems/integration/deep-equal.differ.js.map +1 -0
- package/dist/runtime/subsystems/{sync → integration}/detection-config.schema.d.ts +3 -3
- package/dist/runtime/subsystems/{sync → integration}/detection-config.schema.js +1 -1
- package/dist/runtime/subsystems/integration/detection-config.schema.js.map +1 -0
- package/dist/runtime/subsystems/{sync/execute-sync.use-case.d.ts → integration/execute-integration.use-case.d.ts} +13 -13
- package/dist/runtime/subsystems/{sync/execute-sync.use-case.js → integration/execute-integration.use-case.js} +30 -30
- package/dist/runtime/subsystems/integration/execute-integration.use-case.js.map +1 -0
- package/dist/runtime/subsystems/integration/index.d.ts +28 -0
- package/dist/runtime/subsystems/{sync → integration}/index.js +171 -171
- package/dist/runtime/subsystems/integration/index.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-audit.schema.d.ts → integration/integration-audit.schema.d.ts} +64 -64
- package/dist/runtime/subsystems/{sync/sync-audit.schema.js → integration/integration-audit.schema.js} +47 -47
- package/dist/runtime/subsystems/integration/integration-audit.schema.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-change-source.protocol.d.ts → integration/integration-change-source.protocol.d.ts} +10 -10
- package/dist/runtime/subsystems/integration/integration-change-source.protocol.js +1 -0
- package/dist/runtime/subsystems/{sync/sync-cursor-store.drizzle-backend.d.ts → integration/integration-cursor-store.drizzle-backend.d.ts} +1 -1
- package/dist/runtime/subsystems/{sync/sync-cursor-store.drizzle-backend.js → integration/integration-cursor-store.drizzle-backend.js} +65 -65
- package/dist/runtime/subsystems/integration/integration-cursor-store.drizzle-backend.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-cursor-store.memory-backend.d.ts → integration/integration-cursor-store.memory-backend.d.ts} +6 -6
- package/dist/runtime/subsystems/{sync/sync-cursor-store.memory-backend.js → integration/integration-cursor-store.memory-backend.js} +5 -5
- package/dist/runtime/subsystems/integration/integration-cursor-store.memory-backend.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-cursor-store.protocol.d.ts → integration/integration-cursor-store.protocol.d.ts} +13 -13
- package/dist/runtime/subsystems/integration/integration-cursor-store.protocol.js +1 -0
- package/dist/runtime/subsystems/{sync/sync-errors.d.ts → integration/integration-errors.d.ts} +2 -2
- package/dist/runtime/subsystems/{sync/sync-errors.js → integration/integration-errors.js} +3 -3
- package/dist/runtime/subsystems/integration/integration-errors.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-field-diff.protocol.d.ts → integration/integration-field-diff.protocol.d.ts} +2 -2
- package/dist/runtime/subsystems/{sync/sync-field-diff.protocol.js → integration/integration-field-diff.protocol.js} +2 -2
- package/dist/runtime/subsystems/integration/integration-field-diff.protocol.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-loopback.protocol.d.ts → integration/integration-loopback.protocol.d.ts} +2 -2
- package/dist/runtime/subsystems/integration/integration-loopback.protocol.js +1 -0
- package/dist/runtime/subsystems/{sync/sync-middleware.protocol.d.ts → integration/integration-middleware.protocol.d.ts} +5 -5
- package/dist/runtime/subsystems/integration/integration-middleware.protocol.js +1 -0
- package/dist/runtime/subsystems/{sync/sync-run-recorder.drizzle-backend.d.ts → integration/integration-run-recorder.drizzle-backend.d.ts} +5 -5
- package/dist/runtime/subsystems/{sync/sync-run-recorder.drizzle-backend.js → integration/integration-run-recorder.drizzle-backend.js} +73 -73
- package/dist/runtime/subsystems/integration/integration-run-recorder.drizzle-backend.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-run-recorder.memory-backend.d.ts → integration/integration-run-recorder.memory-backend.d.ts} +15 -15
- package/dist/runtime/subsystems/{sync/sync-run-recorder.memory-backend.js → integration/integration-run-recorder.memory-backend.js} +11 -11
- package/dist/runtime/subsystems/integration/integration-run-recorder.memory-backend.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-run-recorder.protocol.d.ts → integration/integration-run-recorder.protocol.d.ts} +25 -25
- package/dist/runtime/subsystems/integration/integration-run-recorder.protocol.js +1 -0
- package/dist/runtime/subsystems/{sync/sync-sink.protocol.d.ts → integration/integration-sink.protocol.d.ts} +5 -5
- package/dist/runtime/subsystems/integration/integration-sink.protocol.js +1 -0
- package/dist/runtime/subsystems/{sync/sync.module.d.ts → integration/integration.module.d.ts} +24 -24
- package/dist/runtime/subsystems/{sync/sync.module.js → integration/integration.module.js} +132 -132
- package/dist/runtime/subsystems/integration/integration.module.js.map +1 -0
- package/dist/runtime/subsystems/integration/integration.tokens.d.ts +47 -0
- package/dist/runtime/subsystems/integration/integration.tokens.js +18 -0
- package/dist/runtime/subsystems/integration/integration.tokens.js.map +1 -0
- package/dist/runtime/subsystems/{sync → integration}/loopback.middleware.d.ts +5 -5
- package/dist/runtime/subsystems/{sync → integration}/loopback.middleware.js +1 -1
- package/dist/runtime/subsystems/integration/loopback.middleware.js.map +1 -0
- package/dist/runtime/subsystems/{sync → integration}/poll-change-source.d.ts +5 -5
- package/dist/runtime/subsystems/{sync → integration}/poll-change-source.js +1 -1
- package/dist/runtime/subsystems/integration/poll-change-source.js.map +1 -0
- package/dist/runtime/subsystems/{sync → integration}/webhook-change-source.d.ts +5 -5
- package/dist/runtime/subsystems/{sync → integration}/webhook-change-source.js +1 -1
- package/dist/runtime/subsystems/integration/webhook-change-source.js.map +1 -0
- package/dist/runtime/subsystems/jobs/bullmq.config.d.ts +22 -3
- package/dist/runtime/subsystems/jobs/bullmq.config.js.map +1 -1
- package/dist/runtime/subsystems/jobs/index.d.ts +1 -4
- package/dist/runtime/subsystems/jobs/index.js +87 -506
- package/dist/runtime/subsystems/jobs/index.js.map +1 -1
- package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.js.map +1 -1
- package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.js +3 -0
- package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.js.map +1 -1
- package/dist/runtime/subsystems/jobs/job-worker.module.d.ts +10 -3
- package/dist/runtime/subsystems/jobs/job-worker.module.js +248 -664
- package/dist/runtime/subsystems/jobs/job-worker.module.js.map +1 -1
- package/dist/runtime/subsystems/jobs/jobs-domain.module.d.ts +0 -1
- package/dist/runtime/subsystems/jobs/jobs-domain.module.js +89 -391
- package/dist/runtime/subsystems/jobs/jobs-domain.module.js.map +1 -1
- package/dist/runtime/subsystems/observability/index.d.ts +4 -4
- package/dist/runtime/subsystems/observability/index.js +11 -11
- package/dist/runtime/subsystems/observability/index.js.map +1 -1
- package/dist/runtime/subsystems/observability/observability.module.d.ts +2 -2
- package/dist/runtime/subsystems/observability/observability.module.js +11 -11
- package/dist/runtime/subsystems/observability/observability.module.js.map +1 -1
- package/dist/runtime/subsystems/observability/observability.protocol.d.ts +11 -11
- package/dist/runtime/subsystems/observability/observability.service.d.ts +6 -6
- package/dist/runtime/subsystems/observability/observability.service.js +11 -11
- package/dist/runtime/subsystems/observability/observability.service.js.map +1 -1
- package/dist/runtime/subsystems/observability/observability.tokens.d.ts +1 -1
- package/dist/runtime/subsystems/observability/observability.tokens.js.map +1 -1
- package/dist/runtime/subsystems/observability/reporters/bridge-metrics.reporter.d.ts +3 -3
- package/dist/runtime/subsystems/observability/reporters/bridge-metrics.reporter.js.map +1 -1
- package/dist/runtime/subsystems/observability/reporters/index.d.ts +3 -3
- package/dist/runtime/subsystems/observability/reporters/index.js.map +1 -1
- package/dist/src/cli/index.js +412 -302
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/index.d.ts +22 -22
- package/dist/src/index.js +191 -191
- package/dist/src/index.js.map +1 -1
- package/examples/auth-integrations/README.md +32 -32
- package/examples/auth-integrations/definitions/entities/{integration.yaml → connection.yaml} +10 -10
- package/examples/auth-integrations/runtime/{integrations/adapters/integration-grant-sink.adapter.ts → connections/adapters/connection-grant-sink.adapter.ts} +7 -7
- package/examples/auth-integrations/runtime/{integrations/adapters/integration-reader.adapter.ts → connections/adapters/connection-reader.adapter.ts} +10 -10
- package/examples/auth-integrations/runtime/{integrations/adapters/integration-token-writer.adapter.ts → connections/adapters/connection-token-writer.adapter.ts} +11 -11
- package/examples/auth-integrations/runtime/connections/connections-auth.module.ts +81 -0
- package/examples/auth-integrations/runtime/{integrations/facade/integrations.service.ts → connections/facade/connections.service.ts} +35 -35
- package/examples/auth-integrations/runtime/{integrations → connections}/oauth/use-cases/create-or-update-from-oauth-grant.use-case.ts +11 -11
- package/examples/auth-integrations/runtime/{integrations/oauth/use-cases/disconnect-integration.use-case.ts → connections/oauth/use-cases/disconnect-connection.use-case.ts} +6 -6
- package/examples/auth-integrations/runtime/connections/oauth/use-cases/list-user-connections.use-case.ts +21 -0
- package/examples/auth-integrations/runtime/connections/oauth/use-cases/mark-connection-requires-reauth.use-case.ts +21 -0
- package/package.json +1 -1
- package/runtime/base-classes/index.ts +8 -8
- package/runtime/base-classes/{synced-entity-repository.ts → integrated-entity-repository.ts} +36 -36
- package/runtime/base-classes/{synced-entity-service.ts → integrated-entity-service.ts} +6 -6
- package/runtime/base-classes/{sync-upsert-config.ts → integration-upsert-config.ts} +12 -12
- package/runtime/base-classes/{junction-sync-repository.ts → junction-integration-repository.ts} +28 -28
- package/runtime/subsystems/auth/auth-oauth-state.schema.ts +1 -1
- package/runtime/subsystems/auth/auth.module.ts +4 -4
- package/runtime/subsystems/auth/auth.tokens.ts +7 -7
- package/runtime/subsystems/auth/controllers/auth.controller.ts +7 -7
- package/runtime/subsystems/auth/index.ts +19 -19
- package/runtime/subsystems/auth/protocols/auth-strategy.ts +3 -3
- package/runtime/subsystems/auth/protocols/{integration-store.ts → connection-store.ts} +19 -19
- package/runtime/subsystems/auth/protocols/provider-strategy.ts +2 -2
- package/runtime/subsystems/auth/runtime/{integration-broken.error.ts → connection-broken.error.ts} +5 -5
- package/runtime/subsystems/auth/runtime/oauth2-refresh.strategy.ts +35 -35
- package/runtime/subsystems/auth/runtime/with-auth-retry.ts +3 -3
- package/runtime/subsystems/events/event-bus.drizzle-backend.ts +32 -10
- package/runtime/subsystems/events/events.module.ts +38 -6
- package/runtime/subsystems/events/index.ts +7 -1
- package/runtime/subsystems/index.ts +11 -11
- package/runtime/subsystems/{sync → integration}/build-change-source.ts +3 -3
- package/runtime/subsystems/{sync → integration}/deep-equal.differ.ts +7 -7
- package/runtime/subsystems/{sync → integration}/detection-config.schema.ts +3 -3
- package/runtime/subsystems/{sync/execute-sync.use-case.ts → integration/execute-integration.use-case.ts} +40 -40
- package/runtime/subsystems/{sync → integration}/index.ts +47 -47
- package/runtime/subsystems/{sync/sync-audit.schema.ts → integration/integration-audit.schema.ts} +61 -61
- package/runtime/subsystems/{sync/sync-change-source.protocol.ts → integration/integration-change-source.protocol.ts} +9 -9
- package/runtime/subsystems/{sync/sync-cursor-store.drizzle-backend.ts → integration/integration-cursor-store.drizzle-backend.ts} +30 -30
- package/runtime/subsystems/{sync/sync-cursor-store.memory-backend.ts → integration/integration-cursor-store.memory-backend.ts} +9 -9
- package/runtime/subsystems/{sync/sync-cursor-store.protocol.ts → integration/integration-cursor-store.protocol.ts} +13 -13
- package/runtime/subsystems/{sync/sync-errors.ts → integration/integration-errors.ts} +3 -3
- package/runtime/subsystems/{sync/sync-field-diff.protocol.ts → integration/integration-field-diff.protocol.ts} +2 -2
- package/runtime/subsystems/{sync/sync-loopback.protocol.ts → integration/integration-loopback.protocol.ts} +2 -2
- package/runtime/subsystems/{sync/sync-middleware.protocol.ts → integration/integration-middleware.protocol.ts} +6 -6
- package/runtime/subsystems/{sync/sync-run-recorder.drizzle-backend.ts → integration/integration-run-recorder.drizzle-backend.ts} +39 -39
- package/runtime/subsystems/{sync/sync-run-recorder.memory-backend.ts → integration/integration-run-recorder.memory-backend.ts} +23 -23
- package/runtime/subsystems/{sync/sync-run-recorder.protocol.ts → integration/integration-run-recorder.protocol.ts} +25 -25
- package/runtime/subsystems/{sync/sync-sink.protocol.ts → integration/integration-sink.protocol.ts} +4 -4
- package/runtime/subsystems/{sync/sync.module.ts → integration/integration.module.ts} +48 -48
- package/runtime/subsystems/integration/integration.tokens.ts +49 -0
- package/runtime/subsystems/{sync → integration}/loopback.middleware.ts +5 -5
- package/runtime/subsystems/{sync → integration}/poll-change-source.ts +7 -7
- package/runtime/subsystems/{sync → integration}/webhook-change-source.ts +7 -7
- package/runtime/subsystems/jobs/bullmq.config.ts +23 -3
- package/runtime/subsystems/jobs/index.ts +13 -8
- package/runtime/subsystems/jobs/job-worker.bullmq-backend.ts +5 -2
- package/runtime/subsystems/jobs/job-worker.module.ts +27 -7
- package/runtime/subsystems/jobs/jobs-domain.module.ts +27 -2
- package/runtime/subsystems/observability/index.ts +1 -1
- package/runtime/subsystems/observability/observability.module.ts +2 -2
- package/runtime/subsystems/observability/observability.protocol.ts +11 -11
- package/runtime/subsystems/observability/observability.service.ts +13 -13
- package/runtime/subsystems/observability/observability.tokens.ts +1 -1
- package/src/patterns/library/index.ts +4 -4
- package/src/patterns/library/{synced.pattern.ts → integrated.pattern.ts} +12 -12
- package/src/patterns/library/junction.pattern.ts +1 -1
- package/src/patterns/pattern-definition.ts +3 -3
- package/templates/entity/new/backend/modules/core/{sync-source.ejs.t → integration-source.ejs.t} +6 -6
- package/templates/entity/new/backend/modules/core/{sync-source.providers.ejs.t → integration-source.providers.ejs.t} +2 -2
- package/templates/entity/new/clean-lite-ps/entity.ejs.t +1 -1
- package/templates/entity/new/clean-lite-ps/module.ejs.t +1 -1
- package/templates/entity/new/clean-lite-ps/prompt-extension.js +33 -33
- package/templates/entity/new/clean-lite-ps/repository.ejs.t +27 -27
- package/templates/entity/new/frontend/collections/collection.ejs.t +26 -1
- package/templates/entity/new/frontend/collections/collections-base.ejs.t +11 -0
- package/templates/entity/new/frontend/entity/combined.ejs.t +31 -1
- package/templates/entity/new/prompt.js +27 -15
- package/templates/junction/new/entity.ejs.t +1 -1
- package/templates/junction/new/prompt.js +24 -24
- package/templates/junction/new/repository.ejs.t +19 -19
- package/templates/subsystem/auth/auth-oauth-state.schema.ejs.t +2 -2
- package/templates/subsystem/auth-config/prompt.js +1 -1
- package/templates/subsystem/auth-integrations/app-module-hook.ejs.t +5 -5
- package/templates/subsystem/bridge/prompt.js +1 -1
- package/templates/subsystem/events/domain-events.schema.ejs.t +43 -2
- package/templates/subsystem/integration/integration-audit.schema.ejs.t +192 -0
- package/templates/subsystem/{sync → integration}/prompt.js +12 -12
- package/templates/subsystem/{sync-config/codegen-config-sync-block.ejs.t → integration-config/codegen-config-integration-block.ejs.t} +7 -7
- package/templates/subsystem/integration-config/prompt.js +22 -0
- package/templates/subsystem/jobs/worker.ejs.t +2 -2
- package/templates/subsystem/observability/main-hook.ejs.t +1 -1
- package/templates/subsystem/observability/prompt.js +1 -1
- package/templates/subsystem/openapi-config/prompt.js +1 -1
- package/dist/runtime/base-classes/junction-sync-repository.js.map +0 -1
- package/dist/runtime/base-classes/sync-upsert-config.js +0 -1
- package/dist/runtime/base-classes/synced-entity-repository.js.map +0 -1
- package/dist/runtime/base-classes/synced-entity-service.js.map +0 -1
- package/dist/runtime/subsystems/auth/protocols/integration-store.js +0 -1
- package/dist/runtime/subsystems/auth/runtime/integration-broken.error.js +0 -19
- package/dist/runtime/subsystems/auth/runtime/integration-broken.error.js.map +0 -1
- package/dist/runtime/subsystems/sync/build-change-source.js.map +0 -1
- package/dist/runtime/subsystems/sync/deep-equal.differ.js.map +0 -1
- package/dist/runtime/subsystems/sync/detection-config.schema.js.map +0 -1
- package/dist/runtime/subsystems/sync/execute-sync.use-case.js.map +0 -1
- package/dist/runtime/subsystems/sync/index.d.ts +0 -28
- package/dist/runtime/subsystems/sync/index.js.map +0 -1
- package/dist/runtime/subsystems/sync/loopback.middleware.js.map +0 -1
- package/dist/runtime/subsystems/sync/poll-change-source.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-audit.schema.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-change-source.protocol.js +0 -1
- package/dist/runtime/subsystems/sync/sync-cursor-store.drizzle-backend.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-cursor-store.memory-backend.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-cursor-store.protocol.js +0 -1
- package/dist/runtime/subsystems/sync/sync-errors.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-field-diff.protocol.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-loopback.protocol.js +0 -1
- package/dist/runtime/subsystems/sync/sync-middleware.protocol.js +0 -1
- package/dist/runtime/subsystems/sync/sync-run-recorder.drizzle-backend.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-run-recorder.memory-backend.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-run-recorder.protocol.js +0 -1
- package/dist/runtime/subsystems/sync/sync-sink.protocol.js +0 -1
- package/dist/runtime/subsystems/sync/sync.module.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync.tokens.d.ts +0 -47
- package/dist/runtime/subsystems/sync/sync.tokens.js +0 -18
- package/dist/runtime/subsystems/sync/sync.tokens.js.map +0 -1
- package/dist/runtime/subsystems/sync/webhook-change-source.js.map +0 -1
- package/examples/auth-integrations/runtime/integrations/integrations-auth.module.ts +0 -81
- package/examples/auth-integrations/runtime/integrations/oauth/use-cases/list-user-integrations.use-case.ts +0 -21
- package/examples/auth-integrations/runtime/integrations/oauth/use-cases/mark-integration-requires-reauth.use-case.ts +0 -21
- package/runtime/subsystems/sync/sync.tokens.ts +0 -49
- package/templates/subsystem/sync/sync-audit.schema.ejs.t +0 -192
- package/templates/subsystem/sync-config/prompt.js +0 -22
- /package/dist/runtime/base-classes/{sync-upsert-config.js.map → integration-upsert-config.js.map} +0 -0
- /package/dist/runtime/subsystems/auth/protocols/{integration-store.js.map → connection-store.js.map} +0 -0
- /package/dist/runtime/subsystems/{sync/sync-change-source.protocol.js.map → integration/integration-change-source.protocol.js.map} +0 -0
- /package/dist/runtime/subsystems/{sync/sync-cursor-store.protocol.js.map → integration/integration-cursor-store.protocol.js.map} +0 -0
- /package/dist/runtime/subsystems/{sync/sync-loopback.protocol.js.map → integration/integration-loopback.protocol.js.map} +0 -0
- /package/dist/runtime/subsystems/{sync/sync-middleware.protocol.js.map → integration/integration-middleware.protocol.js.map} +0 -0
- /package/dist/runtime/subsystems/{sync/sync-run-recorder.protocol.js.map → integration/integration-run-recorder.protocol.js.map} +0 -0
- /package/dist/runtime/subsystems/{sync/sync-sink.protocol.js.map → integration/integration-sink.protocol.js.map} +0 -0
|
@@ -9,25 +9,25 @@
|
|
|
9
9
|
* AuthController,
|
|
10
10
|
* ENCRYPTION_KEY,
|
|
11
11
|
* OAUTH_STATE_STORE,
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* AUTH_CONNECTION_READER,
|
|
13
|
+
* AUTH_CONNECTION_TOKEN_WRITER,
|
|
14
|
+
* AUTH_CONNECTION_GRANT_SINK,
|
|
15
15
|
* AUTH_USER_CONTEXT,
|
|
16
16
|
* STRATEGY_REGISTRY,
|
|
17
17
|
* AUTH_OPTIONS,
|
|
18
18
|
* OAuth2RefreshStrategy,
|
|
19
19
|
* withAuthRetry,
|
|
20
|
-
*
|
|
20
|
+
* ConnectionBrokenError,
|
|
21
21
|
* SessionExpiredError,
|
|
22
22
|
* OAuthStateError,
|
|
23
23
|
* type IAuthStrategy,
|
|
24
24
|
* type IEncryptionKey,
|
|
25
25
|
* type IOAuthStateStore,
|
|
26
26
|
* type OAuthStateRecord,
|
|
27
|
-
* type
|
|
28
|
-
* type
|
|
29
|
-
* type
|
|
30
|
-
* type
|
|
27
|
+
* type IConnectionReader,
|
|
28
|
+
* type IConnectionTokenWriter,
|
|
29
|
+
* type IConnectionGrantSink,
|
|
30
|
+
* type ConnectionGrantInput,
|
|
31
31
|
* type IUserContext,
|
|
32
32
|
* type IProviderStrategy,
|
|
33
33
|
* type ProviderStrategyRegistry,
|
|
@@ -49,13 +49,13 @@ export type {
|
|
|
49
49
|
} from './protocols/oauth-state-store';
|
|
50
50
|
export { OAuthStateError } from './protocols/oauth-state-store';
|
|
51
51
|
export type {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
} from './protocols/
|
|
52
|
+
DecryptedConnection,
|
|
53
|
+
IConnectionReader,
|
|
54
|
+
IConnectionTokenWriter,
|
|
55
|
+
ConnectionTokenUpdate,
|
|
56
|
+
IConnectionGrantSink,
|
|
57
|
+
ConnectionGrantInput,
|
|
58
|
+
} from './protocols/connection-store';
|
|
59
59
|
export type { IUserContext } from './protocols/user-context';
|
|
60
60
|
export type {
|
|
61
61
|
IProviderStrategy,
|
|
@@ -67,9 +67,9 @@ export type {
|
|
|
67
67
|
export {
|
|
68
68
|
ENCRYPTION_KEY,
|
|
69
69
|
OAUTH_STATE_STORE,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
AUTH_CONNECTION_READER,
|
|
71
|
+
AUTH_CONNECTION_TOKEN_WRITER,
|
|
72
|
+
AUTH_CONNECTION_GRANT_SINK,
|
|
73
73
|
AUTH_USER_CONTEXT,
|
|
74
74
|
STRATEGY_REGISTRY,
|
|
75
75
|
AUTH_OPTIONS,
|
|
@@ -83,7 +83,7 @@ export {
|
|
|
83
83
|
type FetchLike,
|
|
84
84
|
} from './runtime/oauth2-refresh.strategy';
|
|
85
85
|
export { withAuthRetry, type WithAuthRetryOptions } from './runtime/with-auth-retry';
|
|
86
|
-
export {
|
|
86
|
+
export { ConnectionBrokenError } from './runtime/connection-broken.error';
|
|
87
87
|
export {
|
|
88
88
|
SessionExpiredError,
|
|
89
89
|
isSessionExpiredError,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auth subsystem — `IAuthStrategy` port.
|
|
3
3
|
*
|
|
4
|
-
* The credentials-resolution seam used by every
|
|
4
|
+
* The credentials-resolution seam used by every connection adapter. Adapters
|
|
5
5
|
* depend on this interface; concrete strategies (SalesforceAuthStrategy,
|
|
6
6
|
* HubSpotAuthStrategy, future Gmail/Calendar) typically extend the
|
|
7
7
|
* `OAuth2RefreshStrategy` template-method base in `../runtime/`.
|
|
@@ -35,12 +35,12 @@ export interface AuthResolveOptions {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
|
-
* Auth-strategy contract shared by every
|
|
38
|
+
* Auth-strategy contract shared by every connection adapter. Implementations
|
|
39
39
|
* typically extend `OAuth2RefreshStrategy` and override four small hooks.
|
|
40
40
|
*/
|
|
41
41
|
export interface IAuthStrategy {
|
|
42
42
|
resolve(
|
|
43
|
-
|
|
43
|
+
connectionId: string,
|
|
44
44
|
opts?: AuthResolveOptions,
|
|
45
45
|
): Promise<AuthCredentials>;
|
|
46
46
|
}
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Auth subsystem —
|
|
2
|
+
* Auth subsystem — connection storage ports.
|
|
3
3
|
*
|
|
4
|
-
* `OAuth2RefreshStrategy` reads decrypted
|
|
4
|
+
* `OAuth2RefreshStrategy` reads decrypted connection rows and persists
|
|
5
5
|
* refreshed tokens. The subsystem doesn't care what entity framework stores
|
|
6
6
|
* those rows — consumers implement these narrow ports against whatever
|
|
7
|
-
* `
|
|
7
|
+
* `connections` table their app uses.
|
|
8
8
|
*
|
|
9
9
|
* In the extraction-source app both ports are satisfied by a
|
|
10
|
-
* pair of thin adapters over `
|
|
10
|
+
* pair of thin adapters over `ConnectionService` + `RefreshConnectionUseCase`.
|
|
11
11
|
* The codegen-patterns `examples/auth-integrations/` starter (separate PR)
|
|
12
|
-
* ships a canonical `
|
|
12
|
+
* ships a canonical `connection.yaml` whose generated service + use case
|
|
13
13
|
* satisfy the shape out of the box.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* An
|
|
17
|
+
* An connection row with its secrets decrypted and ready to use.
|
|
18
18
|
*
|
|
19
19
|
* Consumers produce this shape from their own storage by passing stored
|
|
20
20
|
* ciphertexts through `IEncryptionKey.decrypt`. The subsystem never sees
|
|
21
21
|
* the ciphertext form.
|
|
22
22
|
*/
|
|
23
|
-
export interface
|
|
23
|
+
export interface DecryptedConnection {
|
|
24
24
|
id: string;
|
|
25
25
|
/** Provider slug — must match the strategy's `provider`. */
|
|
26
26
|
provider: string;
|
|
@@ -35,13 +35,13 @@ export interface DecryptedIntegration {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
|
-
* Read port — fetches a decrypted
|
|
38
|
+
* Read port — fetches a decrypted connection by id.
|
|
39
39
|
*
|
|
40
40
|
* Adapters typically wrap a service/repo call that does the decryption
|
|
41
41
|
* internally. `OAuth2RefreshStrategy.resolve()` calls this on every invocation.
|
|
42
42
|
*/
|
|
43
|
-
export interface
|
|
44
|
-
findByIdDecrypted(
|
|
43
|
+
export interface IConnectionReader {
|
|
44
|
+
findByIdDecrypted(connectionId: string): Promise<DecryptedConnection | null>;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/**
|
|
@@ -54,15 +54,15 @@ export interface IIntegrationReader {
|
|
|
54
54
|
* `refreshToken` semantics: `undefined` means "provider did not rotate; keep
|
|
55
55
|
* existing ciphertext". A rotated token comes through as a string.
|
|
56
56
|
*/
|
|
57
|
-
export interface
|
|
58
|
-
|
|
57
|
+
export interface ConnectionTokenUpdate {
|
|
58
|
+
connectionId: string;
|
|
59
59
|
accessToken: string;
|
|
60
60
|
refreshToken?: string;
|
|
61
61
|
expiresAt: Date;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
export interface
|
|
65
|
-
persistRefresh(update:
|
|
64
|
+
export interface IConnectionTokenWriter {
|
|
65
|
+
persistRefresh(update: ConnectionTokenUpdate): Promise<void>;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
/**
|
|
@@ -71,10 +71,10 @@ export interface IIntegrationTokenWriter {
|
|
|
71
71
|
* re-connected an existing one).
|
|
72
72
|
*
|
|
73
73
|
* `AuthController.callback` invokes this after `IProviderStrategy.exchangeCodeForTokens`.
|
|
74
|
-
* The subsystem itself never imports a concrete `
|
|
74
|
+
* The subsystem itself never imports a concrete `ConnectionsService` — the
|
|
75
75
|
* consumer's `auth-integrations` starter (or any equivalent) adapts this
|
|
76
76
|
* port. Keeps the auth subsystem standalone: a non-codegen consumer can
|
|
77
|
-
* satisfy the port against its own
|
|
77
|
+
* satisfy the port against its own connections storage.
|
|
78
78
|
*
|
|
79
79
|
* Semantics:
|
|
80
80
|
* - Upserts on `(userId, provider)`. Repeated grants for the same pair
|
|
@@ -85,7 +85,7 @@ export interface IIntegrationTokenWriter {
|
|
|
85
85
|
* them (e.g. some providers omit `expires_in`; not every flow returns
|
|
86
86
|
* a refresh token on first grant).
|
|
87
87
|
*/
|
|
88
|
-
export interface
|
|
88
|
+
export interface ConnectionGrantInput {
|
|
89
89
|
userId: string;
|
|
90
90
|
/** Provider slug — must match the strategy's `provider`. */
|
|
91
91
|
provider: string;
|
|
@@ -98,6 +98,6 @@ export interface IntegrationGrantInput {
|
|
|
98
98
|
providerMetadata?: Record<string, unknown>;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
export interface
|
|
102
|
-
createOrUpdateFromOAuthGrant(input:
|
|
101
|
+
export interface IConnectionGrantSink {
|
|
102
|
+
createOrUpdateFromOAuthGrant(input: ConnectionGrantInput): Promise<void>;
|
|
103
103
|
}
|
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
* and dispatches by slug.
|
|
19
19
|
*
|
|
20
20
|
* **Naming convention:** interfaces that describe behavioral ports use the
|
|
21
|
-
* `I` prefix (`IProviderStrategy`, `
|
|
21
|
+
* `I` prefix (`IProviderStrategy`, `IConnectionReader`, `IUserContext`,
|
|
22
22
|
* `IOAuthStateStore`, `IEncryptionKey`). Plain data types / DTOs (e.g.
|
|
23
|
-
* `ExchangedTokens`, `
|
|
23
|
+
* `ExchangedTokens`, `DecryptedConnection`, `ConnectionGrantInput`) do
|
|
24
24
|
* not. Abstract template-method classes (e.g. `OAuth2RefreshStrategy`) also
|
|
25
25
|
* do not — the `I` is for interfaces only.
|
|
26
26
|
*/
|
package/runtime/subsystems/auth/runtime/{integration-broken.error.ts → connection-broken.error.ts}
RENAMED
|
@@ -2,20 +2,20 @@
|
|
|
2
2
|
* Thrown when an OAuth2 provider returns `400 invalid_grant`/`invalid_token`
|
|
3
3
|
* on refresh — the refresh token itself is dead (user revoked, org
|
|
4
4
|
* deactivated, token expired beyond the provider's rotation window). The
|
|
5
|
-
*
|
|
5
|
+
* connection should be marked broken so background sync stops picking it
|
|
6
6
|
* up; the user re-initiates OAuth.
|
|
7
7
|
*
|
|
8
8
|
* Shared across every OAuth2 strategy.
|
|
9
9
|
*/
|
|
10
|
-
export class
|
|
10
|
+
export class ConnectionBrokenError extends Error {
|
|
11
11
|
constructor(
|
|
12
|
-
readonly
|
|
12
|
+
readonly connectionId: string,
|
|
13
13
|
readonly errorCode: string,
|
|
14
14
|
readonly errorDescription: string,
|
|
15
15
|
) {
|
|
16
16
|
super(
|
|
17
|
-
`
|
|
17
|
+
`Connection ${connectionId} broken: ${errorCode} - ${errorDescription}`,
|
|
18
18
|
);
|
|
19
|
-
this.name = '
|
|
19
|
+
this.name = 'ConnectionBrokenError';
|
|
20
20
|
}
|
|
21
21
|
}
|
|
@@ -8,18 +8,18 @@
|
|
|
8
8
|
* later" evidence.
|
|
9
9
|
*
|
|
10
10
|
* Subclass contract:
|
|
11
|
-
* - `provider` — slug matched against `
|
|
11
|
+
* - `provider` — slug matched against `connections.provider`
|
|
12
12
|
* - `defaultExpiresInSec` — fallback when refresh response omits `expires_in`
|
|
13
13
|
* - `tokenEndpoint()` — URL to POST the refresh grant
|
|
14
14
|
* - `refreshBodyExtras()` — provider-specific body params
|
|
15
15
|
* - `parseRefreshResponse()` — raw JSON → ParsedRefreshResponse
|
|
16
16
|
* - `buildCredentials()` — stored or freshly-refreshed access token +
|
|
17
|
-
*
|
|
17
|
+
* connection + optional raw refresh response
|
|
18
18
|
* → provider credentials
|
|
19
19
|
*
|
|
20
20
|
* Base handles: expiry check w/ 5-min safety window, `forceRefresh` escape
|
|
21
21
|
* hatch, POST form-urlencoded body, OAuth2 error mapping to
|
|
22
|
-
* `
|
|
22
|
+
* `ConnectionBrokenError`, refresh-token rotation persistence, fetch +
|
|
23
23
|
* clock injection for tests.
|
|
24
24
|
*/
|
|
25
25
|
import type {
|
|
@@ -28,11 +28,11 @@ import type {
|
|
|
28
28
|
IAuthStrategy,
|
|
29
29
|
} from '../protocols/auth-strategy';
|
|
30
30
|
import type {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
} from '../protocols/
|
|
35
|
-
import {
|
|
31
|
+
DecryptedConnection,
|
|
32
|
+
IConnectionReader,
|
|
33
|
+
IConnectionTokenWriter,
|
|
34
|
+
} from '../protocols/connection-store';
|
|
35
|
+
import { ConnectionBrokenError } from './connection-broken.error';
|
|
36
36
|
|
|
37
37
|
export type FetchLike = (
|
|
38
38
|
input: string | URL | Request,
|
|
@@ -43,8 +43,8 @@ export type FetchLike = (
|
|
|
43
43
|
const REFRESH_SAFETY_MS = 5 * 60 * 1000;
|
|
44
44
|
|
|
45
45
|
export interface OAuth2RefreshStrategyOptions {
|
|
46
|
-
|
|
47
|
-
tokenWriter:
|
|
46
|
+
connectionReader: IConnectionReader;
|
|
47
|
+
tokenWriter: IConnectionTokenWriter;
|
|
48
48
|
/** Injectable fetch for tests. Defaults to the global `fetch`. */
|
|
49
49
|
fetch?: FetchLike;
|
|
50
50
|
/** Injectable clock for tests. Defaults to `Date.now`. */
|
|
@@ -66,65 +66,65 @@ export abstract class OAuth2RefreshStrategy implements IAuthStrategy {
|
|
|
66
66
|
protected abstract readonly provider: string;
|
|
67
67
|
protected abstract readonly defaultExpiresInSec: number;
|
|
68
68
|
|
|
69
|
-
protected readonly
|
|
70
|
-
protected readonly tokenWriter:
|
|
69
|
+
protected readonly connectionReader: IConnectionReader;
|
|
70
|
+
protected readonly tokenWriter: IConnectionTokenWriter;
|
|
71
71
|
protected readonly fetchImpl: FetchLike;
|
|
72
72
|
protected readonly now: () => number;
|
|
73
73
|
|
|
74
74
|
constructor(opts: OAuth2RefreshStrategyOptions) {
|
|
75
|
-
this.
|
|
75
|
+
this.connectionReader = opts.connectionReader;
|
|
76
76
|
this.tokenWriter = opts.tokenWriter;
|
|
77
77
|
this.fetchImpl = opts.fetch ?? fetch;
|
|
78
78
|
this.now = opts.now ?? Date.now;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
async resolve(
|
|
82
|
-
|
|
82
|
+
connectionId: string,
|
|
83
83
|
opts: AuthResolveOptions = {},
|
|
84
84
|
): Promise<AuthCredentials> {
|
|
85
|
-
const
|
|
86
|
-
await this.
|
|
87
|
-
if (!
|
|
88
|
-
throw new Error(`
|
|
85
|
+
const connection =
|
|
86
|
+
await this.connectionReader.findByIdDecrypted(connectionId);
|
|
87
|
+
if (!connection) {
|
|
88
|
+
throw new Error(`Connection ${connectionId} not found`);
|
|
89
89
|
}
|
|
90
|
-
if (
|
|
90
|
+
if (connection.provider !== this.provider) {
|
|
91
91
|
throw new Error(
|
|
92
|
-
`${this.constructor.name} called for non-${this.provider}
|
|
92
|
+
`${this.constructor.name} called for non-${this.provider} connection ${connectionId} (provider=${connection.provider})`,
|
|
93
93
|
);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
const needsRefresh =
|
|
97
97
|
opts.forceRefresh ||
|
|
98
|
-
this.isExpiring(
|
|
99
|
-
!
|
|
98
|
+
this.isExpiring(connection.expiresAt) ||
|
|
99
|
+
!connection.accessToken;
|
|
100
100
|
|
|
101
101
|
if (!needsRefresh) {
|
|
102
|
-
return this.buildCredentials(
|
|
102
|
+
return this.buildCredentials(connection.accessToken, connection);
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
if (!
|
|
106
|
-
throw new
|
|
107
|
-
|
|
105
|
+
if (!connection.refreshToken) {
|
|
106
|
+
throw new ConnectionBrokenError(
|
|
107
|
+
connectionId,
|
|
108
108
|
'no_refresh_token',
|
|
109
|
-
'
|
|
109
|
+
'Connection has no refresh token; user must reconnect',
|
|
110
110
|
);
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
const { parsed, raw } = await this.executeRefresh(
|
|
114
|
-
|
|
115
|
-
|
|
114
|
+
connectionId,
|
|
115
|
+
connection.refreshToken,
|
|
116
116
|
);
|
|
117
117
|
const newExpiresAt = new Date(
|
|
118
118
|
this.now() + (parsed.expiresInSec ?? this.defaultExpiresInSec) * 1000,
|
|
119
119
|
);
|
|
120
120
|
await this.tokenWriter.persistRefresh({
|
|
121
|
-
|
|
121
|
+
connectionId,
|
|
122
122
|
accessToken: parsed.accessToken,
|
|
123
123
|
refreshToken: parsed.refreshToken ?? undefined,
|
|
124
124
|
expiresAt: newExpiresAt,
|
|
125
125
|
});
|
|
126
126
|
|
|
127
|
-
return this.buildCredentials(parsed.accessToken,
|
|
127
|
+
return this.buildCredentials(parsed.accessToken, connection, raw);
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
protected abstract tokenEndpoint(): string;
|
|
@@ -132,12 +132,12 @@ export abstract class OAuth2RefreshStrategy implements IAuthStrategy {
|
|
|
132
132
|
protected abstract parseRefreshResponse(raw: unknown): ParsedRefreshResponse;
|
|
133
133
|
protected abstract buildCredentials(
|
|
134
134
|
accessToken: string,
|
|
135
|
-
|
|
135
|
+
connection: DecryptedConnection,
|
|
136
136
|
refreshRaw?: unknown,
|
|
137
137
|
): AuthCredentials;
|
|
138
138
|
|
|
139
139
|
private async executeRefresh(
|
|
140
|
-
|
|
140
|
+
connectionId: string,
|
|
141
141
|
refreshToken: string,
|
|
142
142
|
): Promise<{ parsed: ParsedRefreshResponse; raw: unknown }> {
|
|
143
143
|
const body = new URLSearchParams({
|
|
@@ -160,8 +160,8 @@ export abstract class OAuth2RefreshStrategy implements IAuthStrategy {
|
|
|
160
160
|
response.status === 400 &&
|
|
161
161
|
(err.error === 'invalid_grant' || err.error === 'invalid_token')
|
|
162
162
|
) {
|
|
163
|
-
throw new
|
|
164
|
-
|
|
163
|
+
throw new ConnectionBrokenError(
|
|
164
|
+
connectionId,
|
|
165
165
|
err.error ?? 'invalid_grant',
|
|
166
166
|
err.error_description ?? err.message ?? 'refresh token rejected',
|
|
167
167
|
);
|
|
@@ -33,18 +33,18 @@ export interface WithAuthRetryOptions {
|
|
|
33
33
|
|
|
34
34
|
export async function withAuthRetry<T>(
|
|
35
35
|
authStrategy: IAuthStrategy,
|
|
36
|
-
|
|
36
|
+
connectionId: string,
|
|
37
37
|
op: (credentials: AuthCredentials) => Promise<T>,
|
|
38
38
|
options: WithAuthRetryOptions = {},
|
|
39
39
|
): Promise<T> {
|
|
40
40
|
const classify = options.isSessionExpired ?? isSessionExpiredError;
|
|
41
41
|
|
|
42
|
-
let creds = await authStrategy.resolve(
|
|
42
|
+
let creds = await authStrategy.resolve(connectionId);
|
|
43
43
|
try {
|
|
44
44
|
return await op(creds);
|
|
45
45
|
} catch (e) {
|
|
46
46
|
if (!classify(e)) throw e;
|
|
47
|
-
creds = await authStrategy.resolve(
|
|
47
|
+
creds = await authStrategy.resolve(connectionId, { forceRefresh: true });
|
|
48
48
|
return op(creds);
|
|
49
49
|
}
|
|
50
50
|
}
|
|
@@ -59,17 +59,16 @@ const POLL_BATCH_SIZE = 50;
|
|
|
59
59
|
* the per-event extraction logic in one place so publish/publishMany stay
|
|
60
60
|
* in sync.
|
|
61
61
|
*/
|
|
62
|
-
function toInsertValues(event: DomainEvent) {
|
|
62
|
+
function toInsertValues(event: DomainEvent, multiTenant: boolean) {
|
|
63
63
|
const metadata = event.metadata ?? undefined;
|
|
64
64
|
const pool = (metadata?.['pool'] as string | undefined) ?? null;
|
|
65
65
|
const direction = (metadata?.['direction'] as string | undefined) ?? null;
|
|
66
|
-
const tenantId = (metadata?.['tenantId'] as string | undefined) ?? null;
|
|
67
66
|
// AUDIT-1: tier defaults to 'domain' when absent. The DB CHECK
|
|
68
67
|
// constraint (`domain_events_tier_routing_check`) enforces the
|
|
69
68
|
// tier ⇔ routing-fields invariant at the storage boundary; no
|
|
70
69
|
// JS-side assertion is needed here.
|
|
71
70
|
const tier = (metadata?.['tier'] as string | undefined) ?? 'domain';
|
|
72
|
-
|
|
71
|
+
const base = {
|
|
73
72
|
id: event.id,
|
|
74
73
|
type: event.type,
|
|
75
74
|
aggregateId: event.aggregateId,
|
|
@@ -82,8 +81,14 @@ function toInsertValues(event: DomainEvent) {
|
|
|
82
81
|
pool,
|
|
83
82
|
direction,
|
|
84
83
|
tier,
|
|
85
|
-
tenantId,
|
|
86
84
|
};
|
|
85
|
+
// EVT-8: `tenant_id` is a scaffold-time conditional column, emitted only
|
|
86
|
+
// when `events.multi_tenant: true`. Only write it when multi-tenancy is
|
|
87
|
+
// on — under single-tenant scaffolds the column does not exist, so the
|
|
88
|
+
// key must be omitted from the insert.
|
|
89
|
+
if (!multiTenant) return base;
|
|
90
|
+
const tenantId = (metadata?.['tenantId'] as string | undefined) ?? null;
|
|
91
|
+
return { ...base, tenantId };
|
|
87
92
|
}
|
|
88
93
|
|
|
89
94
|
/**
|
|
@@ -107,7 +112,11 @@ function toEventSummary(r: DomainEventRecord) {
|
|
|
107
112
|
direction: r.direction,
|
|
108
113
|
tier: r.tier,
|
|
109
114
|
rootRunId: typeof rootRunId === 'string' ? rootRunId : null,
|
|
110
|
-
|
|
115
|
+
// EVT-8: `tenant_id` is a scaffold-time conditional column. Read it
|
|
116
|
+
// structurally so this projection typechecks against both the
|
|
117
|
+
// multi-tenant schema (column present) and the single-tenant schema
|
|
118
|
+
// (column absent → undefined → null).
|
|
119
|
+
tenantId: (r as { tenantId?: string | null }).tenantId ?? null,
|
|
111
120
|
occurredAt:
|
|
112
121
|
r.occurredAt instanceof Date
|
|
113
122
|
? r.occurredAt
|
|
@@ -175,13 +184,17 @@ export class DrizzleEventBus implements IEventBus, IEventReadPort, OnModuleInit,
|
|
|
175
184
|
|
|
176
185
|
async publish(event: DomainEvent, tx?: DrizzleTransaction): Promise<void> {
|
|
177
186
|
const client = (tx ?? this.db) as DrizzleClient;
|
|
178
|
-
|
|
187
|
+
const multiTenant = this.opts.multiTenant ?? false;
|
|
188
|
+
await client.insert(domainEvents).values(toInsertValues(event, multiTenant));
|
|
179
189
|
}
|
|
180
190
|
|
|
181
191
|
async publishMany(events: DomainEvent[], tx?: DrizzleTransaction): Promise<void> {
|
|
182
192
|
if (events.length === 0) return;
|
|
183
193
|
const client = (tx ?? this.db) as DrizzleClient;
|
|
184
|
-
|
|
194
|
+
const multiTenant = this.opts.multiTenant ?? false;
|
|
195
|
+
await client
|
|
196
|
+
.insert(domainEvents)
|
|
197
|
+
.values(events.map((e) => toInsertValues(e, multiTenant)));
|
|
185
198
|
}
|
|
186
199
|
|
|
187
200
|
async findById(eventId: string): Promise<DomainEvent | null> {
|
|
@@ -241,11 +254,20 @@ export class DrizzleEventBus implements IEventBus, IEventReadPort, OnModuleInit,
|
|
|
241
254
|
sql`${domainEvents.metadata}->>'rootRunId' = ${query.rootRunId}`,
|
|
242
255
|
);
|
|
243
256
|
}
|
|
244
|
-
|
|
257
|
+
// EVT-8: `tenant_id` is a scaffold-time conditional column (emitted only
|
|
258
|
+
// under `events.multi_tenant: true`). Guard the filter behind the same
|
|
259
|
+
// `multiTenant` flag, and read the column structurally so this backend
|
|
260
|
+
// typechecks against both the multi-tenant schema (column present) and
|
|
261
|
+
// the single-tenant schema (column absent). When multi-tenancy is off
|
|
262
|
+
// there is no `tenant_id` column to filter on.
|
|
263
|
+
if (this.opts.multiTenant && query.tenantId !== undefined) {
|
|
264
|
+
const tenantIdColumn = (
|
|
265
|
+
domainEvents as unknown as { tenantId: typeof domainEvents.pool }
|
|
266
|
+
).tenantId;
|
|
245
267
|
conditions.push(
|
|
246
268
|
query.tenantId === null
|
|
247
|
-
? (sql`${
|
|
248
|
-
: eq(
|
|
269
|
+
? (sql`${tenantIdColumn} is null` as SQL<unknown>)
|
|
270
|
+
: eq(tenantIdColumn, query.tenantId),
|
|
249
271
|
);
|
|
250
272
|
}
|
|
251
273
|
|
|
@@ -57,9 +57,27 @@ import { DRIZZLE } from '../../constants/tokens';
|
|
|
57
57
|
import type { DrizzleClient } from '../../types/drizzle';
|
|
58
58
|
import { DrizzleEventBus } from './event-bus.drizzle-backend';
|
|
59
59
|
import { MemoryEventBus } from './event-bus.memory-backend';
|
|
60
|
-
|
|
60
|
+
// #6 — `RedisEventBus` is lazy-loaded only when `backend: 'redis'` is selected.
|
|
61
|
+
// The file is filtered out of the vendor set for non-redis installs (see
|
|
62
|
+
// `backendFileFilter` in src/cli/commands/subsystem.ts); the dynamic-string
|
|
63
|
+
// import below makes TS treat the specifier as `any` so the consumer's tsc
|
|
64
|
+
// never tries to resolve the absent file.
|
|
61
65
|
import { TypedEventBus } from './generated/bus';
|
|
62
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Lazy-load the Redis backend. Routed through a non-literal specifier so
|
|
69
|
+
* the consumer's `tsc` doesn't resolve `./event-bus.redis-backend` at type
|
|
70
|
+
* check time — important because that file is filtered out of drizzle/
|
|
71
|
+
* memory installs (#6).
|
|
72
|
+
*/
|
|
73
|
+
async function loadRedisEventBus(): Promise<new (url: string) => object> {
|
|
74
|
+
// Non-literal specifier — TS gives this an `any` module type, sidestepping
|
|
75
|
+
// resolution of a file that may not be vendored.
|
|
76
|
+
const specifier = './event-bus.redis-backend';
|
|
77
|
+
const mod = (await import(specifier)) as { RedisEventBus: new (url: string) => object };
|
|
78
|
+
return mod.RedisEventBus;
|
|
79
|
+
}
|
|
80
|
+
|
|
63
81
|
export interface EventsModuleOptions {
|
|
64
82
|
backend: 'drizzle' | 'memory' | 'redis';
|
|
65
83
|
/**
|
|
@@ -124,11 +142,11 @@ function buildTypedBusProviders(multiTenant: boolean): Provider[] {
|
|
|
124
142
|
* drizzle backend is selected but no DRIZZLE provider is registered, we
|
|
125
143
|
* throw a clear error instead of silently constructing a broken bus.
|
|
126
144
|
*/
|
|
127
|
-
function buildEventBusAsync(
|
|
145
|
+
async function buildEventBusAsync(
|
|
128
146
|
options: EventsModuleOptions,
|
|
129
147
|
db: DrizzleClient | null,
|
|
130
148
|
redisUrl: string,
|
|
131
|
-
): unknown {
|
|
149
|
+
): Promise<unknown> {
|
|
132
150
|
if (options.backend === 'drizzle') {
|
|
133
151
|
if (!db) {
|
|
134
152
|
throw new Error(
|
|
@@ -139,6 +157,9 @@ function buildEventBusAsync(
|
|
|
139
157
|
return new DrizzleEventBus(db, options);
|
|
140
158
|
}
|
|
141
159
|
if (options.backend === 'redis') {
|
|
160
|
+
// #6: lazy import — the redis backend ships only with `--backend redis`
|
|
161
|
+
// installs; drizzle/memory consumers never touch the file.
|
|
162
|
+
const RedisEventBus = await loadRedisEventBus();
|
|
142
163
|
return new RedisEventBus(redisUrl);
|
|
143
164
|
}
|
|
144
165
|
return new MemoryEventBus(options);
|
|
@@ -214,9 +235,20 @@ export class EventsModule {
|
|
|
214
235
|
providers: [
|
|
215
236
|
{ provide: EVENTS_MODULE_OPTIONS, useValue: options },
|
|
216
237
|
{ provide: REDIS_URL, useValue: resolvedUrl },
|
|
217
|
-
{
|
|
218
|
-
|
|
219
|
-
|
|
238
|
+
{
|
|
239
|
+
// #6: useFactory + dynamic import so the consumer's tsc never
|
|
240
|
+
// needs to resolve `event-bus.redis-backend.ts` for drizzle/
|
|
241
|
+
// memory installs (the file is filtered out by
|
|
242
|
+
// `backendFileFilter`). Nest awaits async factories + manages
|
|
243
|
+
// lifecycle on the returned instance, so we drop the old bare
|
|
244
|
+
// `RedisEventBus` provider entry.
|
|
245
|
+
provide: EVENT_BUS,
|
|
246
|
+
useFactory: async (url: string): Promise<object> => {
|
|
247
|
+
const RedisEventBus = await loadRedisEventBus();
|
|
248
|
+
return new RedisEventBus(url);
|
|
249
|
+
},
|
|
250
|
+
inject: [REDIS_URL],
|
|
251
|
+
},
|
|
220
252
|
...buildTypedBusProviders(multiTenant),
|
|
221
253
|
],
|
|
222
254
|
exports: [EVENT_BUS, TYPED_EVENT_BUS, EVENTS_MULTI_TENANT],
|
|
@@ -23,6 +23,12 @@ export { EventsModule } from './events.module';
|
|
|
23
23
|
export type { EventsModuleOptions } from './events.module';
|
|
24
24
|
export { MemoryEventBus } from './event-bus.memory-backend';
|
|
25
25
|
export { DrizzleEventBus } from './event-bus.drizzle-backend';
|
|
26
|
-
|
|
26
|
+
// #6 — backend-specific implementation classes are NOT re-exported here.
|
|
27
|
+
// `RedisEventBus` is only vendored when the consumer installs with
|
|
28
|
+
// `--backend redis`; surfacing it from this barrel would force the consumer's
|
|
29
|
+
// tsc to resolve `./event-bus.redis-backend` even on a drizzle/memory install
|
|
30
|
+
// (the file is filtered out → TS2307). Consumers who select redis import the
|
|
31
|
+
// class directly from `./event-bus.redis-backend` if they need it at all —
|
|
32
|
+
// `EventsModule.forRoot({ backend: 'redis' })` lazy-loads it internally.
|
|
27
33
|
export { domainEvents } from './domain-events.schema';
|
|
28
34
|
export type { DomainEventRecord } from './domain-events.schema';
|
|
@@ -46,7 +46,7 @@ export type {
|
|
|
46
46
|
PoolStatusCount,
|
|
47
47
|
JobRunFailure,
|
|
48
48
|
StatusHistogram,
|
|
49
|
-
|
|
49
|
+
IntegrationRunSummary,
|
|
50
50
|
CursorSnapshot,
|
|
51
51
|
} from './observability';
|
|
52
52
|
|
|
@@ -54,9 +54,9 @@ export type {
|
|
|
54
54
|
export {
|
|
55
55
|
ENCRYPTION_KEY,
|
|
56
56
|
OAUTH_STATE_STORE,
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
AUTH_CONNECTION_READER,
|
|
58
|
+
AUTH_CONNECTION_TOKEN_WRITER,
|
|
59
|
+
AUTH_CONNECTION_GRANT_SINK,
|
|
60
60
|
AUTH_USER_CONTEXT,
|
|
61
61
|
STRATEGY_REGISTRY,
|
|
62
62
|
AUTH_OPTIONS,
|
|
@@ -64,7 +64,7 @@ export {
|
|
|
64
64
|
AuthController,
|
|
65
65
|
OAuth2RefreshStrategy,
|
|
66
66
|
withAuthRetry,
|
|
67
|
-
|
|
67
|
+
ConnectionBrokenError,
|
|
68
68
|
SessionExpiredError,
|
|
69
69
|
isSessionExpiredError,
|
|
70
70
|
OAuthStateError,
|
|
@@ -77,19 +77,19 @@ export type {
|
|
|
77
77
|
IAuthStrategy,
|
|
78
78
|
IEncryptionKey,
|
|
79
79
|
IOAuthStateStore,
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
IConnectionReader,
|
|
81
|
+
IConnectionTokenWriter,
|
|
82
|
+
IConnectionGrantSink,
|
|
83
|
+
ConnectionGrantInput,
|
|
84
84
|
IUserContext,
|
|
85
85
|
IProviderStrategy,
|
|
86
86
|
ProviderStrategyRegistry,
|
|
87
87
|
ExchangedTokens,
|
|
88
88
|
AuthCredentials,
|
|
89
89
|
AuthResolveOptions,
|
|
90
|
-
|
|
90
|
+
DecryptedConnection,
|
|
91
91
|
OAuthStateRecord,
|
|
92
|
-
|
|
92
|
+
ConnectionTokenUpdate,
|
|
93
93
|
ParsedRefreshResponse,
|
|
94
94
|
AuthOAuthState,
|
|
95
95
|
} from './auth';
|