@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
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# auth-integrations starter
|
|
2
2
|
|
|
3
|
-
Canonical OAuth2
|
|
3
|
+
Canonical OAuth2 connection storage for apps using the
|
|
4
4
|
`@pattern-stack/codegen` auth subsystem (ADR-031 / `runtime/subsystems/auth/`).
|
|
5
5
|
|
|
6
6
|
The auth subsystem ships the abstract OAuth2 plumbing — `OAuth2RefreshStrategy`,
|
|
7
7
|
`AuthController`, `withAuthRetry`, encryption, error types, and a set of narrow
|
|
8
|
-
hexagonal ports for
|
|
9
|
-
concrete `
|
|
8
|
+
hexagonal ports for connection storage. It deliberately doesn't ship the
|
|
9
|
+
concrete `connections` table or the adapters that satisfy those ports, because
|
|
10
10
|
every consumer would need to fork them. **This starter is what plugs that gap.**
|
|
11
11
|
|
|
12
12
|
## What ships here
|
|
@@ -15,26 +15,26 @@ every consumer would need to fork them. **This starter is what plugs that gap.**
|
|
|
15
15
|
examples/auth-integrations/
|
|
16
16
|
definitions/
|
|
17
17
|
entities/
|
|
18
|
-
|
|
18
|
+
connection.yaml # canonical entity (run cdp entity new)
|
|
19
19
|
|
|
20
|
-
runtime/
|
|
21
|
-
#
|
|
22
|
-
# <backend_src>/modules/
|
|
20
|
+
runtime/connections/ # vendored next to the codegen-emitted
|
|
21
|
+
# connection entity module — i.e.
|
|
22
|
+
# <backend_src>/modules/connections/
|
|
23
23
|
# (override via paths.modules_dir).
|
|
24
24
|
adapters/
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
connection-reader.adapter.ts # IConnectionReader impl
|
|
26
|
+
connection-token-writer.adapter.ts # IConnectionTokenWriter impl
|
|
27
|
+
connection-grant-sink.adapter.ts # IConnectionGrantSink impl
|
|
28
28
|
facade/
|
|
29
|
-
|
|
29
|
+
connections.service.ts # consumer-facing facade
|
|
30
30
|
oauth/
|
|
31
31
|
use-cases/
|
|
32
32
|
create-or-update-from-oauth-grant.use-case.ts
|
|
33
|
-
mark-
|
|
34
|
-
disconnect-
|
|
35
|
-
list-user-
|
|
36
|
-
|
|
37
|
-
#
|
|
33
|
+
mark-connection-requires-reauth.use-case.ts
|
|
34
|
+
disconnect-connection.use-case.ts
|
|
35
|
+
list-user-connections.use-case.ts
|
|
36
|
+
connections-auth.module.ts # @Global() — binds the three
|
|
37
|
+
# AUTH_CONNECTION_* tokens.
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
## How to install
|
|
@@ -42,22 +42,22 @@ examples/auth-integrations/
|
|
|
42
42
|
```bash
|
|
43
43
|
cdp subsystem install auth # one-time: vendor the auth subsystem
|
|
44
44
|
cdp subsystem install auth-integrations
|
|
45
|
-
cdp entity new
|
|
45
|
+
cdp entity new connection # emits the entity module next to the vendor
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
The `auth-integrations` install:
|
|
49
|
-
- copies `definitions/entities/
|
|
49
|
+
- copies `definitions/entities/connection.yaml` into your configured
|
|
50
50
|
`paths.entities` (or legacy `paths.entities_dir`) directory.
|
|
51
|
-
- vendors `runtime/
|
|
52
|
-
`<backend_src>/modules/
|
|
51
|
+
- vendors `runtime/connections/**` under
|
|
52
|
+
`<backend_src>/modules/connections/` (override via `paths.modules_dir`),
|
|
53
53
|
rewriting bare `@pattern-stack/codegen/runtime/subsystems/auth` imports to
|
|
54
54
|
relative paths that resolve against the vendored auth subsystem at
|
|
55
55
|
`<paths.subsystems>/auth`.
|
|
56
56
|
- appends a TODO to `<backend_src>/app.module.ts` reminding you to register
|
|
57
|
-
`
|
|
57
|
+
`ConnectionsAuthModule` AFTER `AuthModule.forRoot(...)`.
|
|
58
58
|
|
|
59
|
-
`
|
|
60
|
-
`AuthModule`'s injector) resolves the `
|
|
59
|
+
`ConnectionsAuthModule` is `@Global()` because `AuthController` (inside
|
|
60
|
+
`AuthModule`'s injector) resolves the `AUTH_CONNECTION_*` providers exposed
|
|
61
61
|
by it.
|
|
62
62
|
|
|
63
63
|
## Two interfaces, two purposes
|
|
@@ -66,15 +66,15 @@ The auth subsystem requires three narrow ports:
|
|
|
66
66
|
|
|
67
67
|
| Token | Port | Used by |
|
|
68
68
|
| -------------------------------- | -------------------------- | ------------------------------ |
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
69
|
+
| `AUTH_CONNECTION_READER` | `IConnectionReader` | `OAuth2RefreshStrategy.resolve` |
|
|
70
|
+
| `AUTH_CONNECTION_TOKEN_WRITER` | `IConnectionTokenWriter` | `OAuth2RefreshStrategy.resolve` |
|
|
71
|
+
| `AUTH_CONNECTION_GRANT_SINK` | `IConnectionGrantSink` | `AuthController.callback` |
|
|
72
72
|
|
|
73
73
|
These stay narrow on purpose — a non-codegen consumer with a hand-rolled
|
|
74
|
-
|
|
74
|
+
connections table can satisfy them without pulling in the rest of this
|
|
75
75
|
starter.
|
|
76
76
|
|
|
77
|
-
The starter's `
|
|
77
|
+
The starter's `ConnectionsService` is **a separate, richer interface** that
|
|
78
78
|
your app code (controllers, handlers, frontend-facing routes) talks to
|
|
79
79
|
directly. It composes the use cases, applies encryption, and exposes
|
|
80
80
|
consumer-shaped methods like `findByUserAndProvider`, `listByUser`,
|
|
@@ -104,14 +104,14 @@ supports any provider slug your app cares about.
|
|
|
104
104
|
|
|
105
105
|
After install:
|
|
106
106
|
|
|
107
|
-
- [ ] `
|
|
107
|
+
- [ ] `ConnectionsService.findByUserAndProvider(userId, 'hubspot-crm')`
|
|
108
108
|
returns `null` for missing rows, decrypted-creds for present rows.
|
|
109
109
|
- [ ] `createOrUpdateFromOAuthGrant({ userId, provider, accessToken, ... })`
|
|
110
110
|
upserts on `(user_id, provider)`. Re-running with a different access
|
|
111
111
|
token replaces the prior token; omitting `refreshToken` keeps the
|
|
112
112
|
existing ciphertext.
|
|
113
|
-
- [ ] `markRequiresReauth(
|
|
114
|
-
- [ ] `disconnect(
|
|
113
|
+
- [ ] `markRequiresReauth(connectionId)` flips status to `requires_reauth`.
|
|
114
|
+
- [ ] `disconnect(connectionId)` flips status to `revoked` and clears the
|
|
115
115
|
stored ciphertexts.
|
|
116
116
|
- [ ] `OAuth2RefreshStrategy.resolve()` end-to-end refresh works against a
|
|
117
117
|
provider-strategy you've registered (out of scope — provider strategies
|
|
@@ -121,5 +121,5 @@ After install:
|
|
|
121
121
|
|
|
122
122
|
- ADR-031 — auth subsystem (Accepted)
|
|
123
123
|
- #285 — this starter (tracking issue)
|
|
124
|
-
- #286 — `AuthController` +
|
|
124
|
+
- #286 — `AuthController` + connection-store ports (merged in PR #289)
|
|
125
125
|
- #287 — `cdp subsystem install auth-integrations` template (follow-up)
|
package/examples/auth-integrations/definitions/entities/{integration.yaml → connection.yaml}
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# auth-integrations:
|
|
2
|
-
# Canonical OAuth2
|
|
3
|
-
# Vendored alongside the auth subsystem to satisfy
|
|
4
|
-
#
|
|
1
|
+
# auth-integrations: Connection
|
|
2
|
+
# Canonical OAuth2 connection row — one per (user_id, provider) pair.
|
|
3
|
+
# Vendored alongside the auth subsystem to satisfy IConnectionReader /
|
|
4
|
+
# IConnectionTokenWriter / IConnectionGrantSink ports out of the box.
|
|
5
5
|
#
|
|
6
6
|
# `provider` stays a string (not enum) intentionally so consumers can add
|
|
7
7
|
# provider keys ('hubspot-crm', 'salesforce-crm', 'google', …) without
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
# STRATEGY_REGISTRY.
|
|
10
10
|
|
|
11
11
|
entity:
|
|
12
|
-
name:
|
|
13
|
-
plural:
|
|
14
|
-
table:
|
|
12
|
+
name: connection
|
|
13
|
+
plural: connections
|
|
14
|
+
table: connections
|
|
15
15
|
pattern: Base
|
|
16
16
|
folder_structure: nested
|
|
17
17
|
|
|
@@ -35,7 +35,7 @@ fields:
|
|
|
35
35
|
nullable: true
|
|
36
36
|
max_length: 255
|
|
37
37
|
|
|
38
|
-
# Ciphertexts — encryption is applied inside the
|
|
38
|
+
# Ciphertexts — encryption is applied inside the ConnectionsService
|
|
39
39
|
# facade + adapter layer (via IEncryptionKey). The DB row only ever
|
|
40
40
|
# holds the encrypted form; decryption happens on read in the reader
|
|
41
41
|
# adapter.
|
|
@@ -88,11 +88,11 @@ queries:
|
|
|
88
88
|
- by: [user_id, provider]
|
|
89
89
|
unique: true
|
|
90
90
|
|
|
91
|
-
# Settings page: list a user's connected
|
|
91
|
+
# Settings page: list a user's connected connections, newest first.
|
|
92
92
|
- by: [user_id]
|
|
93
93
|
order: created_at desc
|
|
94
94
|
|
|
95
|
-
# Operational query: find broken
|
|
95
|
+
# Operational query: find broken connections needing reauth across
|
|
96
96
|
# all users (admin / monitoring).
|
|
97
97
|
- by: [provider, status]
|
|
98
98
|
order: updated_at desc
|
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
import { Injectable } from '@nestjs/common';
|
|
2
2
|
import type {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
IConnectionGrantSink,
|
|
4
|
+
ConnectionGrantInput,
|
|
5
5
|
} from '@pattern-stack/codegen/runtime/subsystems/auth';
|
|
6
6
|
import { CreateOrUpdateFromOAuthGrantUseCase } from '../oauth/use-cases/create-or-update-from-oauth-grant.use-case';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* `
|
|
9
|
+
* `IConnectionGrantSink` adapter — pass-through to
|
|
10
10
|
* `CreateOrUpdateFromOAuthGrantUseCase`. The auth subsystem's
|
|
11
11
|
* `AuthController.callback` invokes this after
|
|
12
12
|
* `IProviderStrategy.exchangeCodeForTokens`.
|
|
13
13
|
*
|
|
14
14
|
* This adapter injects the use case directly (not the
|
|
15
|
-
* `
|
|
15
|
+
* `ConnectionsService` facade) for symmetry with the reader and
|
|
16
16
|
* token-writer adapters, which also bypass the facade and talk to
|
|
17
17
|
* the codegen-emitted layer directly. The port and the use case share
|
|
18
|
-
* the exact same `
|
|
18
|
+
* the exact same `ConnectionGrantInput` shape, so no field mapping is
|
|
19
19
|
* needed — encryption, upsert resolution, and status handling all live
|
|
20
20
|
* inside the use case.
|
|
21
21
|
*/
|
|
22
22
|
@Injectable()
|
|
23
|
-
export class
|
|
23
|
+
export class ConnectionGrantSinkAdapter implements IConnectionGrantSink {
|
|
24
24
|
constructor(private readonly useCase: CreateOrUpdateFromOAuthGrantUseCase) {}
|
|
25
25
|
|
|
26
|
-
async createOrUpdateFromOAuthGrant(input:
|
|
26
|
+
async createOrUpdateFromOAuthGrant(input: ConnectionGrantInput): Promise<void> {
|
|
27
27
|
await this.useCase.execute(input);
|
|
28
28
|
}
|
|
29
29
|
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { Inject, Injectable } from '@nestjs/common';
|
|
2
2
|
import {
|
|
3
3
|
ENCRYPTION_KEY,
|
|
4
|
-
type
|
|
4
|
+
type DecryptedConnection,
|
|
5
5
|
type IEncryptionKey,
|
|
6
|
-
type
|
|
6
|
+
type IConnectionReader,
|
|
7
7
|
} from '@pattern-stack/codegen/runtime/subsystems/auth';
|
|
8
|
-
import {
|
|
8
|
+
import { ConnectionService } from '../connection.service';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* `
|
|
11
|
+
* `IConnectionReader` adapter — fetches the connection row by id and
|
|
12
12
|
* decrypts its ciphertexts to satisfy the auth subsystem's read port.
|
|
13
13
|
*
|
|
14
14
|
* Stays narrow on purpose: this adapter exists purely to feed
|
|
15
15
|
* `OAuth2RefreshStrategy.resolve()`. Anything wider belongs in
|
|
16
|
-
* `
|
|
16
|
+
* `ConnectionsService` (the consumer-facing facade).
|
|
17
17
|
*
|
|
18
|
-
* Note: this duplicates the decryption logic in `
|
|
18
|
+
* Note: this duplicates the decryption logic in `ConnectionsService`
|
|
19
19
|
* by design — the adapter must not depend on the facade because the
|
|
20
20
|
* facade depends on the use cases which depend on the adapter (well,
|
|
21
21
|
* not directly, but via the same module). Keeping the read path
|
|
@@ -23,14 +23,14 @@ import { IntegrationService } from '../integration.service';
|
|
|
23
23
|
* subsystem's "narrow port" contract.
|
|
24
24
|
*/
|
|
25
25
|
@Injectable()
|
|
26
|
-
export class
|
|
26
|
+
export class ConnectionReaderAdapter implements IConnectionReader {
|
|
27
27
|
constructor(
|
|
28
|
-
private readonly
|
|
28
|
+
private readonly connections: ConnectionService,
|
|
29
29
|
@Inject(ENCRYPTION_KEY) private readonly encryption: IEncryptionKey,
|
|
30
30
|
) {}
|
|
31
31
|
|
|
32
|
-
async findByIdDecrypted(
|
|
33
|
-
const row = await this.
|
|
32
|
+
async findByIdDecrypted(connectionId: string): Promise<DecryptedConnection | null> {
|
|
33
|
+
const row = await this.connections.findById(connectionId);
|
|
34
34
|
if (!row) return null;
|
|
35
35
|
|
|
36
36
|
const accessToken = row.accessTokenEncrypted
|
|
@@ -2,33 +2,33 @@ import { Inject, Injectable } from '@nestjs/common';
|
|
|
2
2
|
import {
|
|
3
3
|
ENCRYPTION_KEY,
|
|
4
4
|
type IEncryptionKey,
|
|
5
|
-
type
|
|
6
|
-
type
|
|
5
|
+
type IConnectionTokenWriter,
|
|
6
|
+
type ConnectionTokenUpdate,
|
|
7
7
|
} from '@pattern-stack/codegen/runtime/subsystems/auth';
|
|
8
|
-
import {
|
|
8
|
+
import { ConnectionService } from '../connection.service';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* `
|
|
11
|
+
* `IConnectionTokenWriter` adapter — encrypts the new access token
|
|
12
12
|
* (and rotated refresh token, if present) and persists them onto the
|
|
13
|
-
*
|
|
13
|
+
* connection row.
|
|
14
14
|
*
|
|
15
|
-
* `
|
|
15
|
+
* `ConnectionTokenUpdate.refreshToken` semantics:
|
|
16
16
|
* - `undefined` → provider didn't rotate, leave existing ciphertext
|
|
17
17
|
* - `string` → provider rotated, re-encrypt + persist
|
|
18
18
|
*
|
|
19
19
|
* On a successful refresh we also flip status back to 'active' — if
|
|
20
20
|
* the row was previously `requires_reauth` and the user re-connected,
|
|
21
|
-
* a successful refresh is the signal that the
|
|
21
|
+
* a successful refresh is the signal that the connection is healthy
|
|
22
22
|
* again.
|
|
23
23
|
*/
|
|
24
24
|
@Injectable()
|
|
25
|
-
export class
|
|
25
|
+
export class ConnectionTokenWriterAdapter implements IConnectionTokenWriter {
|
|
26
26
|
constructor(
|
|
27
|
-
private readonly
|
|
27
|
+
private readonly connections: ConnectionService,
|
|
28
28
|
@Inject(ENCRYPTION_KEY) private readonly encryption: IEncryptionKey,
|
|
29
29
|
) {}
|
|
30
30
|
|
|
31
|
-
async persistRefresh(update:
|
|
31
|
+
async persistRefresh(update: ConnectionTokenUpdate): Promise<void> {
|
|
32
32
|
const accessTokenEncrypted = await this.encryption.encrypt(update.accessToken);
|
|
33
33
|
const patch: Record<string, unknown> = {
|
|
34
34
|
accessTokenEncrypted,
|
|
@@ -38,6 +38,6 @@ export class IntegrationTokenWriterAdapter implements IIntegrationTokenWriter {
|
|
|
38
38
|
if (update.refreshToken !== undefined) {
|
|
39
39
|
patch.refreshTokenEncrypted = await this.encryption.encrypt(update.refreshToken);
|
|
40
40
|
}
|
|
41
|
-
await this.
|
|
41
|
+
await this.connections.update(update.connectionId, patch);
|
|
42
42
|
}
|
|
43
43
|
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Global, Module } from '@nestjs/common';
|
|
2
|
+
import {
|
|
3
|
+
AUTH_CONNECTION_GRANT_SINK,
|
|
4
|
+
AUTH_CONNECTION_READER,
|
|
5
|
+
AUTH_CONNECTION_TOKEN_WRITER,
|
|
6
|
+
} from '@pattern-stack/codegen/runtime/subsystems/auth';
|
|
7
|
+
import { ConnectionsModule } from './connections.module';
|
|
8
|
+
import { ConnectionGrantSinkAdapter } from './adapters/connection-grant-sink.adapter';
|
|
9
|
+
import { ConnectionReaderAdapter } from './adapters/connection-reader.adapter';
|
|
10
|
+
import { ConnectionTokenWriterAdapter } from './adapters/connection-token-writer.adapter';
|
|
11
|
+
import { ConnectionsService } from './facade/connections.service';
|
|
12
|
+
import { CreateOrUpdateFromOAuthGrantUseCase } from './oauth/use-cases/create-or-update-from-oauth-grant.use-case';
|
|
13
|
+
import { DisconnectConnectionUseCase } from './oauth/use-cases/disconnect-connection.use-case';
|
|
14
|
+
import { ListUserConnectionsUseCase } from './oauth/use-cases/list-user-connections.use-case';
|
|
15
|
+
import { MarkConnectionRequiresReauthUseCase } from './oauth/use-cases/mark-connection-requires-reauth.use-case';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* `ConnectionsAuthModule` — wires the consumer-side adapters that
|
|
19
|
+
* satisfy the auth subsystem's three connection-store ports plus the
|
|
20
|
+
* `ConnectionsService` facade and its use cases.
|
|
21
|
+
*
|
|
22
|
+
* Imports `ConnectionsModule` (the codegen-emitted entity module) to
|
|
23
|
+
* pull in `ConnectionService` + its repository.
|
|
24
|
+
*
|
|
25
|
+
* Depends on a registered `ENCRYPTION_KEY` provider — that comes from
|
|
26
|
+
* `AuthModule.forRoot({ encryptionKey: ... })`. Make sure
|
|
27
|
+
* `AuthModule.forRoot(...)` is imported in your app's root module BEFORE
|
|
28
|
+
* `ConnectionsAuthModule` (or globally — `AuthModule` is `global: true`
|
|
29
|
+
* by convention).
|
|
30
|
+
*
|
|
31
|
+
* Token bindings (per #285 / #286):
|
|
32
|
+
* - AUTH_CONNECTION_READER → ConnectionReaderAdapter
|
|
33
|
+
* - AUTH_CONNECTION_TOKEN_WRITER → ConnectionTokenWriterAdapter
|
|
34
|
+
* - AUTH_CONNECTION_GRANT_SINK → ConnectionGrantSinkAdapter
|
|
35
|
+
*
|
|
36
|
+
* `@Global()` is required: `AuthController` lives inside `AuthModule`'s
|
|
37
|
+
* own injector and resolves the `AUTH_CONNECTION_*` providers exposed
|
|
38
|
+
* here. Without `@Global()`, the controller's injector cannot see these
|
|
39
|
+
* tokens and Nest fails to boot. Same pattern as the `auth-bindings`
|
|
40
|
+
* module shipped in #93.
|
|
41
|
+
*/
|
|
42
|
+
@Global()
|
|
43
|
+
@Module({
|
|
44
|
+
imports: [ConnectionsModule],
|
|
45
|
+
providers: [
|
|
46
|
+
// Use cases (consumed by the facade)
|
|
47
|
+
CreateOrUpdateFromOAuthGrantUseCase,
|
|
48
|
+
MarkConnectionRequiresReauthUseCase,
|
|
49
|
+
DisconnectConnectionUseCase,
|
|
50
|
+
ListUserConnectionsUseCase,
|
|
51
|
+
|
|
52
|
+
// Facade (consumer-facing API; controllers/handlers inject this)
|
|
53
|
+
ConnectionsService,
|
|
54
|
+
|
|
55
|
+
// Subsystem port adapters (concrete classes — also exposed under
|
|
56
|
+
// their token aliases for `@Inject(...)` consumers in the auth
|
|
57
|
+
// subsystem).
|
|
58
|
+
ConnectionReaderAdapter,
|
|
59
|
+
ConnectionTokenWriterAdapter,
|
|
60
|
+
ConnectionGrantSinkAdapter,
|
|
61
|
+
{
|
|
62
|
+
provide: AUTH_CONNECTION_READER,
|
|
63
|
+
useExisting: ConnectionReaderAdapter,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
provide: AUTH_CONNECTION_TOKEN_WRITER,
|
|
67
|
+
useExisting: ConnectionTokenWriterAdapter,
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
provide: AUTH_CONNECTION_GRANT_SINK,
|
|
71
|
+
useExisting: ConnectionGrantSinkAdapter,
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
exports: [
|
|
75
|
+
ConnectionsService,
|
|
76
|
+
AUTH_CONNECTION_READER,
|
|
77
|
+
AUTH_CONNECTION_TOKEN_WRITER,
|
|
78
|
+
AUTH_CONNECTION_GRANT_SINK,
|
|
79
|
+
],
|
|
80
|
+
})
|
|
81
|
+
export class ConnectionsAuthModule {}
|
|
@@ -2,22 +2,22 @@ import { Inject, Injectable } from '@nestjs/common';
|
|
|
2
2
|
import {
|
|
3
3
|
ENCRYPTION_KEY,
|
|
4
4
|
type IEncryptionKey,
|
|
5
|
-
type
|
|
5
|
+
type ConnectionGrantInput,
|
|
6
6
|
} from '@pattern-stack/codegen/runtime/subsystems/auth';
|
|
7
|
-
import {
|
|
8
|
-
import type {
|
|
7
|
+
import { ConnectionService } from '../connection.service';
|
|
8
|
+
import type { Connection } from '../connection.entity';
|
|
9
9
|
import { CreateOrUpdateFromOAuthGrantUseCase } from '../oauth/use-cases/create-or-update-from-oauth-grant.use-case';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
10
|
+
import { DisconnectConnectionUseCase } from '../oauth/use-cases/disconnect-connection.use-case';
|
|
11
|
+
import { ListUserConnectionsUseCase } from '../oauth/use-cases/list-user-connections.use-case';
|
|
12
|
+
import { MarkConnectionRequiresReauthUseCase } from '../oauth/use-cases/mark-connection-requires-reauth.use-case';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* Decrypted
|
|
15
|
+
* Decrypted connection shape — used by consumer code that needs to
|
|
16
16
|
* make outbound API calls (frontend never sees this; it's server-side
|
|
17
|
-
* only). Mirrors the auth subsystem's `
|
|
17
|
+
* only). Mirrors the auth subsystem's `DecryptedConnection` but is
|
|
18
18
|
* the consumer-facing return type for `findByUserAndProvider`.
|
|
19
19
|
*/
|
|
20
|
-
export interface
|
|
20
|
+
export interface DecryptedConnectionRow {
|
|
21
21
|
id: string;
|
|
22
22
|
userId: string;
|
|
23
23
|
provider: string;
|
|
@@ -34,11 +34,11 @@ export interface DecryptedIntegrationRow {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
|
-
*
|
|
38
|
-
* `
|
|
37
|
+
* ConnectionsService — consumer-facing facade over the codegen-emitted
|
|
38
|
+
* `ConnectionService` plus the auth subsystem's `IEncryptionKey`.
|
|
39
39
|
*
|
|
40
|
-
* Wider than the auth subsystem ports (`
|
|
41
|
-
* `
|
|
40
|
+
* Wider than the auth subsystem ports (`IConnectionReader`,
|
|
41
|
+
* `IConnectionTokenWriter`, `IConnectionGrantSink`) on purpose: the
|
|
42
42
|
* narrow ports are the subsystem's hexagonal seam (so non-codegen
|
|
43
43
|
* consumers can implement them); the facade is what app code talks to
|
|
44
44
|
* directly (controllers, handlers, frontend-facing use cases).
|
|
@@ -52,18 +52,18 @@ export interface DecryptedIntegrationRow {
|
|
|
52
52
|
* intentionally strips them to a safe metadata shape.
|
|
53
53
|
*/
|
|
54
54
|
@Injectable()
|
|
55
|
-
export class
|
|
55
|
+
export class ConnectionsService {
|
|
56
56
|
constructor(
|
|
57
|
-
private readonly
|
|
57
|
+
private readonly connections: ConnectionService,
|
|
58
58
|
@Inject(ENCRYPTION_KEY) private readonly encryption: IEncryptionKey,
|
|
59
59
|
private readonly createOrUpdateUseCase: CreateOrUpdateFromOAuthGrantUseCase,
|
|
60
|
-
private readonly markReauthUseCase:
|
|
61
|
-
private readonly disconnectUseCase:
|
|
62
|
-
private readonly listUseCase:
|
|
60
|
+
private readonly markReauthUseCase: MarkConnectionRequiresReauthUseCase,
|
|
61
|
+
private readonly disconnectUseCase: DisconnectConnectionUseCase,
|
|
62
|
+
private readonly listUseCase: ListUserConnectionsUseCase,
|
|
63
63
|
) {}
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
|
-
* Loads the
|
|
66
|
+
* Loads the connection for `(userId, provider)` and returns it with
|
|
67
67
|
* decrypted tokens, or `null` if no row exists. Returns the row even
|
|
68
68
|
* if `status !== 'active'` so callers can distinguish "never
|
|
69
69
|
* connected" from "connected but broken" — gate on `status` yourself.
|
|
@@ -71,17 +71,17 @@ export class IntegrationsService {
|
|
|
71
71
|
async findByUserAndProvider(
|
|
72
72
|
userId: string,
|
|
73
73
|
provider: string,
|
|
74
|
-
): Promise<
|
|
75
|
-
const row = await this.
|
|
74
|
+
): Promise<DecryptedConnectionRow | null> {
|
|
75
|
+
const row = await this.connections.findByUserIdAndProvider(userId, provider);
|
|
76
76
|
if (!row) return null;
|
|
77
77
|
return this.decrypt(row);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
|
-
* Lists a user's
|
|
81
|
+
* Lists a user's connections newest-first, with ciphertexts stripped.
|
|
82
82
|
* Safe to return to a frontend.
|
|
83
83
|
*/
|
|
84
|
-
async listByUser(userId: string): Promise<Array<Omit<
|
|
84
|
+
async listByUser(userId: string): Promise<Array<Omit<Connection, 'accessTokenEncrypted' | 'refreshTokenEncrypted'>>> {
|
|
85
85
|
const rows = await this.listUseCase.execute(userId);
|
|
86
86
|
return rows.map((row) => {
|
|
87
87
|
const { accessTokenEncrypted: _accessTokenEncrypted, refreshTokenEncrypted: _refreshTokenEncrypted, ...safe } = row;
|
|
@@ -92,39 +92,39 @@ export class IntegrationsService {
|
|
|
92
92
|
/**
|
|
93
93
|
* Upserts a freshly-minted OAuth2 grant from the authorize-code
|
|
94
94
|
* callback. Pass-through to `CreateOrUpdateFromOAuthGrantUseCase` —
|
|
95
|
-
* the input shape matches the auth subsystem's `
|
|
96
|
-
* exactly so `
|
|
95
|
+
* the input shape matches the auth subsystem's `ConnectionGrantInput`
|
|
96
|
+
* exactly so `ConnectionGrantSinkAdapter` can forward without
|
|
97
97
|
* mapping.
|
|
98
98
|
*/
|
|
99
|
-
async createOrUpdateFromOAuthGrant(input:
|
|
99
|
+
async createOrUpdateFromOAuthGrant(input: ConnectionGrantInput): Promise<void> {
|
|
100
100
|
await this.createOrUpdateUseCase.execute(input);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
/**
|
|
104
104
|
* Flips status to `requires_reauth`. Called from `withAuthRetry`'s
|
|
105
|
-
* broken-
|
|
105
|
+
* broken-connection handler.
|
|
106
106
|
*/
|
|
107
|
-
async markRequiresReauth(
|
|
108
|
-
await this.markReauthUseCase.execute(
|
|
107
|
+
async markRequiresReauth(connectionId: string): Promise<void> {
|
|
108
|
+
await this.markReauthUseCase.execute(connectionId);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
/**
|
|
112
112
|
* User-initiated disconnect. Status → 'revoked', tokens cleared.
|
|
113
113
|
*/
|
|
114
|
-
async disconnect(
|
|
115
|
-
await this.disconnectUseCase.execute(
|
|
114
|
+
async disconnect(connectionId: string): Promise<void> {
|
|
115
|
+
await this.disconnectUseCase.execute(connectionId);
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
/**
|
|
119
|
-
* Decrypts ciphertexts on a raw `
|
|
120
|
-
* by `findByUserAndProvider` and by `
|
|
119
|
+
* Decrypts ciphertexts on a raw `Connection` row. Used internally
|
|
120
|
+
* by `findByUserAndProvider` and by `ConnectionReaderAdapter`.
|
|
121
121
|
*
|
|
122
122
|
* Empty access tokens (e.g. revoked rows where the ciphertext was
|
|
123
123
|
* cleared) decrypt to the empty string — matches
|
|
124
|
-
* `
|
|
124
|
+
* `DecryptedConnection.accessToken`'s "empty if never granted"
|
|
125
125
|
* contract.
|
|
126
126
|
*/
|
|
127
|
-
private async decrypt(row:
|
|
127
|
+
private async decrypt(row: Connection): Promise<DecryptedConnectionRow> {
|
|
128
128
|
const accessToken = row.accessTokenEncrypted
|
|
129
129
|
? await this.encryption.decrypt(row.accessTokenEncrypted)
|
|
130
130
|
: '';
|
|
@@ -2,10 +2,10 @@ import { Inject, Injectable } from '@nestjs/common';
|
|
|
2
2
|
import {
|
|
3
3
|
ENCRYPTION_KEY,
|
|
4
4
|
type IEncryptionKey,
|
|
5
|
-
type
|
|
5
|
+
type ConnectionGrantInput,
|
|
6
6
|
} from '@pattern-stack/codegen/runtime/subsystems/auth';
|
|
7
|
-
import {
|
|
8
|
-
import type {
|
|
7
|
+
import { ConnectionService } from '../../connection.service';
|
|
8
|
+
import type { Connection } from '../../connection.entity';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Persists an OAuth2 grant from the authorize-code callback (initial
|
|
@@ -14,8 +14,8 @@ import type { Integration } from '../../integration.entity';
|
|
|
14
14
|
* - existing row → re-encrypt + persist tokens, status → 'active'
|
|
15
15
|
* - missing row → insert a new row in 'active' status
|
|
16
16
|
*
|
|
17
|
-
* The input shape is exactly `
|
|
18
|
-
* subsystem so `
|
|
17
|
+
* The input shape is exactly `ConnectionGrantInput` from the auth
|
|
18
|
+
* subsystem so `ConnectionGrantSinkAdapter` can be a pass-through —
|
|
19
19
|
* the port and use case share the same boundary type. Encryption is
|
|
20
20
|
* applied here (inside the use case) before ciphertexts hit the row.
|
|
21
21
|
*
|
|
@@ -27,18 +27,18 @@ import type { Integration } from '../../integration.entity';
|
|
|
27
27
|
@Injectable()
|
|
28
28
|
export class CreateOrUpdateFromOAuthGrantUseCase {
|
|
29
29
|
constructor(
|
|
30
|
-
private readonly
|
|
30
|
+
private readonly connections: ConnectionService,
|
|
31
31
|
@Inject(ENCRYPTION_KEY) private readonly encryption: IEncryptionKey,
|
|
32
32
|
) {}
|
|
33
33
|
|
|
34
|
-
async execute(input:
|
|
34
|
+
async execute(input: ConnectionGrantInput): Promise<Connection> {
|
|
35
35
|
const accessTokenEncrypted = await this.encryption.encrypt(input.accessToken);
|
|
36
36
|
const refreshTokenEncrypted =
|
|
37
37
|
input.refreshToken !== undefined
|
|
38
38
|
? await this.encryption.encrypt(input.refreshToken)
|
|
39
39
|
: undefined;
|
|
40
40
|
|
|
41
|
-
const existing = await this.
|
|
41
|
+
const existing = await this.connections.findByUserIdAndProvider(
|
|
42
42
|
input.userId,
|
|
43
43
|
input.provider,
|
|
44
44
|
);
|
|
@@ -57,17 +57,17 @@ export class CreateOrUpdateFromOAuthGrantUseCase {
|
|
|
57
57
|
if (existing) {
|
|
58
58
|
// Preserve the existing refresh-token ciphertext when the grant
|
|
59
59
|
// didn't include a new refresh token (common on re-grants).
|
|
60
|
-
const patch: Partial<
|
|
60
|
+
const patch: Partial<Connection> = {
|
|
61
61
|
...baseRow,
|
|
62
62
|
refreshTokenEncrypted:
|
|
63
63
|
refreshTokenEncrypted !== undefined
|
|
64
64
|
? refreshTokenEncrypted
|
|
65
65
|
: existing.refreshTokenEncrypted,
|
|
66
66
|
};
|
|
67
|
-
return this.
|
|
67
|
+
return this.connections.update(existing.id, patch);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
return this.
|
|
70
|
+
return this.connections.create({
|
|
71
71
|
...baseRow,
|
|
72
72
|
refreshTokenEncrypted: refreshTokenEncrypted ?? null,
|
|
73
73
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Injectable } from '@nestjs/common';
|
|
2
|
-
import {
|
|
3
|
-
import type {
|
|
2
|
+
import { ConnectionService } from '../../connection.service';
|
|
3
|
+
import type { Connection } from '../../connection.entity';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* User-initiated disconnect. Flips status to `revoked` and clears the
|
|
@@ -15,11 +15,11 @@ import type { Integration } from '../../integration.entity';
|
|
|
15
15
|
* starter).
|
|
16
16
|
*/
|
|
17
17
|
@Injectable()
|
|
18
|
-
export class
|
|
19
|
-
constructor(private readonly
|
|
18
|
+
export class DisconnectConnectionUseCase {
|
|
19
|
+
constructor(private readonly connections: ConnectionService) {}
|
|
20
20
|
|
|
21
|
-
async execute(
|
|
22
|
-
return this.
|
|
21
|
+
async execute(connectionId: string): Promise<Connection> {
|
|
22
|
+
return this.connections.update(connectionId, {
|
|
23
23
|
status: 'revoked',
|
|
24
24
|
accessTokenEncrypted: null,
|
|
25
25
|
refreshTokenEncrypted: null,
|