@sixb/core 0.1.1 → 0.1.3
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/README.md +31 -20
- package/dist/actions/commit-edits.d.ts +0 -2
- package/dist/actions/commit-edits.d.ts.map +1 -1
- package/dist/actions/execution.d.ts +21 -0
- package/dist/actions/execution.d.ts.map +1 -0
- package/dist/actions/index.d.ts +1 -1
- package/dist/actions/index.d.ts.map +1 -1
- package/dist/actions/index.js +38 -24
- package/dist/actions/index.js.map +1 -1
- package/dist/actions/registry.d.ts +16 -7
- package/dist/actions/registry.d.ts.map +1 -1
- package/dist/actions/request.d.ts +3 -2
- package/dist/actions/request.d.ts.map +1 -1
- package/dist/actions/run-dispatch.d.ts +21 -0
- package/dist/actions/run-dispatch.d.ts.map +1 -0
- package/dist/actions/types/context.d.ts +2 -2
- package/dist/actions/types/context.d.ts.map +1 -1
- package/dist/actions/validation.d.ts +1 -1
- package/dist/actions/validation.d.ts.map +1 -1
- package/dist/actions/worker.js +7 -7
- package/dist/actions/worker.js.map +1 -1
- package/dist/agents/adapters.d.ts +21 -1
- package/dist/agents/adapters.d.ts.map +1 -1
- package/dist/agents/authority.d.ts +31 -0
- package/dist/agents/authority.d.ts.map +1 -0
- package/dist/agents/context.js +2 -2
- package/dist/agents/dispatch.d.ts.map +1 -1
- package/dist/agents/errors.d.ts +2 -2
- package/dist/agents/errors.d.ts.map +1 -1
- package/dist/agents/execution.d.ts +48 -0
- package/dist/agents/execution.d.ts.map +1 -0
- package/dist/agents/index.d.ts +12 -9
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +77 -18
- package/dist/agents/index.js.map +1 -1
- package/dist/agents/prompt.d.ts +8 -2
- package/dist/agents/prompt.d.ts.map +1 -1
- package/dist/agents/request.d.ts +6 -2
- package/dist/agents/request.d.ts.map +1 -1
- package/dist/agents/streams.d.ts +29 -3
- package/dist/agents/streams.d.ts.map +1 -1
- package/dist/agents/streams.js +22 -3
- package/dist/agents/streams.js.map +1 -1
- package/dist/agents/thread-context-projection.d.ts +19 -0
- package/dist/agents/thread-context-projection.d.ts.map +1 -0
- package/dist/agents/tool-definition.d.ts.map +1 -1
- package/dist/agents/tool-result.d.ts +6 -0
- package/dist/agents/tool-result.d.ts.map +1 -0
- package/dist/agents/types.d.ts +39 -3
- package/dist/agents/types.d.ts.map +1 -1
- package/dist/agents/validation.d.ts +3 -3
- package/dist/agents/validation.d.ts.map +1 -1
- package/dist/auth/attribution.d.ts +17 -0
- package/dist/auth/attribution.d.ts.map +1 -0
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +11 -5
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/runtime.d.ts +4 -4
- package/dist/auth/runtime.d.ts.map +1 -1
- package/dist/authorization/decision.d.ts +24 -11
- package/dist/authorization/decision.d.ts.map +1 -1
- package/dist/authorization/grant-kinds.d.ts +2 -1
- package/dist/authorization/grant-kinds.d.ts.map +1 -1
- package/dist/authorization/index.d.ts +1 -1
- package/dist/authorization/index.d.ts.map +1 -1
- package/dist/authorization/index.js +8 -4
- package/dist/authorization/index.js.map +1 -1
- package/dist/blob-storage/browser.js +2 -2
- package/dist/blob-storage/execution.d.ts +16 -0
- package/dist/blob-storage/execution.d.ts.map +1 -0
- package/dist/blob-storage/index.js +3 -3
- package/dist/bootstrap/discovery.d.ts +90 -24
- package/dist/bootstrap/discovery.d.ts.map +1 -1
- package/dist/bootstrap/index.d.ts +2 -1
- package/dist/bootstrap/index.d.ts.map +1 -1
- package/dist/bootstrap/index.js +63 -60
- package/dist/bootstrap/index.js.map +1 -1
- package/dist/broker/in-memory.d.ts.map +1 -1
- package/dist/broker/index.js +2 -2
- package/dist/broker/types.d.ts +5 -0
- package/dist/broker/types.d.ts.map +1 -1
- package/dist/chunk-0172wgm9.js +333 -0
- package/dist/chunk-0172wgm9.js.map +11 -0
- package/dist/chunk-177ed2mp.js +3118 -0
- package/dist/chunk-177ed2mp.js.map +46 -0
- package/dist/chunk-1ywq4hzk.js +607 -0
- package/dist/chunk-1ywq4hzk.js.map +14 -0
- package/dist/chunk-261ag4t4.js +252 -0
- package/dist/chunk-261ag4t4.js.map +12 -0
- package/dist/{chunk-y2ygpd0m.js → chunk-3j7573mj.js} +2 -2
- package/dist/{chunk-y2ygpd0m.js.map → chunk-3j7573mj.js.map} +2 -2
- package/dist/{chunk-p7vj8xgb.js → chunk-3rsf30p4.js} +81 -58
- package/dist/chunk-3rsf30p4.js.map +35 -0
- package/dist/chunk-4aw0jwk0.js +118 -0
- package/dist/chunk-4aw0jwk0.js.map +10 -0
- package/dist/{chunk-md9jbrm9.js → chunk-4gz41why.js} +1 -1
- package/dist/chunk-4p9rh7jj.js +164 -0
- package/dist/chunk-4p9rh7jj.js.map +12 -0
- package/dist/chunk-4z3n1dzt.js +1206 -0
- package/dist/chunk-4z3n1dzt.js.map +23 -0
- package/dist/chunk-5gd7tpv6.js +28 -0
- package/dist/chunk-5gd7tpv6.js.map +11 -0
- package/dist/{chunk-867jxh7c.js → chunk-5sz8ta9r.js} +6 -295
- package/dist/chunk-5sz8ta9r.js.map +16 -0
- package/dist/chunk-62c7t4jc.js +27 -0
- package/dist/chunk-62c7t4jc.js.map +11 -0
- package/dist/chunk-63te770v.js.map +1 -1
- package/dist/{chunk-xz5g49bp.js → chunk-6gjqgd3d.js} +3 -1
- package/dist/chunk-6gjqgd3d.js.map +11 -0
- package/dist/{chunk-6rzdtvn5.js → chunk-8b55ygje.js} +1 -1
- package/dist/chunk-8m5ejdpn.js +33 -0
- package/dist/chunk-8m5ejdpn.js.map +10 -0
- package/dist/chunk-96swp2wd.js +47 -0
- package/dist/chunk-96swp2wd.js.map +10 -0
- package/dist/chunk-979ww6b3.js +298 -0
- package/dist/chunk-979ww6b3.js.map +12 -0
- package/dist/{chunk-ya6brh8a.js → chunk-9t8f5xfx.js} +10 -5
- package/dist/chunk-9t8f5xfx.js.map +11 -0
- package/dist/{chunk-r82k4z7z.js → chunk-a31h57d5.js} +2 -2
- package/dist/{chunk-t72dvahb.js → chunk-bhxaq2nn.js} +61 -44
- package/dist/chunk-bhxaq2nn.js.map +12 -0
- package/dist/chunk-bv41fmjn.js +3294 -0
- package/dist/chunk-bv41fmjn.js.map +35 -0
- package/dist/chunk-c6cfqydk.js +128 -0
- package/dist/chunk-c6cfqydk.js.map +13 -0
- package/dist/chunk-c8bw2b6c.js +317 -0
- package/dist/chunk-c8bw2b6c.js.map +11 -0
- package/dist/{chunk-qzyemcj7.js → chunk-ckxfnx7f.js} +2 -2
- package/dist/chunk-cy0n5dms.js +27 -0
- package/dist/chunk-cy0n5dms.js.map +11 -0
- package/dist/{chunk-xt6qx9sh.js → chunk-d2adj15j.js} +91 -9
- package/dist/chunk-d2adj15j.js.map +37 -0
- package/dist/{chunk-avppcgt6.js → chunk-d6y2wsfg.js} +241 -144
- package/dist/chunk-d6y2wsfg.js.map +15 -0
- package/dist/chunk-dsrkgtnh.js +50 -0
- package/dist/chunk-dsrkgtnh.js.map +10 -0
- package/dist/{chunk-50kv63vj.js → chunk-dysw7nsm.js} +118 -26
- package/dist/chunk-dysw7nsm.js.map +14 -0
- package/dist/{chunk-rjqm6ksg.js → chunk-ettb0cy4.js} +725 -266
- package/dist/chunk-ettb0cy4.js.map +45 -0
- package/dist/chunk-ezake7g1.js +32 -0
- package/dist/chunk-ezake7g1.js.map +11 -0
- package/dist/{chunk-b1bqvhpe.js → chunk-f1cyvnrk.js} +14 -34
- package/dist/chunk-f1cyvnrk.js.map +13 -0
- package/dist/{chunk-yn947ck3.js → chunk-f2rt4vkh.js} +48 -29
- package/dist/chunk-f2rt4vkh.js.map +14 -0
- package/dist/{chunk-hb9xcx9c.js → chunk-fmx1sz2h.js} +117 -56
- package/dist/chunk-fmx1sz2h.js.map +15 -0
- package/dist/{chunk-f6kyyeef.js → chunk-ghteq96b.js} +8 -8
- package/dist/{chunk-f6kyyeef.js.map → chunk-ghteq96b.js.map} +4 -4
- package/dist/chunk-h9kxd41f.js +271 -0
- package/dist/chunk-h9kxd41f.js.map +11 -0
- package/dist/chunk-htq0qh7z.js +59 -0
- package/dist/chunk-htq0qh7z.js.map +11 -0
- package/dist/chunk-km4rkj4t.js +402 -0
- package/dist/chunk-km4rkj4t.js.map +14 -0
- package/dist/chunk-kqxgf6kv.js +898 -0
- package/dist/chunk-kqxgf6kv.js.map +15 -0
- package/dist/chunk-n5x5y80a.js +137 -0
- package/dist/chunk-n5x5y80a.js.map +10 -0
- package/dist/chunk-nf4r78r4.js +44 -0
- package/dist/chunk-nf4r78r4.js.map +10 -0
- package/dist/chunk-pxtrxm3p.js +55 -0
- package/dist/chunk-pxtrxm3p.js.map +11 -0
- package/dist/chunk-qer6am8w.js +133 -0
- package/dist/chunk-qer6am8w.js.map +11 -0
- package/dist/{chunk-sb0shdvv.js → chunk-rdeagz6b.js} +3 -3
- package/dist/{chunk-sb0shdvv.js.map → chunk-rdeagz6b.js.map} +3 -3
- package/dist/chunk-rhrbz7d4.js +36 -0
- package/dist/chunk-rhrbz7d4.js.map +10 -0
- package/dist/{chunk-53s4asfm.js → chunk-rnshztfq.js} +2 -2
- package/dist/chunk-t0jjkep3.js +35 -0
- package/dist/chunk-t0jjkep3.js.map +10 -0
- package/dist/{chunk-be9vm29v.js → chunk-t8t39h7v.js} +1 -1
- package/dist/{chunk-be9vm29v.js.map → chunk-t8t39h7v.js.map} +3 -3
- package/dist/chunk-t9xghkva.js +28 -0
- package/dist/chunk-t9xghkva.js.map +10 -0
- package/dist/{chunk-nbywkytr.js → chunk-ttba8x6z.js} +13 -33
- package/dist/chunk-ttba8x6z.js.map +15 -0
- package/dist/{chunk-8kv2sez0.js → chunk-vybf6pkp.js} +1 -1
- package/dist/chunk-wb4051cx.js +404 -0
- package/dist/chunk-wb4051cx.js.map +12 -0
- package/dist/{chunk-4tvj3ate.js → chunk-wtmzkcht.js} +4 -3
- package/dist/chunk-wtmzkcht.js.map +11 -0
- package/dist/{chunk-xqbtf4wn.js → chunk-xc7hq65b.js} +5 -3
- package/dist/{chunk-xqbtf4wn.js.map → chunk-xc7hq65b.js.map} +3 -3
- package/dist/{chunk-v5c085yp.js → chunk-xhy826cz.js} +3 -2
- package/dist/chunk-xhy826cz.js.map +10 -0
- package/dist/chunk-xkthy56g.js +33 -0
- package/dist/chunk-xkthy56g.js.map +11 -0
- package/dist/{chunk-raq0x6bd.js → chunk-xmggaxg4.js} +2199 -380
- package/dist/chunk-xmggaxg4.js.map +54 -0
- package/dist/{chunk-6zrpewy4.js → chunk-z3zp0bj1.js} +1 -1
- package/dist/chunk-z8j83gzd.js +90 -0
- package/dist/chunk-z8j83gzd.js.map +10 -0
- package/dist/chunk-zcnhxkvd.js +7 -0
- package/dist/chunk-zcnhxkvd.js.map +10 -0
- package/dist/chunk-zpw39qhj.js +194 -0
- package/dist/chunk-zpw39qhj.js.map +10 -0
- package/dist/chunk-zqqz8052.js +3 -0
- package/dist/{storage/projection-runs/lifecycle.js.map → chunk-zqqz8052.js.map} +1 -1
- package/dist/{chunk-hz8m927w.js → chunk-zqysyfjt.js} +1 -1
- package/dist/connectors/builders.d.ts +2 -1
- package/dist/connectors/builders.d.ts.map +1 -1
- package/dist/connectors/connections/authorizations.d.ts +64 -0
- package/dist/connectors/connections/authorizations.d.ts.map +1 -0
- package/dist/connectors/connections/capability.d.ts +7 -0
- package/dist/connectors/connections/capability.d.ts.map +1 -0
- package/dist/connectors/connections/command-context.d.ts +23 -0
- package/dist/connectors/connections/command-context.d.ts.map +1 -0
- package/dist/connectors/connections/contracts.d.ts +127 -0
- package/dist/connectors/connections/contracts.d.ts.map +1 -0
- package/dist/connectors/connections/credential-codec.d.ts +15 -0
- package/dist/connectors/connections/credential-codec.d.ts.map +1 -0
- package/dist/connectors/connections/credential-mutations.d.ts +63 -0
- package/dist/connectors/connections/credential-mutations.d.ts.map +1 -0
- package/dist/connectors/connections/execution.d.ts +20 -0
- package/dist/connectors/connections/execution.d.ts.map +1 -0
- package/dist/connectors/connections/internal.d.ts +5 -0
- package/dist/connectors/connections/internal.d.ts.map +1 -0
- package/dist/connectors/connections/internal.js +16 -0
- package/dist/connectors/connections/internal.js.map +9 -0
- package/dist/connectors/connections/provider-operation.d.ts +7 -0
- package/dist/connectors/connections/provider-operation.d.ts.map +1 -0
- package/dist/connectors/connections/request.d.ts +84 -0
- package/dist/connectors/connections/request.d.ts.map +1 -0
- package/dist/connectors/connections/runs.d.ts +51 -0
- package/dist/connectors/connections/runs.d.ts.map +1 -0
- package/dist/connectors/connections/service.d.ts +52 -0
- package/dist/connectors/connections/service.d.ts.map +1 -0
- package/dist/connectors/connections/storage-boundary.d.ts +3 -0
- package/dist/connectors/connections/storage-boundary.d.ts.map +1 -0
- package/dist/connectors/connections/tokens.d.ts +32 -0
- package/dist/connectors/connections/tokens.d.ts.map +1 -0
- package/dist/connectors/connections/validation.d.ts +27 -0
- package/dist/connectors/connections/validation.d.ts.map +1 -0
- package/dist/connectors/connections/views.d.ts +7 -0
- package/dist/connectors/connections/views.d.ts.map +1 -0
- package/dist/connectors/credentials.d.ts +25 -0
- package/dist/connectors/credentials.d.ts.map +1 -0
- package/dist/connectors/errors.d.ts +35 -3
- package/dist/connectors/errors.d.ts.map +1 -1
- package/dist/connectors/execution.d.ts +19 -0
- package/dist/connectors/execution.d.ts.map +1 -0
- package/dist/connectors/index.d.ts +5 -3
- package/dist/connectors/index.d.ts.map +1 -1
- package/dist/connectors/service.d.ts +23 -0
- package/dist/connectors/service.d.ts.map +1 -0
- package/dist/connectors/types.d.ts +94 -2
- package/dist/connectors/types.d.ts.map +1 -1
- package/dist/datasets/execution.d.ts +8 -0
- package/dist/datasets/execution.d.ts.map +1 -0
- package/dist/error-reporting/capability.d.ts +4 -49
- package/dist/error-reporting/capability.d.ts.map +1 -1
- package/dist/error-reporting/internal.d.ts +2 -2
- package/dist/error-reporting/internal.d.ts.map +1 -1
- package/dist/error-reporting/internal.js +10 -4
- package/dist/error-reporting/internal.js.map +1 -1
- package/dist/error-reporting/reporter.d.ts +4 -1
- package/dist/error-reporting/reporter.d.ts.map +1 -1
- package/dist/error-reporting/reports.d.ts +64 -0
- package/dist/error-reporting/reports.d.ts.map +1 -0
- package/dist/error-reporting/types.d.ts +88 -34
- package/dist/error-reporting/types.d.ts.map +1 -1
- package/dist/errors/catalog.d.ts +141 -0
- package/dist/errors/catalog.d.ts.map +1 -0
- package/dist/errors/internal.d.ts +68 -0
- package/dist/errors/internal.d.ts.map +1 -0
- package/dist/errors/internal.js +29 -0
- package/dist/errors/internal.js.map +9 -0
- package/dist/errors/types.d.ts +20 -0
- package/dist/errors/types.d.ts.map +1 -0
- package/dist/events/definitions.d.ts +0 -1
- package/dist/events/definitions.d.ts.map +1 -1
- package/dist/events/execution.d.ts +13 -0
- package/dist/events/execution.d.ts.map +1 -0
- package/dist/events/index.d.ts +3 -2
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +10 -8
- package/dist/events/index.js.map +1 -1
- package/dist/events/outbox-dispatcher.d.ts +4 -2
- package/dist/events/outbox-dispatcher.d.ts.map +1 -1
- package/dist/events/{runtime.d.ts → service.d.ts} +13 -14
- package/dist/events/service.d.ts.map +1 -0
- package/dist/events/types/pipelines.d.ts +4 -1
- package/dist/events/types/pipelines.d.ts.map +1 -1
- package/dist/events/types/syncs.d.ts +3 -0
- package/dist/events/types/syncs.d.ts.map +1 -1
- package/dist/events/types/workflows.d.ts +4 -2
- package/dist/events/types/workflows.d.ts.map +1 -1
- package/dist/execution/agent.d.ts +42 -0
- package/dist/execution/agent.d.ts.map +1 -0
- package/dist/execution/agent.js +73 -0
- package/dist/execution/agent.js.map +9 -0
- package/dist/execution/authorization.d.ts +60 -0
- package/dist/execution/authorization.d.ts.map +1 -0
- package/dist/execution/dispatch.d.ts +8 -0
- package/dist/execution/dispatch.d.ts.map +1 -0
- package/dist/execution/durable.d.ts +37 -0
- package/dist/execution/durable.d.ts.map +1 -0
- package/dist/execution/index.d.ts +2 -0
- package/dist/execution/index.d.ts.map +1 -0
- package/dist/execution/primitive.d.ts +21 -0
- package/dist/execution/primitive.d.ts.map +1 -0
- package/dist/execution/primitive.js +91 -0
- package/dist/execution/primitive.js.map +10 -0
- package/dist/execution/request.d.ts +38 -0
- package/dist/execution/request.d.ts.map +1 -0
- package/dist/execution/request.js +100 -0
- package/dist/execution/request.js.map +10 -0
- package/dist/execution/scopes.d.ts +30 -0
- package/dist/execution/scopes.d.ts.map +1 -0
- package/dist/execution/types.d.ts +111 -0
- package/dist/execution/types.d.ts.map +1 -0
- package/dist/index.d.ts +31 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +133 -105
- package/dist/index.js.map +1 -1
- package/dist/json.d.ts +0 -1
- package/dist/json.d.ts.map +1 -1
- package/dist/lake-storage/index.js +5 -5
- package/dist/logging/execution.d.ts +9 -0
- package/dist/logging/execution.d.ts.map +1 -0
- package/dist/logging/index.d.ts +1 -1
- package/dist/logging/index.d.ts.map +1 -1
- package/dist/logging/index.js +8 -8
- package/dist/logging/index.js.map +1 -1
- package/dist/logging/{runtime.d.ts → service.d.ts} +7 -7
- package/dist/logging/{runtime.d.ts.map → service.d.ts.map} +1 -1
- package/dist/logging/types.js +2 -2
- package/dist/materialization/events.d.ts +1 -0
- package/dist/materialization/events.d.ts.map +1 -1
- package/dist/materialization/index.d.ts +1 -1
- package/dist/materialization/index.d.ts.map +1 -1
- package/dist/materialization/index.js +2 -2
- package/dist/materialization/model.d.ts +30 -17
- package/dist/materialization/model.d.ts.map +1 -1
- package/dist/materializer/edits/apply.d.ts +6 -1
- package/dist/materializer/edits/apply.d.ts.map +1 -1
- package/dist/materializer/edits/commit.d.ts +2 -1
- package/dist/materializer/edits/commit.d.ts.map +1 -1
- package/dist/materializer/edits/load-state.d.ts +1 -1
- package/dist/materializer/edits/load-state.d.ts.map +1 -1
- package/dist/materializer/edits/operations.d.ts +1 -13
- package/dist/materializer/edits/operations.d.ts.map +1 -1
- package/dist/materializer/edits/plan.d.ts +2 -1
- package/dist/materializer/edits/plan.d.ts.map +1 -1
- package/dist/materializer/edits/working-state.d.ts +38 -8
- package/dist/materializer/edits/working-state.d.ts.map +1 -1
- package/dist/materializer/effective/build-events.d.ts +1 -0
- package/dist/materializer/effective/build-events.d.ts.map +1 -1
- package/dist/materializer/effective/diff.d.ts +7 -0
- package/dist/materializer/effective/diff.d.ts.map +1 -1
- package/dist/materializer/effective/load-state.d.ts +0 -2
- package/dist/materializer/effective/load-state.d.ts.map +1 -1
- package/dist/materializer/effective/resolve.d.ts +19 -2
- package/dist/materializer/effective/resolve.d.ts.map +1 -1
- package/dist/materializer/execution/commit-lifecycle.d.ts +2 -2
- package/dist/materializer/execution/commit-lifecycle.d.ts.map +1 -1
- package/dist/materializer/execution/plan-stream.d.ts.map +1 -1
- package/dist/materializer/execution/scope.d.ts +26 -0
- package/dist/materializer/execution/scope.d.ts.map +1 -0
- package/dist/materializer/execution/work-executor.d.ts.map +1 -1
- package/dist/materializer/execution/work-records.d.ts +6 -5
- package/dist/materializer/execution/work-records.d.ts.map +1 -1
- package/dist/materializer/index.d.ts +2 -2
- package/dist/materializer/index.d.ts.map +1 -1
- package/dist/materializer/index.js +33 -18
- package/dist/materializer/index.js.map +1 -1
- package/dist/materializer/materializer.d.ts +37 -5
- package/dist/materializer/materializer.d.ts.map +1 -1
- package/dist/materializer/projections/finish-run.d.ts +2 -1
- package/dist/materializer/projections/finish-run.d.ts.map +1 -1
- package/dist/materializer/projections/replace.d.ts +2 -1
- package/dist/materializer/projections/replace.d.ts.map +1 -1
- package/dist/materializer/projections/replacement-plan.d.ts +1 -0
- package/dist/materializer/projections/replacement-plan.d.ts.map +1 -1
- package/dist/materializer/shared/batching.d.ts.map +1 -1
- package/dist/materializer/shared/normalize.d.ts.map +1 -1
- package/dist/materializer/telemetry/append.d.ts +2 -1
- package/dist/materializer/telemetry/append.d.ts.map +1 -1
- package/dist/materializer/telemetry/plan.d.ts +7 -1
- package/dist/materializer/telemetry/plan.d.ts.map +1 -1
- package/dist/objects/action/request.d.ts +3 -3
- package/dist/objects/action/request.d.ts.map +1 -1
- package/dist/objects/context/index.d.ts +1 -1
- package/dist/objects/context/index.d.ts.map +1 -1
- package/dist/objects/context/types.d.ts +5 -0
- package/dist/objects/context/types.d.ts.map +1 -1
- package/dist/objects/execution.d.ts +100 -0
- package/dist/objects/execution.d.ts.map +1 -0
- package/dist/objects/index.js +40 -24
- package/dist/objects/index.js.map +1 -1
- package/dist/objects/materializer-adapter.d.ts +1 -1
- package/dist/objects/materializer-adapter.d.ts.map +1 -1
- package/dist/objects/query/executor.d.ts +3 -8
- package/dist/objects/query/executor.d.ts.map +1 -1
- package/dist/objects/query/index.d.ts +2 -2
- package/dist/objects/query/index.d.ts.map +1 -1
- package/dist/objects/query/index.js +9 -13
- package/dist/objects/query/index.js.map +1 -1
- package/dist/objects/query/planner.d.ts +0 -3
- package/dist/objects/query/planner.d.ts.map +1 -1
- package/dist/objects/sdk/object-handle.d.ts +2 -2
- package/dist/objects/sdk/object-handle.d.ts.map +1 -1
- package/dist/objects/sdk/object-set.d.ts +2 -0
- package/dist/objects/sdk/object-set.d.ts.map +1 -1
- package/dist/objects/sdk/runtime-query-executor.d.ts +2 -0
- package/dist/objects/sdk/runtime-query-executor.d.ts.map +1 -1
- package/dist/objects/sdk/telemetry-channel.d.ts.map +1 -1
- package/dist/objects/service/action-service.d.ts +3 -2
- package/dist/objects/service/action-service.d.ts.map +1 -1
- package/dist/objects/service/list-service.d.ts +1 -1
- package/dist/objects/telemetry/history.d.ts +2 -0
- package/dist/objects/telemetry/history.d.ts.map +1 -1
- package/dist/objects/telemetry/write-batch.d.ts.map +1 -1
- package/dist/ontology/index.d.ts +1 -1
- package/dist/ontology/index.d.ts.map +1 -1
- package/dist/ontology/index.js +5 -5
- package/dist/ontology/internal.js +4 -4
- package/dist/ontology/registry.d.ts +15 -2
- package/dist/ontology/registry.d.ts.map +1 -1
- package/dist/pipelines/execution.d.ts +19 -0
- package/dist/pipelines/execution.d.ts.map +1 -0
- package/dist/pipelines/index.d.ts +1 -0
- package/dist/pipelines/index.d.ts.map +1 -1
- package/dist/pipelines/index.js +68 -0
- package/dist/pipelines/index.js.map +9 -0
- package/dist/pipelines/request.d.ts +6 -9
- package/dist/pipelines/request.d.ts.map +1 -1
- package/dist/pipelines/run-dispatch.d.ts +47 -0
- package/dist/pipelines/run-dispatch.d.ts.map +1 -0
- package/dist/projections/builders.d.ts +19 -8
- package/dist/projections/builders.d.ts.map +1 -1
- package/dist/projections/capability.d.ts +7 -0
- package/dist/projections/capability.d.ts.map +1 -0
- package/dist/projections/execution.d.ts +19 -0
- package/dist/projections/execution.d.ts.map +1 -0
- package/dist/projections/index.d.ts +5 -2
- package/dist/projections/index.d.ts.map +1 -1
- package/dist/projections/internal.d.ts +8 -11
- package/dist/projections/internal.d.ts.map +1 -1
- package/dist/projections/internal.js +48 -6
- package/dist/projections/internal.js.map +1 -1
- package/dist/projections/ownership.d.ts +2 -2
- package/dist/projections/ownership.d.ts.map +1 -1
- package/dist/projections/registry.d.ts +21 -11
- package/dist/projections/registry.d.ts.map +1 -1
- package/dist/projections/revision.d.ts +2 -2
- package/dist/projections/revision.d.ts.map +1 -1
- package/dist/projections/run-dispatch.d.ts +37 -0
- package/dist/projections/run-dispatch.d.ts.map +1 -0
- package/dist/projections/run-id.d.ts +3 -0
- package/dist/projections/run-id.d.ts.map +1 -0
- package/dist/projections/timestamp.d.ts +3 -0
- package/dist/projections/timestamp.d.ts.map +1 -0
- package/dist/projections/types.d.ts +12 -0
- package/dist/projections/types.d.ts.map +1 -1
- package/dist/projections/validation.d.ts +2 -2
- package/dist/projections/validation.d.ts.map +1 -1
- package/dist/queues/in-memory.d.ts +11 -10
- package/dist/queues/in-memory.d.ts.map +1 -1
- package/dist/queues/index.d.ts +1 -1
- package/dist/queues/index.d.ts.map +1 -1
- package/dist/queues/index.js +1 -1
- package/dist/queues/types.d.ts +59 -42
- package/dist/queues/types.d.ts.map +1 -1
- package/dist/rules/execution.d.ts +14 -0
- package/dist/rules/execution.d.ts.map +1 -0
- package/dist/rules/index.d.ts +1 -1
- package/dist/rules/index.d.ts.map +1 -1
- package/dist/rules/validation.d.ts +1 -2
- package/dist/rules/validation.d.ts.map +1 -1
- package/dist/runtime/ai-cost.d.ts +8 -0
- package/dist/runtime/ai-cost.d.ts.map +1 -0
- package/dist/runtime/ai-usage.d.ts +8 -0
- package/dist/runtime/ai-usage.d.ts.map +1 -0
- package/dist/runtime/create.d.ts +7 -5
- package/dist/runtime/create.d.ts.map +1 -1
- package/dist/runtime/definitions.d.ts +35 -0
- package/dist/runtime/definitions.d.ts.map +1 -0
- package/dist/runtime/host.d.ts +123 -0
- package/dist/runtime/host.d.ts.map +1 -0
- package/dist/runtime/index.d.ts +6 -5
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/internal.d.ts +4 -0
- package/dist/runtime/internal.d.ts.map +1 -1
- package/dist/runtime/internal.js +6 -2
- package/dist/runtime/internal.js.map +1 -1
- package/dist/runtime/ontology-mutations.d.ts +4 -2
- package/dist/runtime/ontology-mutations.d.ts.map +1 -1
- package/dist/runtime/resolve-definitions.d.ts +42 -0
- package/dist/runtime/resolve-definitions.d.ts.map +1 -0
- package/dist/runtime/sixb.d.ts +43 -219
- package/dist/runtime/sixb.d.ts.map +1 -1
- package/dist/runtime/types.d.ts +16 -216
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/scheduler/index.d.ts +1 -1
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/index.js +3 -6
- package/dist/scheduler/index.js.map +1 -1
- package/dist/scheduler/runtime.d.ts +7 -2
- package/dist/scheduler/runtime.d.ts.map +1 -1
- package/dist/schedules/execution.d.ts +6 -0
- package/dist/schedules/execution.d.ts.map +1 -0
- package/dist/schedules/index.js +9 -7
- package/dist/schedules/index.js.map +1 -1
- package/dist/schedules/types.d.ts +1 -3
- package/dist/schedules/types.d.ts.map +1 -1
- package/dist/security/every.d.ts +4 -1
- package/dist/security/every.d.ts.map +1 -1
- package/dist/security/grants.d.ts +4 -1
- package/dist/security/grants.d.ts.map +1 -1
- package/dist/security/index.d.ts +3 -1
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/registry.d.ts +42 -0
- package/dist/security/registry.d.ts.map +1 -0
- package/dist/security/types.d.ts +7 -12
- package/dist/security/types.d.ts.map +1 -1
- package/dist/security/validation.d.ts +2 -0
- package/dist/security/validation.d.ts.map +1 -1
- package/dist/storage/action-runs/failure.d.ts +7 -0
- package/dist/storage/action-runs/failure.d.ts.map +1 -0
- package/dist/storage/action-runs/failure.js +14 -0
- package/dist/storage/action-runs/failure.js.map +9 -0
- package/dist/storage/action-runs/idempotency.d.ts.map +1 -1
- package/dist/storage/action-runs/in-memory.d.ts +3 -1
- package/dist/storage/action-runs/in-memory.d.ts.map +1 -1
- package/dist/storage/action-runs/index.d.ts +2 -1
- package/dist/storage/action-runs/index.d.ts.map +1 -1
- package/dist/storage/action-runs/provider.d.ts +10 -0
- package/dist/storage/action-runs/provider.d.ts.map +1 -0
- package/dist/storage/action-runs/provider.js +13 -0
- package/dist/storage/action-runs/provider.js.map +9 -0
- package/dist/storage/action-runs/types.d.ts +37 -18
- package/dist/storage/action-runs/types.d.ts.map +1 -1
- package/dist/storage/agents/errors.d.ts +1 -1
- package/dist/storage/agents/errors.d.ts.map +1 -1
- package/dist/storage/agents/in-memory.d.ts +22 -5
- package/dist/storage/agents/in-memory.d.ts.map +1 -1
- package/dist/storage/agents/index.d.ts +2 -2
- package/dist/storage/agents/index.d.ts.map +1 -1
- package/dist/storage/agents/provider.d.ts +41 -0
- package/dist/storage/agents/provider.d.ts.map +1 -0
- package/dist/storage/agents/provider.js +28 -0
- package/dist/storage/agents/provider.js.map +9 -0
- package/dist/storage/agents/types.d.ts +83 -26
- package/dist/storage/agents/types.d.ts.map +1 -1
- package/dist/storage/ai-cost/analytics.d.ts +61 -0
- package/dist/storage/ai-cost/analytics.d.ts.map +1 -0
- package/dist/storage/ai-cost/codec.d.ts +14 -0
- package/dist/storage/ai-cost/codec.d.ts.map +1 -0
- package/dist/storage/ai-cost/errors.d.ts +7 -0
- package/dist/storage/ai-cost/errors.d.ts.map +1 -0
- package/dist/storage/ai-cost/in-memory.d.ts +32 -0
- package/dist/storage/ai-cost/in-memory.d.ts.map +1 -0
- package/dist/storage/ai-cost/index.d.ts +6 -0
- package/dist/storage/ai-cost/index.d.ts.map +1 -0
- package/dist/storage/ai-cost/provider.d.ts +7 -0
- package/dist/storage/ai-cost/provider.d.ts.map +1 -0
- package/dist/storage/ai-cost/provider.js +132 -0
- package/dist/storage/ai-cost/provider.js.map +10 -0
- package/dist/storage/ai-cost/types.d.ts +154 -0
- package/dist/storage/ai-cost/types.d.ts.map +1 -0
- package/dist/storage/ai-cost/validation.d.ts +7 -0
- package/dist/storage/ai-cost/validation.d.ts.map +1 -0
- package/dist/storage/ai-usage/errors.d.ts +8 -0
- package/dist/storage/ai-usage/errors.d.ts.map +1 -0
- package/dist/storage/ai-usage/in-memory.d.ts +28 -0
- package/dist/storage/ai-usage/in-memory.d.ts.map +1 -0
- package/dist/storage/ai-usage/index.d.ts +8 -0
- package/dist/storage/ai-usage/index.d.ts.map +1 -0
- package/dist/storage/ai-usage/record.d.ts +6 -0
- package/dist/storage/ai-usage/record.d.ts.map +1 -0
- package/dist/storage/ai-usage/types.d.ts +89 -0
- package/dist/storage/ai-usage/types.d.ts.map +1 -0
- package/dist/storage/ai-usage/usage.d.ts +11 -0
- package/dist/storage/ai-usage/usage.d.ts.map +1 -0
- package/dist/storage/auth/types.d.ts +1 -0
- package/dist/storage/auth/types.d.ts.map +1 -1
- package/dist/storage/connector-connections/errors.d.ts +12 -0
- package/dist/storage/connector-connections/errors.d.ts.map +1 -0
- package/dist/storage/connector-connections/in-memory.d.ts +81 -0
- package/dist/storage/connector-connections/in-memory.d.ts.map +1 -0
- package/dist/storage/connector-connections/index.d.ts +7 -0
- package/dist/storage/connector-connections/index.d.ts.map +1 -0
- package/dist/storage/connector-connections/provider/authorizations.d.ts +20 -0
- package/dist/storage/connector-connections/provider/authorizations.d.ts.map +1 -0
- package/dist/storage/connector-connections/provider/connections.d.ts +17 -0
- package/dist/storage/connector-connections/provider/connections.d.ts.map +1 -0
- package/dist/storage/connector-connections/provider/runs.d.ts +14 -0
- package/dist/storage/connector-connections/provider/runs.d.ts.map +1 -0
- package/dist/storage/connector-connections/provider/shared.d.ts +74 -0
- package/dist/storage/connector-connections/provider/shared.d.ts.map +1 -0
- package/dist/storage/connector-connections/provider.d.ts +49 -0
- package/dist/storage/connector-connections/provider.d.ts.map +1 -0
- package/dist/storage/connector-connections/provider.js +1025 -0
- package/dist/storage/connector-connections/provider.js.map +14 -0
- package/dist/storage/connector-connections/types.d.ts +402 -0
- package/dist/storage/connector-connections/types.d.ts.map +1 -0
- package/dist/storage/executions/errors.d.ts +8 -0
- package/dist/storage/executions/errors.d.ts.map +1 -0
- package/dist/storage/executions/in-memory.d.ts +21 -0
- package/dist/storage/executions/in-memory.d.ts.map +1 -0
- package/dist/storage/executions/index.d.ts +5 -0
- package/dist/storage/executions/index.d.ts.map +1 -0
- package/dist/storage/executions/provider.d.ts +29 -0
- package/dist/storage/executions/provider.d.ts.map +1 -0
- package/dist/storage/executions/provider.js +208 -0
- package/dist/storage/executions/provider.js.map +10 -0
- package/dist/storage/executions/run-link.d.ts +24 -0
- package/dist/storage/executions/run-link.d.ts.map +1 -0
- package/dist/storage/executions/types.d.ts +62 -0
- package/dist/storage/executions/types.d.ts.map +1 -0
- package/dist/storage/executions/validation.d.ts +13 -0
- package/dist/storage/executions/validation.d.ts.map +1 -0
- package/dist/storage/in-memory/index.d.ts +25 -5
- package/dist/storage/in-memory/index.d.ts.map +1 -1
- package/dist/storage/index.d.ts +30 -17
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +99 -29
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/ontology/commits.d.ts +1 -0
- package/dist/storage/ontology/commits.d.ts.map +1 -1
- package/dist/storage/ontology/in-memory/index.d.ts +1 -0
- package/dist/storage/ontology/in-memory/index.d.ts.map +1 -1
- package/dist/storage/ontology/in-memory/materializations-state.d.ts +3 -2
- package/dist/storage/ontology/in-memory/materializations-state.d.ts.map +1 -1
- package/dist/storage/ontology/in-memory/materializations-work.d.ts +2 -2
- package/dist/storage/ontology/in-memory/materializations-work.d.ts.map +1 -1
- package/dist/storage/ontology/in-memory/materializations.d.ts +3 -1
- package/dist/storage/ontology/in-memory/materializations.d.ts.map +1 -1
- package/dist/storage/ontology/in-memory/shared-state.d.ts +7 -2
- package/dist/storage/ontology/in-memory/shared-state.d.ts.map +1 -1
- package/dist/storage/ontology/index.d.ts +3 -2
- package/dist/storage/ontology/index.d.ts.map +1 -1
- package/dist/storage/ontology/materializations.d.ts +53 -5
- package/dist/storage/ontology/materializations.d.ts.map +1 -1
- package/dist/storage/ontology/outbox.d.ts +8 -2
- package/dist/storage/ontology/outbox.d.ts.map +1 -1
- package/dist/storage/ontology/provider-work.d.ts.map +1 -1
- package/dist/storage/ontology/provider.d.ts +5 -22
- package/dist/storage/ontology/provider.d.ts.map +1 -1
- package/dist/storage/ontology/provider.js +6 -7
- package/dist/storage/ontology/provider.js.map +1 -1
- package/dist/storage/operation-scope.d.ts.map +1 -1
- package/dist/storage/operation-scope.js +1 -1
- package/dist/storage/pipeline-runs/idempotency.d.ts +3 -0
- package/dist/storage/pipeline-runs/idempotency.d.ts.map +1 -0
- package/dist/storage/pipeline-runs/in-memory.d.ts +5 -1
- package/dist/storage/pipeline-runs/in-memory.d.ts.map +1 -1
- package/dist/storage/pipeline-runs/index.d.ts +3 -1
- package/dist/storage/pipeline-runs/index.d.ts.map +1 -1
- package/dist/storage/pipeline-runs/provider.d.ts +10 -0
- package/dist/storage/pipeline-runs/provider.d.ts.map +1 -0
- package/dist/storage/pipeline-runs/provider.js +11 -0
- package/dist/storage/pipeline-runs/provider.js.map +9 -0
- package/dist/storage/pipeline-runs/types.d.ts +30 -14
- package/dist/storage/pipeline-runs/types.d.ts.map +1 -1
- package/dist/storage/projection-runs/in-memory.d.ts +8 -3
- package/dist/storage/projection-runs/in-memory.d.ts.map +1 -1
- package/dist/storage/projection-runs/index.d.ts +2 -2
- package/dist/storage/projection-runs/index.d.ts.map +1 -1
- package/dist/storage/projection-runs/lifecycle.d.ts +14 -8
- package/dist/storage/projection-runs/lifecycle.d.ts.map +1 -1
- package/dist/storage/projection-runs/provider.d.ts +13 -0
- package/dist/storage/projection-runs/provider.d.ts.map +1 -0
- package/dist/storage/projection-runs/{lifecycle.js → provider.js} +15 -4
- package/dist/storage/projection-runs/provider.js.map +9 -0
- package/dist/storage/projection-runs/types.d.ts +28 -11
- package/dist/storage/projection-runs/types.d.ts.map +1 -1
- package/dist/storage/run-listing.d.ts +8 -9
- package/dist/storage/run-listing.d.ts.map +1 -1
- package/dist/storage/sync-runs/idempotency.d.ts +3 -0
- package/dist/storage/sync-runs/idempotency.d.ts.map +1 -0
- package/dist/storage/sync-runs/in-memory.d.ts +5 -1
- package/dist/storage/sync-runs/in-memory.d.ts.map +1 -1
- package/dist/storage/sync-runs/index.d.ts +3 -1
- package/dist/storage/sync-runs/index.d.ts.map +1 -1
- package/dist/storage/sync-runs/provider.d.ts +10 -0
- package/dist/storage/sync-runs/provider.d.ts.map +1 -0
- package/dist/storage/sync-runs/provider.js +11 -0
- package/dist/storage/sync-runs/provider.js.map +9 -0
- package/dist/storage/sync-runs/types.d.ts +28 -11
- package/dist/storage/sync-runs/types.d.ts.map +1 -1
- package/dist/storage/types.d.ts +22 -15
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/storage/webhook-runs/errors.d.ts +3 -0
- package/dist/storage/webhook-runs/errors.d.ts.map +1 -1
- package/dist/storage/webhook-runs/in-memory.d.ts +13 -1
- package/dist/storage/webhook-runs/in-memory.d.ts.map +1 -1
- package/dist/storage/webhook-runs/index.d.ts +3 -2
- package/dist/storage/webhook-runs/index.d.ts.map +1 -1
- package/dist/storage/webhook-runs/provider.d.ts +10 -0
- package/dist/storage/webhook-runs/provider.d.ts.map +1 -0
- package/dist/storage/webhook-runs/provider.js +11 -0
- package/dist/storage/webhook-runs/provider.js.map +9 -0
- package/dist/storage/webhook-runs/types.d.ts +37 -11
- package/dist/storage/webhook-runs/types.d.ts.map +1 -1
- package/dist/storage/workflow-runs/in-memory.d.ts +6 -2
- package/dist/storage/workflow-runs/in-memory.d.ts.map +1 -1
- package/dist/storage/workflow-runs/index.d.ts +2 -1
- package/dist/storage/workflow-runs/index.d.ts.map +1 -1
- package/dist/storage/workflow-runs/provider.d.ts +19 -0
- package/dist/storage/workflow-runs/provider.d.ts.map +1 -0
- package/dist/storage/workflow-runs/provider.js +20 -0
- package/dist/storage/workflow-runs/provider.js.map +9 -0
- package/dist/storage/workflow-runs/types.d.ts +28 -30
- package/dist/storage/workflow-runs/types.d.ts.map +1 -1
- package/dist/syncs/execution.d.ts +18 -0
- package/dist/syncs/execution.d.ts.map +1 -0
- package/dist/syncs/index.d.ts +3 -1
- package/dist/syncs/index.d.ts.map +1 -1
- package/dist/syncs/index.js +67 -0
- package/dist/syncs/index.js.map +9 -0
- package/dist/syncs/request.d.ts +6 -16
- package/dist/syncs/request.d.ts.map +1 -1
- package/dist/syncs/run-dispatch.d.ts +47 -0
- package/dist/syncs/run-dispatch.d.ts.map +1 -0
- package/dist/syncs/sources.d.ts +8 -0
- package/dist/syncs/sources.d.ts.map +1 -0
- package/dist/syncs/types.d.ts +16 -9
- package/dist/syncs/types.d.ts.map +1 -1
- package/dist/testing/action-execution.d.ts +12 -0
- package/dist/testing/action-execution.d.ts.map +1 -0
- package/dist/testing/agent-execution.d.ts +14 -0
- package/dist/testing/agent-execution.d.ts.map +1 -0
- package/dist/testing/agent-storage-contract.d.ts +12 -4
- package/dist/testing/agent-storage-contract.d.ts.map +1 -1
- package/dist/testing/ai-cost-storage-contract.d.ts +12 -0
- package/dist/testing/ai-cost-storage-contract.d.ts.map +1 -0
- package/dist/testing/ai-usage-storage-contract.d.ts +13 -0
- package/dist/testing/ai-usage-storage-contract.d.ts.map +1 -0
- package/dist/testing/auth-storage-contract.d.ts.map +1 -1
- package/dist/testing/broker-contract.d.ts.map +1 -1
- package/dist/testing/connector-connection-storage-contract.d.ts +16 -0
- package/dist/testing/connector-connection-storage-contract.d.ts.map +1 -0
- package/dist/testing/execution-storage-contract.d.ts +15 -0
- package/dist/testing/execution-storage-contract.d.ts.map +1 -0
- package/dist/testing/execution.d.ts +23 -0
- package/dist/testing/execution.d.ts.map +1 -0
- package/dist/testing/index.d.ts +13 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +4754 -1592
- package/dist/testing/index.js.map +26 -13
- package/dist/testing/materializer-fixture.d.ts +2 -2
- package/dist/testing/materializer-fixture.d.ts.map +1 -1
- package/dist/testing/materializer-storage-contract.d.ts.map +1 -1
- package/dist/testing/ontology-contract-fixture.d.ts +1 -0
- package/dist/testing/ontology-contract-fixture.d.ts.map +1 -1
- package/dist/testing/ontology-storage-contract.d.ts.map +1 -1
- package/dist/testing/pipeline-execution.d.ts +16 -0
- package/dist/testing/pipeline-execution.d.ts.map +1 -0
- package/dist/testing/projection-execution.d.ts +20 -0
- package/dist/testing/projection-execution.d.ts.map +1 -0
- package/dist/testing/projection-run-storage-contract.d.ts +9 -4
- package/dist/testing/projection-run-storage-contract.d.ts.map +1 -1
- package/dist/testing/queues-contract.d.ts.map +1 -1
- package/dist/testing/sync-execution.d.ts +16 -0
- package/dist/testing/sync-execution.d.ts.map +1 -0
- package/dist/testing/webhook-execution.d.ts +12 -0
- package/dist/testing/webhook-execution.d.ts.map +1 -0
- package/dist/testing/webhook-run-storage-contract.d.ts +11 -0
- package/dist/testing/webhook-run-storage-contract.d.ts.map +1 -0
- package/dist/testing/workflow-execution.d.ts +23 -0
- package/dist/testing/workflow-execution.d.ts.map +1 -0
- package/dist/webhooks/index.d.ts +2 -1
- package/dist/webhooks/index.d.ts.map +1 -1
- package/dist/webhooks/internal.d.ts +2 -0
- package/dist/webhooks/internal.d.ts.map +1 -0
- package/dist/webhooks/internal.js +152 -0
- package/dist/webhooks/internal.js.map +10 -0
- package/dist/webhooks/registry.d.ts +18 -4
- package/dist/webhooks/registry.d.ts.map +1 -1
- package/dist/webhooks/run-admission.d.ts +25 -0
- package/dist/webhooks/run-admission.d.ts.map +1 -0
- package/dist/webhooks/types.d.ts +8 -5
- package/dist/webhooks/types.d.ts.map +1 -1
- package/dist/workers/index.js +29 -20
- package/dist/workers/index.js.map +4 -4
- package/dist/workers/queue-delivery.d.ts +7 -7
- package/dist/workers/queue-delivery.d.ts.map +1 -1
- package/dist/workers/queue-worker.d.ts +32 -11
- package/dist/workers/queue-worker.d.ts.map +1 -1
- package/dist/workflows/execution.d.ts +52 -0
- package/dist/workflows/execution.d.ts.map +1 -0
- package/dist/workflows/failure.d.ts +28 -0
- package/dist/workflows/failure.d.ts.map +1 -0
- package/dist/workflows/index.d.ts +4 -2
- package/dist/workflows/index.d.ts.map +1 -1
- package/dist/workflows/index.js +67 -36
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/request.d.ts +2 -1
- package/dist/workflows/request.d.ts.map +1 -1
- package/dist/workflows/run-dispatch.d.ts +59 -0
- package/dist/workflows/run-dispatch.d.ts.map +1 -0
- package/dist/workflows/types.d.ts +38 -8
- package/dist/workflows/types.d.ts.map +1 -1
- package/package.json +113 -5
- package/src/actions/commit-edits.ts +0 -3
- package/src/actions/execution.ts +83 -0
- package/src/actions/index.ts +1 -1
- package/src/actions/registry.ts +22 -10
- package/src/actions/request.ts +55 -177
- package/src/actions/run-dispatch.ts +276 -0
- package/src/actions/types/context.ts +2 -4
- package/src/actions/validation.ts +2 -2
- package/src/agents/adapters.ts +71 -26
- package/src/agents/authority.ts +189 -0
- package/src/agents/dispatch.ts +1 -6
- package/src/agents/errors.ts +4 -2
- package/src/agents/execution.ts +266 -0
- package/src/agents/index.ts +33 -4
- package/src/agents/prompt.ts +47 -7
- package/src/agents/request.ts +158 -13
- package/src/agents/streams.ts +150 -3
- package/src/agents/thread-context-projection.ts +93 -0
- package/src/agents/tool-definition.ts +17 -2
- package/src/agents/tool-result.ts +61 -0
- package/src/agents/types.ts +49 -5
- package/src/agents/validation.ts +3 -3
- package/src/auth/attribution.ts +48 -0
- package/src/auth/index.ts +1 -0
- package/src/auth/runtime.ts +5 -5
- package/src/authorization/decision.ts +75 -14
- package/src/authorization/grant-kinds.ts +9 -0
- package/src/authorization/index.ts +2 -0
- package/src/blob-storage/execution.ts +78 -0
- package/src/bootstrap/discovery.ts +88 -268
- package/src/bootstrap/index.ts +2 -13
- package/src/broker/in-memory.ts +8 -1
- package/src/broker/types.ts +5 -0
- package/src/connectors/builders.ts +37 -2
- package/src/connectors/connections/authorizations.ts +646 -0
- package/src/connectors/connections/capability.ts +59 -0
- package/src/connectors/connections/command-context.ts +98 -0
- package/src/connectors/connections/contracts.ts +208 -0
- package/src/connectors/connections/credential-codec.ts +48 -0
- package/src/connectors/connections/credential-mutations.ts +574 -0
- package/src/connectors/connections/execution.ts +138 -0
- package/src/connectors/connections/internal.ts +17 -0
- package/src/connectors/connections/provider-operation.ts +41 -0
- package/src/connectors/connections/request.ts +447 -0
- package/src/connectors/connections/runs.ts +702 -0
- package/src/connectors/connections/service.ts +644 -0
- package/src/connectors/connections/storage-boundary.ts +13 -0
- package/src/connectors/connections/tokens.ts +292 -0
- package/src/connectors/connections/validation.ts +316 -0
- package/src/connectors/connections/views.ts +37 -0
- package/src/connectors/credentials.ts +149 -0
- package/src/connectors/errors.ts +111 -3
- package/src/connectors/execution.ts +157 -0
- package/src/connectors/index.ts +26 -2
- package/src/connectors/service.ts +142 -0
- package/src/connectors/types.ts +156 -7
- package/src/datasets/execution.ts +24 -0
- package/src/error-reporting/capability.ts +33 -106
- package/src/error-reporting/internal.ts +3 -1
- package/src/error-reporting/reporter.ts +19 -3
- package/src/error-reporting/reports.ts +172 -0
- package/src/error-reporting/types.ts +97 -43
- package/src/errors/catalog.ts +149 -0
- package/src/errors/internal.ts +322 -0
- package/src/errors/types.ts +21 -0
- package/src/events/definitions.ts +0 -4
- package/src/events/execution.ts +45 -0
- package/src/events/index.ts +15 -14
- package/src/events/outbox-dispatcher.ts +53 -18
- package/src/events/{runtime.ts → service.ts} +19 -23
- package/src/events/types/pipelines.ts +4 -1
- package/src/events/types/syncs.ts +3 -0
- package/src/events/types/workflows.ts +4 -2
- package/src/execution/agent.ts +130 -0
- package/src/execution/authorization.ts +409 -0
- package/src/execution/dispatch.ts +7 -0
- package/src/execution/durable.ts +193 -0
- package/src/execution/index.ts +12 -0
- package/src/execution/primitive.ts +50 -0
- package/src/execution/request.ts +109 -0
- package/src/execution/scopes.ts +198 -0
- package/src/execution/types.ts +113 -0
- package/src/http/api-routes.ts +3 -3
- package/src/index.ts +122 -9
- package/src/json.ts +0 -4
- package/src/logging/execution.ts +28 -0
- package/src/logging/index.ts +4 -4
- package/src/logging/{runtime.ts → service.ts} +7 -7
- package/src/materialization/events.ts +1 -0
- package/src/materialization/index.ts +2 -0
- package/src/materialization/model.ts +34 -18
- package/src/materializer/README.md +9 -13
- package/src/materializer/edits/apply.ts +55 -2
- package/src/materializer/edits/commit.ts +85 -28
- package/src/materializer/edits/load-state.ts +79 -16
- package/src/materializer/edits/operations.ts +80 -37
- package/src/materializer/edits/plan.ts +95 -22
- package/src/materializer/edits/working-state.ts +93 -55
- package/src/materializer/effective/build-events.ts +2 -0
- package/src/materializer/effective/diff.ts +22 -0
- package/src/materializer/effective/load-state.ts +0 -31
- package/src/materializer/effective/resolve.ts +140 -9
- package/src/materializer/execution/commit-lifecycle.ts +9 -1
- package/src/materializer/execution/plan-stream.ts +33 -9
- package/src/materializer/execution/scope.ts +95 -0
- package/src/materializer/execution/work-executor.ts +6 -2
- package/src/materializer/execution/work-records.ts +42 -7
- package/src/materializer/index.ts +9 -1
- package/src/materializer/materializer.ts +67 -5
- package/src/materializer/projections/entry-validator.ts +23 -1
- package/src/materializer/projections/finish-run.ts +29 -11
- package/src/materializer/projections/replace.ts +55 -22
- package/src/materializer/projections/replacement-plan.ts +39 -7
- package/src/materializer/shared/batching.ts +4 -1
- package/src/materializer/shared/normalize.ts +16 -19
- package/src/materializer/telemetry/append.ts +57 -26
- package/src/materializer/telemetry/plan.ts +15 -6
- package/src/objects/action/request.ts +5 -5
- package/src/objects/context/index.ts +1 -0
- package/src/objects/context/types.ts +6 -0
- package/src/objects/execution.ts +346 -0
- package/src/objects/materializer-adapter.ts +1 -23
- package/src/objects/query/executor.ts +15 -29
- package/src/objects/query/index.ts +1 -2
- package/src/objects/query/planner.ts +0 -6
- package/src/objects/sdk/object-handle.ts +2 -2
- package/src/objects/sdk/object-set.ts +17 -7
- package/src/objects/sdk/runtime-query-executor.ts +9 -2
- package/src/objects/sdk/telemetry-channel.ts +5 -1
- package/src/objects/service/action-service.ts +5 -2
- package/src/objects/service/list-service.ts +1 -1
- package/src/objects/telemetry/history.ts +8 -3
- package/src/objects/telemetry/write-batch.ts +0 -2
- package/src/ontology/index.ts +6 -1
- package/src/ontology/registry.ts +16 -2
- package/src/pipelines/execution.ts +105 -0
- package/src/pipelines/index.ts +7 -0
- package/src/pipelines/request.ts +32 -26
- package/src/pipelines/run-dispatch.ts +308 -0
- package/src/projections/builders.ts +100 -44
- package/src/projections/capability.ts +37 -0
- package/src/projections/execution.ts +85 -0
- package/src/projections/index.ts +8 -1
- package/src/projections/internal.ts +21 -61
- package/src/projections/ownership.ts +2 -2
- package/src/projections/registry.ts +33 -11
- package/src/projections/revision.ts +11 -5
- package/src/projections/run-dispatch.ts +497 -0
- package/src/projections/run-id.ts +20 -0
- package/src/projections/timestamp.ts +66 -0
- package/src/projections/types.ts +19 -0
- package/src/projections/validation.ts +27 -3
- package/src/queues/in-memory.ts +37 -11
- package/src/queues/index.ts +10 -2
- package/src/queues/types.ts +78 -45
- package/src/rules/execution.ts +49 -0
- package/src/rules/index.ts +0 -1
- package/src/rules/validation.ts +2 -6
- package/src/runtime/ai-cost.ts +15 -0
- package/src/runtime/ai-usage.ts +17 -0
- package/src/runtime/create.ts +28 -68
- package/src/runtime/definitions.ts +44 -0
- package/src/runtime/host.ts +377 -0
- package/src/runtime/index.ts +5 -13
- package/src/runtime/internal.ts +12 -0
- package/src/runtime/ontology-mutations.ts +5 -3
- package/src/runtime/resolve-definitions.ts +238 -0
- package/src/runtime/sixb.ts +95 -822
- package/src/runtime/types.ts +17 -290
- package/src/scheduler/index.ts +1 -1
- package/src/scheduler/runtime.ts +37 -6
- package/src/schedules/execution.ts +14 -0
- package/src/schedules/types.ts +0 -8
- package/src/security/every.ts +4 -0
- package/src/security/grants.ts +13 -1
- package/src/security/index.ts +3 -1
- package/src/security/registry.ts +107 -0
- package/src/security/types.ts +8 -12
- package/src/security/validation.ts +7 -0
- package/src/storage/action-runs/failure.ts +64 -0
- package/src/storage/action-runs/idempotency.ts +3 -1
- package/src/storage/action-runs/in-memory.ts +39 -9
- package/src/storage/action-runs/index.ts +3 -0
- package/src/storage/action-runs/provider.ts +25 -0
- package/src/storage/action-runs/types.ts +61 -27
- package/src/storage/agents/errors.ts +1 -0
- package/src/storage/agents/in-memory.ts +219 -14
- package/src/storage/agents/index.ts +7 -1
- package/src/storage/agents/provider.ts +252 -0
- package/src/storage/agents/types.ts +96 -22
- package/src/storage/ai-cost/analytics.ts +602 -0
- package/src/storage/ai-cost/codec.ts +136 -0
- package/src/storage/ai-cost/errors.ts +12 -0
- package/src/storage/ai-cost/in-memory.ts +164 -0
- package/src/storage/ai-cost/index.ts +35 -0
- package/src/storage/ai-cost/provider.ts +20 -0
- package/src/storage/ai-cost/types.ts +193 -0
- package/src/storage/ai-cost/validation.ts +337 -0
- package/src/storage/ai-usage/errors.ts +13 -0
- package/src/storage/ai-usage/in-memory.ts +162 -0
- package/src/storage/ai-usage/index.ts +21 -0
- package/src/storage/ai-usage/record.ts +74 -0
- package/src/storage/ai-usage/types.ts +102 -0
- package/src/storage/ai-usage/usage.ts +104 -0
- package/src/storage/auth/types.ts +1 -0
- package/src/storage/connector-connections/errors.ts +24 -0
- package/src/storage/connector-connections/in-memory.ts +1377 -0
- package/src/storage/connector-connections/index.ts +66 -0
- package/src/storage/connector-connections/provider/authorizations.ts +446 -0
- package/src/storage/connector-connections/provider/connections.ts +291 -0
- package/src/storage/connector-connections/provider/runs.ts +369 -0
- package/src/storage/connector-connections/provider/shared.ts +342 -0
- package/src/storage/connector-connections/provider.ts +219 -0
- package/src/storage/connector-connections/types.ts +546 -0
- package/src/storage/executions/errors.ts +21 -0
- package/src/storage/executions/in-memory.ts +75 -0
- package/src/storage/executions/index.ts +16 -0
- package/src/storage/executions/provider.ts +292 -0
- package/src/storage/executions/run-link.ts +70 -0
- package/src/storage/executions/types.ts +49 -0
- package/src/storage/executions/validation.ts +370 -0
- package/src/storage/in-memory/index.ts +100 -16
- package/src/storage/index.ts +191 -15
- package/src/storage/ontology/commits.ts +1 -0
- package/src/storage/ontology/in-memory/index.ts +2 -0
- package/src/storage/ontology/in-memory/materializations-finalization.ts +2 -1
- package/src/storage/ontology/in-memory/materializations-state.ts +10 -0
- package/src/storage/ontology/in-memory/materializations-work.ts +2 -1
- package/src/storage/ontology/in-memory/materializations.ts +132 -9
- package/src/storage/ontology/in-memory/outbox.ts +1 -1
- package/src/storage/ontology/in-memory/shared-state.ts +12 -1
- package/src/storage/ontology/index.ts +6 -0
- package/src/storage/ontology/materializations.ts +53 -4
- package/src/storage/ontology/outbox.ts +13 -2
- package/src/storage/ontology/provider-header-validation.ts +1 -0
- package/src/storage/ontology/provider-work.ts +33 -14
- package/src/storage/ontology/provider.ts +72 -52
- package/src/storage/operation-scope.ts +2 -0
- package/src/storage/pipeline-runs/idempotency.ts +14 -0
- package/src/storage/pipeline-runs/in-memory.ts +88 -8
- package/src/storage/pipeline-runs/index.ts +5 -1
- package/src/storage/pipeline-runs/provider.ts +25 -0
- package/src/storage/pipeline-runs/types.ts +37 -14
- package/src/storage/projection-runs/in-memory.ts +93 -24
- package/src/storage/projection-runs/index.ts +4 -0
- package/src/storage/projection-runs/lifecycle.ts +165 -36
- package/src/storage/projection-runs/provider.ts +34 -0
- package/src/storage/projection-runs/types.ts +34 -11
- package/src/storage/run-listing.ts +22 -13
- package/src/storage/sync-runs/idempotency.ts +18 -0
- package/src/storage/sync-runs/in-memory.ts +90 -7
- package/src/storage/sync-runs/index.ts +4 -1
- package/src/storage/sync-runs/provider.ts +25 -0
- package/src/storage/sync-runs/types.ts +35 -11
- package/src/storage/types.ts +46 -17
- package/src/storage/webhook-runs/errors.ts +14 -0
- package/src/storage/webhook-runs/in-memory.ts +84 -9
- package/src/storage/webhook-runs/index.ts +5 -1
- package/src/storage/webhook-runs/provider.ts +30 -0
- package/src/storage/webhook-runs/types.ts +52 -11
- package/src/storage/workflow-runs/in-memory.ts +89 -55
- package/src/storage/workflow-runs/index.ts +2 -0
- package/src/storage/workflow-runs/provider.ts +70 -0
- package/src/storage/workflow-runs/types.ts +36 -25
- package/src/syncs/builders.ts +2 -2
- package/src/syncs/execution.ts +78 -0
- package/src/syncs/index.ts +13 -0
- package/src/syncs/request.ts +32 -43
- package/src/syncs/run-dispatch.ts +312 -0
- package/src/syncs/sources.ts +20 -0
- package/src/syncs/types.ts +32 -8
- package/src/testing/action-execution.ts +61 -0
- package/src/testing/agent-execution.ts +81 -0
- package/src/testing/agent-storage-contract.ts +498 -64
- package/src/testing/ai-cost-storage-contract.ts +320 -0
- package/src/testing/ai-usage-storage-contract.ts +528 -0
- package/src/testing/auth-storage-contract.ts +17 -1
- package/src/testing/broker-contract.ts +16 -1
- package/src/testing/connector-connection-storage-contract.ts +1052 -0
- package/src/testing/execution-storage-contract.ts +435 -0
- package/src/testing/execution.ts +48 -0
- package/src/testing/index.ts +57 -0
- package/src/testing/materializer-fixture.ts +11 -3
- package/src/testing/materializer-storage-contract.ts +333 -13
- package/src/testing/ontology-contract-fixture.ts +35 -2
- package/src/testing/ontology-storage-contract.ts +75 -14
- package/src/testing/pipeline-execution.ts +64 -0
- package/src/testing/projection-execution.ts +98 -0
- package/src/testing/projection-run-storage-contract.ts +215 -29
- package/src/testing/queues-contract.ts +40 -51
- package/src/testing/sync-execution.ts +61 -0
- package/src/testing/webhook-execution.ts +47 -0
- package/src/testing/webhook-run-storage-contract.ts +240 -0
- package/src/testing/workflow-execution.ts +77 -0
- package/src/webhooks/index.ts +2 -1
- package/src/webhooks/internal.ts +5 -0
- package/src/webhooks/registry.ts +60 -36
- package/src/webhooks/run-admission.ts +172 -0
- package/src/webhooks/types.ts +8 -5
- package/src/workers/queue-delivery.ts +25 -22
- package/src/workers/queue-worker.ts +76 -33
- package/src/workflows/execution.ts +313 -0
- package/src/workflows/failure.ts +63 -0
- package/src/workflows/index.ts +10 -1
- package/src/workflows/request.ts +39 -118
- package/src/workflows/run-dispatch.ts +359 -0
- package/src/workflows/types.ts +39 -8
- package/dist/actions/runtime.d.ts +0 -11
- package/dist/actions/runtime.d.ts.map +0 -1
- package/dist/agents/runtime.d.ts +0 -41
- package/dist/agents/runtime.d.ts.map +0 -1
- package/dist/chunk-4hwxq5e6.js +0 -49
- package/dist/chunk-4hwxq5e6.js.map +0 -10
- package/dist/chunk-4tvj3ate.js.map +0 -11
- package/dist/chunk-50kv63vj.js.map +0 -12
- package/dist/chunk-86459jn0.js +0 -1091
- package/dist/chunk-86459jn0.js.map +0 -22
- package/dist/chunk-867jxh7c.js.map +0 -19
- package/dist/chunk-9k3hgv8h.js +0 -352
- package/dist/chunk-9k3hgv8h.js.map +0 -13
- package/dist/chunk-avppcgt6.js.map +0 -14
- package/dist/chunk-b1bqvhpe.js.map +0 -13
- package/dist/chunk-b5dp7gaf.js +0 -1092
- package/dist/chunk-b5dp7gaf.js.map +0 -26
- package/dist/chunk-bh8bwma3.js +0 -37
- package/dist/chunk-bh8bwma3.js.map +0 -10
- package/dist/chunk-btcsywk4.js +0 -985
- package/dist/chunk-btcsywk4.js.map +0 -15
- package/dist/chunk-exrfbt2d.js +0 -354
- package/dist/chunk-exrfbt2d.js.map +0 -17
- package/dist/chunk-hb9xcx9c.js.map +0 -14
- package/dist/chunk-k25jm1x5.js +0 -102
- package/dist/chunk-k25jm1x5.js.map +0 -10
- package/dist/chunk-k6j72mtt.js +0 -9
- package/dist/chunk-k6j72mtt.js.map +0 -10
- package/dist/chunk-m9w0artv.js +0 -554
- package/dist/chunk-m9w0artv.js.map +0 -13
- package/dist/chunk-nbywkytr.js.map +0 -16
- package/dist/chunk-p7vj8xgb.js.map +0 -34
- package/dist/chunk-raq0x6bd.js.map +0 -48
- package/dist/chunk-rgzed50y.js +0 -1445
- package/dist/chunk-rgzed50y.js.map +0 -22
- package/dist/chunk-rjqm6ksg.js.map +0 -44
- package/dist/chunk-t72dvahb.js.map +0 -12
- package/dist/chunk-v5c085yp.js.map +0 -10
- package/dist/chunk-xt6qx9sh.js.map +0 -36
- package/dist/chunk-xz5g49bp.js.map +0 -11
- package/dist/chunk-ya6brh8a.js.map +0 -11
- package/dist/chunk-yn947ck3.js.map +0 -14
- package/dist/connectors/runtime.d.ts +0 -13
- package/dist/connectors/runtime.d.ts.map +0 -1
- package/dist/events/runtime.d.ts.map +0 -1
- package/dist/objects/contexts.d.ts +0 -45
- package/dist/objects/contexts.d.ts.map +0 -1
- package/dist/objects/helpers.d.ts +0 -7
- package/dist/objects/helpers.d.ts.map +0 -1
- package/dist/runtime/scoped.d.ts +0 -158
- package/dist/runtime/scoped.d.ts.map +0 -1
- package/dist/security/runtime.d.ts +0 -16
- package/dist/security/runtime.d.ts.map +0 -1
- package/dist/storage/webhook-deliveries/in-memory.d.ts +0 -18
- package/dist/storage/webhook-deliveries/in-memory.d.ts.map +0 -1
- package/dist/storage/webhook-deliveries/index.d.ts +0 -3
- package/dist/storage/webhook-deliveries/index.d.ts.map +0 -1
- package/dist/storage/webhook-deliveries/types.d.ts +0 -43
- package/dist/storage/webhook-deliveries/types.d.ts.map +0 -1
- package/dist/workflows/runtime.d.ts +0 -33
- package/dist/workflows/runtime.d.ts.map +0 -1
- package/src/actions/runtime.ts +0 -31
- package/src/agents/runtime.ts +0 -124
- package/src/connectors/runtime.ts +0 -100
- package/src/objects/contexts.ts +0 -64
- package/src/objects/helpers.ts +0 -18
- package/src/runtime/scoped.ts +0 -436
- package/src/security/runtime.ts +0 -101
- package/src/storage/webhook-deliveries/in-memory.ts +0 -108
- package/src/storage/webhook-deliveries/index.ts +0 -9
- package/src/storage/webhook-deliveries/types.ts +0 -41
- package/src/workflows/runtime.ts +0 -68
- /package/dist/{chunk-md9jbrm9.js.map → chunk-4gz41why.js.map} +0 -0
- /package/dist/{chunk-6rzdtvn5.js.map → chunk-8b55ygje.js.map} +0 -0
- /package/dist/{chunk-r82k4z7z.js.map → chunk-a31h57d5.js.map} +0 -0
- /package/dist/{chunk-qzyemcj7.js.map → chunk-ckxfnx7f.js.map} +0 -0
- /package/dist/{chunk-53s4asfm.js.map → chunk-rnshztfq.js.map} +0 -0
- /package/dist/{chunk-8kv2sez0.js.map → chunk-vybf6pkp.js.map} +0 -0
- /package/dist/{chunk-6zrpewy4.js.map → chunk-z3zp0bj1.js.map} +0 -0
- /package/dist/{chunk-hz8m927w.js.map → chunk-zqysyfjt.js.map} +0 -0
|
@@ -0,0 +1,3294 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
SchedulerRuntime
|
|
4
|
+
} from "./chunk-9t8f5xfx.js";
|
|
5
|
+
import {
|
|
6
|
+
registerProjectionRegistry
|
|
7
|
+
} from "./chunk-wb4051cx.js";
|
|
8
|
+
import {
|
|
9
|
+
LoggingService
|
|
10
|
+
} from "./chunk-ghteq96b.js";
|
|
11
|
+
import {
|
|
12
|
+
OntologyOutboxDispatcher,
|
|
13
|
+
createDomainEventService
|
|
14
|
+
} from "./chunk-f2rt4vkh.js";
|
|
15
|
+
import {
|
|
16
|
+
discoverOntologySources,
|
|
17
|
+
discoverProjectDefinitions,
|
|
18
|
+
validateRulesAtStartup
|
|
19
|
+
} from "./chunk-1ywq4hzk.js";
|
|
20
|
+
import {
|
|
21
|
+
validateSchedulesAtStartup
|
|
22
|
+
} from "./chunk-5sz8ta9r.js";
|
|
23
|
+
import {
|
|
24
|
+
assertDatasetDefinition
|
|
25
|
+
} from "./chunk-a31h57d5.js";
|
|
26
|
+
import {
|
|
27
|
+
createBoundSixb,
|
|
28
|
+
validateAgentGroupReferences,
|
|
29
|
+
validateAgentToolsAtStartup,
|
|
30
|
+
validateWorkflowsAtStartup
|
|
31
|
+
} from "./chunk-177ed2mp.js";
|
|
32
|
+
import {
|
|
33
|
+
ConnectorError,
|
|
34
|
+
ConnectorNotFoundError,
|
|
35
|
+
SyncValidationError,
|
|
36
|
+
createAmbiguousProviderOperationError,
|
|
37
|
+
createConnectorCodedError,
|
|
38
|
+
isConnectorStorageError,
|
|
39
|
+
oauthErrorKind,
|
|
40
|
+
providerBoundaryError,
|
|
41
|
+
providerFailureCode,
|
|
42
|
+
recoverConnectorFailure,
|
|
43
|
+
storageBoundaryError
|
|
44
|
+
} from "./chunk-km4rkj4t.js";
|
|
45
|
+
import {
|
|
46
|
+
RuntimeError
|
|
47
|
+
} from "./chunk-3rsf30p4.js";
|
|
48
|
+
import {
|
|
49
|
+
createOntologyMutationRuntime,
|
|
50
|
+
registerOntologyMutationRuntime
|
|
51
|
+
} from "./chunk-xhy826cz.js";
|
|
52
|
+
import {
|
|
53
|
+
isOAuthConnectorDefinition,
|
|
54
|
+
isStaticConnectorDefinition
|
|
55
|
+
} from "./chunk-8m5ejdpn.js";
|
|
56
|
+
import {
|
|
57
|
+
registerConnectorConnectionCallbackProcess
|
|
58
|
+
} from "./chunk-nf4r78r4.js";
|
|
59
|
+
import {
|
|
60
|
+
ActionRegistry
|
|
61
|
+
} from "./chunk-ttba8x6z.js";
|
|
62
|
+
import {
|
|
63
|
+
ProjectionRegistry,
|
|
64
|
+
createOntologyMaterializer
|
|
65
|
+
} from "./chunk-ettb0cy4.js";
|
|
66
|
+
import {
|
|
67
|
+
ensureExecutionRecord
|
|
68
|
+
} from "./chunk-4aw0jwk0.js";
|
|
69
|
+
import {
|
|
70
|
+
checkStorageSchema,
|
|
71
|
+
isMigrationCapableStorage
|
|
72
|
+
} from "./chunk-xmggaxg4.js";
|
|
73
|
+
import {
|
|
74
|
+
CONNECTOR_CONNECTION_RUN_FAILURE_CODES,
|
|
75
|
+
ConnectorConnectionStorageError
|
|
76
|
+
} from "./chunk-5gd7tpv6.js";
|
|
77
|
+
import {
|
|
78
|
+
OntologyRegistry
|
|
79
|
+
} from "./chunk-3j7573mj.js";
|
|
80
|
+
import {
|
|
81
|
+
attachSixbErrorReporter,
|
|
82
|
+
shareSixbErrorReporter
|
|
83
|
+
} from "./chunk-dsrkgtnh.js";
|
|
84
|
+
import {
|
|
85
|
+
reportEventDeliveryFailure
|
|
86
|
+
} from "./chunk-bhxaq2nn.js";
|
|
87
|
+
import {
|
|
88
|
+
AuthRuntime,
|
|
89
|
+
SecurityRegistry
|
|
90
|
+
} from "./chunk-d2adj15j.js";
|
|
91
|
+
import {
|
|
92
|
+
AuthRuntimeError,
|
|
93
|
+
isMagicLinkAuthStrategy,
|
|
94
|
+
isOidcAuthStrategy
|
|
95
|
+
} from "./chunk-xwnf87e1.js";
|
|
96
|
+
import {
|
|
97
|
+
AuthorizationError,
|
|
98
|
+
resolveExecutionScopeAuthorization
|
|
99
|
+
} from "./chunk-4z3n1dzt.js";
|
|
100
|
+
import {
|
|
101
|
+
captureSixbFailure,
|
|
102
|
+
isSixbError
|
|
103
|
+
} from "./chunk-0172wgm9.js";
|
|
104
|
+
// src/webhooks/errors.ts
|
|
105
|
+
class WebhookValidationError extends Error {
|
|
106
|
+
name = "WebhookValidationError";
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/webhooks/builders.ts
|
|
110
|
+
function defineWebhook(id) {
|
|
111
|
+
assertNonEmpty(id, "Webhook id");
|
|
112
|
+
return new WebhookMethodBuilder(id);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
class WebhookMethodBuilder {
|
|
116
|
+
id;
|
|
117
|
+
constructor(id) {
|
|
118
|
+
this.id = id;
|
|
119
|
+
}
|
|
120
|
+
post() {
|
|
121
|
+
return new WebhookBodyBuilder(this.id, "POST");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
class WebhookBodyBuilder {
|
|
126
|
+
id;
|
|
127
|
+
method;
|
|
128
|
+
constructor(id, method) {
|
|
129
|
+
this.id = id;
|
|
130
|
+
this.method = method;
|
|
131
|
+
}
|
|
132
|
+
json(schema) {
|
|
133
|
+
if (schema !== undefined && typeof schema.parse !== "function") {
|
|
134
|
+
throw new WebhookValidationError("[Sixb] Webhook JSON schema must provide parse(value).");
|
|
135
|
+
}
|
|
136
|
+
return new WebhookBuilder({
|
|
137
|
+
id: this.id,
|
|
138
|
+
method: this.method,
|
|
139
|
+
body: {
|
|
140
|
+
format: "json",
|
|
141
|
+
parse(value) {
|
|
142
|
+
return schema ? schema.parse(value) : value;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
text() {
|
|
148
|
+
return new WebhookBuilder({
|
|
149
|
+
id: this.id,
|
|
150
|
+
method: this.method,
|
|
151
|
+
body: {
|
|
152
|
+
format: "text",
|
|
153
|
+
parse(value) {
|
|
154
|
+
if (typeof value !== "string") {
|
|
155
|
+
throw new Error("Expected webhook body to be text.");
|
|
156
|
+
}
|
|
157
|
+
return value;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
raw() {
|
|
163
|
+
return new WebhookBuilder({
|
|
164
|
+
id: this.id,
|
|
165
|
+
method: this.method,
|
|
166
|
+
body: {
|
|
167
|
+
format: "raw",
|
|
168
|
+
parse(value) {
|
|
169
|
+
if (!(value instanceof Uint8Array)) {
|
|
170
|
+
throw new Error("Expected webhook body to be raw bytes.");
|
|
171
|
+
}
|
|
172
|
+
return value;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
class WebhookBuilder {
|
|
180
|
+
draft;
|
|
181
|
+
constructor(draft) {
|
|
182
|
+
this.draft = draft;
|
|
183
|
+
}
|
|
184
|
+
verify(verify) {
|
|
185
|
+
if (typeof verify !== "function") {
|
|
186
|
+
throw new WebhookValidationError("[Sixb] Webhook verify must be a function.");
|
|
187
|
+
}
|
|
188
|
+
return new WebhookBuilder({
|
|
189
|
+
...this.draft,
|
|
190
|
+
verify
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
idempotencyKey(resolver) {
|
|
194
|
+
if (typeof resolver !== "function") {
|
|
195
|
+
throw new WebhookValidationError("[Sixb] Webhook idempotencyKey must be a function.");
|
|
196
|
+
}
|
|
197
|
+
return new WebhookBuilder({
|
|
198
|
+
...this.draft,
|
|
199
|
+
idempotencyKey: resolver
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
handle(handle) {
|
|
203
|
+
if (typeof handle !== "function") {
|
|
204
|
+
throw new WebhookValidationError("[Sixb] Webhook handle must be a function.");
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
kind: "webhook",
|
|
208
|
+
id: this.draft.id,
|
|
209
|
+
method: this.draft.method,
|
|
210
|
+
body: this.draft.body,
|
|
211
|
+
idempotencyKey: this.draft.idempotencyKey,
|
|
212
|
+
verify: this.draft.verify,
|
|
213
|
+
handle
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
function assertNonEmpty(value, field) {
|
|
218
|
+
if (!value.trim()) {
|
|
219
|
+
throw new WebhookValidationError(`[Sixb] ${field} must not be empty.`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// src/webhooks/connector.ts
|
|
223
|
+
function webhookConnector(options) {
|
|
224
|
+
return {
|
|
225
|
+
type: "webhook",
|
|
226
|
+
webhooks: options.webhooks,
|
|
227
|
+
connect() {
|
|
228
|
+
return { kind: "webhook" };
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
// src/webhooks/registry.ts
|
|
233
|
+
class WebhookRegistry {
|
|
234
|
+
webhooks;
|
|
235
|
+
byRoute = new Map;
|
|
236
|
+
constructor(options) {
|
|
237
|
+
const registered = [];
|
|
238
|
+
for (const connector of options.connectors) {
|
|
239
|
+
if (!isStaticConnectorDefinition(connector))
|
|
240
|
+
continue;
|
|
241
|
+
const webhooks = connector.adapter.webhooks ?? [];
|
|
242
|
+
if (!Array.isArray(webhooks)) {
|
|
243
|
+
throw new WebhookValidationError(`[Sixb] Connector '${connector.id}' webhooks must be an array when provided.`);
|
|
244
|
+
}
|
|
245
|
+
const ids = new Set;
|
|
246
|
+
for (const webhook of webhooks) {
|
|
247
|
+
assertValidWebhook(connector.id, webhook);
|
|
248
|
+
if (ids.has(webhook.id)) {
|
|
249
|
+
throw new WebhookValidationError(`[Sixb] Duplicate webhook id '${webhook.id}' for connector '${connector.id}'.`);
|
|
250
|
+
}
|
|
251
|
+
ids.add(webhook.id);
|
|
252
|
+
const route = webhookRoute(connector.id, webhook.id);
|
|
253
|
+
const duplicate = this.byRoute.get(route);
|
|
254
|
+
if (duplicate) {
|
|
255
|
+
throw new WebhookValidationError(`[Sixb] Duplicate webhook route '${route}' for connectors '${duplicate.connector.id}' and '${connector.id}'.`);
|
|
256
|
+
}
|
|
257
|
+
const registeredWebhook = {
|
|
258
|
+
connector,
|
|
259
|
+
webhook,
|
|
260
|
+
route
|
|
261
|
+
};
|
|
262
|
+
this.byRoute.set(route, registeredWebhook);
|
|
263
|
+
registered.push(registeredWebhook);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
this.webhooks = Object.freeze(registered);
|
|
267
|
+
}
|
|
268
|
+
list() {
|
|
269
|
+
return this.webhooks;
|
|
270
|
+
}
|
|
271
|
+
getByRoute(route) {
|
|
272
|
+
return this.byRoute.get(route) ?? null;
|
|
273
|
+
}
|
|
274
|
+
getById(connectorId, webhookId) {
|
|
275
|
+
return this.getByRoute(webhookRoute(connectorId, webhookId));
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
function webhookRoute(connectorId, webhookId) {
|
|
279
|
+
return `/api/webhooks/${connectorId}/${webhookId}`;
|
|
280
|
+
}
|
|
281
|
+
function assertValidWebhook(connectorId, webhook) {
|
|
282
|
+
if (!isRecord(webhook)) {
|
|
283
|
+
throw new WebhookValidationError(`[Sixb] Connector '${connectorId}' has an invalid webhook.`);
|
|
284
|
+
}
|
|
285
|
+
if (webhook.kind !== "webhook") {
|
|
286
|
+
throw new WebhookValidationError(`[Sixb] Connector '${connectorId}' webhook must be created with defineWebhook(...).`);
|
|
287
|
+
}
|
|
288
|
+
if (typeof webhook.id !== "string" || !webhook.id.trim()) {
|
|
289
|
+
throw new WebhookValidationError(`[Sixb] Connector '${connectorId}' webhook id must not be empty.`);
|
|
290
|
+
}
|
|
291
|
+
if (webhook.method !== "POST") {
|
|
292
|
+
throw new WebhookValidationError(`[Sixb] Webhook '${connectorId}/${webhook.id}' must use method POST.`);
|
|
293
|
+
}
|
|
294
|
+
if (!isRecord(webhook.body)) {
|
|
295
|
+
throw new WebhookValidationError(`[Sixb] Webhook '${connectorId}/${webhook.id}' body is required.`);
|
|
296
|
+
}
|
|
297
|
+
if (webhook.body.format !== "json" && webhook.body.format !== "text" && webhook.body.format !== "raw") {
|
|
298
|
+
throw new WebhookValidationError(`[Sixb] Webhook '${connectorId}/${webhook.id}' body format must be json, text, or raw.`);
|
|
299
|
+
}
|
|
300
|
+
if (typeof webhook.body.parse !== "function") {
|
|
301
|
+
throw new WebhookValidationError(`[Sixb] Webhook '${connectorId}/${webhook.id}' body parser must provide parse(value).`);
|
|
302
|
+
}
|
|
303
|
+
if (webhook.verify !== undefined && typeof webhook.verify !== "function") {
|
|
304
|
+
throw new WebhookValidationError(`[Sixb] Webhook '${connectorId}/${webhook.id}' verify must be a function.`);
|
|
305
|
+
}
|
|
306
|
+
if (webhook.idempotencyKey !== undefined && typeof webhook.idempotencyKey !== "function") {
|
|
307
|
+
throw new WebhookValidationError(`[Sixb] Webhook '${connectorId}/${webhook.id}' idempotencyKey must be a function.`);
|
|
308
|
+
}
|
|
309
|
+
if (typeof webhook.handle !== "function") {
|
|
310
|
+
throw new WebhookValidationError(`[Sixb] Webhook '${connectorId}/${webhook.id}' handle must be a function.`);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
function isRecord(value) {
|
|
314
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
315
|
+
}
|
|
316
|
+
// src/webhooks/unverified.ts
|
|
317
|
+
class UnverifiedWebhookError extends Error {
|
|
318
|
+
name = "UnverifiedWebhookError";
|
|
319
|
+
}
|
|
320
|
+
function resolveWebhookVerification(subject, options) {
|
|
321
|
+
if (options.credential)
|
|
322
|
+
return { credential: options.credential };
|
|
323
|
+
if (!options.allowUnverified) {
|
|
324
|
+
throw new UnverifiedWebhookError(`[${subject.connector}] This webhook has no credential, so ${subject.verifies} cannot be ` + `checked and the route would accept unverified requests from anyone who can reach it. ` + `Set ${subject.credentialOption}, or pass ${subject.allowOption} to accept that.`);
|
|
325
|
+
}
|
|
326
|
+
return { allowUnverified: true };
|
|
327
|
+
}
|
|
328
|
+
function warnUnverifiedWebhook(subject, verification) {
|
|
329
|
+
if (verification.credential)
|
|
330
|
+
return;
|
|
331
|
+
console.warn(`[${subject.connector}] ${subject.allowOption}, so ${subject.verifies} is not checked ` + `and this route accepts unverified requests from anyone who can reach it. ` + `Set ${subject.credentialOption} before exposing it.`);
|
|
332
|
+
}
|
|
333
|
+
// src/runtime/create.ts
|
|
334
|
+
import { resolve as resolve2 } from "path";
|
|
335
|
+
|
|
336
|
+
// src/runtime/host.ts
|
|
337
|
+
import { resolve } from "path";
|
|
338
|
+
|
|
339
|
+
// src/connectors/credentials.ts
|
|
340
|
+
import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
|
|
341
|
+
function createConnectorCredentialProtectorFromKey(encodedKey) {
|
|
342
|
+
let key;
|
|
343
|
+
try {
|
|
344
|
+
key = decodeBase64Url(encodedKey);
|
|
345
|
+
} catch (error) {
|
|
346
|
+
throw createConnectorCodedError("connector.configuration_invalid", "connectorConnections.encryptionKey must be canonical base64url encoding.", { cause: error });
|
|
347
|
+
}
|
|
348
|
+
if (key.byteLength !== 32) {
|
|
349
|
+
throw createConnectorCodedError("connector.configuration_invalid", "connectorConnections.encryptionKey must encode exactly 32 random bytes.");
|
|
350
|
+
}
|
|
351
|
+
return createAesGcmConnectorCredentialProtector(key);
|
|
352
|
+
}
|
|
353
|
+
function createAesGcmConnectorCredentialProtector(encryptionKey) {
|
|
354
|
+
if (encryptionKey.byteLength !== 32) {
|
|
355
|
+
throw createConnectorCodedError("connector.configuration_invalid", "Connector credential encryption key must contain exactly 32 bytes.");
|
|
356
|
+
}
|
|
357
|
+
const key = Buffer.from(encryptionKey);
|
|
358
|
+
return {
|
|
359
|
+
async seal(plaintext, context) {
|
|
360
|
+
assertCredentialContext(context);
|
|
361
|
+
const nonce = randomBytes(12);
|
|
362
|
+
const cipher = createCipheriv("aes-256-gcm", key, nonce);
|
|
363
|
+
cipher.setAAD(credentialAad(context));
|
|
364
|
+
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
365
|
+
return {
|
|
366
|
+
version: 1,
|
|
367
|
+
algorithm: "A256GCM",
|
|
368
|
+
nonce: nonce.toString("base64url"),
|
|
369
|
+
ciphertext: ciphertext.toString("base64url"),
|
|
370
|
+
tag: cipher.getAuthTag().toString("base64url")
|
|
371
|
+
};
|
|
372
|
+
},
|
|
373
|
+
async open(envelope, context) {
|
|
374
|
+
assertCredentialContext(context);
|
|
375
|
+
if (envelope.version !== 1 || envelope.algorithm !== "A256GCM") {
|
|
376
|
+
throw createConnectorCodedError("connector.credentials_unavailable", "Connector credential envelope format is not supported.");
|
|
377
|
+
}
|
|
378
|
+
try {
|
|
379
|
+
const nonce = decodeBase64Url(envelope.nonce);
|
|
380
|
+
const ciphertext = decodeBase64Url(envelope.ciphertext);
|
|
381
|
+
const tag = decodeBase64Url(envelope.tag);
|
|
382
|
+
if (nonce.byteLength !== 12 || tag.byteLength !== 16)
|
|
383
|
+
throw new Error("invalid envelope");
|
|
384
|
+
const decipher = createDecipheriv("aes-256-gcm", key, nonce);
|
|
385
|
+
decipher.setAAD(credentialAad(context));
|
|
386
|
+
decipher.setAuthTag(tag);
|
|
387
|
+
return Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
388
|
+
} catch (error) {
|
|
389
|
+
throw createConnectorCodedError("connector.credentials_unavailable", "Connector credential envelope authentication failed.", { cause: error });
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
function decodeBase64Url(value) {
|
|
395
|
+
if (!/^[A-Za-z0-9_-]*$/.test(value))
|
|
396
|
+
throw new Error("invalid base64url");
|
|
397
|
+
const decoded = Buffer.from(value, "base64url");
|
|
398
|
+
if (decoded.toString("base64url") !== value)
|
|
399
|
+
throw new Error("non-canonical base64url");
|
|
400
|
+
return decoded;
|
|
401
|
+
}
|
|
402
|
+
function createEphemeralConnectorCredentialProtector() {
|
|
403
|
+
return createAesGcmConnectorCredentialProtector(randomBytes(32));
|
|
404
|
+
}
|
|
405
|
+
function credentialAad(context) {
|
|
406
|
+
return Buffer.from(JSON.stringify({
|
|
407
|
+
version: 1,
|
|
408
|
+
projectId: context.projectId,
|
|
409
|
+
connectorId: context.connectorId,
|
|
410
|
+
recordId: context.recordId,
|
|
411
|
+
purpose: context.purpose
|
|
412
|
+
}));
|
|
413
|
+
}
|
|
414
|
+
function assertCredentialContext(context) {
|
|
415
|
+
assertNonblank(context.projectId, "context projectId");
|
|
416
|
+
assertNonblank(context.connectorId, "context connectorId");
|
|
417
|
+
assertNonblank(context.recordId, "context recordId");
|
|
418
|
+
}
|
|
419
|
+
function assertNonblank(value, field) {
|
|
420
|
+
if (!value.trim()) {
|
|
421
|
+
throw createConnectorCodedError("connector.configuration_invalid", `Connector credential ${field} must not be empty.`);
|
|
422
|
+
}
|
|
423
|
+
return value;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// src/connectors/connections/service.ts
|
|
427
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
428
|
+
|
|
429
|
+
// src/connectors/connections/authorizations.ts
|
|
430
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
431
|
+
|
|
432
|
+
// src/connectors/connections/validation.ts
|
|
433
|
+
import { createHash } from "crypto";
|
|
434
|
+
function validateCredentials(credentials) {
|
|
435
|
+
if (!isRecord2(credentials)) {
|
|
436
|
+
throw createConnectorCodedError("connector.adapter_invalid", "OAuth connector adapter returned invalid credentials.");
|
|
437
|
+
}
|
|
438
|
+
const normalized = {
|
|
439
|
+
accessToken: nonblank(credentials.accessToken, "provider access token", "connector.adapter_invalid")
|
|
440
|
+
};
|
|
441
|
+
if (credentials.refreshToken !== undefined) {
|
|
442
|
+
normalized.refreshToken = nonblank(credentials.refreshToken, "provider refresh token", "connector.adapter_invalid");
|
|
443
|
+
}
|
|
444
|
+
if (credentials.tokenType !== undefined) {
|
|
445
|
+
normalized.tokenType = nonblank(credentials.tokenType, "provider token type", "connector.adapter_invalid");
|
|
446
|
+
}
|
|
447
|
+
if (credentials.scopes !== undefined) {
|
|
448
|
+
if (!Array.isArray(credentials.scopes)) {
|
|
449
|
+
throw createConnectorCodedError("connector.adapter_invalid", "OAuth connector adapter returned invalid scopes.");
|
|
450
|
+
}
|
|
451
|
+
normalized.scopes = [
|
|
452
|
+
...new Set(credentials.scopes.map((scope) => nonblank(scope, "provider scope", "connector.adapter_invalid")))
|
|
453
|
+
];
|
|
454
|
+
}
|
|
455
|
+
if (credentials.expiresAt !== undefined) {
|
|
456
|
+
if (!(credentials.expiresAt instanceof Date) || Number.isNaN(credentials.expiresAt.getTime())) {
|
|
457
|
+
throw createConnectorCodedError("connector.adapter_invalid", "OAuth connector adapter returned an invalid token expiry.");
|
|
458
|
+
}
|
|
459
|
+
normalized.expiresAt = new Date(credentials.expiresAt);
|
|
460
|
+
}
|
|
461
|
+
return normalized;
|
|
462
|
+
}
|
|
463
|
+
function validateAccounts(accounts) {
|
|
464
|
+
if (!Array.isArray(accounts)) {
|
|
465
|
+
throw createConnectorCodedError("connector.adapter_invalid", "OAuth connector adapter returned an invalid account list.");
|
|
466
|
+
}
|
|
467
|
+
const ids = new Set;
|
|
468
|
+
return accounts.map((account) => {
|
|
469
|
+
if (!isRecord2(account)) {
|
|
470
|
+
throw createConnectorCodedError("connector.adapter_invalid", "OAuth connector adapter returned an invalid account.");
|
|
471
|
+
}
|
|
472
|
+
const id = nonblank(account.id, "external account id", "connector.adapter_invalid");
|
|
473
|
+
if (ids.has(id)) {
|
|
474
|
+
throw createConnectorCodedError("connector.adapter_invalid", `OAuth connector adapter returned duplicate account '${id}'.`);
|
|
475
|
+
}
|
|
476
|
+
ids.add(id);
|
|
477
|
+
const description = account.description === undefined ? undefined : nonblank(account.description, "external account description", "connector.adapter_invalid");
|
|
478
|
+
const avatarUrl = account.avatarUrl === undefined ? undefined : normalizedHttpUrl(nonblank(account.avatarUrl, "external account avatar URL", "connector.adapter_invalid"), "external account avatar URL", "connector.adapter_invalid");
|
|
479
|
+
return {
|
|
480
|
+
id,
|
|
481
|
+
label: nonblank(account.label, "external account label", "connector.adapter_invalid"),
|
|
482
|
+
...description === undefined ? {} : { description },
|
|
483
|
+
...avatarUrl === undefined ? {} : { avatarUrl }
|
|
484
|
+
};
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
function serializeCredentials(credentials) {
|
|
488
|
+
return JSON.stringify({
|
|
489
|
+
version: 1,
|
|
490
|
+
accessToken: credentials.accessToken,
|
|
491
|
+
...credentials.refreshToken === undefined ? {} : { refreshToken: credentials.refreshToken },
|
|
492
|
+
...credentials.tokenType === undefined ? {} : { tokenType: credentials.tokenType },
|
|
493
|
+
...credentials.scopes === undefined ? {} : { scopes: credentials.scopes },
|
|
494
|
+
...credentials.expiresAt === undefined ? {} : { expiresAt: credentials.expiresAt.toISOString() }
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
function parseCredentials(serialized) {
|
|
498
|
+
let value;
|
|
499
|
+
try {
|
|
500
|
+
value = JSON.parse(serialized);
|
|
501
|
+
} catch (error) {
|
|
502
|
+
throw createConnectorCodedError("connector.credentials_unavailable", "Stored connector credentials are invalid.", { cause: error });
|
|
503
|
+
}
|
|
504
|
+
if (!isRecord2(value) || value.version !== 1 || typeof value.accessToken !== "string") {
|
|
505
|
+
throw createConnectorCodedError("connector.credentials_unavailable", "Stored connector credentials are invalid.");
|
|
506
|
+
}
|
|
507
|
+
if (value.refreshToken !== undefined && typeof value.refreshToken !== "string" || value.tokenType !== undefined && typeof value.tokenType !== "string" || value.scopes !== undefined && (!Array.isArray(value.scopes) || value.scopes.some((scope) => typeof scope !== "string")) || value.expiresAt !== undefined && typeof value.expiresAt !== "string") {
|
|
508
|
+
throw createConnectorCodedError("connector.credentials_unavailable", "Stored connector credentials are invalid.");
|
|
509
|
+
}
|
|
510
|
+
const expiresAt = value.expiresAt === undefined ? undefined : new Date(value.expiresAt);
|
|
511
|
+
try {
|
|
512
|
+
return validateCredentials({
|
|
513
|
+
accessToken: value.accessToken,
|
|
514
|
+
...value.refreshToken === undefined ? {} : { refreshToken: value.refreshToken },
|
|
515
|
+
...value.tokenType === undefined ? {} : { tokenType: value.tokenType },
|
|
516
|
+
...value.scopes === undefined ? {} : { scopes: value.scopes },
|
|
517
|
+
...expiresAt === undefined ? {} : { expiresAt }
|
|
518
|
+
});
|
|
519
|
+
} catch (error) {
|
|
520
|
+
throw createConnectorCodedError("connector.credentials_unavailable", "Stored connector credentials are invalid.", { cause: error });
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
function shouldRefresh(authorization, now, skewMs) {
|
|
524
|
+
return authorization.credentialExpiresAt !== undefined && authorization.credentialExpiresAt.getTime() <= now.getTime() + skewMs;
|
|
525
|
+
}
|
|
526
|
+
function tokenView(credentials) {
|
|
527
|
+
return {
|
|
528
|
+
accessToken: credentials.accessToken,
|
|
529
|
+
...credentials.tokenType === undefined ? {} : { tokenType: credentials.tokenType }
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
function parseAttemptId(state) {
|
|
533
|
+
const separator = state.indexOf(".");
|
|
534
|
+
if (separator <= 0 || separator === state.length - 1) {
|
|
535
|
+
throw createConnectorCodedError("connector.authorization_invalid", "OAuth state is invalid.");
|
|
536
|
+
}
|
|
537
|
+
return state.slice(0, separator);
|
|
538
|
+
}
|
|
539
|
+
function hashSecret(value) {
|
|
540
|
+
return createHash("sha256").update(value).digest("base64url");
|
|
541
|
+
}
|
|
542
|
+
function normalizedHttpUrl(value, field, code = "connector.configuration_invalid") {
|
|
543
|
+
let url;
|
|
544
|
+
try {
|
|
545
|
+
if (typeof value !== "string" && !(value instanceof URL))
|
|
546
|
+
throw new Error("invalid URL");
|
|
547
|
+
url = new URL(value);
|
|
548
|
+
} catch (error) {
|
|
549
|
+
throw createConnectorCodedError(code, `${field} must be an absolute URL.`, { cause: error });
|
|
550
|
+
}
|
|
551
|
+
if (url.username || url.password) {
|
|
552
|
+
throw createConnectorCodedError(code, `${field} must not contain credentials.`);
|
|
553
|
+
}
|
|
554
|
+
if (url.protocol !== "https:" && !(url.protocol === "http:" && isLoopbackHost(url.hostname))) {
|
|
555
|
+
throw createConnectorCodedError(code, `${field} must use HTTPS, except on a loopback host.`);
|
|
556
|
+
}
|
|
557
|
+
return url.toString();
|
|
558
|
+
}
|
|
559
|
+
function assertAuthorizationUrlParameters(authorizationUrl, expected) {
|
|
560
|
+
const parameters = new URL(authorizationUrl).searchParams;
|
|
561
|
+
if (!sameSingleParameter(parameters, "state", expected.state) || !sameSingleParameter(parameters, "code_challenge", expected.codeChallenge) || !sameSingleParameter(parameters, "code_challenge_method", "S256")) {
|
|
562
|
+
throw createConnectorCodedError("connector.adapter_invalid", "OAuth connector authorization URL must preserve the framework-provided state and PKCE S256 parameters.");
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
function nonblank(value, field, code = "connector.configuration_invalid") {
|
|
566
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
567
|
+
throw createConnectorCodedError(code, `${field} must be a non-empty string.`);
|
|
568
|
+
}
|
|
569
|
+
return value;
|
|
570
|
+
}
|
|
571
|
+
function positiveDuration(value, field) {
|
|
572
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
573
|
+
throw createConnectorCodedError("connector.configuration_invalid", `Connector ${field} must be a positive duration.`);
|
|
574
|
+
}
|
|
575
|
+
return value;
|
|
576
|
+
}
|
|
577
|
+
function nonNegativeDuration(value, field) {
|
|
578
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
579
|
+
throw createConnectorCodedError("connector.configuration_invalid", `Connector ${field} must not be negative.`);
|
|
580
|
+
}
|
|
581
|
+
return value;
|
|
582
|
+
}
|
|
583
|
+
function delay(milliseconds) {
|
|
584
|
+
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
585
|
+
}
|
|
586
|
+
function sameIds(left, right) {
|
|
587
|
+
const leftSorted = [...left].sort();
|
|
588
|
+
const rightSorted = [...right].sort();
|
|
589
|
+
return leftSorted.length === rightSorted.length && leftSorted.every((value, index) => value === rightSorted[index]);
|
|
590
|
+
}
|
|
591
|
+
function sameSingleParameter(parameters, name, expected) {
|
|
592
|
+
const values = parameters.getAll(name);
|
|
593
|
+
return values.length === 1 && values[0] === expected;
|
|
594
|
+
}
|
|
595
|
+
function isLoopbackHost(hostname) {
|
|
596
|
+
return hostname === "localhost" || hostname.endsWith(".localhost") || hostname === "127.0.0.1" || hostname === "[::1]";
|
|
597
|
+
}
|
|
598
|
+
function isRecord2(value) {
|
|
599
|
+
return typeof value === "object" && value !== null;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// src/connectors/connections/credential-codec.ts
|
|
603
|
+
var textEncoder = new TextEncoder;
|
|
604
|
+
var textDecoder = new TextDecoder;
|
|
605
|
+
|
|
606
|
+
class ConnectorCredentialCodec {
|
|
607
|
+
options;
|
|
608
|
+
constructor(options) {
|
|
609
|
+
this.options = options;
|
|
610
|
+
}
|
|
611
|
+
seal(connectorId, authorizationId, credentials) {
|
|
612
|
+
return this.options.protector.seal(textEncoder.encode(serializeCredentials(credentials)), this.context(connectorId, authorizationId));
|
|
613
|
+
}
|
|
614
|
+
async open(connectorId, authorizationId, credentials) {
|
|
615
|
+
const plaintext = await this.options.protector.open(credentials, this.context(connectorId, authorizationId));
|
|
616
|
+
return parseCredentials(textDecoder.decode(plaintext));
|
|
617
|
+
}
|
|
618
|
+
context(connectorId, authorizationId) {
|
|
619
|
+
return {
|
|
620
|
+
projectId: this.options.projectId,
|
|
621
|
+
connectorId,
|
|
622
|
+
recordId: authorizationId,
|
|
623
|
+
purpose: "oauth-authorization"
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// src/connectors/connections/credential-mutations.ts
|
|
629
|
+
import { randomUUID } from "crypto";
|
|
630
|
+
|
|
631
|
+
// src/connectors/connections/provider-operation.ts
|
|
632
|
+
async function runBoundedConnectorProviderOperation(options, run, interruptionError = createAmbiguousProviderOperationError) {
|
|
633
|
+
const controller = new AbortController;
|
|
634
|
+
const abortFromHost = () => controller.abort(interruptionError());
|
|
635
|
+
if (options.hostSignal.aborted)
|
|
636
|
+
abortFromHost();
|
|
637
|
+
else
|
|
638
|
+
options.hostSignal.addEventListener("abort", abortFromHost, { once: true });
|
|
639
|
+
const timeout = setTimeout(() => controller.abort(interruptionError()), options.timeoutMs);
|
|
640
|
+
const aborted = new Promise((_resolve, reject) => {
|
|
641
|
+
const rejectAborted = () => reject(abortReason(controller.signal));
|
|
642
|
+
if (controller.signal.aborted)
|
|
643
|
+
rejectAborted();
|
|
644
|
+
else
|
|
645
|
+
controller.signal.addEventListener("abort", rejectAborted, { once: true });
|
|
646
|
+
});
|
|
647
|
+
const operation = Promise.resolve().then(() => {
|
|
648
|
+
if (controller.signal.aborted)
|
|
649
|
+
throw abortReason(controller.signal);
|
|
650
|
+
return run(controller.signal);
|
|
651
|
+
});
|
|
652
|
+
operation.catch(() => {});
|
|
653
|
+
try {
|
|
654
|
+
return await Promise.race([operation, aborted]);
|
|
655
|
+
} finally {
|
|
656
|
+
clearTimeout(timeout);
|
|
657
|
+
options.hostSignal.removeEventListener("abort", abortFromHost);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
function abortReason(signal) {
|
|
661
|
+
return signal.reason instanceof Error ? signal.reason : createAmbiguousProviderOperationError();
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// src/connectors/connections/storage-boundary.ts
|
|
665
|
+
async function withConnectorStorageBoundary(message, operation) {
|
|
666
|
+
try {
|
|
667
|
+
return await operation();
|
|
668
|
+
} catch (error) {
|
|
669
|
+
throw storageBoundaryError(error, message);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// src/connectors/connections/credential-mutations.ts
|
|
674
|
+
var MUTATION_POLL_MS = 25;
|
|
675
|
+
var MUTATION_WAIT_MS = 5000;
|
|
676
|
+
|
|
677
|
+
class ConnectorCredentialMutationCoordinator {
|
|
678
|
+
projectId;
|
|
679
|
+
storage;
|
|
680
|
+
leaseDurationMs;
|
|
681
|
+
operationTimeoutMs;
|
|
682
|
+
hostSignal;
|
|
683
|
+
discoverReauthorizationAccounts;
|
|
684
|
+
holderId;
|
|
685
|
+
localMutationTails = new Map;
|
|
686
|
+
constructor(options) {
|
|
687
|
+
this.projectId = options.projectId;
|
|
688
|
+
this.storage = options.storage;
|
|
689
|
+
this.leaseDurationMs = positiveDuration(options.leaseDurationMs, "credential mutation lease duration");
|
|
690
|
+
this.operationTimeoutMs = positiveDuration(options.operationTimeoutMs, "credential mutation timeout");
|
|
691
|
+
this.hostSignal = options.hostSignal;
|
|
692
|
+
this.discoverReauthorizationAccounts = options.discoverReauthorizationAccounts;
|
|
693
|
+
this.holderId = options.holderId ?? `connector-service-${randomUUID()}`;
|
|
694
|
+
}
|
|
695
|
+
async claimCredentialMutation(connectorId, initial, kind, expectedConnectionIds) {
|
|
696
|
+
const waitDeadline = Date.now() + MUTATION_WAIT_MS;
|
|
697
|
+
const initialRevision = initial.revision;
|
|
698
|
+
let authorization = initial;
|
|
699
|
+
while (true) {
|
|
700
|
+
const claimed = await withConnectorStorageBoundary("Connector credential mutation could not be claimed.", () => this.storage.claimCredentialMutation({
|
|
701
|
+
projectId: this.projectId,
|
|
702
|
+
connectorId,
|
|
703
|
+
authorizationId: authorization.id,
|
|
704
|
+
expectedRevision: authorization.revision,
|
|
705
|
+
mutation: { id: `ccm_${randomUUID()}`, kind, holderId: this.holderId },
|
|
706
|
+
...expectedConnectionIds === undefined ? {} : { expectedConnectionIds },
|
|
707
|
+
leaseDurationMs: this.leaseDurationMs,
|
|
708
|
+
operationTimeoutMs: this.operationTimeoutMs
|
|
709
|
+
}));
|
|
710
|
+
if (claimed)
|
|
711
|
+
return { type: "claimed", claim: claimed };
|
|
712
|
+
await delay(MUTATION_POLL_MS);
|
|
713
|
+
authorization = await this.prepareAuthorizationForMutation(connectorId, authorization, kind);
|
|
714
|
+
if (kind === "reauthorization" && authorization.status === "revoked") {
|
|
715
|
+
throw createConnectorCodedError("connector.authorization_invalid", "Revoked connector credentials cannot be reauthorized.");
|
|
716
|
+
}
|
|
717
|
+
if (kind === "reauthorization" && authorization.status === "revocation_pending") {
|
|
718
|
+
throw createConnectorCodedError("connector.authorization_invalid", "Connector authorization revocation is pending.");
|
|
719
|
+
}
|
|
720
|
+
if (kind === "reauthorization" && authorization.revision !== initialRevision) {
|
|
721
|
+
throw staleReauthorizationError();
|
|
722
|
+
}
|
|
723
|
+
if (kind === "revocation" && authorization.status === "revoked") {
|
|
724
|
+
return { type: "superseded", authorization };
|
|
725
|
+
}
|
|
726
|
+
if (kind === "refresh" && authorization.revision !== initialRevision) {
|
|
727
|
+
if (authorization.status !== "active") {
|
|
728
|
+
throw connectorAuthorizationStatusError(authorization.status);
|
|
729
|
+
}
|
|
730
|
+
return { type: "superseded", authorization };
|
|
731
|
+
}
|
|
732
|
+
if (kind === "reauthorization" && expectedConnectionIds) {
|
|
733
|
+
await this.assertAttachedConnections(connectorId, authorization.id, expectedConnectionIds);
|
|
734
|
+
}
|
|
735
|
+
if (Date.now() >= waitDeadline) {
|
|
736
|
+
throw createConnectorCodedError("connector.operation_in_progress", "Connector credentials are being changed by another operation; retry shortly.");
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
async prepareAuthorizationForMutation(connectorId, initial, requestedKind) {
|
|
741
|
+
const authorization = await this.requireAuthorization(connectorId, initial.id);
|
|
742
|
+
const mutation = authorization.credentialMutation;
|
|
743
|
+
if (!mutation)
|
|
744
|
+
return authorization;
|
|
745
|
+
if (mutation.phase === "result_staged") {
|
|
746
|
+
if (requestedKind === "revocation" && mutation.kind !== "revocation") {
|
|
747
|
+
const marked = await this.markNeedsReauthorization(authorization);
|
|
748
|
+
return marked ?? this.requireAuthorization(connectorId, authorization.id);
|
|
749
|
+
}
|
|
750
|
+
return this.finalizeStagedMutation(authorization);
|
|
751
|
+
}
|
|
752
|
+
const recovered = await this.recoverExpiredCredentialMutation(connectorId, authorization.id);
|
|
753
|
+
return recovered ?? authorization;
|
|
754
|
+
}
|
|
755
|
+
async recoverExpiredCredentialMutation(connectorId, authorizationId) {
|
|
756
|
+
return withConnectorStorageBoundary("Expired connector credential mutation could not be recovered.", () => this.storage.recoverExpiredCredentialMutation({
|
|
757
|
+
projectId: this.projectId,
|
|
758
|
+
connectorId,
|
|
759
|
+
authorizationId
|
|
760
|
+
}));
|
|
761
|
+
}
|
|
762
|
+
async executeCredentialMutation(authorization, run) {
|
|
763
|
+
if (!authorization.credentialMutation) {
|
|
764
|
+
throw createConnectorCodedError("internal.unexpected", "Connector credential mutation was not claimed.");
|
|
765
|
+
}
|
|
766
|
+
const executing = await withConnectorStorageBoundary("Connector credential mutation could not be marked as executing.", () => this.storage.markCredentialMutationExecuting({
|
|
767
|
+
...credentialMutationFence(authorization),
|
|
768
|
+
holderId: this.holderId
|
|
769
|
+
}));
|
|
770
|
+
if (!executing)
|
|
771
|
+
throw createAmbiguousProviderOperationError();
|
|
772
|
+
return this.withCredentialMutationSignal(executing, (signal) => run(executing, signal));
|
|
773
|
+
}
|
|
774
|
+
async stageCredentialMutationCredentials(executing, signal, input) {
|
|
775
|
+
assertCredentialMutationOperationActive(signal);
|
|
776
|
+
const staged = await withConnectorStorageBoundary("Connector credential mutation result could not be staged.", () => this.storage.stageCredentialMutationCredentials({
|
|
777
|
+
...credentialMutationFence(executing),
|
|
778
|
+
holderId: this.holderId,
|
|
779
|
+
credentials: input.credentials,
|
|
780
|
+
...input.credentialExpiresAt === undefined ? {} : { credentialExpiresAt: input.credentialExpiresAt },
|
|
781
|
+
scopes: input.scopes
|
|
782
|
+
}));
|
|
783
|
+
if (!staged)
|
|
784
|
+
throw createAmbiguousProviderOperationError();
|
|
785
|
+
return staged;
|
|
786
|
+
}
|
|
787
|
+
async stageCredentialMutationRevocation(executing, signal) {
|
|
788
|
+
assertCredentialMutationOperationActive(signal);
|
|
789
|
+
const staged = await withConnectorStorageBoundary("Connector revocation result could not be staged.", () => this.storage.stageCredentialMutationRevocation({
|
|
790
|
+
...credentialMutationFence(executing),
|
|
791
|
+
holderId: this.holderId
|
|
792
|
+
}));
|
|
793
|
+
if (!staged)
|
|
794
|
+
throw createAmbiguousProviderOperationError();
|
|
795
|
+
return staged;
|
|
796
|
+
}
|
|
797
|
+
async withBoundedProviderSignal(run, interruptionError = createAmbiguousProviderOperationError) {
|
|
798
|
+
return runBoundedConnectorProviderOperation({ hostSignal: this.hostSignal(), timeoutMs: this.operationTimeoutMs }, run, interruptionError);
|
|
799
|
+
}
|
|
800
|
+
async finalizeStagedMutation(authorization) {
|
|
801
|
+
const mutation = authorization.credentialMutation;
|
|
802
|
+
if (!mutation || mutation.phase !== "result_staged")
|
|
803
|
+
return authorization;
|
|
804
|
+
const fence = credentialMutationFence(authorization);
|
|
805
|
+
let finalized;
|
|
806
|
+
if (mutation.kind === "refresh") {
|
|
807
|
+
finalized = await withConnectorStorageBoundary("Connector credential refresh could not be finalized.", () => this.storage.finalizeRefresh(fence));
|
|
808
|
+
} else if (mutation.kind === "revocation") {
|
|
809
|
+
finalized = await withConnectorStorageBoundary("Connector credential revocation could not be finalized.", () => this.storage.finalizeRevocation(fence));
|
|
810
|
+
} else {
|
|
811
|
+
const staged = mutation.stagedCredentials;
|
|
812
|
+
if (!staged) {
|
|
813
|
+
throw createConnectorCodedError("internal.unexpected", "Staged connector credentials are missing.");
|
|
814
|
+
}
|
|
815
|
+
let accounts;
|
|
816
|
+
try {
|
|
817
|
+
accounts = await this.discoverReauthorizationAccounts(authorization, staged);
|
|
818
|
+
} catch (error) {
|
|
819
|
+
throw providerBoundaryError(error, "connector.provider_failed", "Connector account discovery failed; reauthorization remains safely staged.");
|
|
820
|
+
}
|
|
821
|
+
try {
|
|
822
|
+
finalized = await this.storage.finalizeReauthorization({
|
|
823
|
+
...fence,
|
|
824
|
+
accounts
|
|
825
|
+
});
|
|
826
|
+
} catch (error) {
|
|
827
|
+
if (isConnectorStorageError(error)) {
|
|
828
|
+
await recoverConnectorFailure(error, "Connector authorization could not be failed closed after incompatible account discovery.", () => this.markNeedsReauthorization(authorization));
|
|
829
|
+
throw createConnectorCodedError("connector.authorization_required", "Reauthorized connector accounts changed incompatibly; reauthorization is required.", { cause: error });
|
|
830
|
+
}
|
|
831
|
+
throw storageBoundaryError(error, "Connector credential reauthorization could not be finalized.");
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
if (finalized)
|
|
835
|
+
return finalized;
|
|
836
|
+
const latest = await this.requireAuthorization(authorization.connectorId, authorization.id);
|
|
837
|
+
if (latest.revision !== authorization.revision || !latest.credentialMutation)
|
|
838
|
+
return latest;
|
|
839
|
+
throw createConnectorCodedError("internal.unexpected", "Connector credential mutation could not be finalized safely.");
|
|
840
|
+
}
|
|
841
|
+
async markNeedsReauthorization(authorization) {
|
|
842
|
+
if (!authorization.credentialMutation)
|
|
843
|
+
return null;
|
|
844
|
+
return withConnectorStorageBoundary("Connector authorization could not be marked for reauthorization.", () => this.storage.markNeedsReauthorization(credentialMutationFence(authorization)));
|
|
845
|
+
}
|
|
846
|
+
async releaseCredentialMutation(authorization) {
|
|
847
|
+
if (!authorization.credentialMutation)
|
|
848
|
+
return false;
|
|
849
|
+
return withConnectorStorageBoundary("Connector credential mutation could not be released.", () => this.storage.releaseCredentialMutation({
|
|
850
|
+
...credentialMutationFence(authorization),
|
|
851
|
+
holderId: this.holderId
|
|
852
|
+
}));
|
|
853
|
+
}
|
|
854
|
+
async requireStableActiveAuthorization(connectorId, authorizationId) {
|
|
855
|
+
const waitDeadline = Date.now() + MUTATION_WAIT_MS;
|
|
856
|
+
while (true) {
|
|
857
|
+
let authorization = await this.requireAuthorization(connectorId, authorizationId);
|
|
858
|
+
if (authorization.credentialMutation) {
|
|
859
|
+
authorization = await this.prepareAuthorizationForMutation(connectorId, authorization, "refresh");
|
|
860
|
+
if (authorization.credentialMutation) {
|
|
861
|
+
if (Date.now() >= waitDeadline) {
|
|
862
|
+
throw createConnectorCodedError("connector.operation_in_progress", "Connector credentials are being changed by another operation; retry shortly.");
|
|
863
|
+
}
|
|
864
|
+
await delay(MUTATION_POLL_MS);
|
|
865
|
+
continue;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
if (authorization.status !== "active") {
|
|
869
|
+
throw connectorAuthorizationStatusError(authorization.status);
|
|
870
|
+
}
|
|
871
|
+
return authorization;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
async assertAttachedConnections(connectorId, authorizationId, expectedConnectionIds) {
|
|
875
|
+
const attachedIds = (await withConnectorStorageBoundary("Connector authorization connections could not be read.", () => this.storage.listConnectionsByAuthorization({
|
|
876
|
+
projectId: this.projectId,
|
|
877
|
+
connectorId,
|
|
878
|
+
authorizationId
|
|
879
|
+
}))).map((connection) => connection.id);
|
|
880
|
+
if (!sameIds(attachedIds, expectedConnectionIds)) {
|
|
881
|
+
throw createConnectorCodedError("connector.operation_conflict", "Connections attached to this authorization changed; restart reauthorization.");
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
async runLocallySerialized(authorizationId, run) {
|
|
885
|
+
const previous = this.localMutationTails.get(authorizationId) ?? Promise.resolve();
|
|
886
|
+
const operation = previous.catch(() => {}).then(run);
|
|
887
|
+
const tail = operation.then(() => {
|
|
888
|
+
return;
|
|
889
|
+
}, () => {
|
|
890
|
+
return;
|
|
891
|
+
});
|
|
892
|
+
this.localMutationTails.set(authorizationId, tail);
|
|
893
|
+
try {
|
|
894
|
+
return await operation;
|
|
895
|
+
} finally {
|
|
896
|
+
if (this.localMutationTails.get(authorizationId) === tail) {
|
|
897
|
+
this.localMutationTails.delete(authorizationId);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
isStagedMutation(authorization, claimed, kind) {
|
|
902
|
+
const claimedMutation = claimed.credentialMutation;
|
|
903
|
+
return claimedMutation !== undefined && authorization.credentialMutation?.id === claimedMutation.id && authorization.credentialMutation.kind === kind && authorization.credentialMutation.phase === "result_staged";
|
|
904
|
+
}
|
|
905
|
+
async withCredentialMutationSignal(authorization, run) {
|
|
906
|
+
const controller = new AbortController;
|
|
907
|
+
const hostSignal = this.hostSignal();
|
|
908
|
+
const abortFromHost = () => controller.abort(createAmbiguousProviderOperationError());
|
|
909
|
+
if (hostSignal.aborted)
|
|
910
|
+
abortFromHost();
|
|
911
|
+
else
|
|
912
|
+
hostSignal.addEventListener("abort", abortFromHost, { once: true });
|
|
913
|
+
const timeout = setTimeout(() => controller.abort(createAmbiguousProviderOperationError()), this.operationTimeoutMs);
|
|
914
|
+
const heartbeat = setInterval(() => {
|
|
915
|
+
this.storage.renewCredentialMutation({
|
|
916
|
+
...credentialMutationFence(authorization),
|
|
917
|
+
holderId: this.holderId,
|
|
918
|
+
leaseDurationMs: this.leaseDurationMs
|
|
919
|
+
}).then((renewed) => {
|
|
920
|
+
if (!renewed)
|
|
921
|
+
controller.abort(createAmbiguousProviderOperationError());
|
|
922
|
+
}).catch((error) => controller.abort(createAmbiguousProviderOperationError({ cause: error })));
|
|
923
|
+
}, Math.max(5, Math.floor(this.leaseDurationMs / 3)));
|
|
924
|
+
const aborted = new Promise((_resolve, reject) => {
|
|
925
|
+
const rejectAborted = () => reject(abortReason2(controller.signal));
|
|
926
|
+
if (controller.signal.aborted)
|
|
927
|
+
rejectAborted();
|
|
928
|
+
else
|
|
929
|
+
controller.signal.addEventListener("abort", rejectAborted, { once: true });
|
|
930
|
+
});
|
|
931
|
+
const operation = Promise.resolve().then(() => {
|
|
932
|
+
assertCredentialMutationOperationActive(controller.signal);
|
|
933
|
+
return run(controller.signal);
|
|
934
|
+
});
|
|
935
|
+
operation.catch(() => {});
|
|
936
|
+
try {
|
|
937
|
+
return await Promise.race([operation, aborted]);
|
|
938
|
+
} finally {
|
|
939
|
+
clearInterval(heartbeat);
|
|
940
|
+
clearTimeout(timeout);
|
|
941
|
+
hostSignal.removeEventListener("abort", abortFromHost);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
async requireAuthorization(connectorId, authorizationId) {
|
|
945
|
+
const authorization = await withConnectorStorageBoundary("Connector authorization could not be read.", () => this.storage.getAuthorization({
|
|
946
|
+
projectId: this.projectId,
|
|
947
|
+
connectorId,
|
|
948
|
+
authorizationId
|
|
949
|
+
}));
|
|
950
|
+
if (!authorization) {
|
|
951
|
+
throw createConnectorCodedError("connector.not_found", "Connector authorization was not found.");
|
|
952
|
+
}
|
|
953
|
+
return authorization;
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
function assertCredentialMutationOperationActive(signal) {
|
|
957
|
+
if (signal.aborted)
|
|
958
|
+
throw abortReason2(signal);
|
|
959
|
+
}
|
|
960
|
+
function connectorAuthorizationStatusError(status) {
|
|
961
|
+
if (status === "needs_reauthorization") {
|
|
962
|
+
return createConnectorCodedError("connector.authorization_required", "Connector credentials require reauthorization.");
|
|
963
|
+
}
|
|
964
|
+
if (status === "pending_selection") {
|
|
965
|
+
return createConnectorCodedError("connector.authorization_required", "Connector authorization requires account selection.");
|
|
966
|
+
}
|
|
967
|
+
if (status === "revocation_pending") {
|
|
968
|
+
return createConnectorCodedError("connector.authorization_required", "Connector authorization revocation is pending.");
|
|
969
|
+
}
|
|
970
|
+
return createConnectorCodedError("connector.authorization_required", "Connector authorization has been revoked.");
|
|
971
|
+
}
|
|
972
|
+
function credentialMutationFence(authorization) {
|
|
973
|
+
const mutation = authorization.credentialMutation;
|
|
974
|
+
if (!mutation) {
|
|
975
|
+
throw createConnectorCodedError("internal.unexpected", "Connector credential mutation is missing.");
|
|
976
|
+
}
|
|
977
|
+
return {
|
|
978
|
+
projectId: authorization.projectId,
|
|
979
|
+
connectorId: authorization.connectorId,
|
|
980
|
+
authorizationId: authorization.id,
|
|
981
|
+
expectedRevision: authorization.revision,
|
|
982
|
+
mutationId: mutation.id
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
function abortReason2(signal) {
|
|
986
|
+
return signal.reason instanceof Error ? signal.reason : createAmbiguousProviderOperationError();
|
|
987
|
+
}
|
|
988
|
+
function staleReauthorizationError() {
|
|
989
|
+
return createConnectorCodedError("connector.authorization_invalid", "Connector reauthorization attempt is stale; restart authorization.");
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
// src/connectors/connections/tokens.ts
|
|
993
|
+
class ConnectorTokenAccess {
|
|
994
|
+
projectId;
|
|
995
|
+
storage;
|
|
996
|
+
credentials;
|
|
997
|
+
mutations;
|
|
998
|
+
refreshSkewMs;
|
|
999
|
+
now;
|
|
1000
|
+
constructor(options) {
|
|
1001
|
+
this.projectId = options.projectId;
|
|
1002
|
+
this.storage = options.storage;
|
|
1003
|
+
this.credentials = options.credentials;
|
|
1004
|
+
this.mutations = options.mutations;
|
|
1005
|
+
this.refreshSkewMs = nonNegativeDuration(options.refreshSkewMs, "refresh skew");
|
|
1006
|
+
this.now = options.now;
|
|
1007
|
+
}
|
|
1008
|
+
requireStableActiveAuthorization(definition, authorizationId) {
|
|
1009
|
+
return this.mutations.requireStableActiveAuthorization(definition.id, authorizationId);
|
|
1010
|
+
}
|
|
1011
|
+
createTokenSource(definition, authorizationId) {
|
|
1012
|
+
return new RevisionAwareConnectorTokenSource((rejectedRevision) => this.getAccessToken(definition, authorizationId, rejectedRevision));
|
|
1013
|
+
}
|
|
1014
|
+
open(connectorId, authorization) {
|
|
1015
|
+
if (!authorization.credentials) {
|
|
1016
|
+
throw createConnectorCodedError("connector.credentials_unavailable", "Stored connector credentials are unavailable.");
|
|
1017
|
+
}
|
|
1018
|
+
return this.credentials.open(connectorId, authorization.id, authorization.credentials);
|
|
1019
|
+
}
|
|
1020
|
+
async getAccessToken(definition, authorizationId, rejectedRevision) {
|
|
1021
|
+
let authorization = await this.mutations.requireStableActiveAuthorization(definition.id, authorizationId);
|
|
1022
|
+
const credentials = await this.open(definition.id, authorization);
|
|
1023
|
+
const refreshRejectedToken = rejectedRevision !== undefined && rejectedRevision === authorization.revision;
|
|
1024
|
+
if (refreshRejectedToken || shouldRefresh(authorization, this.now(), this.refreshSkewMs)) {
|
|
1025
|
+
authorization = await this.refreshAuthorization(definition, authorization);
|
|
1026
|
+
const refreshed = await this.open(definition.id, authorization);
|
|
1027
|
+
return { token: tokenView(refreshed), revision: authorization.revision };
|
|
1028
|
+
}
|
|
1029
|
+
return { token: tokenView(credentials), revision: authorization.revision };
|
|
1030
|
+
}
|
|
1031
|
+
async refreshAuthorization(definition, initial) {
|
|
1032
|
+
return this.mutations.runLocallySerialized(initial.id, async () => {
|
|
1033
|
+
let authorization = await this.mutations.requireStableActiveAuthorization(definition.id, initial.id);
|
|
1034
|
+
if (authorization.revision !== initial.revision)
|
|
1035
|
+
return authorization;
|
|
1036
|
+
authorization = await this.mutations.prepareAuthorizationForMutation(definition.id, authorization, "refresh");
|
|
1037
|
+
if (authorization.revision !== initial.revision)
|
|
1038
|
+
return authorization;
|
|
1039
|
+
const claimOutcome = await this.mutations.claimCredentialMutation(definition.id, authorization, "refresh");
|
|
1040
|
+
if (claimOutcome.type === "superseded")
|
|
1041
|
+
return claimOutcome.authorization;
|
|
1042
|
+
const { claim } = claimOutcome;
|
|
1043
|
+
let current;
|
|
1044
|
+
try {
|
|
1045
|
+
current = await this.open(definition.id, claim.authorization);
|
|
1046
|
+
} catch (error) {
|
|
1047
|
+
await recoverConnectorFailure(error, "Connector credential mutation could not be released after credential decryption failed.", () => this.mutations.releaseCredentialMutation(claim.authorization));
|
|
1048
|
+
throw createConnectorCodedError("connector.credentials_unavailable", "Stored connector credentials could not be opened.", { cause: error });
|
|
1049
|
+
}
|
|
1050
|
+
if (!current.refreshToken) {
|
|
1051
|
+
const error = createConnectorCodedError("connector.authorization_required", "Connector credentials require reauthorization.");
|
|
1052
|
+
await recoverConnectorFailure(error, "Connector authorization could not be marked for reauthorization after finding no refresh token.", () => this.mutations.markNeedsReauthorization(claim.authorization));
|
|
1053
|
+
throw error;
|
|
1054
|
+
}
|
|
1055
|
+
try {
|
|
1056
|
+
await this.mutations.executeCredentialMutation(claim.authorization, async (executing, signal) => {
|
|
1057
|
+
const refreshed = validateCredentials(await definition.adapter.authentication.refresh(this.adapterContext(definition.id, signal), current));
|
|
1058
|
+
assertCredentialMutationOperationActive(signal);
|
|
1059
|
+
const normalized = {
|
|
1060
|
+
...refreshed,
|
|
1061
|
+
...refreshed.refreshToken === undefined ? { refreshToken: current.refreshToken } : {},
|
|
1062
|
+
...refreshed.tokenType === undefined && current.tokenType !== undefined ? { tokenType: current.tokenType } : {},
|
|
1063
|
+
scopes: refreshed.scopes ?? current.scopes ?? authorization.scopes
|
|
1064
|
+
};
|
|
1065
|
+
const envelope = await this.credentials.seal(definition.id, authorization.id, normalized);
|
|
1066
|
+
return this.mutations.stageCredentialMutationCredentials(executing, signal, {
|
|
1067
|
+
credentials: envelope,
|
|
1068
|
+
...normalized.expiresAt === undefined ? {} : { credentialExpiresAt: normalized.expiresAt },
|
|
1069
|
+
scopes: normalized.scopes
|
|
1070
|
+
});
|
|
1071
|
+
});
|
|
1072
|
+
} catch (error) {
|
|
1073
|
+
const latest = await recoverConnectorFailure(error, "Connector refresh outcome could not be recovered from storage.", () => this.requireAuthorization(definition.id, authorization.id));
|
|
1074
|
+
if (this.mutations.isStagedMutation(latest, claim.authorization, "refresh")) {
|
|
1075
|
+
return recoverConnectorFailure(error, "Staged connector refresh could not be finalized during recovery.", () => this.mutations.finalizeStagedMutation(latest));
|
|
1076
|
+
}
|
|
1077
|
+
if (oauthErrorKind(error) === "retryable") {
|
|
1078
|
+
await recoverConnectorFailure(error, "Connector refresh mutation could not be released after a retryable provider failure.", () => this.mutations.releaseCredentialMutation(claim.authorization));
|
|
1079
|
+
throw createConnectorCodedError("connector.provider_unavailable", "Connector credential refresh failed; retry later.", { cause: error });
|
|
1080
|
+
}
|
|
1081
|
+
const marked = await recoverConnectorFailure(error, "Connector authorization could not be failed closed after an ambiguous refresh.", () => this.mutations.markNeedsReauthorization(claim.authorization));
|
|
1082
|
+
if (marked) {
|
|
1083
|
+
throw createConnectorCodedError("connector.authorization_required", "Connector credentials require reauthorization.", { cause: error });
|
|
1084
|
+
}
|
|
1085
|
+
const recovered = await recoverConnectorFailure(error, "Connector refresh state could not be reloaded after failed closed recovery.", () => this.requireAuthorization(definition.id, authorization.id));
|
|
1086
|
+
if (recovered.revision !== authorization.revision && recovered.status === "active") {
|
|
1087
|
+
return recovered;
|
|
1088
|
+
}
|
|
1089
|
+
throw connectorAuthorizationStatusError(recovered.status);
|
|
1090
|
+
}
|
|
1091
|
+
const staged = await this.requireAuthorization(definition.id, authorization.id);
|
|
1092
|
+
return this.mutations.finalizeStagedMutation(staged);
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
async requireAuthorization(connectorId, authorizationId) {
|
|
1096
|
+
const authorization = await withConnectorStorageBoundary("Connector authorization could not be read.", () => this.storage.getAuthorization({
|
|
1097
|
+
projectId: this.projectId,
|
|
1098
|
+
connectorId,
|
|
1099
|
+
authorizationId
|
|
1100
|
+
}));
|
|
1101
|
+
if (!authorization) {
|
|
1102
|
+
throw createConnectorCodedError("connector.not_found", "Connector authorization was not found.");
|
|
1103
|
+
}
|
|
1104
|
+
return authorization;
|
|
1105
|
+
}
|
|
1106
|
+
adapterContext(connectorId, signal) {
|
|
1107
|
+
return { projectId: this.projectId, connectorId, signal };
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
class RevisionAwareConnectorTokenSource {
|
|
1112
|
+
resolveToken;
|
|
1113
|
+
rejectedRevision;
|
|
1114
|
+
constructor(resolveToken) {
|
|
1115
|
+
this.resolveToken = resolveToken;
|
|
1116
|
+
}
|
|
1117
|
+
async get() {
|
|
1118
|
+
const consumedRejection = this.rejectedRevision;
|
|
1119
|
+
const result = await this.resolveToken(consumedRejection);
|
|
1120
|
+
if (this.rejectedRevision === consumedRejection)
|
|
1121
|
+
this.rejectedRevision = undefined;
|
|
1122
|
+
let invalidated = false;
|
|
1123
|
+
return Object.freeze({
|
|
1124
|
+
...result.token,
|
|
1125
|
+
invalidate: () => {
|
|
1126
|
+
if (invalidated)
|
|
1127
|
+
return;
|
|
1128
|
+
invalidated = true;
|
|
1129
|
+
this.rejectedRevision = Math.max(this.rejectedRevision ?? -1, result.revision);
|
|
1130
|
+
}
|
|
1131
|
+
});
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
// src/connectors/connections/authorizations.ts
|
|
1136
|
+
class DefaultConnectorAuthorizationLifecycle {
|
|
1137
|
+
projectId;
|
|
1138
|
+
storage;
|
|
1139
|
+
resolveDefinition;
|
|
1140
|
+
accountSelectionTtlMs;
|
|
1141
|
+
mutations;
|
|
1142
|
+
credentials;
|
|
1143
|
+
tokens;
|
|
1144
|
+
constructor(options) {
|
|
1145
|
+
this.projectId = options.projectId;
|
|
1146
|
+
this.storage = options.storage;
|
|
1147
|
+
this.resolveDefinition = options.resolveDefinition;
|
|
1148
|
+
this.accountSelectionTtlMs = positiveDuration(options.accountSelectionTtlMs, "account selection TTL");
|
|
1149
|
+
this.credentials = new ConnectorCredentialCodec({
|
|
1150
|
+
projectId: options.projectId,
|
|
1151
|
+
protector: options.credentialProtector
|
|
1152
|
+
});
|
|
1153
|
+
this.mutations = new ConnectorCredentialMutationCoordinator({
|
|
1154
|
+
projectId: options.projectId,
|
|
1155
|
+
storage: options.storage,
|
|
1156
|
+
leaseDurationMs: options.credentialMutationLeaseMs,
|
|
1157
|
+
operationTimeoutMs: options.providerOperationTimeoutMs,
|
|
1158
|
+
hostSignal: options.hostSignal,
|
|
1159
|
+
discoverReauthorizationAccounts: async (authorization, staged) => {
|
|
1160
|
+
const definition = this.definitionFor(authorization.connectorId);
|
|
1161
|
+
const credentials = await this.credentials.open(definition.id, authorization.id, staged.credentials);
|
|
1162
|
+
return this.discoverAccounts(definition, credentials);
|
|
1163
|
+
}
|
|
1164
|
+
});
|
|
1165
|
+
this.tokens = new ConnectorTokenAccess({
|
|
1166
|
+
projectId: options.projectId,
|
|
1167
|
+
storage: options.storage,
|
|
1168
|
+
credentials: this.credentials,
|
|
1169
|
+
mutations: this.mutations,
|
|
1170
|
+
refreshSkewMs: options.refreshSkewMs,
|
|
1171
|
+
now: options.now
|
|
1172
|
+
});
|
|
1173
|
+
}
|
|
1174
|
+
async prepareReauthorization(input) {
|
|
1175
|
+
this.assertRegistered(input.definition);
|
|
1176
|
+
const authorization = await this.requireAuthorization(input.definition.id, input.authorizationId);
|
|
1177
|
+
if (authorization.status === "pending_selection") {
|
|
1178
|
+
throw createConnectorCodedError("connector.authorization_invalid", "Pending connector credentials cannot be reauthorized; restart authorization or select an account.");
|
|
1179
|
+
}
|
|
1180
|
+
if (authorization.status === "revoked" || authorization.status === "revocation_pending") {
|
|
1181
|
+
throw createConnectorCodedError("connector.authorization_invalid", "Revoked connector credentials cannot be reauthorized.");
|
|
1182
|
+
}
|
|
1183
|
+
assertConnectorAuthorizationActor(authorization, input.principal);
|
|
1184
|
+
const connections = await withConnectorStorageBoundary("Connector authorization connections could not be read.", () => this.storage.listConnectionsByAuthorization({
|
|
1185
|
+
projectId: this.projectId,
|
|
1186
|
+
connectorId: input.definition.id,
|
|
1187
|
+
authorizationId: authorization.id
|
|
1188
|
+
}));
|
|
1189
|
+
return { authorization, connections };
|
|
1190
|
+
}
|
|
1191
|
+
async completeNewAuthorization(input) {
|
|
1192
|
+
this.assertRegistered(input.definition);
|
|
1193
|
+
const authorizationId = `cau_${randomUUID2()}`;
|
|
1194
|
+
const credentials = await this.exchangeAuthorizationCode(input.definition, input.code, input.codeVerifier, input.redirectUri);
|
|
1195
|
+
const credentialsEnvelope = await this.credentials.seal(input.definition.id, authorizationId, credentials);
|
|
1196
|
+
let pending;
|
|
1197
|
+
try {
|
|
1198
|
+
pending = await this.storage.createAuthorization({
|
|
1199
|
+
id: authorizationId,
|
|
1200
|
+
projectId: this.projectId,
|
|
1201
|
+
connectorId: input.definition.id,
|
|
1202
|
+
authorizedBy: input.principal,
|
|
1203
|
+
credentials: credentialsEnvelope,
|
|
1204
|
+
...credentials.expiresAt === undefined ? {} : { credentialExpiresAt: credentials.expiresAt },
|
|
1205
|
+
scopes: credentials.scopes ?? [],
|
|
1206
|
+
accounts: [],
|
|
1207
|
+
selectionTtlMs: this.accountSelectionTtlMs
|
|
1208
|
+
});
|
|
1209
|
+
} catch (error) {
|
|
1210
|
+
throw storageBoundaryError(error, "Connector authorization could not be persisted.");
|
|
1211
|
+
}
|
|
1212
|
+
if (input.onAuthorizationPersisted) {
|
|
1213
|
+
try {
|
|
1214
|
+
await input.onAuthorizationPersisted(pending);
|
|
1215
|
+
} catch (error) {
|
|
1216
|
+
await this.revokeAbandonedAuthorization(input.definition, authorizationId, input.principal, error);
|
|
1217
|
+
throw error;
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
let accounts;
|
|
1221
|
+
try {
|
|
1222
|
+
accounts = await this.discoverAccounts(input.definition, credentials);
|
|
1223
|
+
} catch (error) {
|
|
1224
|
+
const discoveryError = providerBoundaryError(error, "connector.provider_failed", "Connector account discovery failed; the unused authorization is being revoked.");
|
|
1225
|
+
await this.revokeAbandonedAuthorization(input.definition, authorizationId, input.principal, discoveryError);
|
|
1226
|
+
throw discoveryError;
|
|
1227
|
+
}
|
|
1228
|
+
const initialized = await withConnectorStorageBoundary("Connector authorization accounts could not be persisted.", () => this.storage.initializeAuthorizationAccounts({
|
|
1229
|
+
projectId: this.projectId,
|
|
1230
|
+
connectorId: input.definition.id,
|
|
1231
|
+
authorizationId,
|
|
1232
|
+
expectedRevision: pending.revision,
|
|
1233
|
+
accounts
|
|
1234
|
+
}));
|
|
1235
|
+
if (!initialized) {
|
|
1236
|
+
throw createConnectorCodedError("connector.operation_conflict", "Connector authorization changed while accounts were discovered.");
|
|
1237
|
+
}
|
|
1238
|
+
return initialized;
|
|
1239
|
+
}
|
|
1240
|
+
async completeReauthorization(input) {
|
|
1241
|
+
this.assertRegistered(input.definition);
|
|
1242
|
+
return this.mutations.runLocallySerialized(input.authorizationId, () => this.completeReauthorizationMutation(input));
|
|
1243
|
+
}
|
|
1244
|
+
async revokeAuthorization(input) {
|
|
1245
|
+
this.assertRegistered(input.definition);
|
|
1246
|
+
return this.mutations.runLocallySerialized(input.authorizationId, async () => {
|
|
1247
|
+
const authorization = await this.requireAuthorization(input.definition.id, input.authorizationId);
|
|
1248
|
+
assertConnectorAuthorizationActor(authorization, input.principal);
|
|
1249
|
+
return this.revokeAuthorizationRecord(input.definition, authorization);
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1252
|
+
async continuePendingRevocation(definition, authorizationId) {
|
|
1253
|
+
this.assertRegistered(definition);
|
|
1254
|
+
return this.mutations.runLocallySerialized(authorizationId, async () => {
|
|
1255
|
+
const authorization = await this.requireAuthorization(definition.id, authorizationId);
|
|
1256
|
+
if (authorization.status !== "revocation_pending" && authorization.status !== "revoked") {
|
|
1257
|
+
throw createConnectorCodedError("internal.unexpected", "Connector cleanup requires an authorization already pending revocation.");
|
|
1258
|
+
}
|
|
1259
|
+
return this.revokeAuthorizationRecord(definition, authorization);
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
async requireStableActiveAuthorization(definition, authorizationId) {
|
|
1263
|
+
this.assertRegistered(definition);
|
|
1264
|
+
return this.tokens.requireStableActiveAuthorization(definition, authorizationId);
|
|
1265
|
+
}
|
|
1266
|
+
createTokenSource(definition, authorizationId) {
|
|
1267
|
+
this.assertRegistered(definition);
|
|
1268
|
+
return this.tokens.createTokenSource(definition, authorizationId);
|
|
1269
|
+
}
|
|
1270
|
+
async requireAuthorization(connectorId, authorizationId) {
|
|
1271
|
+
const authorization = await withConnectorStorageBoundary("Connector authorization could not be read.", () => this.storage.getAuthorization({
|
|
1272
|
+
projectId: this.projectId,
|
|
1273
|
+
connectorId,
|
|
1274
|
+
authorizationId
|
|
1275
|
+
}));
|
|
1276
|
+
if (!authorization) {
|
|
1277
|
+
throw createConnectorCodedError("connector.not_found", "Connector authorization was not found.");
|
|
1278
|
+
}
|
|
1279
|
+
return authorization;
|
|
1280
|
+
}
|
|
1281
|
+
async revokeAuthorizationRecord(definition, initial) {
|
|
1282
|
+
if (initial.status === "revoked")
|
|
1283
|
+
return { disconnected: [] };
|
|
1284
|
+
const authorization = await this.mutations.prepareAuthorizationForMutation(definition.id, initial, "revocation");
|
|
1285
|
+
if (authorization.status === "revoked")
|
|
1286
|
+
return { disconnected: [] };
|
|
1287
|
+
const claimOutcome = await this.mutations.claimCredentialMutation(definition.id, authorization, "revocation");
|
|
1288
|
+
if (claimOutcome.type === "superseded")
|
|
1289
|
+
return { disconnected: [] };
|
|
1290
|
+
const { claim } = claimOutcome;
|
|
1291
|
+
let credentials;
|
|
1292
|
+
if (definition.adapter.authentication.revoke) {
|
|
1293
|
+
try {
|
|
1294
|
+
if (!claim.authorization.credentials) {
|
|
1295
|
+
throw createConnectorCodedError("connector.credentials_unavailable", "Stored connector credentials are unavailable.");
|
|
1296
|
+
}
|
|
1297
|
+
credentials = await this.credentials.open(definition.id, claim.authorization.id, claim.authorization.credentials);
|
|
1298
|
+
} catch (error) {
|
|
1299
|
+
await recoverConnectorFailure(error, "Connector credential mutation could not be released after credential decryption failed.", () => this.mutations.releaseCredentialMutation(claim.authorization));
|
|
1300
|
+
throw createConnectorCodedError("connector.credentials_unavailable", "Connector revocation is pending, but stored credentials could not be opened.", { cause: error });
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
try {
|
|
1304
|
+
await this.mutations.executeCredentialMutation(claim.authorization, async (executing, signal) => {
|
|
1305
|
+
if (definition.adapter.authentication.revoke && credentials) {
|
|
1306
|
+
await definition.adapter.authentication.revoke(this.adapterContext(definition.id, signal), credentials);
|
|
1307
|
+
}
|
|
1308
|
+
return this.mutations.stageCredentialMutationRevocation(executing, signal);
|
|
1309
|
+
});
|
|
1310
|
+
} catch (error) {
|
|
1311
|
+
const latest = await recoverConnectorFailure(error, "Connector revocation outcome could not be recovered from storage.", () => this.requireAuthorization(definition.id, authorization.id));
|
|
1312
|
+
if (this.mutations.isStagedMutation(latest, claim.authorization, "revocation")) {
|
|
1313
|
+
await recoverConnectorFailure(error, "Staged connector revocation could not be finalized during recovery.", () => this.mutations.finalizeStagedMutation(latest));
|
|
1314
|
+
return { disconnected: claim.disconnected };
|
|
1315
|
+
}
|
|
1316
|
+
await recoverConnectorFailure(error, "Connector revocation mutation could not be released for retry.", () => this.mutations.releaseCredentialMutation(claim.authorization));
|
|
1317
|
+
throw createConnectorCodedError("connector.revocation_pending", "Connector revocation is pending; provider revocation can be retried safely.", { cause: error });
|
|
1318
|
+
}
|
|
1319
|
+
const staged = await this.requireAuthorization(definition.id, authorization.id);
|
|
1320
|
+
await this.mutations.finalizeStagedMutation(staged);
|
|
1321
|
+
return { disconnected: claim.disconnected };
|
|
1322
|
+
}
|
|
1323
|
+
async revokeAbandonedAuthorization(definition, authorizationId, principal, primaryError) {
|
|
1324
|
+
try {
|
|
1325
|
+
await this.revokeAuthorization({ definition, authorizationId, principal });
|
|
1326
|
+
} catch (cleanupError) {
|
|
1327
|
+
if (isSixbError(cleanupError) && cleanupError.code === "connector.revocation_pending")
|
|
1328
|
+
return;
|
|
1329
|
+
throw createConnectorCodedError("internal.unexpected", "Unused connector credentials could not be scheduled for revocation.", {
|
|
1330
|
+
cause: new AggregateError([primaryError, cleanupError], "Connector authorization failed and cleanup could not be secured.")
|
|
1331
|
+
});
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
async completeReauthorizationMutation(input) {
|
|
1335
|
+
let authorization = await this.requireAuthorization(input.definition.id, input.authorizationId);
|
|
1336
|
+
assertReauthorizationRevision(authorization, input.expectedRevision);
|
|
1337
|
+
if (authorization.status === "revoked" || authorization.status === "revocation_pending") {
|
|
1338
|
+
throw createConnectorCodedError("connector.authorization_invalid", "Revoked connector credentials cannot be reauthorized.");
|
|
1339
|
+
}
|
|
1340
|
+
assertConnectorAuthorizationActor(authorization, input.principal);
|
|
1341
|
+
await this.mutations.assertAttachedConnections(input.definition.id, authorization.id, input.expectedConnectionIds);
|
|
1342
|
+
authorization = await this.mutations.prepareAuthorizationForMutation(input.definition.id, authorization, "reauthorization");
|
|
1343
|
+
assertReauthorizationRevision(authorization, input.expectedRevision);
|
|
1344
|
+
await this.mutations.assertAttachedConnections(input.definition.id, authorization.id, input.expectedConnectionIds);
|
|
1345
|
+
const claimOutcome = await this.mutations.claimCredentialMutation(input.definition.id, authorization, "reauthorization", input.expectedConnectionIds);
|
|
1346
|
+
if (claimOutcome.type === "superseded") {
|
|
1347
|
+
throw createConnectorCodedError("internal.unexpected", "Connector reauthorization was superseded unexpectedly.");
|
|
1348
|
+
}
|
|
1349
|
+
const { claim } = claimOutcome;
|
|
1350
|
+
try {
|
|
1351
|
+
await this.mutations.executeCredentialMutation(claim.authorization, async (executing, signal) => {
|
|
1352
|
+
const credentials = validateCredentials(await input.definition.adapter.authentication.exchangeCode(this.authorizationAdapterContext(input.definition.id, input.redirectUri, signal), { code: input.code, codeVerifier: input.codeVerifier }));
|
|
1353
|
+
assertCredentialMutationOperationActive(signal);
|
|
1354
|
+
const envelope = await this.credentials.seal(input.definition.id, input.authorizationId, credentials);
|
|
1355
|
+
return this.mutations.stageCredentialMutationCredentials(executing, signal, {
|
|
1356
|
+
credentials: envelope,
|
|
1357
|
+
...credentials.expiresAt === undefined ? {} : { credentialExpiresAt: credentials.expiresAt },
|
|
1358
|
+
scopes: credentials.scopes ?? []
|
|
1359
|
+
});
|
|
1360
|
+
});
|
|
1361
|
+
} catch (error) {
|
|
1362
|
+
const latest = await recoverConnectorFailure(error, "Connector reauthorization outcome could not be recovered from storage.", () => this.requireAuthorization(input.definition.id, input.authorizationId));
|
|
1363
|
+
if (this.mutations.isStagedMutation(latest, claim.authorization, "reauthorization")) {
|
|
1364
|
+
return recoverConnectorFailure(error, "Staged connector reauthorization could not be finalized during recovery.", () => this.mutations.finalizeStagedMutation(latest));
|
|
1365
|
+
}
|
|
1366
|
+
if (oauthErrorKind(error) === "retryable" || oauthErrorKind(error) === "terminal") {
|
|
1367
|
+
await recoverConnectorFailure(error, "Connector reauthorization mutation could not be released after provider failure.", () => this.mutations.releaseCredentialMutation(claim.authorization));
|
|
1368
|
+
throw createConnectorCodedError("connector.provider_failed", "Connector authorization code exchange failed; restart authorization.", { cause: error });
|
|
1369
|
+
}
|
|
1370
|
+
await recoverConnectorFailure(error, "Connector authorization could not be failed closed after an ambiguous code exchange.", () => this.mutations.markNeedsReauthorization(claim.authorization));
|
|
1371
|
+
throw createConnectorCodedError("connector.authorization_required", "Connector authorization code exchange had an ambiguous outcome; reauthorization is required.", { cause: error });
|
|
1372
|
+
}
|
|
1373
|
+
const staged = await this.requireAuthorization(input.definition.id, input.authorizationId);
|
|
1374
|
+
return this.mutations.finalizeStagedMutation(staged);
|
|
1375
|
+
}
|
|
1376
|
+
async exchangeAuthorizationCode(definition, code, codeVerifier, redirectUri) {
|
|
1377
|
+
try {
|
|
1378
|
+
return validateCredentials(await this.mutations.withBoundedProviderSignal((signal) => definition.adapter.authentication.exchangeCode(this.authorizationAdapterContext(definition.id, redirectUri, signal), { code, codeVerifier })));
|
|
1379
|
+
} catch (error) {
|
|
1380
|
+
throw providerBoundaryError(error, "connector.provider_failed", "Connector authorization code exchange failed.");
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
async discoverAccounts(definition, credentials) {
|
|
1384
|
+
return validateAccounts(await this.mutations.withBoundedProviderSignal((signal) => definition.adapter.discoverAccounts(this.adapterContext(definition.id, signal), credentials), () => createConnectorCodedError("connector.provider_failed", "Connector account discovery did not complete.")));
|
|
1385
|
+
}
|
|
1386
|
+
adapterContext(connectorId, signal) {
|
|
1387
|
+
return { projectId: this.projectId, connectorId, signal };
|
|
1388
|
+
}
|
|
1389
|
+
authorizationAdapterContext(connectorId, redirectUri, signal) {
|
|
1390
|
+
return { ...this.adapterContext(connectorId, signal), redirectUri };
|
|
1391
|
+
}
|
|
1392
|
+
definitionFor(connectorId) {
|
|
1393
|
+
const definition = this.resolveDefinition(connectorId);
|
|
1394
|
+
if (definition.id !== connectorId) {
|
|
1395
|
+
throw createConnectorCodedError("internal.unexpected", "Connector definition resolver returned a mismatched definition.");
|
|
1396
|
+
}
|
|
1397
|
+
return definition;
|
|
1398
|
+
}
|
|
1399
|
+
assertRegistered(definition) {
|
|
1400
|
+
if (this.definitionFor(definition.id) !== definition) {
|
|
1401
|
+
throw createConnectorCodedError("connector.configuration_invalid", `Connector '${definition.id}' is not the registered definition instance.`);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
function assertConnectorAuthorizationActor(authorization, principal) {
|
|
1406
|
+
if (authorization.authorizedBy.type !== principal.type || authorization.authorizedBy.id !== principal.id) {
|
|
1407
|
+
throw new AuthorizationError(`authorization:connector:${authorization.connectorId}`, "[Sixb] This connector authorization can only be changed by its authorizing principal.");
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
function assertReauthorizationRevision(authorization, expectedRevision) {
|
|
1411
|
+
if (authorization.revision !== expectedRevision) {
|
|
1412
|
+
throw createConnectorCodedError("connector.authorization_invalid", "Connector reauthorization attempt is stale; restart authorization.");
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
// src/connectors/connections/command-context.ts
|
|
1417
|
+
function requireConnectorConnectionCommandActor(execution, projectId) {
|
|
1418
|
+
const actor = connectorConnectionActorFromExecution(execution, projectId);
|
|
1419
|
+
if (actor)
|
|
1420
|
+
return actor;
|
|
1421
|
+
throw new AuthorizationError("manage:connector", "[Sixb] Connector connection commands require an authenticated request execution.");
|
|
1422
|
+
}
|
|
1423
|
+
function assertConnectorAuthorizationAttemptInitiator(execution, current) {
|
|
1424
|
+
const initiating = execution ? connectorConnectionActorFromExecution(execution, execution.projectId) : null;
|
|
1425
|
+
if (!initiating || !sameConnectorConnectionCommandActor(initiating, current)) {
|
|
1426
|
+
throw new ConnectorConnectionStorageError("attempt_invalid", "[Sixb] Connector authorization attempt is invalid, expired, or already used.");
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
function assertConnectorConnectionRunInitiator(execution, current, connectorId) {
|
|
1430
|
+
const initiating = execution ? connectorConnectionActorFromExecution(execution, execution.projectId) : null;
|
|
1431
|
+
if (!initiating || !sameConnectorConnectionCommandActor(initiating, current)) {
|
|
1432
|
+
throw new AuthorizationError(`connection-run:connector:${connectorId}`, "[Sixb] This connector connection run can only be accessed by its initiating actor.");
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
function connectorConnectionActorFromExecution(execution, projectId) {
|
|
1436
|
+
const authorization = execution.authorizationRef;
|
|
1437
|
+
if (execution.projectId !== projectId || execution.executor.type !== "request" || execution.source.type !== "http" || execution.executor.requestId !== execution.source.requestId || authorization.type !== "principal" || authorization.credential === undefined || execution.requestedBy?.type !== authorization.principal.type || execution.requestedBy.id !== authorization.principal.id) {
|
|
1438
|
+
return null;
|
|
1439
|
+
}
|
|
1440
|
+
return {
|
|
1441
|
+
principal: authorization.principal,
|
|
1442
|
+
credential: authorization.credential
|
|
1443
|
+
};
|
|
1444
|
+
}
|
|
1445
|
+
function sameConnectorConnectionCommandActor(left, right) {
|
|
1446
|
+
return left.principal.type === right.principal.type && left.principal.id === right.principal.id && left.credential.type === right.credential.type && left.credential.id === right.credential.id;
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
// src/connectors/connections/request.ts
|
|
1450
|
+
import { createHash as createHash2, randomBytes as randomBytes2, randomUUID as randomUUID3 } from "crypto";
|
|
1451
|
+
|
|
1452
|
+
// src/connectors/connections/views.ts
|
|
1453
|
+
function assertConnectorConnectionSelector(selector) {
|
|
1454
|
+
if (selector.owner.type !== "project") {
|
|
1455
|
+
throw createConnectorCodedError("connector.configuration_invalid", "Connector connections only support project ownership in V1.");
|
|
1456
|
+
}
|
|
1457
|
+
nonblank(selector.slot, "connection slot");
|
|
1458
|
+
}
|
|
1459
|
+
function connectorConnectionView(connection, status) {
|
|
1460
|
+
return {
|
|
1461
|
+
id: connection.id,
|
|
1462
|
+
connectorId: connection.connectorId,
|
|
1463
|
+
owner: connection.owner,
|
|
1464
|
+
slot: connection.slot,
|
|
1465
|
+
account: connection.account,
|
|
1466
|
+
status: connection.status === "disconnected" || status === "revoked" || status === "revocation_pending" ? "disconnected" : status === "active" ? "connected" : "needs_reauthorization"
|
|
1467
|
+
};
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
// src/connectors/connections/request.ts
|
|
1471
|
+
var textEncoder2 = new TextEncoder;
|
|
1472
|
+
var textDecoder2 = new TextDecoder;
|
|
1473
|
+
|
|
1474
|
+
class ConnectorAuthorizationRequestHandler {
|
|
1475
|
+
projectId;
|
|
1476
|
+
storage;
|
|
1477
|
+
credentialProtector;
|
|
1478
|
+
authorizationAttemptTtlMs;
|
|
1479
|
+
providerOperationTimeoutMs;
|
|
1480
|
+
resolveDefinition;
|
|
1481
|
+
lifecycle;
|
|
1482
|
+
hostSignal;
|
|
1483
|
+
constructor(options) {
|
|
1484
|
+
this.projectId = options.projectId;
|
|
1485
|
+
this.storage = options.storage;
|
|
1486
|
+
this.credentialProtector = options.credentialProtector;
|
|
1487
|
+
this.authorizationAttemptTtlMs = positiveDuration(options.authorizationAttemptTtlMs, "authorization attempt TTL");
|
|
1488
|
+
this.providerOperationTimeoutMs = positiveDuration(options.providerOperationTimeoutMs, "provider operation timeout");
|
|
1489
|
+
this.resolveDefinition = options.resolveDefinition;
|
|
1490
|
+
this.lifecycle = options.lifecycle;
|
|
1491
|
+
this.hostSignal = options.hostSignal;
|
|
1492
|
+
}
|
|
1493
|
+
async startAuthorization(command, connectorId, input) {
|
|
1494
|
+
const prepared = await this.prepareAuthorizationRequest(command, connectorId, input);
|
|
1495
|
+
try {
|
|
1496
|
+
await this.storage.transaction(async (tx) => {
|
|
1497
|
+
await ensureExecutionRecord(tx.executions, command.execution);
|
|
1498
|
+
await requireConnectorConnectionStorage(tx).createAuthorizationAttempt(prepared.attempt);
|
|
1499
|
+
}, { isolation: "serializable" });
|
|
1500
|
+
} catch (error) {
|
|
1501
|
+
throw storageBoundaryError(error, "Connector authorization attempt could not be persisted.");
|
|
1502
|
+
}
|
|
1503
|
+
return {
|
|
1504
|
+
authorizationUrl: prepared.authorizationUrl,
|
|
1505
|
+
affectedConnections: prepared.affectedConnections
|
|
1506
|
+
};
|
|
1507
|
+
}
|
|
1508
|
+
async prepareAuthorizationRequest(command, connectorId, input) {
|
|
1509
|
+
const definition = this.resolveDefinition(nonblank(connectorId, "connector id"));
|
|
1510
|
+
const actor = requireConnectorConnectionCommandActor(command.execution, this.projectId);
|
|
1511
|
+
assertConnectorConnectionSelector(input);
|
|
1512
|
+
const redirectUri = normalizedHttpUrl(input.redirectUri, "OAuth callback URL");
|
|
1513
|
+
const reauthorization = await this.prepareReauthorization(definition, actor, input.reauthorizationId);
|
|
1514
|
+
const attemptId = `cat_${randomUUID3()}`;
|
|
1515
|
+
const state = `${attemptId}.${randomBytes2(32).toString("base64url")}`;
|
|
1516
|
+
const codeVerifier = randomBytes2(32).toString("base64url");
|
|
1517
|
+
const codeChallenge = createHash2("sha256").update(codeVerifier).digest("base64url");
|
|
1518
|
+
let authorizationUrlInput;
|
|
1519
|
+
try {
|
|
1520
|
+
authorizationUrlInput = await runBoundedConnectorProviderOperation({
|
|
1521
|
+
hostSignal: this.hostSignal(),
|
|
1522
|
+
timeoutMs: this.providerOperationTimeoutMs
|
|
1523
|
+
}, (signal) => definition.adapter.authentication.authorizationUrl({
|
|
1524
|
+
projectId: this.projectId,
|
|
1525
|
+
connectorId: definition.id,
|
|
1526
|
+
redirectUri,
|
|
1527
|
+
signal
|
|
1528
|
+
}, { state, codeChallenge, codeChallengeMethod: "S256" }), () => createConnectorCodedError("connector.provider_unavailable", "Connector authorization URL generation did not complete."));
|
|
1529
|
+
} catch (error) {
|
|
1530
|
+
throw providerBoundaryError(error, providerFailureCode(error), "Connector authorization could not be started.");
|
|
1531
|
+
}
|
|
1532
|
+
const authorizationUrl = normalizedHttpUrl(authorizationUrlInput, "provider authorization URL", "connector.adapter_invalid");
|
|
1533
|
+
assertAuthorizationUrlParameters(authorizationUrl, { state, codeChallenge });
|
|
1534
|
+
const codeVerifierEnvelope = await this.credentialProtector.seal(textEncoder2.encode(codeVerifier), {
|
|
1535
|
+
projectId: this.projectId,
|
|
1536
|
+
connectorId: definition.id,
|
|
1537
|
+
recordId: attemptId,
|
|
1538
|
+
purpose: "pkce-verifier"
|
|
1539
|
+
});
|
|
1540
|
+
return {
|
|
1541
|
+
definition,
|
|
1542
|
+
actor,
|
|
1543
|
+
state,
|
|
1544
|
+
authorizationUrl,
|
|
1545
|
+
attempt: {
|
|
1546
|
+
id: attemptId,
|
|
1547
|
+
projectId: this.projectId,
|
|
1548
|
+
connectorId: definition.id,
|
|
1549
|
+
owner: input.owner,
|
|
1550
|
+
slot: input.slot,
|
|
1551
|
+
initiatedByExecutionId: command.execution.id,
|
|
1552
|
+
stateHash: hashSecret(state),
|
|
1553
|
+
codeVerifier: codeVerifierEnvelope,
|
|
1554
|
+
redirectUri,
|
|
1555
|
+
...reauthorization === undefined ? {} : {
|
|
1556
|
+
reauthorizationId: reauthorization.authorization.id,
|
|
1557
|
+
reauthorizationRevision: reauthorization.authorization.revision,
|
|
1558
|
+
reauthorizationConnectionIds: reauthorization.connectionIds
|
|
1559
|
+
},
|
|
1560
|
+
ttlMs: this.authorizationAttemptTtlMs
|
|
1561
|
+
},
|
|
1562
|
+
affectedConnections: reauthorization?.affectedConnections ?? []
|
|
1563
|
+
};
|
|
1564
|
+
}
|
|
1565
|
+
async completeAuthorization(command, connectorId, input) {
|
|
1566
|
+
const definition = this.resolveDefinition(nonblank(connectorId, "connector id"));
|
|
1567
|
+
const actor = requireConnectorConnectionCommandActor(command.execution, this.projectId);
|
|
1568
|
+
const state = nonblank(input.state, "OAuth state", "connector.authorization_invalid");
|
|
1569
|
+
const code = nonblank(input.code, "OAuth authorization code", "connector.authorization_invalid");
|
|
1570
|
+
const redirectUri = normalizedHttpUrl(input.redirectUri, "OAuth callback URL");
|
|
1571
|
+
const attempt = await this.consumeAuthorizationAttempt(definition.id, actor, state, redirectUri);
|
|
1572
|
+
const completed = await this.completeAuthorizationAttempt({
|
|
1573
|
+
definition,
|
|
1574
|
+
attempt,
|
|
1575
|
+
principal: actor.principal,
|
|
1576
|
+
code,
|
|
1577
|
+
redirectUri
|
|
1578
|
+
});
|
|
1579
|
+
return {
|
|
1580
|
+
authorizationId: completed.id,
|
|
1581
|
+
owner: attempt.owner,
|
|
1582
|
+
slot: attempt.slot,
|
|
1583
|
+
accounts: completed.accounts
|
|
1584
|
+
};
|
|
1585
|
+
}
|
|
1586
|
+
async completeAuthorizationAttempt(input) {
|
|
1587
|
+
const verifierBytes = await this.credentialProtector.open(input.attempt.codeVerifier, {
|
|
1588
|
+
projectId: this.projectId,
|
|
1589
|
+
connectorId: input.definition.id,
|
|
1590
|
+
recordId: input.attempt.id,
|
|
1591
|
+
purpose: "pkce-verifier"
|
|
1592
|
+
});
|
|
1593
|
+
return this.completeGrant({
|
|
1594
|
+
...input,
|
|
1595
|
+
codeVerifier: textDecoder2.decode(verifierBytes)
|
|
1596
|
+
});
|
|
1597
|
+
}
|
|
1598
|
+
async prepareReauthorization(definition, actor, reauthorizationId) {
|
|
1599
|
+
if (reauthorizationId === undefined)
|
|
1600
|
+
return;
|
|
1601
|
+
const prepared = await this.lifecycle.prepareReauthorization({
|
|
1602
|
+
definition,
|
|
1603
|
+
authorizationId: nonblank(reauthorizationId, "reauthorization id"),
|
|
1604
|
+
principal: actor.principal
|
|
1605
|
+
});
|
|
1606
|
+
const connectionIds = prepared.connections.map((connection) => connection.id).sort();
|
|
1607
|
+
return {
|
|
1608
|
+
authorization: prepared.authorization,
|
|
1609
|
+
connectionIds,
|
|
1610
|
+
affectedConnections: prepared.connections.map((connection) => connectorConnectionView(connection, prepared.authorization.status))
|
|
1611
|
+
};
|
|
1612
|
+
}
|
|
1613
|
+
async consumeAuthorizationAttempt(connectorId, actor, state, redirectUri) {
|
|
1614
|
+
const attemptId = parseAttemptId(state);
|
|
1615
|
+
let outcome;
|
|
1616
|
+
try {
|
|
1617
|
+
outcome = await this.storage.transaction(async (tx) => {
|
|
1618
|
+
let attempt;
|
|
1619
|
+
try {
|
|
1620
|
+
attempt = await requireConnectorConnectionStorage(tx).consumeAuthorizationAttempt({
|
|
1621
|
+
id: attemptId,
|
|
1622
|
+
projectId: this.projectId,
|
|
1623
|
+
connectorId,
|
|
1624
|
+
stateHash: hashSecret(state),
|
|
1625
|
+
redirectUri
|
|
1626
|
+
});
|
|
1627
|
+
} catch (error) {
|
|
1628
|
+
if (isConnectorStorageError(error, "attempt_invalid")) {
|
|
1629
|
+
return { type: "invalid", cause: error };
|
|
1630
|
+
}
|
|
1631
|
+
throw error;
|
|
1632
|
+
}
|
|
1633
|
+
const initiator = await tx.executions.getById({
|
|
1634
|
+
projectId: this.projectId,
|
|
1635
|
+
id: attempt.initiatedByExecutionId
|
|
1636
|
+
});
|
|
1637
|
+
assertConnectorAuthorizationAttemptInitiator(initiator, actor);
|
|
1638
|
+
return { type: "consumed", attempt };
|
|
1639
|
+
}, { isolation: "serializable" });
|
|
1640
|
+
} catch (error) {
|
|
1641
|
+
if (isConnectorStorageError(error, "attempt_invalid")) {
|
|
1642
|
+
throw createConnectorCodedError("connector.authorization_invalid", "Connector authorization attempt is invalid, expired, or already used.", { cause: error });
|
|
1643
|
+
}
|
|
1644
|
+
throw storageBoundaryError(error, "Connector authorization attempt could not be consumed.");
|
|
1645
|
+
}
|
|
1646
|
+
if (outcome.type === "invalid") {
|
|
1647
|
+
throw createConnectorCodedError("connector.authorization_invalid", "Connector authorization attempt is invalid, expired, or already used.", { cause: outcome.cause });
|
|
1648
|
+
}
|
|
1649
|
+
return outcome.attempt;
|
|
1650
|
+
}
|
|
1651
|
+
completeGrant(input) {
|
|
1652
|
+
if (input.attempt.reauthorizationId === undefined) {
|
|
1653
|
+
return this.lifecycle.completeNewAuthorization({
|
|
1654
|
+
definition: input.definition,
|
|
1655
|
+
principal: input.principal,
|
|
1656
|
+
code: input.code,
|
|
1657
|
+
codeVerifier: input.codeVerifier,
|
|
1658
|
+
redirectUri: input.redirectUri,
|
|
1659
|
+
...input.onAuthorizationPersisted === undefined ? {} : { onAuthorizationPersisted: input.onAuthorizationPersisted }
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1662
|
+
const expectedRevision = input.attempt.reauthorizationRevision;
|
|
1663
|
+
if (expectedRevision === undefined) {
|
|
1664
|
+
throw createConnectorCodedError("internal.unexpected", "Connector reauthorization attempt is missing its revision.");
|
|
1665
|
+
}
|
|
1666
|
+
return this.lifecycle.completeReauthorization({
|
|
1667
|
+
definition: input.definition,
|
|
1668
|
+
authorizationId: input.attempt.reauthorizationId,
|
|
1669
|
+
expectedRevision,
|
|
1670
|
+
expectedConnectionIds: input.attempt.reauthorizationConnectionIds ?? [],
|
|
1671
|
+
principal: input.principal,
|
|
1672
|
+
code: input.code,
|
|
1673
|
+
codeVerifier: input.codeVerifier,
|
|
1674
|
+
redirectUri: input.redirectUri
|
|
1675
|
+
});
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
function requireConnectorConnectionStorage(storage) {
|
|
1679
|
+
if (storage.connectorConnections)
|
|
1680
|
+
return storage.connectorConnections;
|
|
1681
|
+
throw createConnectorCodedError("internal.unexpected", "Connector connection storage is unavailable inside the storage transaction.");
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
// src/connectors/connections/runs.ts
|
|
1685
|
+
import { randomBytes as randomBytes3, randomUUID as randomUUID4 } from "crypto";
|
|
1686
|
+
var CLEANUP_RETRY_DELAY_MS = 30000;
|
|
1687
|
+
var MAX_TIMER_DELAY_MS = 2147483647;
|
|
1688
|
+
|
|
1689
|
+
class ConnectorConnectionRunService {
|
|
1690
|
+
projectId;
|
|
1691
|
+
storage;
|
|
1692
|
+
connectionStorage;
|
|
1693
|
+
requests;
|
|
1694
|
+
authorizations;
|
|
1695
|
+
resolveDefinition;
|
|
1696
|
+
processingTtlMs;
|
|
1697
|
+
selectionTtlMs;
|
|
1698
|
+
now;
|
|
1699
|
+
maintenanceTimers = new Map;
|
|
1700
|
+
constructor(options) {
|
|
1701
|
+
this.projectId = options.projectId;
|
|
1702
|
+
this.storage = options.storage;
|
|
1703
|
+
this.connectionStorage = requireConnectorConnectionStorage2(options.storage);
|
|
1704
|
+
this.requests = options.requestHandler;
|
|
1705
|
+
this.authorizations = options.authorizations;
|
|
1706
|
+
this.resolveDefinition = options.resolveDefinition;
|
|
1707
|
+
this.processingTtlMs = positiveDuration(options.processingTtlMs, "connection run processing TTL");
|
|
1708
|
+
this.selectionTtlMs = positiveDuration(options.selectionTtlMs, "account selection TTL");
|
|
1709
|
+
this.now = options.now;
|
|
1710
|
+
}
|
|
1711
|
+
async startSelection(command, connectorId, input) {
|
|
1712
|
+
const runId = `ccr_${randomUUID4()}`;
|
|
1713
|
+
let run;
|
|
1714
|
+
try {
|
|
1715
|
+
run = await this.storage.transaction(async (tx) => {
|
|
1716
|
+
await ensureExecutionRecord(tx.executions, command.execution);
|
|
1717
|
+
return requireConnectorConnectionStorage2(tx).createConnectionSelectionRun({
|
|
1718
|
+
id: runId,
|
|
1719
|
+
projectId: this.projectId,
|
|
1720
|
+
connectorId,
|
|
1721
|
+
owner: input.owner,
|
|
1722
|
+
slot: input.slot,
|
|
1723
|
+
initiatedByExecutionId: command.execution.id,
|
|
1724
|
+
authorizationId: input.authorizationId,
|
|
1725
|
+
ttlMs: this.selectionTtlMs
|
|
1726
|
+
});
|
|
1727
|
+
}, { isolation: "serializable" });
|
|
1728
|
+
} catch (error) {
|
|
1729
|
+
throw storageBoundaryError(error, "Connector account-selection run could not be persisted.");
|
|
1730
|
+
}
|
|
1731
|
+
this.scheduleRunMaintenance(run, this.selectionTtlMs + 1);
|
|
1732
|
+
return this.view(run);
|
|
1733
|
+
}
|
|
1734
|
+
async start(command, connectorId, input) {
|
|
1735
|
+
const returnTo = normalizedHttpUrl(input.returnTo, "connector return URL");
|
|
1736
|
+
const prepared = await this.requests.prepareAuthorizationRequest(command, connectorId, input);
|
|
1737
|
+
const runId = `ccr_${randomUUID4()}`;
|
|
1738
|
+
const callbackBinding = randomBytes3(32).toString("base64url");
|
|
1739
|
+
let expiresAt;
|
|
1740
|
+
try {
|
|
1741
|
+
expiresAt = await this.storage.transaction(async (tx) => {
|
|
1742
|
+
await ensureExecutionRecord(tx.executions, command.execution);
|
|
1743
|
+
const connections = requireConnectorConnectionStorage2(tx);
|
|
1744
|
+
await connections.createConnectionRun({
|
|
1745
|
+
id: runId,
|
|
1746
|
+
projectId: this.projectId,
|
|
1747
|
+
connectorId: prepared.definition.id,
|
|
1748
|
+
kind: prepared.attempt.reauthorizationId === undefined ? "connect" : "reauthorize",
|
|
1749
|
+
owner: prepared.attempt.owner,
|
|
1750
|
+
slot: prepared.attempt.slot,
|
|
1751
|
+
initiatedByExecutionId: command.execution.id,
|
|
1752
|
+
ttlMs: prepared.attempt.ttlMs
|
|
1753
|
+
});
|
|
1754
|
+
const attempt = await connections.createAuthorizationAttempt({
|
|
1755
|
+
...prepared.attempt,
|
|
1756
|
+
connectionRunId: runId,
|
|
1757
|
+
returnTo,
|
|
1758
|
+
callbackBindingHash: hashSecret(callbackBinding)
|
|
1759
|
+
});
|
|
1760
|
+
return attempt.expiresAt;
|
|
1761
|
+
}, { isolation: "serializable" });
|
|
1762
|
+
} catch (error) {
|
|
1763
|
+
throw storageBoundaryError(error, "Connector connection run could not be persisted.");
|
|
1764
|
+
}
|
|
1765
|
+
return {
|
|
1766
|
+
runId,
|
|
1767
|
+
authorizationUrl: prepared.authorizationUrl,
|
|
1768
|
+
affectedConnections: prepared.affectedConnections,
|
|
1769
|
+
callbackBinding: {
|
|
1770
|
+
attemptId: prepared.attempt.id,
|
|
1771
|
+
secret: callbackBinding,
|
|
1772
|
+
expiresAt
|
|
1773
|
+
}
|
|
1774
|
+
};
|
|
1775
|
+
}
|
|
1776
|
+
async complete(input) {
|
|
1777
|
+
const state = nonblank(input.state, "OAuth state", "connector.authorization_invalid");
|
|
1778
|
+
const redirectUri = normalizedHttpUrl(input.redirectUri, "OAuth callback URL");
|
|
1779
|
+
const callbackBinding = nonblank(input.callbackBinding, "OAuth callback binding", "connector.authorization_invalid");
|
|
1780
|
+
const claimed = await this.claimCallback(state, callbackBinding, redirectUri);
|
|
1781
|
+
if (claimed.type === "expired") {
|
|
1782
|
+
return {
|
|
1783
|
+
runId: claimed.run.id,
|
|
1784
|
+
connectorId: claimed.run.connectorId,
|
|
1785
|
+
returnTo: claimed.returnTo
|
|
1786
|
+
};
|
|
1787
|
+
}
|
|
1788
|
+
const { run, attempt, principal, returnTo } = claimed;
|
|
1789
|
+
this.scheduleRunMaintenance(run, this.processingTtlMs + 1);
|
|
1790
|
+
if ("error" in input) {
|
|
1791
|
+
const finished = input.error === "access_denied" ? await this.cancelRun(run) : await this.failRun(run, providerCallbackError(input.error));
|
|
1792
|
+
return { runId: finished.id, connectorId: finished.connectorId, returnTo };
|
|
1793
|
+
}
|
|
1794
|
+
let authorizationId = run.authorizationId;
|
|
1795
|
+
try {
|
|
1796
|
+
const definition = this.resolveDefinition(attempt.connectorId);
|
|
1797
|
+
const authorization = await this.requests.completeAuthorizationAttempt({
|
|
1798
|
+
definition,
|
|
1799
|
+
attempt,
|
|
1800
|
+
principal,
|
|
1801
|
+
code: nonblank(input.code, "OAuth authorization code", "connector.authorization_invalid"),
|
|
1802
|
+
redirectUri,
|
|
1803
|
+
onAuthorizationPersisted: async (persisted) => {
|
|
1804
|
+
authorizationId = persisted.id;
|
|
1805
|
+
const attached = await withConnectorStorageBoundary("Connector authorization could not be attached to its run.", () => this.connectionStorage.attachConnectionRunAuthorization({
|
|
1806
|
+
projectId: this.projectId,
|
|
1807
|
+
connectorId: run.connectorId,
|
|
1808
|
+
runId: run.id,
|
|
1809
|
+
processingId: run.processingId,
|
|
1810
|
+
authorizationId: persisted.id
|
|
1811
|
+
}));
|
|
1812
|
+
if (!attached) {
|
|
1813
|
+
throw createConnectorCodedError("internal.unexpected", "Connector authorization arrived after its run stopped accepting results.");
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
});
|
|
1817
|
+
const finished = await this.finalizeCompletedRun(run, authorization);
|
|
1818
|
+
return { runId: finished.id, connectorId: finished.connectorId, returnTo };
|
|
1819
|
+
} catch (error) {
|
|
1820
|
+
const failed = await this.failRun(run, error, authorizationId);
|
|
1821
|
+
return { runId: failed.id, connectorId: failed.connectorId, returnTo };
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
async get(command, connectorId, runId) {
|
|
1825
|
+
const id = nonblank(runId, "connection run id");
|
|
1826
|
+
const run = await this.readRun(connectorId, id);
|
|
1827
|
+
if (!run)
|
|
1828
|
+
return null;
|
|
1829
|
+
await this.assertRunActor(command, run);
|
|
1830
|
+
if (run.status === "waiting" || run.status === "running") {
|
|
1831
|
+
if (!this.maintenanceTimers.has(run.id))
|
|
1832
|
+
this.scheduleRunMaintenance(run);
|
|
1833
|
+
} else if (!await this.continueRunCleanup(run)) {
|
|
1834
|
+
this.scheduleRunMaintenance(run, CLEANUP_RETRY_DELAY_MS);
|
|
1835
|
+
} else {
|
|
1836
|
+
this.cancelRunMaintenance(run.id);
|
|
1837
|
+
}
|
|
1838
|
+
return this.view(run);
|
|
1839
|
+
}
|
|
1840
|
+
async selectAccount(command, connectorId, input) {
|
|
1841
|
+
const definition = this.resolveDefinition(connectorId);
|
|
1842
|
+
const runId = nonblank(input.runId, "connection run id");
|
|
1843
|
+
const run = await this.readRun(definition.id, runId);
|
|
1844
|
+
if (!run) {
|
|
1845
|
+
throw createConnectorCodedError("connector.not_found", "Connector connection run not found.");
|
|
1846
|
+
}
|
|
1847
|
+
const actor = await this.assertRunActor(command, run);
|
|
1848
|
+
if (run.status !== "waiting" || run.waitingFor !== "account_selection") {
|
|
1849
|
+
throw createConnectorCodedError("connector.operation_conflict", "Connector connection run is not waiting for account selection.");
|
|
1850
|
+
}
|
|
1851
|
+
const authorization = await this.authorizations.requireAuthorization(definition.id, run.authorizationId);
|
|
1852
|
+
assertConnectorAuthorizationActor(authorization, actor.principal);
|
|
1853
|
+
const account = authorization.accounts.find((candidate) => candidate.id === input.accountId);
|
|
1854
|
+
if (!account) {
|
|
1855
|
+
throw createConnectorCodedError("connector.not_found", `Account '${input.accountId}' is not exposed by this connector connection run.`);
|
|
1856
|
+
}
|
|
1857
|
+
let selected;
|
|
1858
|
+
try {
|
|
1859
|
+
selected = await this.connectionStorage.putConnectionFromRun({
|
|
1860
|
+
id: `ccn_${randomUUID4()}`,
|
|
1861
|
+
projectId: this.projectId,
|
|
1862
|
+
connectorId: definition.id,
|
|
1863
|
+
runId: run.id,
|
|
1864
|
+
account,
|
|
1865
|
+
replace: input.replace === true
|
|
1866
|
+
});
|
|
1867
|
+
} catch (error) {
|
|
1868
|
+
if (isConnectorStorageError(error, "run_invalid")) {
|
|
1869
|
+
const latest = await this.readRun(definition.id, run.id);
|
|
1870
|
+
if (latest)
|
|
1871
|
+
await this.continueRunCleanup(latest);
|
|
1872
|
+
throw createConnectorCodedError("connector.operation_conflict", "Connector connection run is no longer waiting for account selection.", { cause: error });
|
|
1873
|
+
}
|
|
1874
|
+
if (isConnectorStorageError(error, "authorization_conflict")) {
|
|
1875
|
+
throw createConnectorCodedError("connector.operation_conflict", "Connector authorization cannot be selected in its current state.", { cause: error });
|
|
1876
|
+
}
|
|
1877
|
+
if (isConnectorStorageError(error, "connection_conflict")) {
|
|
1878
|
+
throw createConnectorCodedError("connector.replacement_required", "Connector slot is already occupied; explicit replacement is required.", { cause: error });
|
|
1879
|
+
}
|
|
1880
|
+
throw storageBoundaryError(error, "Connector account selection could not be persisted.");
|
|
1881
|
+
}
|
|
1882
|
+
const cleanupCompleted = selected.revocationPendingAuthorizationId ? await this.continuePendingRevocation(definition.id, selected.revocationPendingAuthorizationId) : true;
|
|
1883
|
+
if (cleanupCompleted) {
|
|
1884
|
+
this.cancelRunMaintenance(run.id);
|
|
1885
|
+
} else {
|
|
1886
|
+
this.scheduleRunMaintenance(selected.run, CLEANUP_RETRY_DELAY_MS);
|
|
1887
|
+
}
|
|
1888
|
+
return this.view(selected.run);
|
|
1889
|
+
}
|
|
1890
|
+
async claimCallback(state, callbackBinding, redirectUri) {
|
|
1891
|
+
let claimed;
|
|
1892
|
+
try {
|
|
1893
|
+
claimed = await this.storage.transaction(async (tx) => {
|
|
1894
|
+
const result = await requireConnectorConnectionStorage2(tx).claimConnectionRunCallback({
|
|
1895
|
+
projectId: this.projectId,
|
|
1896
|
+
attemptId: parseAttemptId(state),
|
|
1897
|
+
stateHash: hashSecret(state),
|
|
1898
|
+
callbackBindingHash: hashSecret(callbackBinding),
|
|
1899
|
+
redirectUri,
|
|
1900
|
+
processingId: `ccp_${randomUUID4()}`,
|
|
1901
|
+
processingTtlMs: this.processingTtlMs
|
|
1902
|
+
});
|
|
1903
|
+
if (!result || result.type === "expired")
|
|
1904
|
+
return result;
|
|
1905
|
+
const execution = await tx.executions.getById({
|
|
1906
|
+
projectId: this.projectId,
|
|
1907
|
+
id: result.attempt.initiatedByExecutionId
|
|
1908
|
+
});
|
|
1909
|
+
if (!execution) {
|
|
1910
|
+
throw createConnectorCodedError("internal.unexpected", "Connector connection run initiating execution is unavailable.");
|
|
1911
|
+
}
|
|
1912
|
+
const actor = requireConnectorConnectionCommandActor(execution, this.projectId);
|
|
1913
|
+
return { ...result, principal: actor.principal };
|
|
1914
|
+
}, { isolation: "serializable" });
|
|
1915
|
+
} catch (error) {
|
|
1916
|
+
throw storageBoundaryError(error, "Connector connection run callback could not be claimed.");
|
|
1917
|
+
}
|
|
1918
|
+
if (!claimed) {
|
|
1919
|
+
throw createConnectorCodedError("connector.authorization_invalid", "Connector connection run is invalid, expired, or already used.");
|
|
1920
|
+
}
|
|
1921
|
+
return claimed;
|
|
1922
|
+
}
|
|
1923
|
+
async finalizeCompletedRun(run, authorization) {
|
|
1924
|
+
if (run.kind === "connect") {
|
|
1925
|
+
if (!authorization.selectionExpiresAt) {
|
|
1926
|
+
throw createConnectorCodedError("internal.unexpected", "Connector authorization is missing its account-selection deadline.");
|
|
1927
|
+
}
|
|
1928
|
+
const selectionExpiresAt = authorization.selectionExpiresAt;
|
|
1929
|
+
const waiting = await withConnectorStorageBoundary("Connector connection run could not wait for account selection.", () => this.connectionStorage.waitForConnectionRunSelection({
|
|
1930
|
+
projectId: this.projectId,
|
|
1931
|
+
connectorId: run.connectorId,
|
|
1932
|
+
runId: run.id,
|
|
1933
|
+
processingId: run.processingId,
|
|
1934
|
+
authorizationId: authorization.id,
|
|
1935
|
+
expiresAt: selectionExpiresAt
|
|
1936
|
+
}));
|
|
1937
|
+
if (waiting) {
|
|
1938
|
+
this.scheduleRunMaintenance(waiting, this.selectionTtlMs + 1);
|
|
1939
|
+
return waiting;
|
|
1940
|
+
}
|
|
1941
|
+
} else {
|
|
1942
|
+
const connections = await withConnectorStorageBoundary("Reauthorized connector connections could not be read.", () => this.connectionStorage.listConnectionsByAuthorization({
|
|
1943
|
+
projectId: this.projectId,
|
|
1944
|
+
connectorId: run.connectorId,
|
|
1945
|
+
authorizationId: authorization.id
|
|
1946
|
+
}));
|
|
1947
|
+
const succeeded = await withConnectorStorageBoundary("Connector reauthorization run could not be finalized.", () => this.connectionStorage.finishConnectionRun({
|
|
1948
|
+
projectId: this.projectId,
|
|
1949
|
+
connectorId: run.connectorId,
|
|
1950
|
+
runId: run.id,
|
|
1951
|
+
processingId: run.processingId,
|
|
1952
|
+
status: "succeeded",
|
|
1953
|
+
authorizationId: authorization.id,
|
|
1954
|
+
connections
|
|
1955
|
+
}));
|
|
1956
|
+
if (succeeded) {
|
|
1957
|
+
this.cancelRunMaintenance(run.id);
|
|
1958
|
+
return succeeded;
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
throw createConnectorCodedError("internal.unexpected", "Connector connection run could not be finalized.");
|
|
1962
|
+
}
|
|
1963
|
+
async failRun(run, error, authorizationId = run.authorizationId) {
|
|
1964
|
+
const failed = await withConnectorStorageBoundary("Connector connection run failure could not be persisted.", () => this.connectionStorage.finishConnectionRun({
|
|
1965
|
+
projectId: this.projectId,
|
|
1966
|
+
connectorId: run.connectorId,
|
|
1967
|
+
runId: run.id,
|
|
1968
|
+
processingId: run.processingId,
|
|
1969
|
+
status: "failed",
|
|
1970
|
+
...authorizationId === undefined ? {} : { authorizationId },
|
|
1971
|
+
error: captureSixbFailure(error, {
|
|
1972
|
+
allowedCodes: CONNECTOR_CONNECTION_RUN_FAILURE_CODES,
|
|
1973
|
+
defaultCode: "internal.unexpected",
|
|
1974
|
+
at: this.now()
|
|
1975
|
+
})
|
|
1976
|
+
}));
|
|
1977
|
+
if (failed) {
|
|
1978
|
+
this.cancelRunMaintenance(run.id);
|
|
1979
|
+
if (!await this.continueRunCleanup(failed)) {
|
|
1980
|
+
this.scheduleRunMaintenance(failed, CLEANUP_RETRY_DELAY_MS);
|
|
1981
|
+
}
|
|
1982
|
+
return failed;
|
|
1983
|
+
}
|
|
1984
|
+
throw createConnectorCodedError("internal.unexpected", "Connector connection run failure could not be persisted.", { cause: error });
|
|
1985
|
+
}
|
|
1986
|
+
async cancelRun(run) {
|
|
1987
|
+
const cancelled = await withConnectorStorageBoundary("Connector connection run cancellation could not be persisted.", () => this.connectionStorage.finishConnectionRun({
|
|
1988
|
+
projectId: this.projectId,
|
|
1989
|
+
connectorId: run.connectorId,
|
|
1990
|
+
runId: run.id,
|
|
1991
|
+
processingId: run.processingId,
|
|
1992
|
+
status: "cancelled"
|
|
1993
|
+
}));
|
|
1994
|
+
if (cancelled) {
|
|
1995
|
+
this.cancelRunMaintenance(run.id);
|
|
1996
|
+
return cancelled;
|
|
1997
|
+
}
|
|
1998
|
+
throw createConnectorCodedError("internal.unexpected", "Connector connection run cancellation could not be persisted.");
|
|
1999
|
+
}
|
|
2000
|
+
async readRun(connectorId, runId) {
|
|
2001
|
+
try {
|
|
2002
|
+
return await this.connectionStorage.getConnectionRun({
|
|
2003
|
+
projectId: this.projectId,
|
|
2004
|
+
connectorId,
|
|
2005
|
+
runId
|
|
2006
|
+
});
|
|
2007
|
+
} catch (error) {
|
|
2008
|
+
throw storageBoundaryError(error, "Connector connection run could not be read.");
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
async assertRunActor(command, run) {
|
|
2012
|
+
const actor = requireConnectorConnectionCommandActor(command.execution, this.projectId);
|
|
2013
|
+
const initiating = await withConnectorStorageBoundary("Connector connection run initiating execution could not be read.", () => this.storage.executions.getById({
|
|
2014
|
+
projectId: this.projectId,
|
|
2015
|
+
id: run.initiatedByExecutionId
|
|
2016
|
+
}));
|
|
2017
|
+
assertConnectorConnectionRunInitiator(initiating, actor, run.connectorId);
|
|
2018
|
+
return actor;
|
|
2019
|
+
}
|
|
2020
|
+
async view(run) {
|
|
2021
|
+
const base = {
|
|
2022
|
+
id: run.id,
|
|
2023
|
+
connectorId: run.connectorId,
|
|
2024
|
+
kind: run.kind,
|
|
2025
|
+
owner: run.owner,
|
|
2026
|
+
slot: run.slot,
|
|
2027
|
+
createdAt: run.createdAt,
|
|
2028
|
+
updatedAt: run.updatedAt
|
|
2029
|
+
};
|
|
2030
|
+
if (run.status === "running")
|
|
2031
|
+
return { ...base, status: "running" };
|
|
2032
|
+
if (run.status === "waiting" && run.waitingFor === "provider_authorization") {
|
|
2033
|
+
return {
|
|
2034
|
+
...base,
|
|
2035
|
+
status: "waiting",
|
|
2036
|
+
waitingFor: "provider_authorization",
|
|
2037
|
+
expiresAt: run.expiresAt
|
|
2038
|
+
};
|
|
2039
|
+
}
|
|
2040
|
+
if (run.status === "waiting") {
|
|
2041
|
+
const authorization = await this.authorizations.requireAuthorization(run.connectorId, run.authorizationId);
|
|
2042
|
+
return {
|
|
2043
|
+
...base,
|
|
2044
|
+
status: "waiting",
|
|
2045
|
+
waitingFor: "account_selection",
|
|
2046
|
+
accounts: authorization.accounts,
|
|
2047
|
+
expiresAt: run.expiresAt
|
|
2048
|
+
};
|
|
2049
|
+
}
|
|
2050
|
+
if (run.status === "succeeded") {
|
|
2051
|
+
return {
|
|
2052
|
+
...base,
|
|
2053
|
+
status: "succeeded",
|
|
2054
|
+
connections: run.connections.map((connection) => connectorConnectionView(connection, "active")),
|
|
2055
|
+
finishedAt: run.finishedAt
|
|
2056
|
+
};
|
|
2057
|
+
}
|
|
2058
|
+
if (run.status === "failed") {
|
|
2059
|
+
return { ...base, status: "failed", error: run.error, finishedAt: run.finishedAt };
|
|
2060
|
+
}
|
|
2061
|
+
return { ...base, status: run.status, finishedAt: run.finishedAt };
|
|
2062
|
+
}
|
|
2063
|
+
close() {
|
|
2064
|
+
for (const timer of this.maintenanceTimers.values())
|
|
2065
|
+
clearTimeout(timer);
|
|
2066
|
+
this.maintenanceTimers.clear();
|
|
2067
|
+
}
|
|
2068
|
+
async continueRunCleanup(run) {
|
|
2069
|
+
if (run.kind !== "connect")
|
|
2070
|
+
return true;
|
|
2071
|
+
const authorizationId = run.status === "succeeded" ? run.cleanupAuthorizationId : run.status === "failed" || run.status === "expired" || run.status === "cancelled" ? run.authorizationId : undefined;
|
|
2072
|
+
if (authorizationId === undefined)
|
|
2073
|
+
return true;
|
|
2074
|
+
const authorization = await this.authorizations.requireAuthorization(run.connectorId, authorizationId);
|
|
2075
|
+
if (authorization.status !== "revocation_pending")
|
|
2076
|
+
return true;
|
|
2077
|
+
return this.continuePendingRevocation(run.connectorId, authorization.id);
|
|
2078
|
+
}
|
|
2079
|
+
async continuePendingRevocation(connectorId, authorizationId) {
|
|
2080
|
+
try {
|
|
2081
|
+
await this.authorizations.continuePendingRevocation(this.resolveDefinition(connectorId), authorizationId);
|
|
2082
|
+
return true;
|
|
2083
|
+
} catch (error) {
|
|
2084
|
+
if (isSixbError(error) && error.code === "connector.revocation_pending")
|
|
2085
|
+
return false;
|
|
2086
|
+
throw error;
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
scheduleRunMaintenance(run, delayMs = run.status === "waiting" || run.status === "running" ? Math.max(1000, run.expiresAt.getTime() - this.now().getTime()) : CLEANUP_RETRY_DELAY_MS) {
|
|
2090
|
+
this.cancelRunMaintenance(run.id);
|
|
2091
|
+
const timer = setTimeout(() => void this.maintainRun(run.connectorId, run.id), Math.min(delayMs, MAX_TIMER_DELAY_MS));
|
|
2092
|
+
timer.unref?.();
|
|
2093
|
+
this.maintenanceTimers.set(run.id, timer);
|
|
2094
|
+
}
|
|
2095
|
+
cancelRunMaintenance(runId) {
|
|
2096
|
+
const timer = this.maintenanceTimers.get(runId);
|
|
2097
|
+
if (timer)
|
|
2098
|
+
clearTimeout(timer);
|
|
2099
|
+
this.maintenanceTimers.delete(runId);
|
|
2100
|
+
}
|
|
2101
|
+
async maintainRun(connectorId, runId) {
|
|
2102
|
+
this.maintenanceTimers.delete(runId);
|
|
2103
|
+
try {
|
|
2104
|
+
const run = await this.readRun(connectorId, runId);
|
|
2105
|
+
if (!run)
|
|
2106
|
+
return;
|
|
2107
|
+
if (run.status === "waiting" || run.status === "running") {
|
|
2108
|
+
this.scheduleRunMaintenance(run);
|
|
2109
|
+
return;
|
|
2110
|
+
}
|
|
2111
|
+
if (!await this.continueRunCleanup(run)) {
|
|
2112
|
+
this.scheduleRunMaintenance(run, CLEANUP_RETRY_DELAY_MS);
|
|
2113
|
+
}
|
|
2114
|
+
} catch {
|
|
2115
|
+
console.warn("[Sixb] Connector connection run cleanup failed and will be retried.");
|
|
2116
|
+
const timer = setTimeout(() => void this.maintainRun(connectorId, runId), CLEANUP_RETRY_DELAY_MS);
|
|
2117
|
+
timer.unref?.();
|
|
2118
|
+
this.maintenanceTimers.set(runId, timer);
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
function requireConnectorConnectionStorage2(storage) {
|
|
2123
|
+
if (storage.connectorConnections)
|
|
2124
|
+
return storage.connectorConnections;
|
|
2125
|
+
throw createConnectorCodedError("internal.unexpected", "Connector connection storage is unavailable.");
|
|
2126
|
+
}
|
|
2127
|
+
function providerCallbackError(error) {
|
|
2128
|
+
const code = error === "server_error" || error === "temporarily_unavailable" ? "connector.provider_unavailable" : "connector.provider_failed";
|
|
2129
|
+
return createConnectorCodedError(code, "Connector provider could not complete the authorization request.");
|
|
2130
|
+
}
|
|
2131
|
+
|
|
2132
|
+
// src/connectors/connections/service.ts
|
|
2133
|
+
var DEFAULT_ATTEMPT_TTL_MS = 10 * 60000;
|
|
2134
|
+
var DEFAULT_SELECTION_TTL_MS = 15 * 60000;
|
|
2135
|
+
var DEFAULT_CREDENTIAL_MUTATION_LEASE_MS = 30000;
|
|
2136
|
+
var DEFAULT_PROVIDER_OPERATION_TIMEOUT_MS = 2 * 60000;
|
|
2137
|
+
var DEFAULT_REFRESH_SKEW_MS = 60000;
|
|
2138
|
+
|
|
2139
|
+
class ConnectorConnectionService {
|
|
2140
|
+
projectId;
|
|
2141
|
+
definitionsById;
|
|
2142
|
+
abortController = new AbortController;
|
|
2143
|
+
connectionStorage;
|
|
2144
|
+
providerOperationTimeoutMs;
|
|
2145
|
+
authorizations;
|
|
2146
|
+
requests;
|
|
2147
|
+
runs;
|
|
2148
|
+
callbackProcess;
|
|
2149
|
+
constructor(projectId, definitions, options = {}) {
|
|
2150
|
+
this.projectId = projectId;
|
|
2151
|
+
this.definitionsById = new Map(definitions.map((definition) => [definition.id, definition]));
|
|
2152
|
+
const storage = requireConnectionStorage(options.storage);
|
|
2153
|
+
this.connectionStorage = storage.connectorConnections;
|
|
2154
|
+
const credentialProtector = credentialProtectorFor(storage.connectorConnections, options);
|
|
2155
|
+
const resolveDefinition = (connectorId) => this.requireOAuthDefinition(connectorId);
|
|
2156
|
+
const hostSignal = () => this.abortController.signal;
|
|
2157
|
+
const now = options.now ?? (() => new Date);
|
|
2158
|
+
const accountSelectionTtlMs = options.accountSelectionTtlMs ?? DEFAULT_SELECTION_TTL_MS;
|
|
2159
|
+
const providerOperationTimeoutMs = positiveDuration(options.providerOperationTimeoutMs ?? DEFAULT_PROVIDER_OPERATION_TIMEOUT_MS, "provider operation timeout");
|
|
2160
|
+
this.providerOperationTimeoutMs = providerOperationTimeoutMs;
|
|
2161
|
+
this.authorizations = new DefaultConnectorAuthorizationLifecycle({
|
|
2162
|
+
projectId,
|
|
2163
|
+
storage: storage.connectorConnections,
|
|
2164
|
+
credentialProtector,
|
|
2165
|
+
resolveDefinition,
|
|
2166
|
+
hostSignal,
|
|
2167
|
+
accountSelectionTtlMs,
|
|
2168
|
+
credentialMutationLeaseMs: options.credentialMutationLeaseMs ?? DEFAULT_CREDENTIAL_MUTATION_LEASE_MS,
|
|
2169
|
+
providerOperationTimeoutMs,
|
|
2170
|
+
refreshSkewMs: options.refreshSkewMs ?? DEFAULT_REFRESH_SKEW_MS,
|
|
2171
|
+
now
|
|
2172
|
+
});
|
|
2173
|
+
this.requests = new ConnectorAuthorizationRequestHandler({
|
|
2174
|
+
projectId,
|
|
2175
|
+
storage: storage.root,
|
|
2176
|
+
credentialProtector,
|
|
2177
|
+
authorizationAttemptTtlMs: options.authorizationAttemptTtlMs ?? DEFAULT_ATTEMPT_TTL_MS,
|
|
2178
|
+
providerOperationTimeoutMs,
|
|
2179
|
+
resolveDefinition,
|
|
2180
|
+
lifecycle: this.authorizations,
|
|
2181
|
+
hostSignal
|
|
2182
|
+
});
|
|
2183
|
+
this.runs = new ConnectorConnectionRunService({
|
|
2184
|
+
projectId,
|
|
2185
|
+
storage: storage.root,
|
|
2186
|
+
requestHandler: this.requests,
|
|
2187
|
+
authorizations: this.authorizations,
|
|
2188
|
+
resolveDefinition,
|
|
2189
|
+
processingTtlMs: options.connectionRunProcessingTtlMs ?? providerOperationTimeoutMs * 2 + 30000,
|
|
2190
|
+
selectionTtlMs: accountSelectionTtlMs,
|
|
2191
|
+
now
|
|
2192
|
+
});
|
|
2193
|
+
this.callbackProcess = Object.freeze({
|
|
2194
|
+
completeConnectionRun: (input) => this.completeConnectionRun(input)
|
|
2195
|
+
});
|
|
2196
|
+
}
|
|
2197
|
+
async connectConnection(definition, selector) {
|
|
2198
|
+
this.assertOAuthRegistered(definition);
|
|
2199
|
+
assertConnectorConnectionSelector(selector);
|
|
2200
|
+
const connection = await withConnectorStorageBoundary("Connector connection could not be read.", () => this.connectionStorage.getConnection({
|
|
2201
|
+
projectId: this.projectId,
|
|
2202
|
+
connectorId: definition.id,
|
|
2203
|
+
...selector
|
|
2204
|
+
}));
|
|
2205
|
+
if (!connection) {
|
|
2206
|
+
throw createConnectorCodedError("connector.not_found", `Connector '${definition.id}' has no connection for project slot '${selector.slot}'.`);
|
|
2207
|
+
}
|
|
2208
|
+
return this.connectExecutionConnection(definition, connection);
|
|
2209
|
+
}
|
|
2210
|
+
async listExecutionConnections(definition) {
|
|
2211
|
+
this.assertOAuthRegistered(definition);
|
|
2212
|
+
const connections = await withConnectorStorageBoundary("Connector connections could not be listed.", () => this.connectionStorage.listConnections({
|
|
2213
|
+
projectId: this.projectId,
|
|
2214
|
+
connectorId: definition.id
|
|
2215
|
+
}));
|
|
2216
|
+
return connections.filter((connection) => connection.status === "connected").sort((left, right) => left.id.localeCompare(right.id));
|
|
2217
|
+
}
|
|
2218
|
+
async connectExecutionConnection(definition, connection, signal = this.abortController.signal) {
|
|
2219
|
+
this.assertOAuthRegistered(definition);
|
|
2220
|
+
if (connection.projectId !== this.projectId || connection.connectorId !== definition.id || connection.status !== "connected") {
|
|
2221
|
+
throw createConnectorCodedError("connector.not_found", `Connector '${definition.id}' connection '${connection.id}' is not active in this project.`);
|
|
2222
|
+
}
|
|
2223
|
+
const authorization = await this.authorizations.requireStableActiveAuthorization(definition, connection.authorizationId);
|
|
2224
|
+
const hostSignal = this.abortController.signal;
|
|
2225
|
+
const operationSignal = signal === hostSignal ? hostSignal : AbortSignal.any([hostSignal, signal]);
|
|
2226
|
+
try {
|
|
2227
|
+
return await runBoundedConnectorProviderOperation({
|
|
2228
|
+
hostSignal: operationSignal,
|
|
2229
|
+
timeoutMs: this.providerOperationTimeoutMs
|
|
2230
|
+
}, (providerSignal) => {
|
|
2231
|
+
const clientSignal = AbortSignal.any([operationSignal, providerSignal]);
|
|
2232
|
+
return definition.adapter.connect({
|
|
2233
|
+
projectId: this.projectId,
|
|
2234
|
+
connectorId: definition.id,
|
|
2235
|
+
connectionId: connection.id,
|
|
2236
|
+
account: connection.account,
|
|
2237
|
+
tokenSource: this.authorizations.createTokenSource(definition, authorization.id),
|
|
2238
|
+
signal: clientSignal
|
|
2239
|
+
});
|
|
2240
|
+
}, () => connectorClientInterruptionError(operationSignal));
|
|
2241
|
+
} catch (error) {
|
|
2242
|
+
if (operationSignal.aborted)
|
|
2243
|
+
throw error;
|
|
2244
|
+
throw providerBoundaryError(error, providerFailureCode(error), "Connector connection client creation failed.");
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
async listConnections(command, connectorId) {
|
|
2248
|
+
const definition = this.requireOAuthDefinition(connectorId);
|
|
2249
|
+
requireConnectorConnectionCommandActor(command.execution, this.projectId);
|
|
2250
|
+
let connections;
|
|
2251
|
+
try {
|
|
2252
|
+
connections = await this.connectionStorage.listConnections({
|
|
2253
|
+
projectId: this.projectId,
|
|
2254
|
+
connectorId: definition.id
|
|
2255
|
+
});
|
|
2256
|
+
} catch (error) {
|
|
2257
|
+
throw storageBoundaryError(error, "Connector connections could not be listed.");
|
|
2258
|
+
}
|
|
2259
|
+
return withConnectorStorageBoundary("Connector authorizations could not be read.", () => Promise.all(connections.map(async (connection) => {
|
|
2260
|
+
const authorization = await this.connectionStorage.getAuthorization({
|
|
2261
|
+
projectId: this.projectId,
|
|
2262
|
+
connectorId: definition.id,
|
|
2263
|
+
authorizationId: connection.authorizationId
|
|
2264
|
+
});
|
|
2265
|
+
if (!authorization) {
|
|
2266
|
+
throw createConnectorCodedError("internal.unexpected", "Connector connection references an unavailable authorization.");
|
|
2267
|
+
}
|
|
2268
|
+
return connectorConnectionView(connection, authorization.status);
|
|
2269
|
+
})));
|
|
2270
|
+
}
|
|
2271
|
+
startConnectionRun(command, connectorId, input) {
|
|
2272
|
+
return this.runs.start(command, connectorId, input);
|
|
2273
|
+
}
|
|
2274
|
+
async addConnection(command, connectorId, input) {
|
|
2275
|
+
const definition = this.requireOAuthDefinition(connectorId);
|
|
2276
|
+
const actor = requireConnectorConnectionCommandActor(command.execution, this.projectId);
|
|
2277
|
+
assertConnectorConnectionSelector(input);
|
|
2278
|
+
const source = await withConnectorStorageBoundary("Source connector connection could not be read.", () => this.connectionStorage.getConnectionById({
|
|
2279
|
+
projectId: this.projectId,
|
|
2280
|
+
connectorId: definition.id,
|
|
2281
|
+
connectionId: nonblank(input.fromConnectionId, "source connection id")
|
|
2282
|
+
}));
|
|
2283
|
+
if (!source) {
|
|
2284
|
+
throw createConnectorCodedError("connector.not_found", "Source connection was not found.");
|
|
2285
|
+
}
|
|
2286
|
+
if (source.status !== "connected") {
|
|
2287
|
+
throw createConnectorCodedError("connector.authorization_required", "Source connection is disconnected.");
|
|
2288
|
+
}
|
|
2289
|
+
const authorization = await this.authorizations.requireStableActiveAuthorization(definition, source.authorizationId);
|
|
2290
|
+
assertConnectorAuthorizationActor(authorization, actor.principal);
|
|
2291
|
+
return this.runs.startSelection(command, definition.id, {
|
|
2292
|
+
owner: input.owner,
|
|
2293
|
+
slot: input.slot,
|
|
2294
|
+
authorizationId: authorization.id
|
|
2295
|
+
});
|
|
2296
|
+
}
|
|
2297
|
+
getConnectionRun(command, connectorId, runId) {
|
|
2298
|
+
this.requireOAuthDefinition(connectorId);
|
|
2299
|
+
return this.runs.get(command, connectorId, runId);
|
|
2300
|
+
}
|
|
2301
|
+
selectConnectionRunAccount(command, connectorId, input) {
|
|
2302
|
+
return this.runs.selectAccount(command, connectorId, input);
|
|
2303
|
+
}
|
|
2304
|
+
async startReauthorization(command, connectorId, connectionId, input) {
|
|
2305
|
+
const definition = this.requireOAuthDefinition(connectorId);
|
|
2306
|
+
requireConnectorConnectionCommandActor(command.execution, this.projectId);
|
|
2307
|
+
const connection = await withConnectorStorageBoundary("Connector connection could not be read.", () => this.connectionStorage.getConnectionById({
|
|
2308
|
+
projectId: this.projectId,
|
|
2309
|
+
connectorId: definition.id,
|
|
2310
|
+
connectionId: nonblank(connectionId, "connection id")
|
|
2311
|
+
}));
|
|
2312
|
+
if (!connection) {
|
|
2313
|
+
throw createConnectorCodedError("connector.not_found", "Connector connection was not found.");
|
|
2314
|
+
}
|
|
2315
|
+
if (connection.status !== "connected") {
|
|
2316
|
+
throw createConnectorCodedError("connector.authorization_required", "Disconnected connector connections cannot be reauthorized; start a new connection.");
|
|
2317
|
+
}
|
|
2318
|
+
return this.runs.start(command, definition.id, {
|
|
2319
|
+
owner: connection.owner,
|
|
2320
|
+
slot: connection.slot,
|
|
2321
|
+
redirectUri: input.redirectUri,
|
|
2322
|
+
returnTo: input.returnTo,
|
|
2323
|
+
reauthorizationId: connection.authorizationId
|
|
2324
|
+
});
|
|
2325
|
+
}
|
|
2326
|
+
async revokeConnection(command, connectorId, connectionId) {
|
|
2327
|
+
const definition = this.requireOAuthDefinition(connectorId);
|
|
2328
|
+
requireConnectorConnectionCommandActor(command.execution, this.projectId);
|
|
2329
|
+
const authorization = await withConnectorStorageBoundary("Connector authorization could not be resolved from its connection.", () => this.connectionStorage.getAuthorizationByConnectionId({
|
|
2330
|
+
projectId: this.projectId,
|
|
2331
|
+
connectorId: definition.id,
|
|
2332
|
+
connectionId: nonblank(connectionId, "connection id")
|
|
2333
|
+
}));
|
|
2334
|
+
if (!authorization) {
|
|
2335
|
+
throw createConnectorCodedError("connector.not_found", "Connector connection was not found.");
|
|
2336
|
+
}
|
|
2337
|
+
return this.revokeAuthorization(command, definition.id, authorization.id);
|
|
2338
|
+
}
|
|
2339
|
+
completeConnectionRun(input) {
|
|
2340
|
+
return this.runs.complete(input);
|
|
2341
|
+
}
|
|
2342
|
+
startAuthorization(command, connectorId, input) {
|
|
2343
|
+
return this.requests.startAuthorization(command, connectorId, input);
|
|
2344
|
+
}
|
|
2345
|
+
completeAuthorization(command, connectorId, input) {
|
|
2346
|
+
return this.requests.completeAuthorization(command, connectorId, input);
|
|
2347
|
+
}
|
|
2348
|
+
async selectAccount(command, connectorId, input) {
|
|
2349
|
+
const definition = this.requireOAuthDefinition(connectorId);
|
|
2350
|
+
const actor = requireConnectorConnectionCommandActor(command.execution, this.projectId);
|
|
2351
|
+
assertConnectorConnectionSelector(input);
|
|
2352
|
+
const authorization = await this.authorizations.requireAuthorization(definition.id, input.authorizationId);
|
|
2353
|
+
if (authorization.status !== "pending_selection" && authorization.status !== "active") {
|
|
2354
|
+
throw connectorAuthorizationStatusError(authorization.status);
|
|
2355
|
+
}
|
|
2356
|
+
assertConnectorAuthorizationActor(authorization, actor.principal);
|
|
2357
|
+
const account = authorization.accounts.find((candidate) => candidate.id === input.accountId);
|
|
2358
|
+
if (!account) {
|
|
2359
|
+
throw createConnectorCodedError("connector.not_found", `Account '${input.accountId}' is not exposed by this connector authorization.`);
|
|
2360
|
+
}
|
|
2361
|
+
let result;
|
|
2362
|
+
try {
|
|
2363
|
+
result = await this.connectionStorage.putConnection({
|
|
2364
|
+
id: `ccn_${randomUUID5()}`,
|
|
2365
|
+
projectId: this.projectId,
|
|
2366
|
+
connectorId: definition.id,
|
|
2367
|
+
owner: input.owner,
|
|
2368
|
+
slot: input.slot,
|
|
2369
|
+
authorizationId: authorization.id,
|
|
2370
|
+
account,
|
|
2371
|
+
replace: input.replace === true
|
|
2372
|
+
});
|
|
2373
|
+
} catch (error) {
|
|
2374
|
+
if (isConnectorStorageError(error, "authorization_conflict")) {
|
|
2375
|
+
await this.continueRevocationIfPending(definition, authorization.id);
|
|
2376
|
+
throw createConnectorCodedError("connector.operation_conflict", "Connector authorization cannot be selected in its current state.", { cause: error });
|
|
2377
|
+
}
|
|
2378
|
+
if (isConnectorStorageError(error, "connection_conflict")) {
|
|
2379
|
+
throw createConnectorCodedError("connector.operation_conflict", "Connector slot is already occupied; explicit replacement is required.", { cause: error });
|
|
2380
|
+
}
|
|
2381
|
+
throw storageBoundaryError(error, "Connector account selection could not be persisted.");
|
|
2382
|
+
}
|
|
2383
|
+
if (result.revocationPendingAuthorizationId) {
|
|
2384
|
+
await this.continuePendingRevocation(definition, result.revocationPendingAuthorizationId);
|
|
2385
|
+
}
|
|
2386
|
+
return connectorConnectionView(result.connection, result.authorization.status);
|
|
2387
|
+
}
|
|
2388
|
+
async disconnect(command, connectorId, connectionId) {
|
|
2389
|
+
const definition = this.requireOAuthDefinition(connectorId);
|
|
2390
|
+
requireConnectorConnectionCommandActor(command.execution, this.projectId);
|
|
2391
|
+
const normalizedConnectionId = nonblank(connectionId, "connection id");
|
|
2392
|
+
try {
|
|
2393
|
+
const disconnected = await this.connectionStorage.disconnectConnection({
|
|
2394
|
+
projectId: this.projectId,
|
|
2395
|
+
connectorId: definition.id,
|
|
2396
|
+
connectionId: normalizedConnectionId
|
|
2397
|
+
});
|
|
2398
|
+
if (!disconnected)
|
|
2399
|
+
return null;
|
|
2400
|
+
if (disconnected.revocationPendingAuthorizationId) {
|
|
2401
|
+
await this.continuePendingRevocation(definition, disconnected.revocationPendingAuthorizationId);
|
|
2402
|
+
}
|
|
2403
|
+
return connectorConnectionView(disconnected.connection, disconnected.authorization.status);
|
|
2404
|
+
} catch (error) {
|
|
2405
|
+
if (isConnectorStorageError(error, "authorization_conflict")) {
|
|
2406
|
+
throw createConnectorCodedError("connector.operation_conflict", "Connector connection cannot be disconnected while its authorization is changing.", { cause: error });
|
|
2407
|
+
}
|
|
2408
|
+
throw storageBoundaryError(error, "Connector disconnection could not be persisted.");
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2411
|
+
async revokeAuthorization(command, connectorId, authorizationId) {
|
|
2412
|
+
const definition = this.requireOAuthDefinition(connectorId);
|
|
2413
|
+
const actor = requireConnectorConnectionCommandActor(command.execution, this.projectId);
|
|
2414
|
+
const revoked = await this.authorizations.revokeAuthorization({
|
|
2415
|
+
definition,
|
|
2416
|
+
authorizationId: nonblank(authorizationId, "authorization id", "connector.authorization_invalid"),
|
|
2417
|
+
principal: actor.principal
|
|
2418
|
+
});
|
|
2419
|
+
return {
|
|
2420
|
+
affectedConnections: revoked.disconnected.map((connection) => connectorConnectionView(connection, "revoked"))
|
|
2421
|
+
};
|
|
2422
|
+
}
|
|
2423
|
+
async close() {
|
|
2424
|
+
this.runs.close();
|
|
2425
|
+
this.abortController.abort();
|
|
2426
|
+
this.abortController = new AbortController;
|
|
2427
|
+
}
|
|
2428
|
+
assertOAuthRegistered(definition) {
|
|
2429
|
+
const registered = this.requireOAuthDefinition(definition.id);
|
|
2430
|
+
if (registered !== definition) {
|
|
2431
|
+
throw createConnectorCodedError("connector.configuration_invalid", `Connector '${definition.id}' is not the registered definition instance.`);
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
requireOAuthDefinition(connectorId) {
|
|
2435
|
+
const definition = this.definitionsById.get(nonblank(connectorId, "connector id"));
|
|
2436
|
+
if (!definition) {
|
|
2437
|
+
throw createConnectorCodedError("connector.not_found", `Unknown connector '${connectorId}'.`);
|
|
2438
|
+
}
|
|
2439
|
+
if (!isOAuthConnectorDefinition(definition)) {
|
|
2440
|
+
throw createConnectorCodedError("connector.configuration_invalid", `Connector '${connectorId}' does not use OAuth authentication.`);
|
|
2441
|
+
}
|
|
2442
|
+
return definition;
|
|
2443
|
+
}
|
|
2444
|
+
async continueRevocationIfPending(definition, authorizationId) {
|
|
2445
|
+
const authorization = await this.authorizations.requireAuthorization(definition.id, authorizationId);
|
|
2446
|
+
if (authorization.status !== "revocation_pending")
|
|
2447
|
+
return;
|
|
2448
|
+
await this.continuePendingRevocation(definition, authorization.id);
|
|
2449
|
+
}
|
|
2450
|
+
async continuePendingRevocation(definition, authorizationId) {
|
|
2451
|
+
try {
|
|
2452
|
+
await this.authorizations.continuePendingRevocation(definition, authorizationId);
|
|
2453
|
+
} catch (error) {
|
|
2454
|
+
if (isSixbError(error) && error.code === "connector.revocation_pending")
|
|
2455
|
+
return;
|
|
2456
|
+
throw error;
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
function connectorClientInterruptionError(signal) {
|
|
2461
|
+
if (signal.aborted) {
|
|
2462
|
+
if (signal.reason instanceof Error)
|
|
2463
|
+
return signal.reason;
|
|
2464
|
+
const error = new Error("[Sixb] Connector client creation was cancelled.");
|
|
2465
|
+
error.name = "AbortError";
|
|
2466
|
+
return error;
|
|
2467
|
+
}
|
|
2468
|
+
return createConnectorCodedError("connector.provider_unavailable", "Connector client creation timed out.");
|
|
2469
|
+
}
|
|
2470
|
+
function requireConnectionStorage(storage) {
|
|
2471
|
+
const connectorConnections = storage?.connectorConnections;
|
|
2472
|
+
if (!storage || !connectorConnections) {
|
|
2473
|
+
throw createConnectorCodedError("connector.configuration_invalid", "OAuth connectors require storage.connectorConnections to be configured.");
|
|
2474
|
+
}
|
|
2475
|
+
if (connectorConnections.durability !== "ephemeral" && connectorConnections.durability !== "durable") {
|
|
2476
|
+
throw createConnectorCodedError("connector.configuration_invalid", "Connector connection storage must declare 'ephemeral' or 'durable' durability.");
|
|
2477
|
+
}
|
|
2478
|
+
return { root: storage, connectorConnections };
|
|
2479
|
+
}
|
|
2480
|
+
function credentialProtectorFor(storage, options) {
|
|
2481
|
+
if (storage.durability === "durable" && !options.credentialProtector) {
|
|
2482
|
+
throw createConnectorCodedError("connector.configuration_invalid", "Durable connector connection storage requires connectorConnections.encryptionKey to be configured.");
|
|
2483
|
+
}
|
|
2484
|
+
return options.credentialProtector ?? createEphemeralConnectorCredentialProtector();
|
|
2485
|
+
}
|
|
2486
|
+
|
|
2487
|
+
// src/connectors/service.ts
|
|
2488
|
+
class ConnectorService {
|
|
2489
|
+
projectId;
|
|
2490
|
+
definitionsById;
|
|
2491
|
+
staticConnectionStates = new Map;
|
|
2492
|
+
connections;
|
|
2493
|
+
constructor(projectId, definitions, options = {}) {
|
|
2494
|
+
this.projectId = projectId;
|
|
2495
|
+
this.definitionsById = new Map(definitions.map((definition) => [definition.id, definition]));
|
|
2496
|
+
if (definitions.some(isOAuthConnectorDefinition)) {
|
|
2497
|
+
this.connections = new ConnectorConnectionService(projectId, definitions, options);
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
async connect(definition) {
|
|
2501
|
+
this.assertRegistered(definition);
|
|
2502
|
+
let state = this.staticConnectionStates.get(definition);
|
|
2503
|
+
if (!state) {
|
|
2504
|
+
const controller = new AbortController;
|
|
2505
|
+
const clientPromise = Promise.resolve(definition.adapter.connect({
|
|
2506
|
+
projectId: this.projectId,
|
|
2507
|
+
connectorId: definition.id,
|
|
2508
|
+
signal: controller.signal
|
|
2509
|
+
}));
|
|
2510
|
+
state = { controller, clientPromise };
|
|
2511
|
+
this.staticConnectionStates.set(definition, state);
|
|
2512
|
+
}
|
|
2513
|
+
try {
|
|
2514
|
+
return await state.clientPromise;
|
|
2515
|
+
} catch (error) {
|
|
2516
|
+
if (this.staticConnectionStates.get(definition) === state) {
|
|
2517
|
+
this.staticConnectionStates.delete(definition);
|
|
2518
|
+
}
|
|
2519
|
+
throw error;
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2522
|
+
connectConnection(definition, selector) {
|
|
2523
|
+
return this.requireConnections().connectConnection(definition, selector);
|
|
2524
|
+
}
|
|
2525
|
+
listExecutionConnections(definition) {
|
|
2526
|
+
return this.requireConnections().listExecutionConnections(definition);
|
|
2527
|
+
}
|
|
2528
|
+
connectExecutionConnection(definition, connection, signal) {
|
|
2529
|
+
return this.requireConnections().connectExecutionConnection(definition, connection, signal);
|
|
2530
|
+
}
|
|
2531
|
+
get connectionProcess() {
|
|
2532
|
+
return this.connections;
|
|
2533
|
+
}
|
|
2534
|
+
async close() {
|
|
2535
|
+
await this.connections?.close();
|
|
2536
|
+
const activeConnections = [...this.staticConnectionStates.entries()];
|
|
2537
|
+
this.staticConnectionStates.clear();
|
|
2538
|
+
for (const [definition, state] of activeConnections) {
|
|
2539
|
+
await this.disconnectStaticState(definition, state);
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
assertRegistered(definition) {
|
|
2543
|
+
const registeredDefinition = this.definitionsById.get(definition.id);
|
|
2544
|
+
if (!registeredDefinition)
|
|
2545
|
+
throw new ConnectorNotFoundError(definition.id);
|
|
2546
|
+
if (registeredDefinition !== definition) {
|
|
2547
|
+
throw new ConnectorError(`Connector '${definition.id}' is not the registered definition instance.`);
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
requireConnections() {
|
|
2551
|
+
if (!this.connections) {
|
|
2552
|
+
throw createConnectorCodedError("connector.configuration_invalid", "No OAuth connectors are registered with this runtime.");
|
|
2553
|
+
}
|
|
2554
|
+
return this.connections;
|
|
2555
|
+
}
|
|
2556
|
+
async disconnectStaticState(definition, state) {
|
|
2557
|
+
state.controller.abort();
|
|
2558
|
+
let client;
|
|
2559
|
+
try {
|
|
2560
|
+
client = await state.clientPromise;
|
|
2561
|
+
} catch {
|
|
2562
|
+
return;
|
|
2563
|
+
}
|
|
2564
|
+
if (typeof definition.adapter.disconnect === "function") {
|
|
2565
|
+
await definition.adapter.disconnect(client);
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2570
|
+
// src/maintenance/ontology-maintenance.ts
|
|
2571
|
+
var DEFAULT_INTERVAL_MS = 60000;
|
|
2572
|
+
var DEFAULT_RETENTION_MS = 24 * 60 * 60000;
|
|
2573
|
+
var DEFAULT_CLEANUP_LIMIT = 1000;
|
|
2574
|
+
var DEFAULT_SHUTDOWN_TIMEOUT_MS = 30000;
|
|
2575
|
+
var DEGRADED_ATTEMPT_THRESHOLD = 3;
|
|
2576
|
+
var DEGRADED_FAILURE_THRESHOLD = 2;
|
|
2577
|
+
|
|
2578
|
+
class OntologyMaintenance {
|
|
2579
|
+
projectId;
|
|
2580
|
+
storage;
|
|
2581
|
+
dispatcher;
|
|
2582
|
+
intervalMs;
|
|
2583
|
+
publishedOutboxRetentionMs;
|
|
2584
|
+
terminalSourceRetentionMs;
|
|
2585
|
+
cleanupLimit;
|
|
2586
|
+
shutdownTimeoutMs;
|
|
2587
|
+
now;
|
|
2588
|
+
onError;
|
|
2589
|
+
owners = new Set;
|
|
2590
|
+
timer = null;
|
|
2591
|
+
pass = null;
|
|
2592
|
+
stopping = null;
|
|
2593
|
+
snapshot;
|
|
2594
|
+
constructor(dependencies) {
|
|
2595
|
+
assertNonblank2(dependencies.projectId, "projectId");
|
|
2596
|
+
const options = dependencies.options ?? {};
|
|
2597
|
+
this.projectId = dependencies.projectId;
|
|
2598
|
+
this.storage = dependencies.storage;
|
|
2599
|
+
this.dispatcher = dependencies.dispatcher;
|
|
2600
|
+
this.intervalMs = positiveInteger(options.intervalMs ?? DEFAULT_INTERVAL_MS, "intervalMs");
|
|
2601
|
+
this.publishedOutboxRetentionMs = nonnegativeInteger(options.publishedOutboxRetentionMs ?? DEFAULT_RETENTION_MS, "publishedOutboxRetentionMs");
|
|
2602
|
+
this.terminalSourceRetentionMs = nonnegativeInteger(options.terminalSourceRetentionMs ?? DEFAULT_RETENTION_MS, "terminalSourceRetentionMs");
|
|
2603
|
+
this.cleanupLimit = positiveInteger(options.cleanupLimit ?? DEFAULT_CLEANUP_LIMIT, "cleanupLimit");
|
|
2604
|
+
this.shutdownTimeoutMs = nonnegativeInteger(options.shutdownTimeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS, "shutdownTimeoutMs");
|
|
2605
|
+
this.now = dependencies.now ?? (() => new Date);
|
|
2606
|
+
this.onError = dependencies.onError ?? ((error) => console.error("[Sixb] Ontology maintenance error:", error));
|
|
2607
|
+
this.snapshot = emptySnapshot(this.intervalMs);
|
|
2608
|
+
}
|
|
2609
|
+
async start() {
|
|
2610
|
+
if (this.stopping)
|
|
2611
|
+
await this.stopping;
|
|
2612
|
+
const owner = Symbol("ontology-maintenance-owner");
|
|
2613
|
+
const startsHosting = this.owners.size === 0;
|
|
2614
|
+
this.owners.add(owner);
|
|
2615
|
+
if (startsHosting)
|
|
2616
|
+
this.startHostedPass();
|
|
2617
|
+
let released = false;
|
|
2618
|
+
return {
|
|
2619
|
+
stop: async () => {
|
|
2620
|
+
if (released)
|
|
2621
|
+
return;
|
|
2622
|
+
released = true;
|
|
2623
|
+
this.owners.delete(owner);
|
|
2624
|
+
if (this.owners.size === 0)
|
|
2625
|
+
await this.stopLoop();
|
|
2626
|
+
}
|
|
2627
|
+
};
|
|
2628
|
+
}
|
|
2629
|
+
runNow() {
|
|
2630
|
+
this.pass ??= this.runPass().finally(() => {
|
|
2631
|
+
this.pass = null;
|
|
2632
|
+
});
|
|
2633
|
+
return this.pass;
|
|
2634
|
+
}
|
|
2635
|
+
async stop() {
|
|
2636
|
+
this.owners.clear();
|
|
2637
|
+
await this.stopLoop();
|
|
2638
|
+
}
|
|
2639
|
+
getSnapshot() {
|
|
2640
|
+
return structuredClone(this.snapshot);
|
|
2641
|
+
}
|
|
2642
|
+
getOperationalStatus() {
|
|
2643
|
+
const snapshot = this.getSnapshot();
|
|
2644
|
+
const lastActivityAt = latestTimestamp(snapshot.lastStartedAt, snapshot.lastCompletedAt);
|
|
2645
|
+
const overdue = lastActivityAt !== null && this.now().getTime() - Date.parse(lastActivityAt) > this.intervalMs * 2;
|
|
2646
|
+
const oldestPendingAt = snapshot.outbox?.oldestPendingAt ?? null;
|
|
2647
|
+
const pendingTooLong = oldestPendingAt !== null && this.now().getTime() - Date.parse(oldestPendingAt) > this.intervalMs * 2;
|
|
2648
|
+
const repeatedlyFailing = (snapshot.outbox?.maxAttempts ?? 0) >= DEGRADED_ATTEMPT_THRESHOLD;
|
|
2649
|
+
const degraded = snapshot.consecutiveFailures >= DEGRADED_FAILURE_THRESHOLD || repeatedlyFailing || pendingTooLong || overdue;
|
|
2650
|
+
return { status: degraded ? "degraded" : "ok", maintenance: snapshot };
|
|
2651
|
+
}
|
|
2652
|
+
startHostedPass() {
|
|
2653
|
+
this.runNow().catch((error) => this.reportError(error)).finally(() => this.scheduleNextPass());
|
|
2654
|
+
}
|
|
2655
|
+
async runPass() {
|
|
2656
|
+
const started = this.now();
|
|
2657
|
+
this.snapshot = { ...this.snapshot, running: true, lastStartedAt: started.toISOString() };
|
|
2658
|
+
const failures = [];
|
|
2659
|
+
await captureFailure(() => this.dispatcher.drain(), failures);
|
|
2660
|
+
const cleanup = await this.cleanupExpiredRows(started, failures);
|
|
2661
|
+
const { outbox, terminalSources } = await this.readOperationalSummaries(failures);
|
|
2662
|
+
this.completePass({ started, failures, cleanup, outbox, terminalSources });
|
|
2663
|
+
}
|
|
2664
|
+
async cleanupExpiredRows(started, failures) {
|
|
2665
|
+
const publishedOutboxRowsDeleted = await captureFailure(() => this.storage.ontology.outbox.purgePublished({
|
|
2666
|
+
projectId: this.projectId,
|
|
2667
|
+
publishedBefore: new Date(started.getTime() - this.publishedOutboxRetentionMs).toISOString(),
|
|
2668
|
+
limit: this.cleanupLimit
|
|
2669
|
+
}), failures, 0);
|
|
2670
|
+
const terminalSources = await captureFailure(() => this.storage.ontology.sources.cleanupTerminal({
|
|
2671
|
+
projectId: this.projectId,
|
|
2672
|
+
terminalBefore: new Date(started.getTime() - this.terminalSourceRetentionMs).toISOString(),
|
|
2673
|
+
limit: this.cleanupLimit
|
|
2674
|
+
}), failures, { rowsDeleted: 0, materializationsDeleted: 0 });
|
|
2675
|
+
return {
|
|
2676
|
+
publishedOutboxRowsDeleted,
|
|
2677
|
+
terminalSourceRowsDeleted: terminalSources.rowsDeleted,
|
|
2678
|
+
terminalSourceMaterializationsDeleted: terminalSources.materializationsDeleted
|
|
2679
|
+
};
|
|
2680
|
+
}
|
|
2681
|
+
async readOperationalSummaries(failures) {
|
|
2682
|
+
const outbox = await captureFailure(() => this.storage.ontology.outbox.summarize({ projectId: this.projectId }), failures, this.snapshot.outbox);
|
|
2683
|
+
const terminalSummary = await captureFailure(() => this.storage.ontology.sources.summarizeTerminal({ projectId: this.projectId }), failures, this.snapshot.terminalSources);
|
|
2684
|
+
return { outbox, terminalSources: terminalSummary };
|
|
2685
|
+
}
|
|
2686
|
+
completePass(input) {
|
|
2687
|
+
const completed = this.now();
|
|
2688
|
+
const failure = input.failures.length > 0 ? new AggregateError(input.failures, "Maintenance pass failed.") : null;
|
|
2689
|
+
this.snapshot = {
|
|
2690
|
+
running: false,
|
|
2691
|
+
intervalMs: this.intervalMs,
|
|
2692
|
+
lastStartedAt: input.started.toISOString(),
|
|
2693
|
+
lastCompletedAt: completed.toISOString(),
|
|
2694
|
+
lastDurationMs: Math.max(0, completed.getTime() - input.started.getTime()),
|
|
2695
|
+
consecutiveFailures: failure ? this.snapshot.consecutiveFailures + 1 : 0,
|
|
2696
|
+
lastError: failure ? errorMessage(failure) : null,
|
|
2697
|
+
outbox: input.outbox,
|
|
2698
|
+
terminalSources: input.terminalSources,
|
|
2699
|
+
cleanup: input.cleanup
|
|
2700
|
+
};
|
|
2701
|
+
if (failure)
|
|
2702
|
+
this.reportError(failure);
|
|
2703
|
+
}
|
|
2704
|
+
scheduleNextPass() {
|
|
2705
|
+
if (this.owners.size === 0 || this.timer !== null)
|
|
2706
|
+
return;
|
|
2707
|
+
this.timer = setTimeout(() => {
|
|
2708
|
+
this.timer = null;
|
|
2709
|
+
if (this.owners.size === 0)
|
|
2710
|
+
return;
|
|
2711
|
+
this.runNow().catch((error) => this.reportError(error)).finally(() => this.scheduleNextPass());
|
|
2712
|
+
}, this.intervalMs);
|
|
2713
|
+
this.timer.unref?.();
|
|
2714
|
+
}
|
|
2715
|
+
stopLoop() {
|
|
2716
|
+
if (this.stopping)
|
|
2717
|
+
return this.stopping;
|
|
2718
|
+
if (this.timer !== null) {
|
|
2719
|
+
clearTimeout(this.timer);
|
|
2720
|
+
this.timer = null;
|
|
2721
|
+
}
|
|
2722
|
+
const activePass = this.pass ?? Promise.resolve();
|
|
2723
|
+
const stopping = settlesWithin(activePass, this.shutdownTimeoutMs).then((settled) => {
|
|
2724
|
+
if (!settled) {
|
|
2725
|
+
this.reportError(new Error(`[Sixb] Ontology maintenance did not stop within ${this.shutdownTimeoutMs}ms.`));
|
|
2726
|
+
}
|
|
2727
|
+
});
|
|
2728
|
+
this.stopping = stopping.finally(() => {
|
|
2729
|
+
this.stopping = null;
|
|
2730
|
+
});
|
|
2731
|
+
return this.stopping;
|
|
2732
|
+
}
|
|
2733
|
+
reportError(error) {
|
|
2734
|
+
try {
|
|
2735
|
+
this.onError(error);
|
|
2736
|
+
} catch {}
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2739
|
+
function emptySnapshot(intervalMs) {
|
|
2740
|
+
return {
|
|
2741
|
+
running: false,
|
|
2742
|
+
intervalMs,
|
|
2743
|
+
lastStartedAt: null,
|
|
2744
|
+
lastCompletedAt: null,
|
|
2745
|
+
lastDurationMs: null,
|
|
2746
|
+
consecutiveFailures: 0,
|
|
2747
|
+
lastError: null,
|
|
2748
|
+
outbox: null,
|
|
2749
|
+
terminalSources: null,
|
|
2750
|
+
cleanup: null
|
|
2751
|
+
};
|
|
2752
|
+
}
|
|
2753
|
+
function positiveInteger(value, name) {
|
|
2754
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
2755
|
+
throw new Error(`[Sixb] Ontology maintenance ${name} must be a positive integer.`);
|
|
2756
|
+
}
|
|
2757
|
+
return value;
|
|
2758
|
+
}
|
|
2759
|
+
function nonnegativeInteger(value, name) {
|
|
2760
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
2761
|
+
throw new Error(`[Sixb] Ontology maintenance ${name} must be a nonnegative integer.`);
|
|
2762
|
+
}
|
|
2763
|
+
return value;
|
|
2764
|
+
}
|
|
2765
|
+
function assertNonblank2(value, name) {
|
|
2766
|
+
if (value.trim().length === 0) {
|
|
2767
|
+
throw new Error(`[Sixb] Ontology maintenance ${name} must not be blank.`);
|
|
2768
|
+
}
|
|
2769
|
+
}
|
|
2770
|
+
function errorMessage(error) {
|
|
2771
|
+
if (error instanceof AggregateError) {
|
|
2772
|
+
return [...error.errors].map(errorMessage).join(" | ").slice(0, 2000);
|
|
2773
|
+
}
|
|
2774
|
+
const message = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
2775
|
+
return message.slice(0, 2000);
|
|
2776
|
+
}
|
|
2777
|
+
function latestTimestamp(left, right) {
|
|
2778
|
+
if (left === null)
|
|
2779
|
+
return right;
|
|
2780
|
+
if (right === null)
|
|
2781
|
+
return left;
|
|
2782
|
+
return Date.parse(left) >= Date.parse(right) ? left : right;
|
|
2783
|
+
}
|
|
2784
|
+
async function settlesWithin(promise, timeoutMs) {
|
|
2785
|
+
let timer;
|
|
2786
|
+
const timedOut = new Promise((resolve) => {
|
|
2787
|
+
timer = setTimeout(() => resolve(false), timeoutMs);
|
|
2788
|
+
});
|
|
2789
|
+
const result = await Promise.race([
|
|
2790
|
+
promise.then(() => true, () => true),
|
|
2791
|
+
timedOut
|
|
2792
|
+
]);
|
|
2793
|
+
if (timer !== undefined)
|
|
2794
|
+
clearTimeout(timer);
|
|
2795
|
+
return result;
|
|
2796
|
+
}
|
|
2797
|
+
async function captureFailure(run, failures, fallback) {
|
|
2798
|
+
try {
|
|
2799
|
+
return await run();
|
|
2800
|
+
} catch (error) {
|
|
2801
|
+
failures.push(error);
|
|
2802
|
+
return fallback;
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
// src/syncs/validation.ts
|
|
2806
|
+
function validateKeyedDatasetWriterTopology(input) {
|
|
2807
|
+
for (const sync of input.syncs) {
|
|
2808
|
+
const dataset = input.datasetsById.get(sync.target.dataset.id);
|
|
2809
|
+
if (sync.config.mode === "merge" && dataset?.primaryKey === undefined) {
|
|
2810
|
+
throw new SyncValidationError(`[Sixb] Merge sync '${sync.id}' targets dataset '${sync.target.dataset.id}', which must define a primaryKey.`);
|
|
2811
|
+
}
|
|
2812
|
+
}
|
|
2813
|
+
const firstWriterByDataset = new Map;
|
|
2814
|
+
for (const writer of datasetWriters(input.syncs, input.pipelines)) {
|
|
2815
|
+
const dataset = input.datasetsById.get(writer.datasetId);
|
|
2816
|
+
if (dataset?.primaryKey === undefined)
|
|
2817
|
+
continue;
|
|
2818
|
+
const first = firstWriterByDataset.get(writer.datasetId);
|
|
2819
|
+
if (!first) {
|
|
2820
|
+
firstWriterByDataset.set(writer.datasetId, writer);
|
|
2821
|
+
continue;
|
|
2822
|
+
}
|
|
2823
|
+
throw new SyncValidationError(`[Sixb] Keyed dataset '${writer.datasetId}' has multiple registered writers: ${first.label} and ${writer.label}. V1 allows one writer per keyed dataset.`);
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
function validateMergeSyncProjectionSafety(input) {
|
|
2827
|
+
const mergeSyncByDataset = new Map(input.syncs.filter((sync) => sync.config.mode === "merge").map((sync) => [sync.target.dataset.id, sync]));
|
|
2828
|
+
for (const projection of input.telemetryProjections) {
|
|
2829
|
+
const sync = mergeSyncByDataset.get(projection.datasetId);
|
|
2830
|
+
if (!sync)
|
|
2831
|
+
continue;
|
|
2832
|
+
throw new SyncValidationError(`[Sixb] Telemetry projection '${projection.id}' cannot read merge-written dataset '${projection.datasetId}' from sync '${sync.id}'. V1 merge syncs support object and link replacement projections only.`);
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
function* datasetWriters(syncs, pipelines) {
|
|
2836
|
+
for (const sync of syncs) {
|
|
2837
|
+
yield { datasetId: sync.target.dataset.id, label: `sync '${sync.id}'` };
|
|
2838
|
+
}
|
|
2839
|
+
for (const pipeline of pipelines) {
|
|
2840
|
+
for (const node of pipeline.graph.nodes) {
|
|
2841
|
+
yield {
|
|
2842
|
+
datasetId: node.step.output.id,
|
|
2843
|
+
label: `pipeline '${pipeline.id}' step '${node.step.id}'`
|
|
2844
|
+
};
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
|
|
2849
|
+
// src/runtime/definitions.ts
|
|
2850
|
+
function createDefinitionCatalog(definitionsById) {
|
|
2851
|
+
return Object.freeze({
|
|
2852
|
+
list: () => [...definitionsById.values()],
|
|
2853
|
+
getById: (id) => definitionsById.get(id) ?? null
|
|
2854
|
+
});
|
|
2855
|
+
}
|
|
2856
|
+
|
|
2857
|
+
// src/runtime/resolve-definitions.ts
|
|
2858
|
+
function resolveDefinitions(options) {
|
|
2859
|
+
const ontology = new OntologyRegistry({ sources: options.ontology });
|
|
2860
|
+
const actionRegistry = new ActionRegistry({ actions: options.actions ?? [], ontology });
|
|
2861
|
+
const registeredActionIds = new Set(actionRegistry.list().map((action) => action.id));
|
|
2862
|
+
const agents = options.agents ?? [];
|
|
2863
|
+
validateAgentToolsAtStartup(agents);
|
|
2864
|
+
const agentsById = indexUniqueDefinitions("agent", agents);
|
|
2865
|
+
const connectorsById = indexUniqueDefinitions("connector", options.connectors ?? []);
|
|
2866
|
+
const datasetsById = new Map;
|
|
2867
|
+
for (const dataset of options.datasets ?? []) {
|
|
2868
|
+
assertDatasetDefinition(dataset, (message) => new RuntimeError(message));
|
|
2869
|
+
assertUniqueDefinitionId("dataset", dataset.id, datasetsById);
|
|
2870
|
+
datasetsById.set(dataset.id, dataset);
|
|
2871
|
+
}
|
|
2872
|
+
const schedulesById = indexUniqueDefinitions("schedule", options.schedules ?? []);
|
|
2873
|
+
const syncsById = new Map;
|
|
2874
|
+
for (const sync of options.syncs ?? []) {
|
|
2875
|
+
assertUniqueDefinitionId("sync", sync.id, syncsById);
|
|
2876
|
+
if (!datasetsById.has(sync.target.dataset.id)) {
|
|
2877
|
+
throw new RuntimeError(`Sync '${sync.id}' targets unknown dataset '${sync.target.dataset.id}'. Add it to 'datasets' in createSixb() or export it from 'datasets/'.`);
|
|
2878
|
+
}
|
|
2879
|
+
syncsById.set(sync.id, sync);
|
|
2880
|
+
}
|
|
2881
|
+
const pipelinesById = new Map;
|
|
2882
|
+
for (const pipeline of options.pipelines ?? []) {
|
|
2883
|
+
assertUniqueDefinitionId("pipeline", pipeline.id, pipelinesById);
|
|
2884
|
+
validatePipelineDatasets(pipeline, datasetsById);
|
|
2885
|
+
pipelinesById.set(pipeline.id, pipeline);
|
|
2886
|
+
}
|
|
2887
|
+
const schedules = [...schedulesById.values()];
|
|
2888
|
+
const syncs = [...syncsById.values()];
|
|
2889
|
+
const pipelines = [...pipelinesById.values()];
|
|
2890
|
+
validateKeyedDatasetWriterTopology({ datasetsById, syncs, pipelines });
|
|
2891
|
+
const rules = options.rules ?? [];
|
|
2892
|
+
validateRulesAtStartup(rules, ontology);
|
|
2893
|
+
const rulesById = indexUniqueDefinitions("rule", rules);
|
|
2894
|
+
validateSchedulesAtStartup(schedules, ontology, {
|
|
2895
|
+
registeredRuleIds: new Set(rulesById.keys()),
|
|
2896
|
+
registeredActionIds,
|
|
2897
|
+
registeredDatasetIds: new Set(datasetsById.keys()),
|
|
2898
|
+
registeredSyncIds: new Set(syncsById.keys()),
|
|
2899
|
+
registeredPipelineIds: new Set(pipelinesById.keys())
|
|
2900
|
+
});
|
|
2901
|
+
for (const sync of syncs) {
|
|
2902
|
+
validateScheduleReferences("Sync", sync.id, sync.triggers, schedulesById);
|
|
2903
|
+
}
|
|
2904
|
+
for (const pipeline of pipelines) {
|
|
2905
|
+
validateScheduleReferences("Pipeline", pipeline.id, pipeline.triggers, schedulesById);
|
|
2906
|
+
}
|
|
2907
|
+
const workflows = validateWorkflowsAtStartup({
|
|
2908
|
+
workflows: options.workflows ?? [],
|
|
2909
|
+
registeredSchedules: schedulesById,
|
|
2910
|
+
registeredActionIds,
|
|
2911
|
+
registeredAgentIds: new Set(agentsById.keys())
|
|
2912
|
+
});
|
|
2913
|
+
const workflowsById = indexUniqueDefinitions("workflow", workflows);
|
|
2914
|
+
const security = new SecurityRegistry({
|
|
2915
|
+
groups: options.groups ?? [],
|
|
2916
|
+
roles: options.roles ?? [],
|
|
2917
|
+
membershipPolicies: options.membershipPolicies ?? [],
|
|
2918
|
+
objectTypeIds: new Set(ontology.getObjectTypesById().keys()),
|
|
2919
|
+
datasetIds: new Set(datasetsById.keys()),
|
|
2920
|
+
actionIds: registeredActionIds,
|
|
2921
|
+
workflowIds: new Set(workflowsById.keys()),
|
|
2922
|
+
syncIds: new Set(syncsById.keys()),
|
|
2923
|
+
pipelineIds: new Set(pipelinesById.keys()),
|
|
2924
|
+
agentIds: new Set(agentsById.keys()),
|
|
2925
|
+
connectorIds: new Set(connectorsById.keys()),
|
|
2926
|
+
getSubTypes: (objectTypeId) => ontology.listSubTypes(objectTypeId)
|
|
2927
|
+
});
|
|
2928
|
+
validateAgentGroupReferences(agents, security);
|
|
2929
|
+
const projectionRegistry = new ProjectionRegistry({
|
|
2930
|
+
projections: options.projections ?? [],
|
|
2931
|
+
ontology,
|
|
2932
|
+
datasetsById
|
|
2933
|
+
});
|
|
2934
|
+
validateMergeSyncProjectionSafety({
|
|
2935
|
+
syncs,
|
|
2936
|
+
telemetryProjections: projectionRegistry.listTelemetry()
|
|
2937
|
+
});
|
|
2938
|
+
return Object.freeze({
|
|
2939
|
+
ontology,
|
|
2940
|
+
actions: actionRegistry,
|
|
2941
|
+
agents: createDefinitionCatalog(agentsById),
|
|
2942
|
+
connectors: createDefinitionCatalog(connectorsById),
|
|
2943
|
+
datasets: createDefinitionCatalog(datasetsById),
|
|
2944
|
+
pipelines: createDefinitionCatalog(pipelinesById),
|
|
2945
|
+
projections: projectionRegistry,
|
|
2946
|
+
rules: createDefinitionCatalog(rulesById),
|
|
2947
|
+
schedules: createDefinitionCatalog(schedulesById),
|
|
2948
|
+
security,
|
|
2949
|
+
syncs: createDefinitionCatalog(syncsById),
|
|
2950
|
+
workflows: createDefinitionCatalog(workflowsById)
|
|
2951
|
+
});
|
|
2952
|
+
}
|
|
2953
|
+
function indexUniqueDefinitions(kind, definitions) {
|
|
2954
|
+
const definitionsById = new Map;
|
|
2955
|
+
for (const definition of definitions) {
|
|
2956
|
+
assertUniqueDefinitionId(kind, definition.id, definitionsById);
|
|
2957
|
+
definitionsById.set(definition.id, definition);
|
|
2958
|
+
}
|
|
2959
|
+
return definitionsById;
|
|
2960
|
+
}
|
|
2961
|
+
function assertUniqueDefinitionId(kind, id, definitionsById) {
|
|
2962
|
+
if (definitionsById.has(id)) {
|
|
2963
|
+
throw new RuntimeError(`Duplicate ${kind} id: ${id}`);
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
function validatePipelineDatasets(pipeline, datasetsById) {
|
|
2967
|
+
if (pipeline.graph.nodes.length === 0) {
|
|
2968
|
+
throw new RuntimeError(`Pipeline '${pipeline.id}' must contain at least one step.`);
|
|
2969
|
+
}
|
|
2970
|
+
const stepIds = new Set;
|
|
2971
|
+
for (const node of pipeline.graph.nodes) {
|
|
2972
|
+
const { step } = node;
|
|
2973
|
+
if (stepIds.has(step.id)) {
|
|
2974
|
+
throw new RuntimeError(`Pipeline '${pipeline.id}' contains duplicate step id '${step.id}'.`);
|
|
2975
|
+
}
|
|
2976
|
+
stepIds.add(step.id);
|
|
2977
|
+
for (const [inputName, dataset] of Object.entries(step.inputs)) {
|
|
2978
|
+
if (!datasetsById.has(dataset.id)) {
|
|
2979
|
+
throw new RuntimeError(`Pipeline '${pipeline.id}' step '${step.id}' input '${inputName}' references unknown dataset '${dataset.id}'. Add it to 'datasets' in createSixb() or export it from 'datasets/'.`);
|
|
2980
|
+
}
|
|
2981
|
+
}
|
|
2982
|
+
if (!datasetsById.has(step.output.id)) {
|
|
2983
|
+
throw new RuntimeError(`Pipeline '${pipeline.id}' step '${step.id}' outputs unknown dataset '${step.output.id}'. Add it to 'datasets' in createSixb() or export it from 'datasets/'.`);
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
}
|
|
2987
|
+
function validateScheduleReferences(consumerKind, consumerId, references, schedulesById) {
|
|
2988
|
+
for (const reference of references) {
|
|
2989
|
+
if (!schedulesById.has(reference.scheduleId)) {
|
|
2990
|
+
throw new RuntimeError(`${consumerKind} '${consumerId}' references unknown schedule '${reference.scheduleId}'. Add it to 'schedules' in createSixb() or export it from 'schedules/'.`);
|
|
2991
|
+
}
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
|
|
2995
|
+
// src/runtime/storage-readiness.ts
|
|
2996
|
+
var DEFAULT_SCHEMA_RETRY_DELAY_MS = 60000;
|
|
2997
|
+
|
|
2998
|
+
class StorageReadiness {
|
|
2999
|
+
storage;
|
|
3000
|
+
schema;
|
|
3001
|
+
schemaRetryDelayMs;
|
|
3002
|
+
now;
|
|
3003
|
+
constructor(storage, options = {}) {
|
|
3004
|
+
this.storage = storage;
|
|
3005
|
+
this.schemaRetryDelayMs = positiveInteger2(options.schemaRetryDelayMs ?? DEFAULT_SCHEMA_RETRY_DELAY_MS, "schemaRetryDelayMs");
|
|
3006
|
+
this.now = options.now ?? (() => new Date);
|
|
3007
|
+
this.schema = isMigrationCapableStorage(storage) ? { status: "unchecked" } : { status: "valid" };
|
|
3008
|
+
}
|
|
3009
|
+
startSchemaValidation() {
|
|
3010
|
+
if (this.schema.status === "checking" || this.schema.status === "valid")
|
|
3011
|
+
return;
|
|
3012
|
+
if (this.schema.status === "invalid" && this.now().getTime() < this.schema.retryAt)
|
|
3013
|
+
return;
|
|
3014
|
+
this.schema = { status: "checking" };
|
|
3015
|
+
this.validateSchema();
|
|
3016
|
+
}
|
|
3017
|
+
async check() {
|
|
3018
|
+
try {
|
|
3019
|
+
await this.storage.ping();
|
|
3020
|
+
} catch {
|
|
3021
|
+
return {
|
|
3022
|
+
status: "unready",
|
|
3023
|
+
storage: { reachable: false, schemaValid: false },
|
|
3024
|
+
reason: "Storage is unreachable."
|
|
3025
|
+
};
|
|
3026
|
+
}
|
|
3027
|
+
this.startSchemaValidation();
|
|
3028
|
+
if (this.schema.status === "valid") {
|
|
3029
|
+
return { status: "ready", storage: { reachable: true, schemaValid: true } };
|
|
3030
|
+
}
|
|
3031
|
+
return {
|
|
3032
|
+
status: "unready",
|
|
3033
|
+
storage: { reachable: true, schemaValid: false },
|
|
3034
|
+
reason: this.schema.status === "invalid" ? this.schema.reason : "Storage schema validation is in progress."
|
|
3035
|
+
};
|
|
3036
|
+
}
|
|
3037
|
+
async validateSchema() {
|
|
3038
|
+
const result = await checkStorageSchema(this.storage);
|
|
3039
|
+
this.schema = result.ok ? { status: "valid" } : this.invalidSchema(result.reason ?? "Storage schema is not usable.");
|
|
3040
|
+
}
|
|
3041
|
+
invalidSchema(reason) {
|
|
3042
|
+
return {
|
|
3043
|
+
status: "invalid",
|
|
3044
|
+
reason,
|
|
3045
|
+
retryAt: this.now().getTime() + this.schemaRetryDelayMs
|
|
3046
|
+
};
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
function positiveInteger2(value, name) {
|
|
3050
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
3051
|
+
throw new Error(`[Sixb] Storage readiness ${name} must be a positive integer.`);
|
|
3052
|
+
}
|
|
3053
|
+
return value;
|
|
3054
|
+
}
|
|
3055
|
+
|
|
3056
|
+
// src/runtime/host.ts
|
|
3057
|
+
class SixbHost {
|
|
3058
|
+
projectId;
|
|
3059
|
+
webhookRegistry;
|
|
3060
|
+
hostContext;
|
|
3061
|
+
committedFacts;
|
|
3062
|
+
eventService;
|
|
3063
|
+
ontologyMaintenance;
|
|
3064
|
+
storageReadiness;
|
|
3065
|
+
connectorService;
|
|
3066
|
+
materializer;
|
|
3067
|
+
definitions;
|
|
3068
|
+
broker;
|
|
3069
|
+
events;
|
|
3070
|
+
logging;
|
|
3071
|
+
storage;
|
|
3072
|
+
lakeStorage;
|
|
3073
|
+
blobStorage;
|
|
3074
|
+
queues;
|
|
3075
|
+
sandboxes;
|
|
3076
|
+
projectRoot;
|
|
3077
|
+
scheduler;
|
|
3078
|
+
auth;
|
|
3079
|
+
constructor(options) {
|
|
3080
|
+
const errorReporter = attachSixbErrorReporter(this, options.onError);
|
|
3081
|
+
this.projectId = options.id ?? "default";
|
|
3082
|
+
this.broker = options.broker;
|
|
3083
|
+
this.eventService = createDomainEventService({
|
|
3084
|
+
projectId: this.projectId,
|
|
3085
|
+
broker: this.broker,
|
|
3086
|
+
errorReporter
|
|
3087
|
+
});
|
|
3088
|
+
this.events = this.eventService;
|
|
3089
|
+
this.logging = new LoggingService({
|
|
3090
|
+
projectId: this.projectId,
|
|
3091
|
+
broker: this.broker,
|
|
3092
|
+
logger: options.logger,
|
|
3093
|
+
observability: options.observability?.logs
|
|
3094
|
+
});
|
|
3095
|
+
this.storage = options.storage;
|
|
3096
|
+
this.storageReadiness = new StorageReadiness(this.storage);
|
|
3097
|
+
this.lakeStorage = options.lakeStorage;
|
|
3098
|
+
this.blobStorage = options.blobStorage;
|
|
3099
|
+
this.queues = options.queues;
|
|
3100
|
+
this.sandboxes = options.sandboxes;
|
|
3101
|
+
this.projectRoot = resolve(options.projectRoot ?? process.cwd());
|
|
3102
|
+
const definitions = resolveDefinitions(options);
|
|
3103
|
+
this.definitions = definitions;
|
|
3104
|
+
registerProjectionRegistry(this, definitions.projections);
|
|
3105
|
+
this.auth = new AuthRuntime({
|
|
3106
|
+
projectId: this.projectId,
|
|
3107
|
+
storage: this.storage,
|
|
3108
|
+
security: definitions.security,
|
|
3109
|
+
config: options.auth
|
|
3110
|
+
});
|
|
3111
|
+
validateAuthStrategySecurityReferences(this.auth.getStrategy(), definitions.security);
|
|
3112
|
+
const connectors = definitions.connectors.list();
|
|
3113
|
+
assertConnectorConnectionSurfaces(connectors);
|
|
3114
|
+
const hasOAuthConnectors = connectors.some(isOAuthConnectorDefinition);
|
|
3115
|
+
const credentialProtector = hasOAuthConnectors && options.connectorConnections?.encryptionKey !== undefined ? createConnectorCredentialProtectorFromKey(options.connectorConnections.encryptionKey) : undefined;
|
|
3116
|
+
this.connectorService = new ConnectorService(this.projectId, connectors, {
|
|
3117
|
+
storage: this.storage,
|
|
3118
|
+
credentialProtector
|
|
3119
|
+
});
|
|
3120
|
+
if (this.connectorService.connectionProcess) {
|
|
3121
|
+
registerConnectorConnectionCallbackProcess(this, this.connectorService.connectionProcess.callbackProcess);
|
|
3122
|
+
}
|
|
3123
|
+
assertWebhookRunStorage(connectors, this.storage);
|
|
3124
|
+
this.webhookRegistry = new WebhookRegistry({ connectors });
|
|
3125
|
+
this.materializer = createOntologyMaterializer({
|
|
3126
|
+
projectId: this.projectId,
|
|
3127
|
+
ontology: definitions.ontology,
|
|
3128
|
+
projections: definitions.projections,
|
|
3129
|
+
storage: this.storage
|
|
3130
|
+
});
|
|
3131
|
+
this.committedFacts = new OntologyOutboxDispatcher({
|
|
3132
|
+
projectId: this.projectId,
|
|
3133
|
+
storage: this.storage,
|
|
3134
|
+
events: this.eventService,
|
|
3135
|
+
onDeliveryFailure: (error, failure) => reportEventDeliveryFailure(errorReporter, error, {
|
|
3136
|
+
projectId: this.projectId,
|
|
3137
|
+
...failure
|
|
3138
|
+
})
|
|
3139
|
+
});
|
|
3140
|
+
this.ontologyMaintenance = new OntologyMaintenance({
|
|
3141
|
+
projectId: this.projectId,
|
|
3142
|
+
storage: this.storage,
|
|
3143
|
+
dispatcher: this.committedFacts,
|
|
3144
|
+
options: options.ontologyMaintenance
|
|
3145
|
+
});
|
|
3146
|
+
this.hostContext = {
|
|
3147
|
+
projectId: this.projectId,
|
|
3148
|
+
broker: this.broker,
|
|
3149
|
+
ontology: definitions.ontology,
|
|
3150
|
+
actionRegistry: definitions.actions,
|
|
3151
|
+
events: this.events,
|
|
3152
|
+
storage: this.storage,
|
|
3153
|
+
queues: this.queues
|
|
3154
|
+
};
|
|
3155
|
+
registerProjectionRegistry(this.hostContext, definitions.projections);
|
|
3156
|
+
shareSixbErrorReporter(this, this.hostContext);
|
|
3157
|
+
this.scheduler = new SchedulerRuntime({
|
|
3158
|
+
schedules: definitions.schedules.list(),
|
|
3159
|
+
events: this.eventService
|
|
3160
|
+
});
|
|
3161
|
+
}
|
|
3162
|
+
withScope(scope) {
|
|
3163
|
+
const authorization = resolveExecutionScopeAuthorization(this.projectId, scope);
|
|
3164
|
+
if (authorization.ref.type === "kernel") {
|
|
3165
|
+
throw new Error("[Sixb] Kernel authority cannot be bound to the domain SDK.");
|
|
3166
|
+
}
|
|
3167
|
+
const runtime = {
|
|
3168
|
+
...this.hostContext,
|
|
3169
|
+
runtimeAuthorization: scope.authorization,
|
|
3170
|
+
...authorization.type === "principal" ? { authorization: authorization.context } : {}
|
|
3171
|
+
};
|
|
3172
|
+
registerOntologyMutationRuntime(runtime, createOntologyMutationRuntime({
|
|
3173
|
+
materializer: this.materializer.withScope(scope),
|
|
3174
|
+
notifyCommittedFacts: () => this.committedFacts.notify()
|
|
3175
|
+
}));
|
|
3176
|
+
return createBoundSixb(runtime, this.sixbDependencies(), scope.execution);
|
|
3177
|
+
}
|
|
3178
|
+
get id() {
|
|
3179
|
+
return this.projectId;
|
|
3180
|
+
}
|
|
3181
|
+
listWebhooks() {
|
|
3182
|
+
return this.webhookRegistry.list();
|
|
3183
|
+
}
|
|
3184
|
+
getWebhookById(connectorId, webhookId) {
|
|
3185
|
+
return this.webhookRegistry.getById(connectorId, webhookId);
|
|
3186
|
+
}
|
|
3187
|
+
startOntologyMaintenance() {
|
|
3188
|
+
this.storageReadiness.startSchemaValidation();
|
|
3189
|
+
return this.ontologyMaintenance.start();
|
|
3190
|
+
}
|
|
3191
|
+
getOntologyOperationalStatus() {
|
|
3192
|
+
return this.ontologyMaintenance.getOperationalStatus();
|
|
3193
|
+
}
|
|
3194
|
+
async checkReadiness() {
|
|
3195
|
+
return this.storageReadiness.check();
|
|
3196
|
+
}
|
|
3197
|
+
async closeLogger() {
|
|
3198
|
+
await this.logging.close();
|
|
3199
|
+
}
|
|
3200
|
+
async closeConnectors() {
|
|
3201
|
+
await this.connectorService.close();
|
|
3202
|
+
}
|
|
3203
|
+
async closeBlobs() {
|
|
3204
|
+
await this.blobStorage.close?.();
|
|
3205
|
+
}
|
|
3206
|
+
async closeBroker() {
|
|
3207
|
+
await this.committedFacts.stop();
|
|
3208
|
+
await this.ontologyMaintenance.stop();
|
|
3209
|
+
await this.broker.close?.();
|
|
3210
|
+
}
|
|
3211
|
+
sixbDependencies() {
|
|
3212
|
+
return {
|
|
3213
|
+
definitions: this.definitions,
|
|
3214
|
+
logging: this.logging,
|
|
3215
|
+
connectorService: this.connectorService,
|
|
3216
|
+
...this.connectorService.connectionProcess === undefined ? {} : { connectorConnections: this.connectorService.connectionProcess },
|
|
3217
|
+
blobStorage: this.blobStorage
|
|
3218
|
+
};
|
|
3219
|
+
}
|
|
3220
|
+
}
|
|
3221
|
+
function validateAuthStrategySecurityReferences(strategy, security) {
|
|
3222
|
+
if (!isMagicLinkAuthStrategy(strategy) && !isOidcAuthStrategy(strategy)) {
|
|
3223
|
+
return;
|
|
3224
|
+
}
|
|
3225
|
+
for (const groupId of strategy.bootstrapGroupIds ?? []) {
|
|
3226
|
+
if (!security.getGroupById(groupId)) {
|
|
3227
|
+
throw new AuthRuntimeError("invalid_auth_config", `[Sixb] Auth bootstrapGroups references unknown group '${groupId}'. Add it to 'security/groups/' or pass it to createSixb({ groups }).`);
|
|
3228
|
+
}
|
|
3229
|
+
}
|
|
3230
|
+
}
|
|
3231
|
+
function assertWebhookRunStorage(connectors, storage) {
|
|
3232
|
+
if (storage.webhookRuns !== undefined) {
|
|
3233
|
+
return;
|
|
3234
|
+
}
|
|
3235
|
+
for (const connector of connectors) {
|
|
3236
|
+
const webhooks = connector.adapter.webhooks;
|
|
3237
|
+
if (Array.isArray(webhooks) && webhooks.length > 0) {
|
|
3238
|
+
throw new WebhookValidationError("[Sixb] Webhooks require storage.webhookRuns to be configured.");
|
|
3239
|
+
}
|
|
3240
|
+
}
|
|
3241
|
+
}
|
|
3242
|
+
function assertConnectorConnectionSurfaces(connectors) {
|
|
3243
|
+
for (const connector of connectors) {
|
|
3244
|
+
if (!isOAuthConnectorDefinition(connector))
|
|
3245
|
+
continue;
|
|
3246
|
+
if (connector.adapter.webhooks !== undefined) {
|
|
3247
|
+
throw createConnectorCodedError("connector.configuration_invalid", `OAuth connector '${connector.id}' cannot register webhooks until connection routing is defined.`);
|
|
3248
|
+
}
|
|
3249
|
+
}
|
|
3250
|
+
}
|
|
3251
|
+
|
|
3252
|
+
// src/runtime/create.ts
|
|
3253
|
+
async function createSixb(options) {
|
|
3254
|
+
const projectRoot = resolve2(options.projectRoot ?? process.cwd());
|
|
3255
|
+
const discoveredOntology = await discoverOntologySources(projectRoot);
|
|
3256
|
+
const allSources = [...options.ontologies ?? [], ...discoveredOntology];
|
|
3257
|
+
if (allSources.length === 0) {
|
|
3258
|
+
throw new RuntimeError("No ontology found. Create an 'ontology/' folder or pass 'ontologies' to createSixb().");
|
|
3259
|
+
}
|
|
3260
|
+
const definitions = await discoverProjectDefinitions(projectRoot);
|
|
3261
|
+
return new SixbHost({
|
|
3262
|
+
id: options.id,
|
|
3263
|
+
ontology: allSources,
|
|
3264
|
+
broker: options.broker,
|
|
3265
|
+
storage: options.storage,
|
|
3266
|
+
lakeStorage: options.lakeStorage,
|
|
3267
|
+
blobStorage: options.blobStorage,
|
|
3268
|
+
queues: options.queues,
|
|
3269
|
+
sandboxes: options.sandboxes,
|
|
3270
|
+
logger: options.logger,
|
|
3271
|
+
observability: options.observability,
|
|
3272
|
+
onError: options.onError,
|
|
3273
|
+
ontologyMaintenance: options.ontologyMaintenance,
|
|
3274
|
+
projectRoot,
|
|
3275
|
+
actions: [...options.actions ?? [], ...definitions.actions],
|
|
3276
|
+
datasets: [...options.datasets ?? [], ...definitions.datasets],
|
|
3277
|
+
connectors: [...options.connectors ?? [], ...definitions.connectors],
|
|
3278
|
+
connectorConnections: options.connectorConnections,
|
|
3279
|
+
schedules: [...options.schedules ?? [], ...definitions.schedules],
|
|
3280
|
+
syncs: [...options.syncs ?? [], ...definitions.syncs],
|
|
3281
|
+
pipelines: [...options.pipelines ?? [], ...definitions.pipelines],
|
|
3282
|
+
projections: [...options.projections ?? [], ...definitions.projections],
|
|
3283
|
+
rules: [...options.rules ?? [], ...definitions.rules],
|
|
3284
|
+
workflows: [...options.workflows ?? [], ...definitions.workflows],
|
|
3285
|
+
groups: [...options.groups ?? [], ...definitions.groups],
|
|
3286
|
+
roles: [...options.roles ?? [], ...definitions.roles],
|
|
3287
|
+
membershipPolicies: [...options.membershipPolicies ?? [], ...definitions.membershipPolicies],
|
|
3288
|
+
agents: [...options.agents ?? [], ...definitions.agents],
|
|
3289
|
+
auth: options.auth
|
|
3290
|
+
});
|
|
3291
|
+
}
|
|
3292
|
+
export { WebhookValidationError, defineWebhook, webhookConnector, UnverifiedWebhookError, resolveWebhookVerification, warnUnverifiedWebhook, SixbHost, createSixb };
|
|
3293
|
+
|
|
3294
|
+
//# debugId=D5CA320A2421D32564756E2164756E21
|