@pattern-stack/codegen 0.10.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +62 -0
- package/README.md +5 -5
- package/consumer-skills/codegen/SKILL.md +2 -2
- package/consumer-skills/{sync → integration}/SKILL.md +29 -29
- package/consumer-skills/{sync → integration}/audit-and-detection.md +22 -22
- package/consumer-skills/{sync → integration}/change-sources-and-sinks.md +60 -60
- package/consumer-skills/subsystems/SKILL.md +8 -8
- package/consumer-skills/subsystems/wiring-and-order.md +7 -7
- package/dist/runtime/base-classes/index.d.ts +4 -4
- package/dist/runtime/base-classes/index.js +35 -35
- package/dist/runtime/base-classes/index.js.map +1 -1
- package/dist/runtime/base-classes/{synced-entity-repository.d.ts → integrated-entity-repository.d.ts} +15 -15
- package/dist/runtime/base-classes/{synced-entity-repository.js → integrated-entity-repository.js} +21 -21
- package/dist/runtime/base-classes/integrated-entity-repository.js.map +1 -0
- package/dist/runtime/base-classes/{synced-entity-service.d.ts → integrated-entity-service.d.ts} +6 -6
- package/dist/runtime/base-classes/{synced-entity-service.js → integrated-entity-service.js} +4 -4
- package/dist/runtime/base-classes/integrated-entity-service.js.map +1 -0
- package/dist/runtime/base-classes/{sync-upsert-config.d.ts → integration-upsert-config.d.ts} +13 -13
- package/dist/runtime/base-classes/integration-upsert-config.js +1 -0
- package/dist/runtime/base-classes/{junction-sync-repository.d.ts → junction-integration-repository.d.ts} +11 -11
- package/dist/runtime/base-classes/{junction-sync-repository.js → junction-integration-repository.js} +15 -15
- package/dist/runtime/base-classes/junction-integration-repository.js.map +1 -0
- package/dist/runtime/subsystems/auth/auth-oauth-state.schema.js.map +1 -1
- package/dist/runtime/subsystems/auth/auth.module.d.ts +4 -4
- package/dist/runtime/subsystems/auth/auth.module.js +3 -3
- package/dist/runtime/subsystems/auth/auth.module.js.map +1 -1
- package/dist/runtime/subsystems/auth/auth.tokens.d.ts +8 -8
- package/dist/runtime/subsystems/auth/auth.tokens.js +6 -6
- package/dist/runtime/subsystems/auth/auth.tokens.js.map +1 -1
- package/dist/runtime/subsystems/auth/backends/state-store.drizzle-backend.js.map +1 -1
- package/dist/runtime/subsystems/auth/controllers/auth.controller.d.ts +2 -2
- package/dist/runtime/subsystems/auth/controllers/auth.controller.js +3 -3
- package/dist/runtime/subsystems/auth/controllers/auth.controller.js.map +1 -1
- package/dist/runtime/subsystems/auth/index.d.ts +3 -3
- package/dist/runtime/subsystems/auth/index.js +40 -40
- package/dist/runtime/subsystems/auth/index.js.map +1 -1
- package/dist/runtime/subsystems/auth/middleware/requester-context.js.map +1 -1
- package/dist/runtime/subsystems/auth/protocols/auth-strategy.d.ts +3 -3
- package/dist/runtime/subsystems/auth/protocols/{integration-store.d.ts → connection-store.d.ts} +20 -20
- package/dist/runtime/subsystems/auth/protocols/connection-store.js +1 -0
- package/dist/runtime/subsystems/auth/protocols/provider-strategy.d.ts +3 -3
- package/dist/runtime/subsystems/auth/runtime/{integration-broken.error.d.ts → connection-broken.error.d.ts} +5 -5
- package/dist/runtime/subsystems/auth/runtime/connection-broken.error.js +19 -0
- package/dist/runtime/subsystems/auth/runtime/connection-broken.error.js.map +1 -0
- package/dist/runtime/subsystems/auth/runtime/oauth2-refresh.strategy.d.ts +10 -10
- package/dist/runtime/subsystems/auth/runtime/oauth2-refresh.strategy.js +28 -28
- package/dist/runtime/subsystems/auth/runtime/oauth2-refresh.strategy.js.map +1 -1
- package/dist/runtime/subsystems/auth/runtime/with-auth-retry.d.ts +1 -1
- package/dist/runtime/subsystems/auth/runtime/with-auth-retry.js +3 -3
- package/dist/runtime/subsystems/auth/runtime/with-auth-retry.js.map +1 -1
- package/dist/runtime/subsystems/index.d.ts +7 -7
- package/dist/runtime/subsystems/index.js +51 -51
- package/dist/runtime/subsystems/index.js.map +1 -1
- package/dist/runtime/subsystems/{sync → integration}/build-change-source.d.ts +3 -3
- package/dist/runtime/subsystems/{sync → integration}/build-change-source.js +3 -3
- package/dist/runtime/subsystems/integration/build-change-source.js.map +1 -0
- package/dist/runtime/subsystems/{sync → integration}/deep-equal.differ.d.ts +2 -2
- package/dist/runtime/subsystems/{sync → integration}/deep-equal.differ.js +1 -1
- package/dist/runtime/subsystems/integration/deep-equal.differ.js.map +1 -0
- package/dist/runtime/subsystems/{sync → integration}/detection-config.schema.d.ts +3 -3
- package/dist/runtime/subsystems/{sync → integration}/detection-config.schema.js +1 -1
- package/dist/runtime/subsystems/integration/detection-config.schema.js.map +1 -0
- package/dist/runtime/subsystems/{sync/execute-sync.use-case.d.ts → integration/execute-integration.use-case.d.ts} +13 -13
- package/dist/runtime/subsystems/{sync/execute-sync.use-case.js → integration/execute-integration.use-case.js} +30 -30
- package/dist/runtime/subsystems/integration/execute-integration.use-case.js.map +1 -0
- package/dist/runtime/subsystems/integration/index.d.ts +28 -0
- package/dist/runtime/subsystems/{sync → integration}/index.js +171 -171
- package/dist/runtime/subsystems/integration/index.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-audit.schema.d.ts → integration/integration-audit.schema.d.ts} +64 -64
- package/dist/runtime/subsystems/{sync/sync-audit.schema.js → integration/integration-audit.schema.js} +47 -47
- package/dist/runtime/subsystems/integration/integration-audit.schema.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-change-source.protocol.d.ts → integration/integration-change-source.protocol.d.ts} +10 -10
- package/dist/runtime/subsystems/integration/integration-change-source.protocol.js +1 -0
- package/dist/runtime/subsystems/{sync/sync-cursor-store.drizzle-backend.d.ts → integration/integration-cursor-store.drizzle-backend.d.ts} +1 -1
- package/dist/runtime/subsystems/{sync/sync-cursor-store.drizzle-backend.js → integration/integration-cursor-store.drizzle-backend.js} +65 -65
- package/dist/runtime/subsystems/integration/integration-cursor-store.drizzle-backend.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-cursor-store.memory-backend.d.ts → integration/integration-cursor-store.memory-backend.d.ts} +6 -6
- package/dist/runtime/subsystems/{sync/sync-cursor-store.memory-backend.js → integration/integration-cursor-store.memory-backend.js} +5 -5
- package/dist/runtime/subsystems/integration/integration-cursor-store.memory-backend.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-cursor-store.protocol.d.ts → integration/integration-cursor-store.protocol.d.ts} +13 -13
- package/dist/runtime/subsystems/integration/integration-cursor-store.protocol.js +1 -0
- package/dist/runtime/subsystems/{sync/sync-errors.d.ts → integration/integration-errors.d.ts} +2 -2
- package/dist/runtime/subsystems/{sync/sync-errors.js → integration/integration-errors.js} +3 -3
- package/dist/runtime/subsystems/integration/integration-errors.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-field-diff.protocol.d.ts → integration/integration-field-diff.protocol.d.ts} +2 -2
- package/dist/runtime/subsystems/{sync/sync-field-diff.protocol.js → integration/integration-field-diff.protocol.js} +2 -2
- package/dist/runtime/subsystems/integration/integration-field-diff.protocol.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-loopback.protocol.d.ts → integration/integration-loopback.protocol.d.ts} +2 -2
- package/dist/runtime/subsystems/integration/integration-loopback.protocol.js +1 -0
- package/dist/runtime/subsystems/{sync/sync-middleware.protocol.d.ts → integration/integration-middleware.protocol.d.ts} +5 -5
- package/dist/runtime/subsystems/integration/integration-middleware.protocol.js +1 -0
- package/dist/runtime/subsystems/{sync/sync-run-recorder.drizzle-backend.d.ts → integration/integration-run-recorder.drizzle-backend.d.ts} +5 -5
- package/dist/runtime/subsystems/{sync/sync-run-recorder.drizzle-backend.js → integration/integration-run-recorder.drizzle-backend.js} +73 -73
- package/dist/runtime/subsystems/integration/integration-run-recorder.drizzle-backend.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-run-recorder.memory-backend.d.ts → integration/integration-run-recorder.memory-backend.d.ts} +15 -15
- package/dist/runtime/subsystems/{sync/sync-run-recorder.memory-backend.js → integration/integration-run-recorder.memory-backend.js} +11 -11
- package/dist/runtime/subsystems/integration/integration-run-recorder.memory-backend.js.map +1 -0
- package/dist/runtime/subsystems/{sync/sync-run-recorder.protocol.d.ts → integration/integration-run-recorder.protocol.d.ts} +25 -25
- package/dist/runtime/subsystems/integration/integration-run-recorder.protocol.js +1 -0
- package/dist/runtime/subsystems/{sync/sync-sink.protocol.d.ts → integration/integration-sink.protocol.d.ts} +5 -5
- package/dist/runtime/subsystems/integration/integration-sink.protocol.js +1 -0
- package/dist/runtime/subsystems/{sync/sync.module.d.ts → integration/integration.module.d.ts} +24 -24
- package/dist/runtime/subsystems/{sync/sync.module.js → integration/integration.module.js} +132 -132
- package/dist/runtime/subsystems/integration/integration.module.js.map +1 -0
- package/dist/runtime/subsystems/integration/integration.tokens.d.ts +47 -0
- package/dist/runtime/subsystems/integration/integration.tokens.js +18 -0
- package/dist/runtime/subsystems/integration/integration.tokens.js.map +1 -0
- package/dist/runtime/subsystems/{sync → integration}/loopback.middleware.d.ts +5 -5
- package/dist/runtime/subsystems/{sync → integration}/loopback.middleware.js +1 -1
- package/dist/runtime/subsystems/integration/loopback.middleware.js.map +1 -0
- package/dist/runtime/subsystems/{sync → integration}/poll-change-source.d.ts +5 -5
- package/dist/runtime/subsystems/{sync → integration}/poll-change-source.js +1 -1
- package/dist/runtime/subsystems/integration/poll-change-source.js.map +1 -0
- package/dist/runtime/subsystems/{sync → integration}/webhook-change-source.d.ts +5 -5
- package/dist/runtime/subsystems/{sync → integration}/webhook-change-source.js +1 -1
- package/dist/runtime/subsystems/integration/webhook-change-source.js.map +1 -0
- package/dist/runtime/subsystems/jobs/job-worker.module.d.ts +1 -1
- package/dist/runtime/subsystems/observability/index.d.ts +4 -4
- package/dist/runtime/subsystems/observability/index.js +11 -11
- package/dist/runtime/subsystems/observability/index.js.map +1 -1
- package/dist/runtime/subsystems/observability/observability.module.d.ts +2 -2
- package/dist/runtime/subsystems/observability/observability.module.js +11 -11
- package/dist/runtime/subsystems/observability/observability.module.js.map +1 -1
- package/dist/runtime/subsystems/observability/observability.protocol.d.ts +11 -11
- package/dist/runtime/subsystems/observability/observability.service.d.ts +6 -6
- package/dist/runtime/subsystems/observability/observability.service.js +11 -11
- package/dist/runtime/subsystems/observability/observability.service.js.map +1 -1
- package/dist/runtime/subsystems/observability/observability.tokens.d.ts +1 -1
- package/dist/runtime/subsystems/observability/observability.tokens.js.map +1 -1
- package/dist/runtime/subsystems/observability/reporters/bridge-metrics.reporter.d.ts +3 -3
- package/dist/runtime/subsystems/observability/reporters/bridge-metrics.reporter.js.map +1 -1
- package/dist/runtime/subsystems/observability/reporters/index.d.ts +3 -3
- package/dist/runtime/subsystems/observability/reporters/index.js.map +1 -1
- package/dist/src/cli/index.js +262 -269
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/index.d.ts +22 -22
- package/dist/src/index.js +191 -191
- package/dist/src/index.js.map +1 -1
- package/examples/auth-integrations/README.md +32 -32
- package/examples/auth-integrations/definitions/entities/{integration.yaml → connection.yaml} +10 -10
- package/examples/auth-integrations/runtime/{integrations/adapters/integration-grant-sink.adapter.ts → connections/adapters/connection-grant-sink.adapter.ts} +7 -7
- package/examples/auth-integrations/runtime/{integrations/adapters/integration-reader.adapter.ts → connections/adapters/connection-reader.adapter.ts} +10 -10
- package/examples/auth-integrations/runtime/{integrations/adapters/integration-token-writer.adapter.ts → connections/adapters/connection-token-writer.adapter.ts} +11 -11
- package/examples/auth-integrations/runtime/connections/connections-auth.module.ts +81 -0
- package/examples/auth-integrations/runtime/{integrations/facade/integrations.service.ts → connections/facade/connections.service.ts} +35 -35
- package/examples/auth-integrations/runtime/{integrations → connections}/oauth/use-cases/create-or-update-from-oauth-grant.use-case.ts +11 -11
- package/examples/auth-integrations/runtime/{integrations/oauth/use-cases/disconnect-integration.use-case.ts → connections/oauth/use-cases/disconnect-connection.use-case.ts} +6 -6
- package/examples/auth-integrations/runtime/connections/oauth/use-cases/list-user-connections.use-case.ts +21 -0
- package/examples/auth-integrations/runtime/connections/oauth/use-cases/mark-connection-requires-reauth.use-case.ts +21 -0
- package/package.json +1 -1
- package/runtime/base-classes/index.ts +8 -8
- package/runtime/base-classes/{synced-entity-repository.ts → integrated-entity-repository.ts} +36 -36
- package/runtime/base-classes/{synced-entity-service.ts → integrated-entity-service.ts} +6 -6
- package/runtime/base-classes/{sync-upsert-config.ts → integration-upsert-config.ts} +12 -12
- package/runtime/base-classes/{junction-sync-repository.ts → junction-integration-repository.ts} +28 -28
- package/runtime/subsystems/auth/auth-oauth-state.schema.ts +1 -1
- package/runtime/subsystems/auth/auth.module.ts +4 -4
- package/runtime/subsystems/auth/auth.tokens.ts +7 -7
- package/runtime/subsystems/auth/controllers/auth.controller.ts +7 -7
- package/runtime/subsystems/auth/index.ts +19 -19
- package/runtime/subsystems/auth/protocols/auth-strategy.ts +3 -3
- package/runtime/subsystems/auth/protocols/{integration-store.ts → connection-store.ts} +19 -19
- package/runtime/subsystems/auth/protocols/provider-strategy.ts +2 -2
- package/runtime/subsystems/auth/runtime/{integration-broken.error.ts → connection-broken.error.ts} +5 -5
- package/runtime/subsystems/auth/runtime/oauth2-refresh.strategy.ts +35 -35
- package/runtime/subsystems/auth/runtime/with-auth-retry.ts +3 -3
- package/runtime/subsystems/index.ts +11 -11
- package/runtime/subsystems/{sync → integration}/build-change-source.ts +3 -3
- package/runtime/subsystems/{sync → integration}/deep-equal.differ.ts +7 -7
- package/runtime/subsystems/{sync → integration}/detection-config.schema.ts +3 -3
- package/runtime/subsystems/{sync/execute-sync.use-case.ts → integration/execute-integration.use-case.ts} +40 -40
- package/runtime/subsystems/{sync → integration}/index.ts +47 -47
- package/runtime/subsystems/{sync/sync-audit.schema.ts → integration/integration-audit.schema.ts} +61 -61
- package/runtime/subsystems/{sync/sync-change-source.protocol.ts → integration/integration-change-source.protocol.ts} +9 -9
- package/runtime/subsystems/{sync/sync-cursor-store.drizzle-backend.ts → integration/integration-cursor-store.drizzle-backend.ts} +30 -30
- package/runtime/subsystems/{sync/sync-cursor-store.memory-backend.ts → integration/integration-cursor-store.memory-backend.ts} +9 -9
- package/runtime/subsystems/{sync/sync-cursor-store.protocol.ts → integration/integration-cursor-store.protocol.ts} +13 -13
- package/runtime/subsystems/{sync/sync-errors.ts → integration/integration-errors.ts} +3 -3
- package/runtime/subsystems/{sync/sync-field-diff.protocol.ts → integration/integration-field-diff.protocol.ts} +2 -2
- package/runtime/subsystems/{sync/sync-loopback.protocol.ts → integration/integration-loopback.protocol.ts} +2 -2
- package/runtime/subsystems/{sync/sync-middleware.protocol.ts → integration/integration-middleware.protocol.ts} +6 -6
- package/runtime/subsystems/{sync/sync-run-recorder.drizzle-backend.ts → integration/integration-run-recorder.drizzle-backend.ts} +39 -39
- package/runtime/subsystems/{sync/sync-run-recorder.memory-backend.ts → integration/integration-run-recorder.memory-backend.ts} +23 -23
- package/runtime/subsystems/{sync/sync-run-recorder.protocol.ts → integration/integration-run-recorder.protocol.ts} +25 -25
- package/runtime/subsystems/{sync/sync-sink.protocol.ts → integration/integration-sink.protocol.ts} +4 -4
- package/runtime/subsystems/{sync/sync.module.ts → integration/integration.module.ts} +48 -48
- package/runtime/subsystems/integration/integration.tokens.ts +49 -0
- package/runtime/subsystems/{sync → integration}/loopback.middleware.ts +5 -5
- package/runtime/subsystems/{sync → integration}/poll-change-source.ts +7 -7
- package/runtime/subsystems/{sync → integration}/webhook-change-source.ts +7 -7
- package/runtime/subsystems/observability/index.ts +1 -1
- package/runtime/subsystems/observability/observability.module.ts +2 -2
- package/runtime/subsystems/observability/observability.protocol.ts +11 -11
- package/runtime/subsystems/observability/observability.service.ts +13 -13
- package/runtime/subsystems/observability/observability.tokens.ts +1 -1
- package/src/patterns/library/index.ts +4 -4
- package/src/patterns/library/{synced.pattern.ts → integrated.pattern.ts} +12 -12
- package/src/patterns/library/junction.pattern.ts +1 -1
- package/src/patterns/pattern-definition.ts +3 -3
- package/templates/entity/new/backend/modules/core/{sync-source.ejs.t → integration-source.ejs.t} +6 -6
- package/templates/entity/new/backend/modules/core/{sync-source.providers.ejs.t → integration-source.providers.ejs.t} +2 -2
- package/templates/entity/new/clean-lite-ps/entity.ejs.t +1 -1
- package/templates/entity/new/clean-lite-ps/module.ejs.t +1 -1
- package/templates/entity/new/clean-lite-ps/prompt-extension.js +33 -33
- package/templates/entity/new/clean-lite-ps/repository.ejs.t +27 -27
- package/templates/entity/new/frontend/collections/collection.ejs.t +26 -1
- package/templates/entity/new/frontend/collections/collections-base.ejs.t +11 -0
- package/templates/entity/new/frontend/entity/combined.ejs.t +31 -1
- package/templates/entity/new/prompt.js +27 -15
- package/templates/junction/new/entity.ejs.t +1 -1
- package/templates/junction/new/prompt.js +24 -24
- package/templates/junction/new/repository.ejs.t +19 -19
- package/templates/subsystem/auth/auth-oauth-state.schema.ejs.t +2 -2
- package/templates/subsystem/auth-config/prompt.js +1 -1
- package/templates/subsystem/auth-integrations/app-module-hook.ejs.t +5 -5
- package/templates/subsystem/bridge/prompt.js +1 -1
- package/templates/subsystem/integration/integration-audit.schema.ejs.t +192 -0
- package/templates/subsystem/{sync → integration}/prompt.js +12 -12
- package/templates/subsystem/{sync-config/codegen-config-sync-block.ejs.t → integration-config/codegen-config-integration-block.ejs.t} +7 -7
- package/templates/subsystem/integration-config/prompt.js +22 -0
- package/templates/subsystem/jobs/worker.ejs.t +2 -2
- package/templates/subsystem/observability/main-hook.ejs.t +1 -1
- package/templates/subsystem/observability/prompt.js +1 -1
- package/templates/subsystem/openapi-config/prompt.js +1 -1
- package/dist/runtime/base-classes/junction-sync-repository.js.map +0 -1
- package/dist/runtime/base-classes/sync-upsert-config.js +0 -1
- package/dist/runtime/base-classes/synced-entity-repository.js.map +0 -1
- package/dist/runtime/base-classes/synced-entity-service.js.map +0 -1
- package/dist/runtime/subsystems/auth/protocols/integration-store.js +0 -1
- package/dist/runtime/subsystems/auth/runtime/integration-broken.error.js +0 -19
- package/dist/runtime/subsystems/auth/runtime/integration-broken.error.js.map +0 -1
- package/dist/runtime/subsystems/sync/build-change-source.js.map +0 -1
- package/dist/runtime/subsystems/sync/deep-equal.differ.js.map +0 -1
- package/dist/runtime/subsystems/sync/detection-config.schema.js.map +0 -1
- package/dist/runtime/subsystems/sync/execute-sync.use-case.js.map +0 -1
- package/dist/runtime/subsystems/sync/index.d.ts +0 -28
- package/dist/runtime/subsystems/sync/index.js.map +0 -1
- package/dist/runtime/subsystems/sync/loopback.middleware.js.map +0 -1
- package/dist/runtime/subsystems/sync/poll-change-source.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-audit.schema.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-change-source.protocol.js +0 -1
- package/dist/runtime/subsystems/sync/sync-cursor-store.drizzle-backend.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-cursor-store.memory-backend.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-cursor-store.protocol.js +0 -1
- package/dist/runtime/subsystems/sync/sync-errors.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-field-diff.protocol.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-loopback.protocol.js +0 -1
- package/dist/runtime/subsystems/sync/sync-middleware.protocol.js +0 -1
- package/dist/runtime/subsystems/sync/sync-run-recorder.drizzle-backend.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-run-recorder.memory-backend.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync-run-recorder.protocol.js +0 -1
- package/dist/runtime/subsystems/sync/sync-sink.protocol.js +0 -1
- package/dist/runtime/subsystems/sync/sync.module.js.map +0 -1
- package/dist/runtime/subsystems/sync/sync.tokens.d.ts +0 -47
- package/dist/runtime/subsystems/sync/sync.tokens.js +0 -18
- package/dist/runtime/subsystems/sync/sync.tokens.js.map +0 -1
- package/dist/runtime/subsystems/sync/webhook-change-source.js.map +0 -1
- package/examples/auth-integrations/runtime/integrations/integrations-auth.module.ts +0 -81
- package/examples/auth-integrations/runtime/integrations/oauth/use-cases/list-user-integrations.use-case.ts +0 -21
- package/examples/auth-integrations/runtime/integrations/oauth/use-cases/mark-integration-requires-reauth.use-case.ts +0 -21
- package/runtime/subsystems/sync/sync.tokens.ts +0 -49
- package/templates/subsystem/sync/sync-audit.schema.ejs.t +0 -192
- package/templates/subsystem/sync-config/prompt.js +0 -22
- /package/dist/runtime/base-classes/{sync-upsert-config.js.map → integration-upsert-config.js.map} +0 -0
- /package/dist/runtime/subsystems/auth/protocols/{integration-store.js.map → connection-store.js.map} +0 -0
- /package/dist/runtime/subsystems/{sync/sync-change-source.protocol.js.map → integration/integration-change-source.protocol.js.map} +0 -0
- /package/dist/runtime/subsystems/{sync/sync-cursor-store.protocol.js.map → integration/integration-cursor-store.protocol.js.map} +0 -0
- /package/dist/runtime/subsystems/{sync/sync-loopback.protocol.js.map → integration/integration-loopback.protocol.js.map} +0 -0
- /package/dist/runtime/subsystems/{sync/sync-middleware.protocol.js.map → integration/integration-middleware.protocol.js.map} +0 -0
- /package/dist/runtime/subsystems/{sync/sync-run-recorder.protocol.js.map → integration/integration-run-recorder.protocol.js.map} +0 -0
- /package/dist/runtime/subsystems/{sync/sync-sink.protocol.js.map → integration/integration-sink.protocol.js.map} +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Injectable } from '@nestjs/common';
|
|
2
|
+
import { ConnectionService } from '../../connection.service';
|
|
3
|
+
import type { Connection } from '../../connection.entity';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Lists a user's connections newest-first. Used by the settings page
|
|
7
|
+
* (`GET /connections`) and any "which providers are connected?" UI.
|
|
8
|
+
*
|
|
9
|
+
* Returns rows with ciphertexts intact — callers should NOT pass these
|
|
10
|
+
* to the frontend. Use `ConnectionsService.listByUser` if you need
|
|
11
|
+
* the consumer-facing facade behavior (which strips ciphertexts before
|
|
12
|
+
* returning).
|
|
13
|
+
*/
|
|
14
|
+
@Injectable()
|
|
15
|
+
export class ListUserConnectionsUseCase {
|
|
16
|
+
constructor(private readonly connections: ConnectionService) {}
|
|
17
|
+
|
|
18
|
+
async execute(userId: string): Promise<Connection[]> {
|
|
19
|
+
return this.connections.findByUserId(userId);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Injectable } from '@nestjs/common';
|
|
2
|
+
import { ConnectionService } from '../../connection.service';
|
|
3
|
+
import type { Connection } from '../../connection.entity';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Flips an connection's status to `requires_reauth`. Called when the
|
|
7
|
+
* refresh path raises `ConnectionBrokenError` (refresh token rejected,
|
|
8
|
+
* scopes revoked, etc.) — see `OAuth2RefreshStrategy` + `withAuthRetry`.
|
|
9
|
+
*
|
|
10
|
+
* Idempotent: calling on an already-broken row is a no-op write.
|
|
11
|
+
*/
|
|
12
|
+
@Injectable()
|
|
13
|
+
export class MarkConnectionRequiresReauthUseCase {
|
|
14
|
+
constructor(private readonly connections: ConnectionService) {}
|
|
15
|
+
|
|
16
|
+
async execute(connectionId: string): Promise<Connection> {
|
|
17
|
+
return this.connections.update(connectionId, {
|
|
18
|
+
status: 'requires_reauth',
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
package/package.json
CHANGED
|
@@ -32,24 +32,24 @@ export type { EventCategory } from './lifecycle-events';
|
|
|
32
32
|
export { BaseFindByIdUseCase, BaseListUseCase } from './base-read-use-cases';
|
|
33
33
|
export type { IFindByIdService, IListService } from './base-read-use-cases';
|
|
34
34
|
|
|
35
|
-
//
|
|
36
|
-
export type {
|
|
35
|
+
// Integration upsert config (consumed by IntegratedEntityRepository + JunctionIntegrationRepository)
|
|
36
|
+
export type { IntegrationUpsertConfig, IntegrationFkResolver } from './integration-upsert-config';
|
|
37
37
|
|
|
38
38
|
// Family-specific repository base classes
|
|
39
|
-
export {
|
|
39
|
+
export { IntegratedEntityRepository } from './integrated-entity-repository';
|
|
40
40
|
export {
|
|
41
|
-
|
|
41
|
+
JunctionIntegrationRepository,
|
|
42
42
|
buildCompositeExternalId,
|
|
43
43
|
parseCompositeExternalId,
|
|
44
|
-
} from './junction-
|
|
45
|
-
export type {
|
|
44
|
+
} from './junction-integration-repository';
|
|
45
|
+
export type { JunctionIntegrationConfig } from './junction-integration-repository';
|
|
46
46
|
export { ActivityEntityRepository } from './activity-entity-repository';
|
|
47
47
|
export { MetadataEntityRepository } from './metadata-entity-repository';
|
|
48
48
|
export { KnowledgeEntityRepository } from './knowledge-entity-repository';
|
|
49
49
|
|
|
50
50
|
// Family-specific service base classes
|
|
51
|
-
export {
|
|
52
|
-
export type {
|
|
51
|
+
export { IntegratedEntityService } from './integrated-entity-service';
|
|
52
|
+
export type { IIntegratedEntityRepository } from './integrated-entity-service';
|
|
53
53
|
export { ActivityEntityService } from './activity-entity-service';
|
|
54
54
|
export type { IActivityEntityRepository } from './activity-entity-service';
|
|
55
55
|
export { MetadataEntityService } from './metadata-entity-service';
|
package/runtime/base-classes/{synced-entity-repository.ts → integrated-entity-repository.ts}
RENAMED
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* IntegratedEntityRepository<TEntity, TIntegrationWrite, TIntegrationProjection>
|
|
3
3
|
*
|
|
4
|
-
* Family-specific base for
|
|
5
|
-
* Adds external ID lookups, user-scoped queries, and the generic inbound-
|
|
4
|
+
* Family-specific base for Integrated entities (contacts, accounts, opportunities).
|
|
5
|
+
* Adds external ID lookups, user-scoped queries, and the generic inbound-integration
|
|
6
6
|
* write surface (canonical→Drizzle upsert + provider-scoped FK resolution +
|
|
7
|
-
* EAV dual-write seam), driven by the concrete repo's `
|
|
7
|
+
* EAV dual-write seam), driven by the concrete repo's `integrationConfig`.
|
|
8
8
|
*
|
|
9
9
|
* The type params default so pre-existing single-param subclasses keep
|
|
10
|
-
* compiling; `pattern:
|
|
10
|
+
* compiling; `pattern: Integrated` repos declare all three plus `integrationConfig`.
|
|
11
11
|
*/
|
|
12
12
|
import { and, eq, inArray } from 'drizzle-orm';
|
|
13
13
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
14
|
import type { PgTableWithColumns } from 'drizzle-orm/pg-core';
|
|
15
15
|
import type { DrizzleTx } from '../types/drizzle';
|
|
16
16
|
import { BaseRepository } from './base-repository';
|
|
17
|
-
import type {
|
|
17
|
+
import type { IntegrationUpsertConfig, IntegrationFkResolver } from './integration-upsert-config';
|
|
18
18
|
|
|
19
|
-
export abstract class
|
|
19
|
+
export abstract class IntegratedEntityRepository<
|
|
20
20
|
TEntity,
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
TIntegrationWrite = Partial<TEntity>,
|
|
22
|
+
TIntegrationProjection = TEntity,
|
|
23
23
|
> extends BaseRepository<TEntity> {
|
|
24
24
|
/**
|
|
25
|
-
* Declarative
|
|
25
|
+
* Declarative integration write surface. Concrete (`pattern: Integrated`) repositories
|
|
26
26
|
* declare this — the template emits it from the entity's fields + FKs.
|
|
27
27
|
*/
|
|
28
|
-
protected abstract readonly
|
|
28
|
+
protected abstract readonly integrationConfig: IntegrationUpsertConfig;
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* Find a single entity by its external CRM identifier.
|
|
@@ -57,21 +57,21 @@ export abstract class SyncedEntityRepository<
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
// ==========================================================================
|
|
60
|
-
// Inbound
|
|
60
|
+
// Inbound integration (#374) — canonical→Drizzle write + provider-scoped FK
|
|
61
61
|
// resolution + EAV dual-write seam, all inside a SINGLE transaction.
|
|
62
|
-
// Driven entirely by `this.
|
|
62
|
+
// Driven entirely by `this.integrationConfig`; the per-entity shape lives there.
|
|
63
63
|
// ==========================================================================
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
66
|
* Upsert ONE entity by its `(provider, externalId)` identity, in a single
|
|
67
67
|
* transaction:
|
|
68
|
-
* 1. resolve each `
|
|
68
|
+
* 1. resolve each `integrationConfig.fkResolvers` FK (provider-scoped). Strict
|
|
69
69
|
* resolvers throw on unresolved; non-strict leave the column null.
|
|
70
70
|
* 2. insert-or-update the canonical columns via `onConflictDoUpdate` on the
|
|
71
71
|
* `conflictTarget`. Resolved FKs are only written into `set` when
|
|
72
72
|
* non-null this run (no-clobber).
|
|
73
73
|
* 3. EAV dual-write of `write.fields` via `writeCustomFields` when
|
|
74
|
-
* `
|
|
74
|
+
* `integrationConfig.eav` and the bag is non-empty (same tx).
|
|
75
75
|
*
|
|
76
76
|
* Idempotent: a second call with the same identity updates in place. Returns
|
|
77
77
|
* the canonical projection (so the orchestrator records `local_id`).
|
|
@@ -80,15 +80,15 @@ export abstract class SyncedEntityRepository<
|
|
|
80
80
|
* @param provider adapter/provider label persisted + used to scope lookups
|
|
81
81
|
* @param tx optional outer transaction; when omitted we open our own
|
|
82
82
|
*/
|
|
83
|
-
async
|
|
84
|
-
write:
|
|
83
|
+
async integrationUpsertOne(
|
|
84
|
+
write: TIntegrationWrite,
|
|
85
85
|
provider: string,
|
|
86
86
|
tx?: DrizzleTx,
|
|
87
|
-
): Promise<
|
|
88
|
-
const cfg = this.
|
|
87
|
+
): Promise<TIntegrationProjection> {
|
|
88
|
+
const cfg = this.integrationConfig;
|
|
89
89
|
const w = write as Record<string, unknown>;
|
|
90
90
|
|
|
91
|
-
const run = async (db: DrizzleTx): Promise<
|
|
91
|
+
const run = async (db: DrizzleTx): Promise<TIntegrationProjection> => {
|
|
92
92
|
// 1. FK resolution (provider-scoped). Strict → throw; else opportunistic null.
|
|
93
93
|
const resolvedFks: Record<string, string | null> = {};
|
|
94
94
|
for (const fk of cfg.fkResolvers) {
|
|
@@ -155,7 +155,7 @@ export abstract class SyncedEntityRepository<
|
|
|
155
155
|
async findByExternalIdProjected(
|
|
156
156
|
externalId: string,
|
|
157
157
|
provider: string,
|
|
158
|
-
): Promise<
|
|
158
|
+
): Promise<TIntegrationProjection | null> {
|
|
159
159
|
const rows = await this.db
|
|
160
160
|
.select()
|
|
161
161
|
.from(this.table)
|
|
@@ -171,7 +171,7 @@ export abstract class SyncedEntityRepository<
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
/**
|
|
174
|
-
*
|
|
174
|
+
* Integration "delete" by external id, provider-scoped. When `softDelete: true`,
|
|
175
175
|
* sets `deletedAt`. When `softDelete: false`, tombstone-by-clearing: null out
|
|
176
176
|
* `external_id`/`provider` so the row no longer matches future inbound
|
|
177
177
|
* changes while preserving local-id references. Returns `{ id }` or `null`.
|
|
@@ -182,7 +182,7 @@ export abstract class SyncedEntityRepository<
|
|
|
182
182
|
tx?: DrizzleTx,
|
|
183
183
|
): Promise<{ id: string } | null> {
|
|
184
184
|
const db = this.runner(tx);
|
|
185
|
-
const set = this.
|
|
185
|
+
const set = this.integrationConfig.softDelete
|
|
186
186
|
? { deletedAt: new Date(), updatedAt: new Date() }
|
|
187
187
|
: { externalId: null, provider: null, updatedAt: new Date() };
|
|
188
188
|
const rows = await db
|
|
@@ -199,20 +199,20 @@ export abstract class SyncedEntityRepository<
|
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
/**
|
|
202
|
-
* Batch
|
|
203
|
-
* `
|
|
202
|
+
* Batch integration upsert — concretizes the former abstract stub. Delegates to
|
|
203
|
+
* `integrationUpsertOne` per input inside one transaction. Inputs are raw partial
|
|
204
204
|
* rows: provider is read from each input's own `provider` column; rows
|
|
205
205
|
* missing `externalId`/`provider` are skipped.
|
|
206
206
|
*/
|
|
207
|
-
async
|
|
207
|
+
async integrationUpsert(inputs: Array<Partial<TEntity>>): Promise<TEntity[]> {
|
|
208
208
|
if (inputs.length === 0) return [];
|
|
209
209
|
return this.db.transaction(async (tx) => {
|
|
210
210
|
const out: TEntity[] = [];
|
|
211
211
|
for (const input of inputs) {
|
|
212
212
|
const rec = input as Record<string, unknown>;
|
|
213
213
|
if (!rec['externalId'] || !rec['provider']) continue;
|
|
214
|
-
const proj = await this.
|
|
215
|
-
input as unknown as
|
|
214
|
+
const proj = await this.integrationUpsertOne(
|
|
215
|
+
input as unknown as TIntegrationWrite,
|
|
216
216
|
rec['provider'] as string,
|
|
217
217
|
tx,
|
|
218
218
|
);
|
|
@@ -230,14 +230,14 @@ export abstract class SyncedEntityRepository<
|
|
|
230
230
|
|
|
231
231
|
/**
|
|
232
232
|
* Project a raw row to the canonical differ shape — a generic pick over
|
|
233
|
-
* `
|
|
233
|
+
* `integrationConfig.projectionColumns`. Override only for synthesized projections
|
|
234
234
|
* (e.g. junctions); entities use this verbatim.
|
|
235
235
|
*/
|
|
236
|
-
protected toProjection(row: TEntity):
|
|
236
|
+
protected toProjection(row: TEntity): TIntegrationProjection {
|
|
237
237
|
const r = row as Record<string, unknown>;
|
|
238
238
|
const out: Record<string, unknown> = {};
|
|
239
|
-
for (const col of this.
|
|
240
|
-
return out as
|
|
239
|
+
for (const col of this.integrationConfig.projectionColumns) out[col] = r[col];
|
|
240
|
+
return out as TIntegrationProjection;
|
|
241
241
|
}
|
|
242
242
|
|
|
243
243
|
/**
|
|
@@ -263,7 +263,7 @@ export abstract class SyncedEntityRepository<
|
|
|
263
263
|
*/
|
|
264
264
|
private async resolveFk(
|
|
265
265
|
db: DrizzleTx,
|
|
266
|
-
fk:
|
|
266
|
+
fk: IntegrationFkResolver,
|
|
267
267
|
rawExternalId: unknown,
|
|
268
268
|
provider: string,
|
|
269
269
|
): Promise<string | null> {
|
|
@@ -271,7 +271,7 @@ export abstract class SyncedEntityRepository<
|
|
|
271
271
|
if (!parentExternalId) {
|
|
272
272
|
if (fk.strict) {
|
|
273
273
|
throw new Error(
|
|
274
|
-
`${this.constructor.name}.
|
|
274
|
+
`${this.constructor.name}.integrationUpsertOne: missing required parent ` +
|
|
275
275
|
`external id for '${fk.column}' (writeKey '${fk.writeKey}')`,
|
|
276
276
|
);
|
|
277
277
|
}
|
|
@@ -293,9 +293,9 @@ export abstract class SyncedEntityRepository<
|
|
|
293
293
|
const id = (rows[0]?.id as string | undefined) ?? null;
|
|
294
294
|
if (id === null && fk.strict) {
|
|
295
295
|
throw new Error(
|
|
296
|
-
`${this.constructor.name}.
|
|
296
|
+
`${this.constructor.name}.integrationUpsertOne: unresolved parent ` +
|
|
297
297
|
`'${parentExternalId}' (provider '${provider}') for '${fk.column}' — ` +
|
|
298
|
-
`parent not
|
|
298
|
+
`parent not integrated yet`,
|
|
299
299
|
);
|
|
300
300
|
}
|
|
301
301
|
return id;
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* IntegratedEntityService<TRepo, TEntity>
|
|
3
3
|
*
|
|
4
|
-
* Family-specific base service for
|
|
4
|
+
* Family-specific base service for Integrated entities.
|
|
5
5
|
* Delegates to a CRM repository that provides external ID lookups
|
|
6
6
|
* and user-scoped queries.
|
|
7
7
|
*/
|
|
8
8
|
import { BaseService, type IBaseRepository } from './base-service';
|
|
9
9
|
|
|
10
|
-
export interface
|
|
10
|
+
export interface IIntegratedEntityRepository<TEntity> extends IBaseRepository<TEntity> {
|
|
11
11
|
findByExternalId(externalId: string): Promise<TEntity | null>;
|
|
12
12
|
findManyByExternalIds(externalIds: string[]): Promise<TEntity[]>;
|
|
13
13
|
findAllByUserId(userId: string): Promise<TEntity[]>;
|
|
14
14
|
findVisibleByUserId(userId: string): Promise<TEntity[]>;
|
|
15
|
-
|
|
15
|
+
integrationUpsert(inputs: Array<Partial<TEntity>>): Promise<TEntity[]>;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export abstract class
|
|
19
|
-
TRepo extends
|
|
18
|
+
export abstract class IntegratedEntityService<
|
|
19
|
+
TRepo extends IIntegratedEntityRepository<TEntity>,
|
|
20
20
|
TEntity,
|
|
21
21
|
> extends BaseService<TRepo, TEntity> {
|
|
22
22
|
/**
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* IntegrationUpsertConfig + IntegrationFkResolver
|
|
3
3
|
*
|
|
4
|
-
* Declarative description of an entity's inbound-
|
|
5
|
-
* by `
|
|
6
|
-
* `softDeleteByExternalId` / `toProjection`. Each `pattern:
|
|
7
|
-
* declares a concrete `
|
|
4
|
+
* Declarative description of an entity's inbound-integration write surface, consumed
|
|
5
|
+
* by `IntegratedEntityRepository.integrationUpsertOne` / `findByExternalIdProjected` /
|
|
6
|
+
* `softDeleteByExternalId` / `toProjection`. Each `pattern: Integrated` repository
|
|
7
|
+
* declares a concrete `integrationConfig: IntegrationUpsertConfig` (emitted by the template),
|
|
8
8
|
* the same idiom as `behaviors: BehaviorConfig`.
|
|
9
9
|
*
|
|
10
|
-
* Named `
|
|
11
|
-
* subsystem's `DetectionConfig`/`
|
|
10
|
+
* Named `IntegrationUpsertConfig` (not `IntegrationConfig`) to avoid colliding with the integration
|
|
11
|
+
* subsystem's `DetectionConfig`/`IntegrationConfig` surface.
|
|
12
12
|
*
|
|
13
13
|
* The generic upsert separates three column roles:
|
|
14
14
|
* - identity (`conflictTarget`) — only in `values`, never in `set`
|
|
@@ -27,10 +27,10 @@ import type { PgTableWithColumns } from 'drizzle-orm/pg-core';
|
|
|
27
27
|
* posture); falsy leaves the column null this run (opportunistic, entity
|
|
28
28
|
* posture).
|
|
29
29
|
*/
|
|
30
|
-
export interface
|
|
30
|
+
export interface IntegrationFkResolver {
|
|
31
31
|
/** Local FK column — camel key into `this.table`, e.g. `'parentAccountId'`. */
|
|
32
32
|
column: string;
|
|
33
|
-
/** Key on `
|
|
33
|
+
/** Key on `TIntegrationWrite` carrying the parent external id (see Decision 4). */
|
|
34
34
|
writeKey: string;
|
|
35
35
|
/** Parent table to resolve against; `'self'` → `this.table`. */
|
|
36
36
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -39,7 +39,7 @@ export interface SyncFkResolver {
|
|
|
39
39
|
strict?: boolean;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
export interface
|
|
42
|
+
export interface IntegrationUpsertConfig {
|
|
43
43
|
/** Camel keys into `this.table` forming the conflict target, e.g. `['provider', 'externalId']`. */
|
|
44
44
|
conflictTarget: string[];
|
|
45
45
|
/**
|
|
@@ -48,10 +48,10 @@ export interface SyncUpsertConfig {
|
|
|
48
48
|
*/
|
|
49
49
|
writeColumns: string[];
|
|
50
50
|
/** Conditional, provider-scoped FK resolvers. */
|
|
51
|
-
fkResolvers:
|
|
51
|
+
fkResolvers: IntegrationFkResolver[];
|
|
52
52
|
/** Columns picked into the projection (camel), incl. id/externalId/timestamps. */
|
|
53
53
|
projectionColumns: string[];
|
|
54
|
-
/** When true, `
|
|
54
|
+
/** When true, `integrationUpsertOne` calls `writeCustomFields` for a non-empty `fields` bag. */
|
|
55
55
|
eav: boolean;
|
|
56
56
|
/** When true, deletes set `deletedAt`; when false, tombstone-by-clearing external_id/provider. */
|
|
57
57
|
softDelete: boolean;
|
package/runtime/base-classes/{junction-sync-repository.ts → junction-integration-repository.ts}
RENAMED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* JunctionIntegrationRepository<TEntity, TIntegrationWrite, TIntegrationProjection>
|
|
3
3
|
*
|
|
4
|
-
* Base for junction repos that participate in inbound
|
|
5
|
-
*
|
|
6
|
-
* `external_id`/`provider` column, so the
|
|
4
|
+
* Base for junction repos that participate in inbound integration (#374). A junction's
|
|
5
|
+
* integration identity is the tuple `(leftId, rightId[, role])` — there is no native
|
|
6
|
+
* `external_id`/`provider` column, so the integration seam's externalId is a COMPOSITE
|
|
7
7
|
* string `<leftExternalId>::<rightExternalId>[::<role>]` (see the static
|
|
8
8
|
* build/parse helpers below).
|
|
9
9
|
*
|
|
@@ -19,7 +19,7 @@ import type { PgTableWithColumns } from 'drizzle-orm/pg-core';
|
|
|
19
19
|
import type { DrizzleTx } from '../types/drizzle';
|
|
20
20
|
import { BaseRepository } from './base-repository';
|
|
21
21
|
|
|
22
|
-
export interface
|
|
22
|
+
export interface JunctionIntegrationConfig {
|
|
23
23
|
/** Left endpoint: local FK column (camel) + strict parent table. */
|
|
24
24
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
25
25
|
left: { column: string; refTable: PgTableWithColumns<any> };
|
|
@@ -30,16 +30,16 @@ export interface JunctionSyncConfig {
|
|
|
30
30
|
roleColumn: string | null;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
export abstract class
|
|
33
|
+
export abstract class JunctionIntegrationRepository<
|
|
34
34
|
TEntity,
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
TIntegrationWrite,
|
|
36
|
+
TIntegrationProjection,
|
|
37
37
|
> extends BaseRepository<TEntity> {
|
|
38
38
|
/**
|
|
39
|
-
* Declarative junction
|
|
39
|
+
* Declarative junction integration surface. Concrete repos declare this — the
|
|
40
40
|
* template emits it with live parent-table handles.
|
|
41
41
|
*/
|
|
42
|
-
protected abstract readonly
|
|
42
|
+
protected abstract readonly integrationConfig: JunctionIntegrationConfig;
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
45
|
* Upsert ONE junction row by its composite identity, in a single transaction:
|
|
@@ -54,17 +54,17 @@ export abstract class JunctionSyncRepository<
|
|
|
54
54
|
* @param provider adapter/provider label used to scope the parent lookups
|
|
55
55
|
* @param tx optional outer transaction; when omitted we open our own
|
|
56
56
|
*/
|
|
57
|
-
async
|
|
58
|
-
write:
|
|
57
|
+
async integrationUpsertOne(
|
|
58
|
+
write: TIntegrationWrite,
|
|
59
59
|
provider: string,
|
|
60
60
|
tx?: DrizzleTx,
|
|
61
|
-
): Promise<
|
|
62
|
-
const cfg = this.
|
|
61
|
+
): Promise<TIntegrationProjection> {
|
|
62
|
+
const cfg = this.integrationConfig;
|
|
63
63
|
const w = write as Record<string, unknown>;
|
|
64
64
|
const leftWriteKey = `${cfg.left.column.replace(/Id$/, '')}ExternalId`;
|
|
65
65
|
const rightWriteKey = `${cfg.right.column.replace(/Id$/, '')}ExternalId`;
|
|
66
66
|
|
|
67
|
-
const run = async (db: DrizzleTx): Promise<
|
|
67
|
+
const run = async (db: DrizzleTx): Promise<TIntegrationProjection> => {
|
|
68
68
|
const leftId = await this.resolveStrict(
|
|
69
69
|
db, cfg.left.refTable, w[leftWriteKey] as string, provider, cfg.left.column,
|
|
70
70
|
);
|
|
@@ -109,8 +109,8 @@ export abstract class JunctionSyncRepository<
|
|
|
109
109
|
async findByExternalIdProjected(
|
|
110
110
|
externalId: string,
|
|
111
111
|
provider: string,
|
|
112
|
-
): Promise<
|
|
113
|
-
const cfg = this.
|
|
112
|
+
): Promise<TIntegrationProjection | null> {
|
|
113
|
+
const cfg = this.integrationConfig;
|
|
114
114
|
const parsed = parseCompositeExternalId(externalId, cfg.roleColumn !== null);
|
|
115
115
|
if (!parsed) return null;
|
|
116
116
|
|
|
@@ -138,7 +138,7 @@ export abstract class JunctionSyncRepository<
|
|
|
138
138
|
|
|
139
139
|
/**
|
|
140
140
|
* Hard-delete the junction by composite externalId. Junctions have no
|
|
141
|
-
* `deleted_at` and no external-linkage columns to clear, so a
|
|
141
|
+
* `deleted_at` and no external-linkage columns to clear, so a integration "delete"
|
|
142
142
|
* removes the row. Resolves both parents NON-throwing, then deletes by the
|
|
143
143
|
* identity tuple. Returns the composite id, or `null` when nothing matched.
|
|
144
144
|
*/
|
|
@@ -147,7 +147,7 @@ export abstract class JunctionSyncRepository<
|
|
|
147
147
|
provider: string,
|
|
148
148
|
tx?: DrizzleTx,
|
|
149
149
|
): Promise<{ id: string } | null> {
|
|
150
|
-
const cfg = this.
|
|
150
|
+
const cfg = this.integrationConfig;
|
|
151
151
|
const parsed = parseCompositeExternalId(externalId, cfg.roleColumn !== null);
|
|
152
152
|
if (!parsed) return null;
|
|
153
153
|
const db = this.runner(tx);
|
|
@@ -168,16 +168,16 @@ export abstract class JunctionSyncRepository<
|
|
|
168
168
|
* Project a raw junction row to the differ shape: the COMPOSITE externalId
|
|
169
169
|
* `id` (junctions have no surrogate id) plus the local FK columns, role (when
|
|
170
170
|
* role-bearing) and timestamps — matching the emitted
|
|
171
|
-
* `<Junction>
|
|
171
|
+
* `<Junction>IntegrationProjection` interface (id + leftId + rightId + role? +
|
|
172
172
|
* createdAt + updatedAt). Junction projections are purely structural, so this
|
|
173
|
-
* is fully generic over `
|
|
173
|
+
* is fully generic over `integrationConfig` — no per-junction override is emitted.
|
|
174
174
|
*/
|
|
175
175
|
protected toProjection(
|
|
176
176
|
row: TEntity,
|
|
177
177
|
write: Record<string, unknown>,
|
|
178
178
|
_provider: string,
|
|
179
|
-
):
|
|
180
|
-
const cfg = this.
|
|
179
|
+
): TIntegrationProjection {
|
|
180
|
+
const cfg = this.integrationConfig;
|
|
181
181
|
const r = row as Record<string, unknown>;
|
|
182
182
|
const leftExt = write[`${cfg.left.column.replace(/Id$/, '')}ExternalId`] as string;
|
|
183
183
|
const rightExt = write[`${cfg.right.column.replace(/Id$/, '')}ExternalId`] as string;
|
|
@@ -190,12 +190,12 @@ export abstract class JunctionSyncRepository<
|
|
|
190
190
|
if (cfg.roleColumn) out[cfg.roleColumn] = r[cfg.roleColumn];
|
|
191
191
|
out['createdAt'] = r['createdAt'];
|
|
192
192
|
out['updatedAt'] = r['updatedAt'];
|
|
193
|
-
return out as
|
|
193
|
+
return out as TIntegrationProjection;
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
/** Build the identity WHERE clause `(left, right[, role])`. */
|
|
197
197
|
private identityWhere(leftId: string, rightId: string, role: string | undefined) {
|
|
198
|
-
const cfg = this.
|
|
198
|
+
const cfg = this.integrationConfig;
|
|
199
199
|
const conds = [
|
|
200
200
|
eq(this.table[cfg.left.column], leftId),
|
|
201
201
|
eq(this.table[cfg.right.column], rightId),
|
|
@@ -218,9 +218,9 @@ export abstract class JunctionSyncRepository<
|
|
|
218
218
|
const id = await this.resolveLoose(db, refTable, parentExternalId, provider);
|
|
219
219
|
if (!id) {
|
|
220
220
|
throw new Error(
|
|
221
|
-
`${this.constructor.name}.
|
|
221
|
+
`${this.constructor.name}.integrationUpsertOne: unresolved parent ` +
|
|
222
222
|
`'${parentExternalId}' (provider '${provider}') for '${column}' — ` +
|
|
223
|
-
`parent not
|
|
223
|
+
`parent not integrated yet`,
|
|
224
224
|
);
|
|
225
225
|
}
|
|
226
226
|
return id;
|
|
@@ -250,7 +250,7 @@ export abstract class JunctionSyncRepository<
|
|
|
250
250
|
}
|
|
251
251
|
|
|
252
252
|
// ============================================================================
|
|
253
|
-
// Composite externalId — the junction
|
|
253
|
+
// Composite externalId — the junction integration seam's deterministic identity.
|
|
254
254
|
//
|
|
255
255
|
// Format: `<leftExternalId>::<rightExternalId>[::<role>]`
|
|
256
256
|
// e.g. `hubspot:42::hubspot:99::employee` (role-bearing)
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* - `expires_at` — TTL boundary; entries past this are treated as absent.
|
|
16
16
|
*
|
|
17
17
|
* Convention: schema files live at the root of the subsystem dir
|
|
18
|
-
* (mirrors `cache.schema.ts`, `
|
|
18
|
+
* (mirrors `cache.schema.ts`, `integration-audit.schema.ts`, `domain-events.schema.ts`).
|
|
19
19
|
*/
|
|
20
20
|
import { pgTable, text, timestamp } from 'drizzle-orm/pg-core';
|
|
21
21
|
import type { InferSelectModel } from 'drizzle-orm';
|
|
@@ -9,17 +9,17 @@
|
|
|
9
9
|
* - `AUTH_OPTIONS` → resolved options bag (used by AuthController
|
|
10
10
|
* for `redirectUriBase`).
|
|
11
11
|
*
|
|
12
|
-
* The
|
|
13
|
-
* `
|
|
12
|
+
* The connection-store ports (`AUTH_CONNECTION_READER`,
|
|
13
|
+
* `AUTH_CONNECTION_TOKEN_WRITER`, `AUTH_CONNECTION_GRANT_SINK`),
|
|
14
14
|
* `AUTH_USER_CONTEXT`, and `STRATEGY_REGISTRY` are deliberately **not**
|
|
15
15
|
* wired here — they are always consumer-specific:
|
|
16
|
-
* -
|
|
16
|
+
* - connection-store ports adapt the consumer's `connections` storage;
|
|
17
17
|
* - `IUserContext` adapts the app's session/JWT scheme;
|
|
18
18
|
* - `STRATEGY_REGISTRY` is populated from the per-provider strategy
|
|
19
19
|
* classes the consumer maintains.
|
|
20
20
|
*
|
|
21
21
|
* Consumers provide them in their app module (or by importing the
|
|
22
|
-
* `auth-integrations` starter, which binds the three
|
|
22
|
+
* `auth-integrations` starter, which binds the three connection-store
|
|
23
23
|
* ports off a single canonical entity).
|
|
24
24
|
*
|
|
25
25
|
* Usage in AppModule:
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
* constructor(
|
|
11
11
|
* @Inject(ENCRYPTION_KEY) private readonly key: IEncryptionKey,
|
|
12
12
|
* @Inject(OAUTH_STATE_STORE) private readonly states: IOAuthStateStore,
|
|
13
|
-
* @Inject(
|
|
14
|
-
* @Inject(
|
|
15
|
-
* @Inject(
|
|
13
|
+
* @Inject(AUTH_CONNECTION_READER) private readonly reader: IConnectionReader,
|
|
14
|
+
* @Inject(AUTH_CONNECTION_TOKEN_WRITER) private readonly writer: IConnectionTokenWriter,
|
|
15
|
+
* @Inject(AUTH_CONNECTION_GRANT_SINK) private readonly grants: IConnectionGrantSink,
|
|
16
16
|
* @Inject(AUTH_USER_CONTEXT) private readonly userCtx: IUserContext,
|
|
17
17
|
* @Inject(STRATEGY_REGISTRY) private readonly registry: ProviderStrategyRegistry,
|
|
18
18
|
* ) {}
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*
|
|
21
21
|
* `IAuthStrategy` implementations are provider-specific and registered under
|
|
22
22
|
* provider-specific tokens (e.g. `SALESFORCE_AUTH_STRATEGY`,
|
|
23
|
-
* `HUBSPOT_AUTH_STRATEGY`) by each
|
|
23
|
+
* `HUBSPOT_AUTH_STRATEGY`) by each connection module — this subsystem does
|
|
24
24
|
* not mandate a single `AUTH_STRATEGY` token because an app typically has
|
|
25
25
|
* many concurrent strategies, one per provider. They are dispatched through
|
|
26
26
|
* `STRATEGY_REGISTRY` (a `ReadonlyMap<slug, IProviderStrategy>`), populated
|
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
*/
|
|
29
29
|
export const ENCRYPTION_KEY = Symbol('ENCRYPTION_KEY');
|
|
30
30
|
export const OAUTH_STATE_STORE = Symbol('OAUTH_STATE_STORE');
|
|
31
|
-
export const
|
|
32
|
-
export const
|
|
33
|
-
export const
|
|
31
|
+
export const AUTH_CONNECTION_READER = Symbol('AUTH_CONNECTION_READER');
|
|
32
|
+
export const AUTH_CONNECTION_TOKEN_WRITER = Symbol('AUTH_CONNECTION_TOKEN_WRITER');
|
|
33
|
+
export const AUTH_CONNECTION_GRANT_SINK = Symbol('AUTH_CONNECTION_GRANT_SINK');
|
|
34
34
|
export const AUTH_USER_CONTEXT = Symbol('AUTH_USER_CONTEXT');
|
|
35
35
|
export const STRATEGY_REGISTRY = Symbol('STRATEGY_REGISTRY');
|
|
36
36
|
/**
|
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
* - `AUTH_USER_CONTEXT` (IUserContext) — resolves "who is this request"
|
|
16
16
|
* from the consumer's session/JWT/etc.
|
|
17
17
|
* - `OAUTH_STATE_STORE` (IOAuthStateStore) — CSRF state minting/consume.
|
|
18
|
-
* - `
|
|
18
|
+
* - `AUTH_CONNECTION_GRANT_SINK` (IConnectionGrantSink) — persists the
|
|
19
19
|
* freshly-minted grant. Adapter lives consumer-side (e.g. the
|
|
20
20
|
* auth-integrations starter from #285).
|
|
21
21
|
*
|
|
22
|
-
* The controller never imports `
|
|
22
|
+
* The controller never imports `ConnectionsService` or any other concrete
|
|
23
23
|
* consumer type — it goes through ports only.
|
|
24
24
|
*/
|
|
25
25
|
import {
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
HttpStatus,
|
|
35
35
|
} from '@nestjs/common';
|
|
36
36
|
import {
|
|
37
|
-
|
|
37
|
+
AUTH_CONNECTION_GRANT_SINK,
|
|
38
38
|
AUTH_OPTIONS,
|
|
39
39
|
AUTH_USER_CONTEXT,
|
|
40
40
|
OAUTH_STATE_STORE,
|
|
@@ -47,7 +47,7 @@ import type {
|
|
|
47
47
|
IProviderStrategy,
|
|
48
48
|
ProviderStrategyRegistry,
|
|
49
49
|
} from '../protocols/provider-strategy';
|
|
50
|
-
import type {
|
|
50
|
+
import type { IConnectionGrantSink } from '../protocols/connection-store';
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
53
|
* Minimal response surface used by the controller — typed loosely so we
|
|
@@ -67,8 +67,8 @@ export class AuthController {
|
|
|
67
67
|
private readonly userContext: IUserContext,
|
|
68
68
|
@Inject(OAUTH_STATE_STORE)
|
|
69
69
|
private readonly stateStore: IOAuthStateStore,
|
|
70
|
-
@Inject(
|
|
71
|
-
private readonly grantSink:
|
|
70
|
+
@Inject(AUTH_CONNECTION_GRANT_SINK)
|
|
71
|
+
private readonly grantSink: IConnectionGrantSink,
|
|
72
72
|
@Inject(AUTH_OPTIONS)
|
|
73
73
|
private readonly options: AuthModuleOptions,
|
|
74
74
|
) {}
|
|
@@ -127,7 +127,7 @@ export class AuthController {
|
|
|
127
127
|
});
|
|
128
128
|
return res.redirect(
|
|
129
129
|
HttpStatus.FOUND,
|
|
130
|
-
redirect ?? `/settings/
|
|
130
|
+
redirect ?? `/settings/connections?connected=${encodeURIComponent(slug)}`,
|
|
131
131
|
);
|
|
132
132
|
}
|
|
133
133
|
|