@qlever-llc/trellis 0.10.22 → 0.11.0-rc.1
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/esm/auth/mod.d.ts +1 -1
- package/esm/auth/mod.d.ts.map +1 -1
- package/esm/auth/mod.js +1 -1
- package/esm/auth/protocol.d.ts +55 -0
- package/esm/auth/protocol.d.ts.map +1 -1
- package/esm/browser.d.ts +3 -3
- package/esm/browser.d.ts.map +1 -1
- package/esm/browser.js +1 -1
- package/esm/client_connect.d.ts +12 -3
- package/esm/client_connect.d.ts.map +1 -1
- package/esm/client_connect.js +112 -7
- package/esm/codec.d.ts +7 -6
- package/esm/codec.d.ts.map +1 -1
- package/esm/codec.js +125 -6
- package/esm/contract_support/canonical.d.ts +3 -0
- package/esm/contract_support/canonical.d.ts.map +1 -1
- package/esm/contract_support/canonical.js +3 -0
- package/esm/contract_support/mod.d.ts +125 -10
- package/esm/contract_support/mod.d.ts.map +1 -1
- package/esm/contract_support/mod.js +159 -5
- package/esm/contract_support/protocol.d.ts +92 -7
- package/esm/contract_support/protocol.d.ts.map +1 -1
- package/esm/contract_support/protocol.js +41 -7
- package/esm/contract_support/runtime.d.ts +4 -1
- package/esm/contract_support/runtime.d.ts.map +1 -1
- package/esm/errors/SchemaValidationError.d.ts +49 -0
- package/esm/errors/SchemaValidationError.d.ts.map +1 -0
- package/esm/errors/SchemaValidationError.js +67 -0
- package/esm/errors/index.d.ts +38 -9
- package/esm/errors/index.d.ts.map +1 -1
- package/esm/errors/index.js +16 -0
- package/esm/generated-sdk/auth/api.d.ts +33 -605
- package/esm/generated-sdk/auth/api.d.ts.map +1 -1
- package/esm/generated-sdk/auth/api.js +6 -1
- package/esm/generated-sdk/auth/client.d.ts +129 -134
- package/esm/generated-sdk/auth/client.d.ts.map +1 -1
- package/esm/generated-sdk/auth/contract.d.ts +2 -601
- package/esm/generated-sdk/auth/contract.d.ts.map +1 -1
- package/esm/generated-sdk/auth/contract.js +310 -93
- package/esm/generated-sdk/auth/mod.d.ts +1 -1
- package/esm/generated-sdk/auth/mod.d.ts.map +1 -1
- package/esm/generated-sdk/auth/owned_api.d.ts +88 -86
- package/esm/generated-sdk/auth/owned_api.d.ts.map +1 -1
- package/esm/generated-sdk/auth/owned_api.js +6 -0
- package/esm/generated-sdk/auth/schemas.d.ts +284 -136
- package/esm/generated-sdk/auth/schemas.d.ts.map +1 -1
- package/esm/generated-sdk/auth/schemas.js +231 -92
- package/esm/generated-sdk/auth/types.d.ts +565 -89
- package/esm/generated-sdk/auth/types.d.ts.map +1 -1
- package/esm/generated-sdk/auth/types.js +1 -1
- package/esm/generated-sdk/health/api.d.ts +29 -18
- package/esm/generated-sdk/health/api.d.ts.map +1 -1
- package/esm/generated-sdk/health/client.d.ts +7 -21
- package/esm/generated-sdk/health/client.d.ts.map +1 -1
- package/esm/generated-sdk/health/contract.d.ts +2 -15
- package/esm/generated-sdk/health/contract.d.ts.map +1 -1
- package/esm/generated-sdk/health/contract.js +2 -10
- package/esm/generated-sdk/health/mod.d.ts +1 -1
- package/esm/generated-sdk/health/mod.d.ts.map +1 -1
- package/esm/generated-sdk/health/owned_api.d.ts +2 -2
- package/esm/generated-sdk/health/schemas.d.ts +1 -14
- package/esm/generated-sdk/health/schemas.d.ts.map +1 -1
- package/esm/generated-sdk/health/schemas.js +1 -9
- package/esm/generated-sdk/health/types.d.ts +10 -5
- package/esm/generated-sdk/health/types.d.ts.map +1 -1
- package/esm/generated-sdk/health/types.js +1 -2
- package/esm/generated-sdk/jobs/api.d.ts +29 -109
- package/esm/generated-sdk/jobs/api.d.ts.map +1 -1
- package/esm/generated-sdk/jobs/client.d.ts +21 -33
- package/esm/generated-sdk/jobs/client.d.ts.map +1 -1
- package/esm/generated-sdk/jobs/contract.d.ts +2 -106
- package/esm/generated-sdk/jobs/contract.d.ts.map +1 -1
- package/esm/generated-sdk/jobs/contract.js +268 -1
- package/esm/generated-sdk/jobs/mod.d.ts +1 -1
- package/esm/generated-sdk/jobs/mod.d.ts.map +1 -1
- package/esm/generated-sdk/jobs/owned_api.d.ts +22 -9
- package/esm/generated-sdk/jobs/owned_api.d.ts.map +1 -1
- package/esm/generated-sdk/jobs/owned_api.js +20 -1
- package/esm/generated-sdk/jobs/schemas.d.ts +437 -0
- package/esm/generated-sdk/jobs/schemas.d.ts.map +1 -1
- package/esm/generated-sdk/jobs/schemas.js +211 -0
- package/esm/generated-sdk/jobs/types.d.ts +195 -12
- package/esm/generated-sdk/jobs/types.d.ts.map +1 -1
- package/esm/generated-sdk/jobs/types.js +1 -1
- package/esm/generated-sdk/state/api.d.ts +29 -68
- package/esm/generated-sdk/state/api.d.ts.map +1 -1
- package/esm/generated-sdk/state/client.d.ts +17 -31
- package/esm/generated-sdk/state/client.d.ts.map +1 -1
- package/esm/generated-sdk/state/contract.d.ts +1 -64
- package/esm/generated-sdk/state/contract.d.ts.map +1 -1
- package/esm/generated-sdk/state/mod.d.ts +1 -1
- package/esm/generated-sdk/state/mod.d.ts.map +1 -1
- package/esm/generated-sdk/state/owned_api.d.ts +7 -7
- package/esm/generated-sdk/state/types.d.ts +52 -9
- package/esm/generated-sdk/state/types.d.ts.map +1 -1
- package/esm/generated-sdk/trellis-core/api.d.ts +33 -45
- package/esm/generated-sdk/trellis-core/api.d.ts.map +1 -1
- package/esm/generated-sdk/trellis-core/api.js +6 -1
- package/esm/generated-sdk/trellis-core/client.d.ts +31 -20
- package/esm/generated-sdk/trellis-core/client.d.ts.map +1 -1
- package/esm/generated-sdk/trellis-core/contract.d.ts +2 -41
- package/esm/generated-sdk/trellis-core/contract.d.ts.map +1 -1
- package/esm/generated-sdk/trellis-core/contract.js +91 -1
- package/esm/generated-sdk/trellis-core/mod.d.ts +1 -1
- package/esm/generated-sdk/trellis-core/mod.d.ts.map +1 -1
- package/esm/generated-sdk/trellis-core/owned_api.d.ts +4 -4
- package/esm/generated-sdk/trellis-core/schemas.d.ts +113 -0
- package/esm/generated-sdk/trellis-core/schemas.d.ts.map +1 -1
- package/esm/generated-sdk/trellis-core/schemas.js +81 -0
- package/esm/generated-sdk/trellis-core/types.d.ts +54 -7
- package/esm/generated-sdk/trellis-core/types.d.ts.map +1 -1
- package/esm/generated-sdk/trellis-core/types.js +1 -1
- package/esm/health.d.ts +1 -1
- package/esm/health.d.ts.map +1 -1
- package/esm/health.js +1 -1
- package/esm/index.d.ts +4 -4
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +2 -2
- package/esm/jobs.d.ts +69 -3
- package/esm/jobs.d.ts.map +1 -1
- package/esm/jobs.js +92 -3
- package/esm/server/health.d.ts +2 -6
- package/esm/server/health.d.ts.map +1 -1
- package/esm/server/health_schemas.d.ts +0 -8
- package/esm/server/health_schemas.d.ts.map +1 -1
- package/esm/server/health_schemas.js +0 -5
- package/esm/server/internal_jobs/bindings.d.ts +11 -0
- package/esm/server/internal_jobs/bindings.d.ts.map +1 -1
- package/esm/server/internal_jobs/job-manager.d.ts +58 -2
- package/esm/server/internal_jobs/job-manager.d.ts.map +1 -1
- package/esm/server/internal_jobs/job-manager.js +429 -55
- package/esm/server/internal_jobs/key-coordinator.d.ts +269 -0
- package/esm/server/internal_jobs/key-coordinator.d.ts.map +1 -0
- package/esm/server/internal_jobs/key-coordinator.js +677 -0
- package/esm/server/internal_jobs/projection.d.ts.map +1 -1
- package/esm/server/internal_jobs/projection.js +2 -0
- package/esm/server/internal_jobs/runtime-worker.d.ts +5 -1
- package/esm/server/internal_jobs/runtime-worker.d.ts.map +1 -1
- package/esm/server/internal_jobs/runtime-worker.js +14 -2
- package/esm/server/internal_jobs/types.d.ts +5 -5
- package/esm/server/internal_jobs/types.d.ts.map +1 -1
- package/esm/server/internal_jobs/types.js +6 -0
- package/esm/server/runtime.d.ts.map +1 -1
- package/esm/server/service.d.ts +81 -112
- package/esm/server/service.d.ts.map +1 -1
- package/esm/server/service.js +372 -130
- package/esm/server.d.ts +3 -3
- package/esm/server.d.ts.map +1 -1
- package/esm/service/deno.d.ts +1 -1
- package/esm/service/deno.d.ts.map +1 -1
- package/esm/service/drizzle.d.ts +52 -0
- package/esm/service/drizzle.d.ts.map +1 -0
- package/esm/service/drizzle.js +108 -0
- package/esm/service/mod.d.ts +3 -3
- package/esm/service/mod.d.ts.map +1 -1
- package/esm/service/mod.js +2 -2
- package/esm/service/node.d.ts +1 -1
- package/esm/service/node.d.ts.map +1 -1
- package/esm/service/outbox_inbox.d.ts +76 -22
- package/esm/service/outbox_inbox.d.ts.map +1 -1
- package/esm/service/outbox_inbox.js +231 -70
- package/esm/telemetry/metrics.d.ts +55 -0
- package/esm/telemetry/metrics.d.ts.map +1 -1
- package/esm/telemetry/metrics.js +86 -0
- package/esm/telemetry/mod.d.ts +2 -1
- package/esm/telemetry/mod.d.ts.map +1 -1
- package/esm/telemetry/mod.js +1 -1
- package/esm/trellis.d.ts +62 -30
- package/esm/trellis.d.ts.map +1 -1
- package/esm/trellis.js +125 -42
- package/package.json +14 -2
- package/script/auth/mod.d.ts +1 -1
- package/script/auth/mod.d.ts.map +1 -1
- package/script/auth/mod.js +11 -4
- package/script/auth/protocol.d.ts +55 -0
- package/script/auth/protocol.d.ts.map +1 -1
- package/script/browser.d.ts +3 -3
- package/script/browser.d.ts.map +1 -1
- package/script/browser.js +3 -2
- package/script/client_connect.d.ts +12 -3
- package/script/client_connect.d.ts.map +1 -1
- package/script/client_connect.js +111 -6
- package/script/codec.d.ts +7 -6
- package/script/codec.d.ts.map +1 -1
- package/script/codec.js +124 -5
- package/script/contract_support/canonical.d.ts +3 -0
- package/script/contract_support/canonical.d.ts.map +1 -1
- package/script/contract_support/canonical.js +3 -0
- package/script/contract_support/mod.d.ts +125 -10
- package/script/contract_support/mod.d.ts.map +1 -1
- package/script/contract_support/mod.js +165 -5
- package/script/contract_support/protocol.d.ts +92 -7
- package/script/contract_support/protocol.d.ts.map +1 -1
- package/script/contract_support/protocol.js +42 -8
- package/script/contract_support/runtime.d.ts +4 -1
- package/script/contract_support/runtime.d.ts.map +1 -1
- package/script/errors/SchemaValidationError.d.ts +49 -0
- package/script/errors/SchemaValidationError.d.ts.map +1 -0
- package/script/errors/SchemaValidationError.js +74 -0
- package/script/errors/index.d.ts +38 -9
- package/script/errors/index.d.ts.map +1 -1
- package/script/errors/index.js +20 -1
- package/script/generated-sdk/auth/api.d.ts +33 -605
- package/script/generated-sdk/auth/api.d.ts.map +1 -1
- package/script/generated-sdk/auth/api.js +6 -1
- package/script/generated-sdk/auth/client.d.ts +129 -134
- package/script/generated-sdk/auth/client.d.ts.map +1 -1
- package/script/generated-sdk/auth/contract.d.ts +2 -601
- package/script/generated-sdk/auth/contract.d.ts.map +1 -1
- package/script/generated-sdk/auth/contract.js +310 -93
- package/script/generated-sdk/auth/mod.d.ts +1 -1
- package/script/generated-sdk/auth/mod.d.ts.map +1 -1
- package/script/generated-sdk/auth/owned_api.d.ts +88 -86
- package/script/generated-sdk/auth/owned_api.d.ts.map +1 -1
- package/script/generated-sdk/auth/owned_api.js +6 -0
- package/script/generated-sdk/auth/schemas.d.ts +284 -136
- package/script/generated-sdk/auth/schemas.d.ts.map +1 -1
- package/script/generated-sdk/auth/schemas.js +231 -92
- package/script/generated-sdk/auth/types.d.ts +565 -89
- package/script/generated-sdk/auth/types.d.ts.map +1 -1
- package/script/generated-sdk/auth/types.js +1 -1
- package/script/generated-sdk/health/api.d.ts +29 -18
- package/script/generated-sdk/health/api.d.ts.map +1 -1
- package/script/generated-sdk/health/client.d.ts +7 -21
- package/script/generated-sdk/health/client.d.ts.map +1 -1
- package/script/generated-sdk/health/contract.d.ts +2 -15
- package/script/generated-sdk/health/contract.d.ts.map +1 -1
- package/script/generated-sdk/health/contract.js +2 -10
- package/script/generated-sdk/health/mod.d.ts +1 -1
- package/script/generated-sdk/health/mod.d.ts.map +1 -1
- package/script/generated-sdk/health/owned_api.d.ts +2 -2
- package/script/generated-sdk/health/schemas.d.ts +1 -14
- package/script/generated-sdk/health/schemas.d.ts.map +1 -1
- package/script/generated-sdk/health/schemas.js +1 -9
- package/script/generated-sdk/health/types.d.ts +10 -5
- package/script/generated-sdk/health/types.d.ts.map +1 -1
- package/script/generated-sdk/health/types.js +1 -2
- package/script/generated-sdk/jobs/api.d.ts +29 -109
- package/script/generated-sdk/jobs/api.d.ts.map +1 -1
- package/script/generated-sdk/jobs/client.d.ts +21 -33
- package/script/generated-sdk/jobs/client.d.ts.map +1 -1
- package/script/generated-sdk/jobs/contract.d.ts +2 -106
- package/script/generated-sdk/jobs/contract.d.ts.map +1 -1
- package/script/generated-sdk/jobs/contract.js +268 -1
- package/script/generated-sdk/jobs/mod.d.ts +1 -1
- package/script/generated-sdk/jobs/mod.d.ts.map +1 -1
- package/script/generated-sdk/jobs/owned_api.d.ts +22 -9
- package/script/generated-sdk/jobs/owned_api.d.ts.map +1 -1
- package/script/generated-sdk/jobs/owned_api.js +19 -0
- package/script/generated-sdk/jobs/schemas.d.ts +437 -0
- package/script/generated-sdk/jobs/schemas.d.ts.map +1 -1
- package/script/generated-sdk/jobs/schemas.js +212 -1
- package/script/generated-sdk/jobs/types.d.ts +195 -12
- package/script/generated-sdk/jobs/types.d.ts.map +1 -1
- package/script/generated-sdk/jobs/types.js +1 -1
- package/script/generated-sdk/state/api.d.ts +29 -68
- package/script/generated-sdk/state/api.d.ts.map +1 -1
- package/script/generated-sdk/state/client.d.ts +17 -31
- package/script/generated-sdk/state/client.d.ts.map +1 -1
- package/script/generated-sdk/state/contract.d.ts +1 -64
- package/script/generated-sdk/state/contract.d.ts.map +1 -1
- package/script/generated-sdk/state/mod.d.ts +1 -1
- package/script/generated-sdk/state/mod.d.ts.map +1 -1
- package/script/generated-sdk/state/owned_api.d.ts +7 -7
- package/script/generated-sdk/state/types.d.ts +52 -9
- package/script/generated-sdk/state/types.d.ts.map +1 -1
- package/script/generated-sdk/trellis-core/api.d.ts +33 -45
- package/script/generated-sdk/trellis-core/api.d.ts.map +1 -1
- package/script/generated-sdk/trellis-core/api.js +6 -1
- package/script/generated-sdk/trellis-core/client.d.ts +31 -20
- package/script/generated-sdk/trellis-core/client.d.ts.map +1 -1
- package/script/generated-sdk/trellis-core/contract.d.ts +2 -41
- package/script/generated-sdk/trellis-core/contract.d.ts.map +1 -1
- package/script/generated-sdk/trellis-core/contract.js +91 -1
- package/script/generated-sdk/trellis-core/mod.d.ts +1 -1
- package/script/generated-sdk/trellis-core/mod.d.ts.map +1 -1
- package/script/generated-sdk/trellis-core/owned_api.d.ts +4 -4
- package/script/generated-sdk/trellis-core/schemas.d.ts +113 -0
- package/script/generated-sdk/trellis-core/schemas.d.ts.map +1 -1
- package/script/generated-sdk/trellis-core/schemas.js +81 -0
- package/script/generated-sdk/trellis-core/types.d.ts +54 -7
- package/script/generated-sdk/trellis-core/types.d.ts.map +1 -1
- package/script/generated-sdk/trellis-core/types.js +1 -1
- package/script/health.d.ts +1 -1
- package/script/health.d.ts.map +1 -1
- package/script/health.js +1 -2
- package/script/index.d.ts +4 -4
- package/script/index.d.ts.map +1 -1
- package/script/index.js +2 -2
- package/script/jobs.d.ts +69 -3
- package/script/jobs.d.ts.map +1 -1
- package/script/jobs.js +93 -3
- package/script/server/health.d.ts +2 -6
- package/script/server/health.d.ts.map +1 -1
- package/script/server/health_schemas.d.ts +0 -8
- package/script/server/health_schemas.d.ts.map +1 -1
- package/script/server/health_schemas.js +1 -6
- package/script/server/internal_jobs/bindings.d.ts +11 -0
- package/script/server/internal_jobs/bindings.d.ts.map +1 -1
- package/script/server/internal_jobs/job-manager.d.ts +58 -2
- package/script/server/internal_jobs/job-manager.d.ts.map +1 -1
- package/script/server/internal_jobs/job-manager.js +429 -54
- package/script/server/internal_jobs/key-coordinator.d.ts +269 -0
- package/script/server/internal_jobs/key-coordinator.d.ts.map +1 -0
- package/script/server/internal_jobs/key-coordinator.js +690 -0
- package/script/server/internal_jobs/projection.d.ts.map +1 -1
- package/script/server/internal_jobs/projection.js +2 -0
- package/script/server/internal_jobs/runtime-worker.d.ts +5 -1
- package/script/server/internal_jobs/runtime-worker.d.ts.map +1 -1
- package/script/server/internal_jobs/runtime-worker.js +14 -2
- package/script/server/internal_jobs/types.d.ts +5 -5
- package/script/server/internal_jobs/types.d.ts.map +1 -1
- package/script/server/internal_jobs/types.js +6 -0
- package/script/server/runtime.d.ts.map +1 -1
- package/script/server/service.d.ts +81 -112
- package/script/server/service.d.ts.map +1 -1
- package/script/server/service.js +369 -127
- package/script/server.d.ts +3 -3
- package/script/server.d.ts.map +1 -1
- package/script/service/deno.d.ts +1 -1
- package/script/service/deno.d.ts.map +1 -1
- package/script/service/drizzle.d.ts +52 -0
- package/script/service/drizzle.d.ts.map +1 -0
- package/script/service/drizzle.js +113 -0
- package/script/service/mod.d.ts +3 -3
- package/script/service/mod.d.ts.map +1 -1
- package/script/service/mod.js +4 -3
- package/script/service/node.d.ts +1 -1
- package/script/service/node.d.ts.map +1 -1
- package/script/service/outbox_inbox.d.ts +76 -22
- package/script/service/outbox_inbox.d.ts.map +1 -1
- package/script/service/outbox_inbox.js +233 -70
- package/script/telemetry/metrics.d.ts +55 -0
- package/script/telemetry/metrics.d.ts.map +1 -1
- package/script/telemetry/metrics.js +88 -0
- package/script/telemetry/mod.d.ts +2 -1
- package/script/telemetry/mod.d.ts.map +1 -1
- package/script/telemetry/mod.js +3 -1
- package/script/trellis.d.ts +62 -30
- package/script/trellis.d.ts.map +1 -1
- package/script/trellis.js +124 -41
- package/src/auth/mod.ts +7 -0
- package/src/browser.ts +5 -7
- package/src/client_connect.ts +200 -12
- package/src/codec.ts +186 -11
- package/src/contract_support/canonical.ts +3 -0
- package/src/contract_support/mod.ts +380 -9
- package/src/contract_support/protocol.ts +57 -10
- package/src/contract_support/runtime.ts +7 -0
- package/src/errors/SchemaValidationError.ts +67 -0
- package/src/errors/index.ts +28 -0
- package/src/health.ts +0 -1
- package/src/index.ts +7 -8
- package/src/jobs.ts +138 -1
- package/src/sdk/_generated/auth/api.ts +38 -7
- package/src/sdk/_generated/auth/client.ts +168 -530
- package/src/sdk/_generated/auth/contract.ts +310 -93
- package/src/sdk/_generated/auth/mod.ts +0 -1
- package/src/sdk/_generated/auth/owned_api.ts +92 -86
- package/src/sdk/_generated/auth/schemas.ts +231 -92
- package/src/sdk/_generated/auth/types.ts +1069 -273
- package/src/sdk/_generated/core/api.ts +38 -7
- package/src/sdk/_generated/core/client.ts +47 -73
- package/src/sdk/_generated/core/contract.ts +91 -1
- package/src/sdk/_generated/core/mod.ts +0 -1
- package/src/sdk/_generated/core/owned_api.ts +4 -4
- package/src/sdk/_generated/core/schemas.ts +81 -0
- package/src/sdk/_generated/core/types.ts +79 -15
- package/src/sdk/_generated/health/api.ts +29 -5
- package/src/sdk/_generated/health/client.ts +11 -58
- package/src/sdk/_generated/health/contract.ts +2 -10
- package/src/sdk/_generated/health/mod.ts +0 -1
- package/src/sdk/_generated/health/owned_api.ts +2 -2
- package/src/sdk/_generated/health/schemas.ts +1 -9
- package/src/sdk/_generated/health/types.ts +23 -2
- package/src/sdk/_generated/jobs/api.ts +29 -5
- package/src/sdk/_generated/jobs/client.ts +25 -109
- package/src/sdk/_generated/jobs/contract.ts +269 -1
- package/src/sdk/_generated/jobs/mod.ts +0 -1
- package/src/sdk/_generated/jobs/owned_api.ts +30 -9
- package/src/sdk/_generated/jobs/schemas.ts +213 -0
- package/src/sdk/_generated/jobs/types.ts +252 -18
- package/src/sdk/_generated/state/api.ts +29 -5
- package/src/sdk/_generated/state/client.ts +18 -95
- package/src/sdk/_generated/state/mod.ts +0 -1
- package/src/sdk/_generated/state/owned_api.ts +7 -7
- package/src/sdk/_generated/state/types.ts +91 -15
- package/src/server/health.ts +2 -6
- package/src/server/health_schemas.ts +0 -6
- package/src/server/internal_jobs/bindings.ts +11 -0
- package/src/server/internal_jobs/job-manager.ts +553 -24
- package/src/server/internal_jobs/key-coordinator.ts +1080 -0
- package/src/server/internal_jobs/projection.ts +2 -0
- package/src/server/internal_jobs/runtime-worker.ts +17 -1
- package/src/server/internal_jobs/types.ts +6 -0
- package/src/server/runtime.ts +4 -0
- package/src/server/service.ts +645 -625
- package/src/server.ts +14 -7
- package/src/service/deno.ts +0 -5
- package/src/service/drizzle.ts +184 -0
- package/src/service/mod.ts +17 -7
- package/src/service/node.ts +0 -5
- package/src/service/outbox_inbox.ts +354 -88
- package/src/telemetry/metrics.ts +198 -1
- package/src/telemetry/mod.ts +6 -0
- package/src/trellis.ts +286 -105
package/src/server/service.ts
CHANGED
|
@@ -51,8 +51,8 @@ import { Value } from "typebox/value";
|
|
|
51
51
|
import {
|
|
52
52
|
type HealthCheckFn,
|
|
53
53
|
ServiceHealth,
|
|
54
|
-
type
|
|
55
|
-
type
|
|
54
|
+
type ServiceHealthCheckFn,
|
|
55
|
+
type ServiceHealthInfoFn,
|
|
56
56
|
} from "./health.js";
|
|
57
57
|
import { mountStandardHealthRpc } from "./health_rpc.js";
|
|
58
58
|
import type { EventDesc, RPCDesc } from "../contracts.js";
|
|
@@ -67,6 +67,7 @@ import type {
|
|
|
67
67
|
FeedRegistration as RootFeedRegistration,
|
|
68
68
|
HandlerTrellis,
|
|
69
69
|
OperationHandlerContext,
|
|
70
|
+
OperationHandlerErrorOf,
|
|
70
71
|
OperationOutputOf,
|
|
71
72
|
OperationProgressOf,
|
|
72
73
|
OperationRegistration as RootOperationRegistration,
|
|
@@ -105,17 +106,33 @@ import {
|
|
|
105
106
|
ActiveJob as PublicActiveJob,
|
|
106
107
|
type JobIdentity,
|
|
107
108
|
type JobLogEntry,
|
|
109
|
+
JobNotEnqueuedError,
|
|
108
110
|
type JobProgress,
|
|
109
111
|
JobRef,
|
|
110
112
|
type JobSnapshot,
|
|
113
|
+
type JobSubmitOutcome,
|
|
111
114
|
JobWorkerHostAdapter,
|
|
112
115
|
type TerminalJob,
|
|
113
116
|
} from "../jobs.js";
|
|
117
|
+
import { ulid } from "ulid";
|
|
114
118
|
import {
|
|
119
|
+
createJobContext,
|
|
115
120
|
JobManager as InternalJobManager,
|
|
116
121
|
JobProcessError as InternalJobProcessError,
|
|
117
122
|
} from "./internal_jobs/job-manager.js";
|
|
118
123
|
import { startNatsWorkerHostFromBinding } from "./internal_jobs/runtime-worker.js";
|
|
124
|
+
import {
|
|
125
|
+
createNatsJobKeyCoordinator,
|
|
126
|
+
normalizeJobKeyPolicy,
|
|
127
|
+
} from "./internal_jobs/key-coordinator.js";
|
|
128
|
+
import type {
|
|
129
|
+
JobKeyConcurrencyBinding,
|
|
130
|
+
JobQueuePolicyBinding,
|
|
131
|
+
} from "./internal_jobs/key-coordinator.js";
|
|
132
|
+
import type {
|
|
133
|
+
JobsBinding,
|
|
134
|
+
JobsQueueBinding,
|
|
135
|
+
} from "./internal_jobs/bindings.js";
|
|
119
136
|
import type { ActiveJob as InternalActiveJob } from "./internal_jobs/active-job.js";
|
|
120
137
|
import {
|
|
121
138
|
type Job as InternalJob,
|
|
@@ -128,6 +145,20 @@ import {
|
|
|
128
145
|
type TrellisConnection,
|
|
129
146
|
} from "../connection.js";
|
|
130
147
|
import { initTelemetry } from "../telemetry/init.js";
|
|
148
|
+
import { recordTrellisDuration } from "../telemetry/mod.js";
|
|
149
|
+
import {
|
|
150
|
+
defaultSqlOutboxTables,
|
|
151
|
+
OutboxDispatcher,
|
|
152
|
+
type OutboxDispatcherOptions,
|
|
153
|
+
type OutboxDispatchRuntime,
|
|
154
|
+
type OutboxMessage,
|
|
155
|
+
type PreparedOutboxRecord,
|
|
156
|
+
preparedTrellisEventToOutboxRecord,
|
|
157
|
+
type SqlDialect,
|
|
158
|
+
type SqlExecutor,
|
|
159
|
+
SqlOutboxRepository,
|
|
160
|
+
type SqlOutboxTables,
|
|
161
|
+
} from "../service/outbox_inbox.js";
|
|
131
162
|
|
|
132
163
|
type ExtraNatsConnectOpts = Omit<
|
|
133
164
|
NatsConnectOpts,
|
|
@@ -149,6 +180,8 @@ type ResourceBindingJobsQueue = {
|
|
|
149
180
|
logs: boolean;
|
|
150
181
|
dlq: boolean;
|
|
151
182
|
concurrency: number;
|
|
183
|
+
keyConcurrency?: JobKeyConcurrencyBinding;
|
|
184
|
+
queue?: JobQueuePolicyBinding;
|
|
152
185
|
};
|
|
153
186
|
|
|
154
187
|
type ResourceBindingJobs = {
|
|
@@ -157,6 +190,71 @@ type ResourceBindingJobs = {
|
|
|
157
190
|
queues: Record<string, ResourceBindingJobsQueue>;
|
|
158
191
|
};
|
|
159
192
|
|
|
193
|
+
function normalizeResourceJobsBinding(
|
|
194
|
+
binding: ResourceBindingJobs,
|
|
195
|
+
): JobsBinding {
|
|
196
|
+
const queues: Record<string, JobsQueueBinding> = {};
|
|
197
|
+
for (const [name, queue] of Object.entries(binding.queues)) {
|
|
198
|
+
const baseQueue = baseJobsQueueBinding(queue);
|
|
199
|
+
if (!queue.keyConcurrency) {
|
|
200
|
+
queues[name] = {
|
|
201
|
+
...baseQueue,
|
|
202
|
+
...(queue.queue ? { queue: normalizeQueuePolicy(queue.queue) } : {}),
|
|
203
|
+
};
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const policy = normalizeJobKeyPolicy({
|
|
208
|
+
keyConcurrency: queue.keyConcurrency,
|
|
209
|
+
queue: queue.queue,
|
|
210
|
+
});
|
|
211
|
+
queues[name] = {
|
|
212
|
+
...baseQueue,
|
|
213
|
+
keyConcurrency: {
|
|
214
|
+
key: policy.key,
|
|
215
|
+
maxActive: policy.maxActive,
|
|
216
|
+
heartbeatIntervalMs: policy.heartbeatIntervalMs,
|
|
217
|
+
heartbeatTtlMs: policy.heartbeatTtlMs,
|
|
218
|
+
stalePolicy: policy.stalePolicy,
|
|
219
|
+
},
|
|
220
|
+
queue: policy.queue,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
return { namespace: binding.namespace, queues };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function baseJobsQueueBinding(
|
|
227
|
+
queue: ResourceBindingJobsQueue,
|
|
228
|
+
): Omit<JobsQueueBinding, "keyConcurrency" | "queue"> {
|
|
229
|
+
return {
|
|
230
|
+
queueType: queue.queueType,
|
|
231
|
+
publishPrefix: queue.publishPrefix,
|
|
232
|
+
workSubject: queue.workSubject,
|
|
233
|
+
consumerName: queue.consumerName,
|
|
234
|
+
payload: queue.payload,
|
|
235
|
+
...(queue.result ? { result: queue.result } : {}),
|
|
236
|
+
maxDeliver: queue.maxDeliver,
|
|
237
|
+
backoffMs: queue.backoffMs,
|
|
238
|
+
ackWaitMs: queue.ackWaitMs,
|
|
239
|
+
...(queue.defaultDeadlineMs !== undefined
|
|
240
|
+
? { defaultDeadlineMs: queue.defaultDeadlineMs }
|
|
241
|
+
: {}),
|
|
242
|
+
progress: queue.progress,
|
|
243
|
+
logs: queue.logs,
|
|
244
|
+
dlq: queue.dlq,
|
|
245
|
+
concurrency: queue.concurrency,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function normalizeQueuePolicy(
|
|
250
|
+
queue: JobQueuePolicyBinding,
|
|
251
|
+
): JobsQueueBinding["queue"] {
|
|
252
|
+
return {
|
|
253
|
+
maxQueuedPerKey: queue.maxQueuedPerKey ?? 0,
|
|
254
|
+
whenFull: queue.whenFull ?? "reject",
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
160
258
|
type ResourceBindingEventConsumer = {
|
|
161
259
|
stream: string;
|
|
162
260
|
consumerName: string;
|
|
@@ -981,14 +1079,6 @@ type ContractKvOf<
|
|
|
981
1079
|
>,
|
|
982
1080
|
> = NonNullable<TContract[typeof CONTRACT_KV_METADATA]>;
|
|
983
1081
|
|
|
984
|
-
type ContractJobName<
|
|
985
|
-
TContract extends ServiceContract<
|
|
986
|
-
TrellisAPI,
|
|
987
|
-
TrellisAPI | undefined,
|
|
988
|
-
ContractJobsMetadata
|
|
989
|
-
>,
|
|
990
|
-
> = keyof ContractJobsOf<TContract> & string;
|
|
991
|
-
|
|
992
1082
|
type ContractEventName<
|
|
993
1083
|
TContract extends ServiceContract<
|
|
994
1084
|
TrellisAPI,
|
|
@@ -997,14 +1087,6 @@ type ContractEventName<
|
|
|
997
1087
|
>,
|
|
998
1088
|
> = ServiceEventName<ContractTrellisApi<TContract>>;
|
|
999
1089
|
|
|
1000
|
-
type ContractFeedName<
|
|
1001
|
-
TContract extends ServiceContract<
|
|
1002
|
-
TrellisAPI,
|
|
1003
|
-
TrellisAPI | undefined,
|
|
1004
|
-
ContractJobsMetadata
|
|
1005
|
-
>,
|
|
1006
|
-
> = keyof ContractOwnedApi<TContract>["feeds"] & string;
|
|
1007
|
-
|
|
1008
1090
|
type ContractOperationName<
|
|
1009
1091
|
TContract extends ServiceContract<
|
|
1010
1092
|
TrellisAPI,
|
|
@@ -1013,59 +1095,6 @@ type ContractOperationName<
|
|
|
1013
1095
|
>,
|
|
1014
1096
|
> = keyof ContractOwnedApi<TContract>["operations"] & string;
|
|
1015
1097
|
|
|
1016
|
-
type ContractJobPayload<
|
|
1017
|
-
TContract extends ServiceContract<
|
|
1018
|
-
TrellisAPI,
|
|
1019
|
-
TrellisAPI | undefined,
|
|
1020
|
-
ContractJobsMetadata
|
|
1021
|
-
>,
|
|
1022
|
-
TJob extends ContractJobName<TContract>,
|
|
1023
|
-
> = ContractJobsOf<TContract>[TJob]["payload"];
|
|
1024
|
-
|
|
1025
|
-
type ContractJobResult<
|
|
1026
|
-
TContract extends ServiceContract<
|
|
1027
|
-
TrellisAPI,
|
|
1028
|
-
TrellisAPI | undefined,
|
|
1029
|
-
ContractJobsMetadata
|
|
1030
|
-
>,
|
|
1031
|
-
TJob extends ContractJobName<TContract>,
|
|
1032
|
-
> = ContractJobsOf<TContract>[TJob]["result"];
|
|
1033
|
-
|
|
1034
|
-
/** Arguments passed to a typed Trellis service job handler. */
|
|
1035
|
-
export type JobArgs<
|
|
1036
|
-
TContract extends ServiceContract<
|
|
1037
|
-
TrellisAPI,
|
|
1038
|
-
TrellisAPI | undefined,
|
|
1039
|
-
ContractJobsMetadata,
|
|
1040
|
-
ContractKvMetadata
|
|
1041
|
-
>,
|
|
1042
|
-
TJob extends ContractJobName<TContract>,
|
|
1043
|
-
TDeps = undefined,
|
|
1044
|
-
> = {
|
|
1045
|
-
job: PublicActiveJob<
|
|
1046
|
-
ContractJobPayload<TContract, TJob>,
|
|
1047
|
-
ContractJobResult<TContract, TJob>
|
|
1048
|
-
>;
|
|
1049
|
-
client: Trellis<
|
|
1050
|
-
ContractTrellisApi<TContract>,
|
|
1051
|
-
ContractKvOf<TContract>,
|
|
1052
|
-
ContractJobsOf<TContract>
|
|
1053
|
-
>;
|
|
1054
|
-
} & WithDeps<TDeps>;
|
|
1055
|
-
|
|
1056
|
-
/** Result returned by a typed Trellis service job handler. */
|
|
1057
|
-
export type JobResult<
|
|
1058
|
-
TContract extends ServiceContract<
|
|
1059
|
-
TrellisAPI,
|
|
1060
|
-
TrellisAPI | undefined,
|
|
1061
|
-
ContractJobsMetadata,
|
|
1062
|
-
ContractKvMetadata
|
|
1063
|
-
>,
|
|
1064
|
-
TJob extends ContractJobName<TContract>,
|
|
1065
|
-
> = Result<ContractJobResult<TContract, TJob>, BaseError>;
|
|
1066
|
-
|
|
1067
|
-
type WithDeps<TDeps> = [TDeps] extends [undefined] ? {} : { deps: TDeps };
|
|
1068
|
-
|
|
1069
1098
|
/** Typed RPC handler function for an extracted Trellis service handler. */
|
|
1070
1099
|
export type RpcHandler<
|
|
1071
1100
|
TContract extends ServiceContract<
|
|
@@ -1075,7 +1104,6 @@ export type RpcHandler<
|
|
|
1075
1104
|
ContractKvMetadata
|
|
1076
1105
|
>,
|
|
1077
1106
|
M extends RpcMethodName<ContractOwnedApi<TContract>>,
|
|
1078
|
-
TDeps = undefined,
|
|
1079
1107
|
> = ({
|
|
1080
1108
|
input,
|
|
1081
1109
|
context,
|
|
@@ -1088,7 +1116,7 @@ export type RpcHandler<
|
|
|
1088
1116
|
ContractKvOf<TContract>,
|
|
1089
1117
|
ContractJobsOf<TContract>
|
|
1090
1118
|
>;
|
|
1091
|
-
}
|
|
1119
|
+
}) =>
|
|
1092
1120
|
| Promise<
|
|
1093
1121
|
Result<
|
|
1094
1122
|
RpcMethodOutput<ContractOwnedApi<TContract>, M>,
|
|
@@ -1109,7 +1137,6 @@ export type ServiceEventHandler<
|
|
|
1109
1137
|
ContractKvMetadata
|
|
1110
1138
|
>,
|
|
1111
1139
|
E extends ContractEventName<TContract>,
|
|
1112
|
-
TDeps = undefined,
|
|
1113
1140
|
> = (
|
|
1114
1141
|
args: {
|
|
1115
1142
|
event: ServiceEventOf<ContractTrellisApi<TContract>, E>;
|
|
@@ -1119,49 +1146,9 @@ export type ServiceEventHandler<
|
|
|
1119
1146
|
ContractKvOf<TContract>,
|
|
1120
1147
|
ContractJobsOf<TContract>
|
|
1121
1148
|
>;
|
|
1122
|
-
}
|
|
1149
|
+
},
|
|
1123
1150
|
) => MaybeAsync<void, BaseError>;
|
|
1124
1151
|
|
|
1125
|
-
/** Typed feed handler function for an extracted Trellis service handler. */
|
|
1126
|
-
export type FeedHandler<
|
|
1127
|
-
TContract extends ServiceContract<
|
|
1128
|
-
TrellisAPI,
|
|
1129
|
-
TrellisAPI | undefined,
|
|
1130
|
-
ContractJobsMetadata,
|
|
1131
|
-
ContractKvMetadata
|
|
1132
|
-
>,
|
|
1133
|
-
F extends ContractFeedName<TContract>,
|
|
1134
|
-
TDeps = undefined,
|
|
1135
|
-
> = (
|
|
1136
|
-
context: {
|
|
1137
|
-
input: FeedInputOf<ContractOwnedApi<TContract>, F>;
|
|
1138
|
-
caller: unknown;
|
|
1139
|
-
signal: AbortSignal;
|
|
1140
|
-
emit(
|
|
1141
|
-
event: FeedEventOf<ContractOwnedApi<TContract>, F>,
|
|
1142
|
-
): AsyncResult<void, ValidationError | UnexpectedError>;
|
|
1143
|
-
client: Trellis<
|
|
1144
|
-
ContractTrellisApi<TContract>,
|
|
1145
|
-
ContractKvOf<TContract>,
|
|
1146
|
-
ContractJobsOf<TContract>
|
|
1147
|
-
>;
|
|
1148
|
-
} & WithDeps<TDeps>,
|
|
1149
|
-
) => unknown | Promise<unknown>;
|
|
1150
|
-
|
|
1151
|
-
/** Typed job handler function for an extracted Trellis service job handler. */
|
|
1152
|
-
export type JobHandler<
|
|
1153
|
-
TContract extends ServiceContract<
|
|
1154
|
-
TrellisAPI,
|
|
1155
|
-
TrellisAPI | undefined,
|
|
1156
|
-
ContractJobsMetadata,
|
|
1157
|
-
ContractKvMetadata
|
|
1158
|
-
>,
|
|
1159
|
-
TJob extends ContractJobName<TContract>,
|
|
1160
|
-
TDeps = undefined,
|
|
1161
|
-
> = (args: JobArgs<TContract, TJob, TDeps>) => Promise<
|
|
1162
|
-
JobResult<TContract, TJob>
|
|
1163
|
-
>;
|
|
1164
|
-
|
|
1165
1152
|
/** Typed operation handler function for an extracted Trellis service handler. */
|
|
1166
1153
|
export type OperationHandler<
|
|
1167
1154
|
TContract extends ServiceContract<
|
|
@@ -1171,14 +1158,14 @@ export type OperationHandler<
|
|
|
1171
1158
|
ContractKvMetadata
|
|
1172
1159
|
>,
|
|
1173
1160
|
O extends ContractOperationName<TContract>,
|
|
1174
|
-
TDeps = undefined,
|
|
1175
1161
|
> = (
|
|
1176
1162
|
args:
|
|
1177
1163
|
& OperationHandlerContext<
|
|
1178
1164
|
InferSchemaType<ContractOwnedApi<TContract>["operations"][O]["input"]>,
|
|
1179
1165
|
OperationProgressOf<ContractOwnedApi<TContract>, O>,
|
|
1180
1166
|
OperationOutputOf<ContractOwnedApi<TContract>, O>,
|
|
1181
|
-
OperationTransferContextOf<ContractOwnedApi<TContract>, O
|
|
1167
|
+
OperationTransferContextOf<ContractOwnedApi<TContract>, O>,
|
|
1168
|
+
OperationHandlerErrorOf<ContractOwnedApi<TContract>, O>
|
|
1182
1169
|
>
|
|
1183
1170
|
& {
|
|
1184
1171
|
client: Trellis<
|
|
@@ -1186,19 +1173,14 @@ export type OperationHandler<
|
|
|
1186
1173
|
ContractKvOf<TContract>,
|
|
1187
1174
|
ContractJobsOf<TContract>
|
|
1188
1175
|
>;
|
|
1189
|
-
}
|
|
1190
|
-
& WithDeps<TDeps>,
|
|
1176
|
+
},
|
|
1191
1177
|
) => unknown | Promise<unknown>;
|
|
1192
1178
|
|
|
1193
|
-
/** Typed health info function for an extracted
|
|
1194
|
-
export type HealthInfoHandler
|
|
1195
|
-
args: WithDeps<TDeps>,
|
|
1196
|
-
) => ServiceHealthInfo | undefined | Promise<ServiceHealthInfo | undefined>;
|
|
1179
|
+
/** Typed health info function for an extracted service health handler. */
|
|
1180
|
+
export type HealthInfoHandler = ServiceHealthInfoFn;
|
|
1197
1181
|
|
|
1198
|
-
/** Typed health check function for an extracted
|
|
1199
|
-
export type HealthCheckHandler
|
|
1200
|
-
args: WithDeps<TDeps>,
|
|
1201
|
-
) => ServiceHealthCheck | Promise<ServiceHealthCheck>;
|
|
1182
|
+
/** Typed health check function for an extracted service health handler. */
|
|
1183
|
+
export type HealthCheckHandler = ServiceHealthCheckFn;
|
|
1202
1184
|
|
|
1203
1185
|
export type JobQueue<
|
|
1204
1186
|
TPayload,
|
|
@@ -1208,6 +1190,9 @@ export type JobQueue<
|
|
|
1208
1190
|
TJobs extends ContractJobsMetadata = {},
|
|
1209
1191
|
> = {
|
|
1210
1192
|
create(payload: TPayload): AsyncResult<JobRef<TPayload, TResult>, BaseError>;
|
|
1193
|
+
submit(
|
|
1194
|
+
payload: TPayload,
|
|
1195
|
+
): AsyncResult<JobSubmitOutcome<TPayload, TResult>, BaseError>;
|
|
1211
1196
|
handle(
|
|
1212
1197
|
handler: (args: {
|
|
1213
1198
|
job: PublicActiveJob<TPayload, TResult>;
|
|
@@ -1239,28 +1224,42 @@ type ServiceEventOf<
|
|
|
1239
1224
|
type ServiceEventPayloadOf<
|
|
1240
1225
|
TA extends TrellisAPI,
|
|
1241
1226
|
E extends ServiceEventName<TA>,
|
|
1242
|
-
> =
|
|
1227
|
+
> = ServiceEventOf<TA, E> & Record<string, unknown>;
|
|
1228
|
+
|
|
1229
|
+
/** Runs SQL outbox work inside a caller-owned service database transaction. */
|
|
1230
|
+
export type SqlOutboxTransactionRunner<TTx> = <TResult>(
|
|
1231
|
+
work: (context: { tx: TTx; executor: SqlExecutor }) =>
|
|
1232
|
+
| Promise<TResult>
|
|
1233
|
+
| TResult,
|
|
1234
|
+
) => Promise<TResult>;
|
|
1235
|
+
|
|
1236
|
+
/** Options shared by all Trellis SQL outbox service bindings. */
|
|
1237
|
+
export type TrellisServiceSqlOutboxCommonOptions = {
|
|
1238
|
+
/** SQL dialect used by the Trellis helper tables. */
|
|
1239
|
+
readonly dialect: SqlDialect;
|
|
1240
|
+
/** Optional Trellis helper-table names; omitted names use Trellis defaults. */
|
|
1241
|
+
readonly tables?: Partial<SqlOutboxTables>;
|
|
1242
|
+
/** Optional process-local dispatcher tuning. */
|
|
1243
|
+
readonly dispatcher?: OutboxDispatcherOptions;
|
|
1244
|
+
};
|
|
1243
1245
|
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
context: EventListenerContext;
|
|
1254
|
-
client: Trellis<TTrellisApi, TKv, TJobs>;
|
|
1255
|
-
deps: TDeps;
|
|
1256
|
-
}) => MaybeAsync<void, BaseError>;
|
|
1246
|
+
/** Options for binding a Trellis service to generic caller-owned SQL storage. */
|
|
1247
|
+
export type TrellisServiceSqlOutboxExecutorOptions<TTx> =
|
|
1248
|
+
& TrellisServiceSqlOutboxCommonOptions
|
|
1249
|
+
& {
|
|
1250
|
+
/** Non-transactional executor used by the process-local dispatcher. */
|
|
1251
|
+
readonly executor: SqlExecutor;
|
|
1252
|
+
/** Service-owned transaction runner for handler-scoped work. */
|
|
1253
|
+
readonly transaction: SqlOutboxTransactionRunner<TTx>;
|
|
1254
|
+
};
|
|
1257
1255
|
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1256
|
+
/** Options for binding a Trellis service to caller-owned SQL outbox storage. */
|
|
1257
|
+
export type TrellisServiceSqlOutboxOptions<TTx> =
|
|
1258
|
+
TrellisServiceSqlOutboxExecutorOptions<TTx>;
|
|
1259
|
+
|
|
1260
|
+
/** Typed transaction-scoped event facade that enqueues prepared events. */
|
|
1261
|
+
export type SqlOutboxEventEnqueueFacade<
|
|
1262
|
+
TEventApi extends TrellisAPI = TrellisAPI,
|
|
1264
1263
|
> = {
|
|
1265
1264
|
readonly [TGroup in SurfaceGroupName<ServiceEventName<TEventApi>>]: {
|
|
1266
1265
|
readonly [
|
|
@@ -1269,237 +1268,64 @@ type BoundActiveEventFacade<
|
|
|
1269
1268
|
TGroup
|
|
1270
1269
|
> as SurfaceLeafName<E>
|
|
1271
1270
|
]: {
|
|
1272
|
-
|
|
1273
|
-
event: ServiceEventPayloadOf<TEventApi, E>,
|
|
1274
|
-
): Result<
|
|
1275
|
-
PreparedTrellisEvent<ServiceEventPayloadOf<TEventApi, E>>,
|
|
1276
|
-
ValidationError | UnexpectedError
|
|
1277
|
-
>;
|
|
1278
|
-
publish(
|
|
1271
|
+
enqueue(
|
|
1279
1272
|
event: ServiceEventPayloadOf<TEventApi, E>,
|
|
1280
|
-
): AsyncResult<
|
|
1281
|
-
listen(
|
|
1282
|
-
handler: BoundEventHandleFn<
|
|
1283
|
-
TEventApi,
|
|
1284
|
-
TTrellisApi,
|
|
1285
|
-
E,
|
|
1286
|
-
TKv,
|
|
1287
|
-
TJobs,
|
|
1288
|
-
TDeps
|
|
1289
|
-
>,
|
|
1290
|
-
subjectData?: Record<string, unknown>,
|
|
1291
|
-
opts?: EventOpts,
|
|
1292
|
-
): AsyncResult<void, ValidationError | UnexpectedError>;
|
|
1273
|
+
): AsyncResult<OutboxMessage, ValidationError | UnexpectedError>;
|
|
1293
1274
|
};
|
|
1294
1275
|
};
|
|
1295
1276
|
};
|
|
1296
1277
|
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
> = (args: {
|
|
1305
|
-
input: RpcMethodInput<TOwnedApi, M>;
|
|
1306
|
-
context: RpcHandlerContext;
|
|
1307
|
-
client: Trellis<TTrellisApi, TKv, TJobs>;
|
|
1308
|
-
deps: TDeps;
|
|
1309
|
-
}) =>
|
|
1310
|
-
| Promise<
|
|
1311
|
-
Result<RpcMethodOutput<TOwnedApi, M>, RpcHandlerErrorOf<TOwnedApi, M>>
|
|
1312
|
-
>
|
|
1313
|
-
| Result<RpcMethodOutput<TOwnedApi, M>, RpcHandlerErrorOf<TOwnedApi, M>>;
|
|
1314
|
-
|
|
1315
|
-
type BoundFeedHandleFn<
|
|
1316
|
-
TOwnedApi extends TrellisAPI,
|
|
1317
|
-
TTrellisApi extends TrellisAPI,
|
|
1318
|
-
F extends keyof TOwnedApi["feeds"] & string,
|
|
1319
|
-
TKv extends ContractKvMetadata,
|
|
1320
|
-
TJobs extends ContractJobsMetadata,
|
|
1321
|
-
TDeps,
|
|
1322
|
-
> = (context: {
|
|
1323
|
-
input: FeedInputOf<TOwnedApi, F>;
|
|
1324
|
-
caller: unknown;
|
|
1325
|
-
signal: AbortSignal;
|
|
1326
|
-
emit(
|
|
1327
|
-
event: FeedEventOf<TOwnedApi, F>,
|
|
1328
|
-
): AsyncResult<void, ValidationError | UnexpectedError>;
|
|
1329
|
-
client: Trellis<TTrellisApi, TKv, TJobs>;
|
|
1330
|
-
deps: TDeps;
|
|
1331
|
-
}) => unknown | Promise<unknown>;
|
|
1332
|
-
|
|
1333
|
-
type BoundOperationHandleFn<
|
|
1334
|
-
TOwnedApi extends TrellisAPI,
|
|
1335
|
-
TTrellisApi extends TrellisAPI,
|
|
1336
|
-
O extends keyof TOwnedApi["operations"] & string,
|
|
1337
|
-
TKv extends ContractKvMetadata,
|
|
1338
|
-
TJobs extends ContractJobsMetadata,
|
|
1339
|
-
TDeps,
|
|
1340
|
-
> =
|
|
1341
|
-
& ((
|
|
1342
|
-
handler: (
|
|
1343
|
-
context:
|
|
1344
|
-
& OperationHandlerContext<
|
|
1345
|
-
InferSchemaType<TOwnedApi["operations"][O]["input"]>,
|
|
1346
|
-
OperationProgressOf<TOwnedApi, O>,
|
|
1347
|
-
OperationOutputOf<TOwnedApi, O>,
|
|
1348
|
-
OperationTransferContextOf<TOwnedApi, O>
|
|
1349
|
-
>
|
|
1350
|
-
& {
|
|
1351
|
-
client: Trellis<TTrellisApi, TKv, TJobs>;
|
|
1352
|
-
deps: TDeps;
|
|
1353
|
-
},
|
|
1354
|
-
) => unknown | Promise<unknown>,
|
|
1355
|
-
) => Promise<void>)
|
|
1356
|
-
& Pick<
|
|
1357
|
-
OperationHandleFn<TOwnedApi, TTrellisApi, O, TKv, TJobs>,
|
|
1358
|
-
"accept" | "control"
|
|
1359
|
-
>;
|
|
1278
|
+
/** Returned by outbox.job.<queue>.create/submit inside a transaction. */
|
|
1279
|
+
export type SqlOutboxJobSubmission = {
|
|
1280
|
+
readonly submissionId: string;
|
|
1281
|
+
readonly jobId: string;
|
|
1282
|
+
readonly queue: string;
|
|
1283
|
+
readonly mode: "create" | "submit";
|
|
1284
|
+
};
|
|
1360
1285
|
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
readonly [
|
|
1371
|
-
M in SurfaceKeysForGroup<
|
|
1372
|
-
RpcMethodName<TOwnedApi>,
|
|
1373
|
-
TGroup
|
|
1374
|
-
> as SurfaceLeafName<M>
|
|
1375
|
-
]: (
|
|
1376
|
-
handler: BoundRpcHandleFn<TOwnedApi, TTrellisApi, M, TKv, TJobs, TDeps>,
|
|
1377
|
-
) => Promise<void>;
|
|
1378
|
-
};
|
|
1379
|
-
};
|
|
1380
|
-
readonly feed: {
|
|
1381
|
-
readonly [TGroup in SurfaceGroupName<keyof TOwnedApi["feeds"] & string>]: {
|
|
1382
|
-
readonly [
|
|
1383
|
-
F in SurfaceKeysForGroup<
|
|
1384
|
-
keyof TOwnedApi["feeds"] & string,
|
|
1385
|
-
TGroup
|
|
1386
|
-
> as SurfaceLeafName<F>
|
|
1387
|
-
]: (
|
|
1388
|
-
handler: BoundFeedHandleFn<
|
|
1389
|
-
TOwnedApi,
|
|
1390
|
-
TTrellisApi,
|
|
1391
|
-
F,
|
|
1392
|
-
TKv,
|
|
1393
|
-
TJobs,
|
|
1394
|
-
TDeps
|
|
1395
|
-
>,
|
|
1396
|
-
) => Promise<void>;
|
|
1397
|
-
};
|
|
1398
|
-
};
|
|
1399
|
-
readonly operation: {
|
|
1400
|
-
readonly [
|
|
1401
|
-
TGroup in SurfaceGroupName<keyof TOwnedApi["operations"] & string>
|
|
1402
|
-
]: {
|
|
1403
|
-
readonly [
|
|
1404
|
-
O in SurfaceKeysForGroup<
|
|
1405
|
-
keyof TOwnedApi["operations"] & string,
|
|
1406
|
-
TGroup
|
|
1407
|
-
> as SurfaceLeafName<O>
|
|
1408
|
-
]: BoundOperationHandleFn<TOwnedApi, TTrellisApi, O, TKv, TJobs, TDeps>;
|
|
1409
|
-
};
|
|
1286
|
+
/** Transaction-scoped facade for enqueuing job creation/submission intents. */
|
|
1287
|
+
export type SqlOutboxJobEnqueueFacade<TJobs extends ContractJobsMetadata> = {
|
|
1288
|
+
readonly [K in keyof TJobs]: {
|
|
1289
|
+
create(
|
|
1290
|
+
payload: TJobs[K]["payload"],
|
|
1291
|
+
): AsyncResult<SqlOutboxJobSubmission, ValidationError | UnexpectedError>;
|
|
1292
|
+
submit(
|
|
1293
|
+
payload: TJobs[K]["payload"],
|
|
1294
|
+
): AsyncResult<SqlOutboxJobSubmission, ValidationError | UnexpectedError>;
|
|
1410
1295
|
};
|
|
1411
1296
|
};
|
|
1412
1297
|
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
TJobs extends ContractJobsMetadata,
|
|
1419
|
-
TDeps,
|
|
1298
|
+
/** Context supplied to `outbox.transaction(...)` work callbacks. */
|
|
1299
|
+
export type SqlOutboxTransactionContext<
|
|
1300
|
+
TTx,
|
|
1301
|
+
TEventApi extends TrellisAPI = TrellisAPI,
|
|
1302
|
+
TJobs extends ContractJobsMetadata = ContractJobsMetadata,
|
|
1420
1303
|
> = {
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
}) => Promise<Result<TResult, BaseError>>,
|
|
1428
|
-
): void;
|
|
1304
|
+
/** Service-owned transaction object supplied by the configured runner. */
|
|
1305
|
+
readonly tx: TTx;
|
|
1306
|
+
/** Transaction-scoped typed event enqueue facade. */
|
|
1307
|
+
readonly event: SqlOutboxEventEnqueueFacade<TEventApi>;
|
|
1308
|
+
/** Transaction-scoped typed job enqueue facade. */
|
|
1309
|
+
readonly job: SqlOutboxJobEnqueueFacade<TJobs>;
|
|
1429
1310
|
};
|
|
1430
1311
|
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1312
|
+
/** Startup-created SQL outbox dependency for transactional event/job enqueue. */
|
|
1313
|
+
export type SqlOutbox<
|
|
1314
|
+
TTx,
|
|
1315
|
+
TEventApi extends TrellisAPI = TrellisAPI,
|
|
1316
|
+
TJobs extends ContractJobsMetadata = ContractJobsMetadata,
|
|
1436
1317
|
> = {
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
TJobs
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
type BoundServiceHealth<TDeps> = Omit<ServiceHealth, "add" | "setInfo"> & {
|
|
1448
|
-
setInfo(info: ServiceHealthInfo | HealthInfoHandler<TDeps>): void;
|
|
1449
|
-
add(
|
|
1450
|
-
name: string,
|
|
1451
|
-
check: HealthCheckHandler<TDeps>,
|
|
1452
|
-
): () => void;
|
|
1318
|
+
/**
|
|
1319
|
+
* Runs service DB work and typed event/job enqueue operations in one SQL
|
|
1320
|
+
* transaction, notifying the dispatcher once after a successful commit.
|
|
1321
|
+
*/
|
|
1322
|
+
transaction<TResult>(
|
|
1323
|
+
work: (context: SqlOutboxTransactionContext<TTx, TEventApi, TJobs>) =>
|
|
1324
|
+
| Promise<TResult>
|
|
1325
|
+
| TResult,
|
|
1326
|
+
): AsyncResult<TResult, ValidationError | UnexpectedError>;
|
|
1453
1327
|
};
|
|
1454
1328
|
|
|
1455
|
-
/** Service wrapper returned by `TrellisService.with(deps)`. */
|
|
1456
|
-
export type BoundTrellisService<
|
|
1457
|
-
TOwnedApi extends TrellisAPI = TrellisAPI,
|
|
1458
|
-
TTrellisApi extends TrellisAPI = TOwnedApi,
|
|
1459
|
-
TJobs extends ContractJobsMetadata = {},
|
|
1460
|
-
TKv extends ContractKvMetadata = ContractKvMetadata,
|
|
1461
|
-
TDeps = unknown,
|
|
1462
|
-
> =
|
|
1463
|
-
& Pick<
|
|
1464
|
-
TrellisService<TOwnedApi, TTrellisApi, TJobs, TKv>,
|
|
1465
|
-
| "name"
|
|
1466
|
-
| "auth"
|
|
1467
|
-
| "kv"
|
|
1468
|
-
| "store"
|
|
1469
|
-
| "connection"
|
|
1470
|
-
| "createTransfer"
|
|
1471
|
-
| "completeOperation"
|
|
1472
|
-
| "publishPrepared"
|
|
1473
|
-
| "wait"
|
|
1474
|
-
| "stop"
|
|
1475
|
-
>
|
|
1476
|
-
& {
|
|
1477
|
-
readonly event: BoundActiveEventFacade<
|
|
1478
|
-
TTrellisApi,
|
|
1479
|
-
TTrellisApi,
|
|
1480
|
-
TKv,
|
|
1481
|
-
TJobs,
|
|
1482
|
-
TDeps
|
|
1483
|
-
>;
|
|
1484
|
-
readonly health: BoundServiceHealth<TDeps>;
|
|
1485
|
-
readonly jobs: BoundJobsFacadeOf<TJobs, TTrellisApi, TKv, TDeps>;
|
|
1486
|
-
readonly handle: BoundTypedServiceHandleFacade<
|
|
1487
|
-
TOwnedApi,
|
|
1488
|
-
TTrellisApi,
|
|
1489
|
-
TKv,
|
|
1490
|
-
TJobs,
|
|
1491
|
-
TDeps
|
|
1492
|
-
>;
|
|
1493
|
-
/** Returns a new bound wrapper that injects the provided dependencies. */
|
|
1494
|
-
with<TNextDeps>(deps: TNextDeps): BoundTrellisService<
|
|
1495
|
-
TOwnedApi,
|
|
1496
|
-
TTrellisApi,
|
|
1497
|
-
TJobs,
|
|
1498
|
-
TKv,
|
|
1499
|
-
TNextDeps
|
|
1500
|
-
>;
|
|
1501
|
-
};
|
|
1502
|
-
|
|
1503
1329
|
const MANAGED_JOB_WORKERS = Symbol("trellis.managedJobWorkers");
|
|
1504
1330
|
|
|
1505
1331
|
type ManagedJobWorkers = {
|
|
@@ -1520,10 +1346,16 @@ type ServiceHandleOperationLeaf =
|
|
|
1520
1346
|
& {
|
|
1521
1347
|
accept(
|
|
1522
1348
|
args: { sessionKey: string },
|
|
1523
|
-
): AsyncResult<
|
|
1349
|
+
): AsyncResult<
|
|
1350
|
+
AcceptedOperation<unknown, unknown, BaseError>,
|
|
1351
|
+
UnexpectedError
|
|
1352
|
+
>;
|
|
1524
1353
|
control(
|
|
1525
1354
|
operationId: string,
|
|
1526
|
-
): AsyncResult<
|
|
1355
|
+
): AsyncResult<
|
|
1356
|
+
OperationRuntimeHandle<unknown, unknown, BaseError>,
|
|
1357
|
+
BaseError
|
|
1358
|
+
>;
|
|
1527
1359
|
};
|
|
1528
1360
|
|
|
1529
1361
|
type ServiceHandleFacade = {
|
|
@@ -1561,19 +1393,6 @@ type ServiceEventLeaf = ServiceEventPublishLeaf & {
|
|
|
1561
1393
|
): AsyncResult<void, ValidationError | UnexpectedError>;
|
|
1562
1394
|
};
|
|
1563
1395
|
|
|
1564
|
-
type BoundServiceEventLeaf<TDeps> = ServiceEventPublishLeaf & {
|
|
1565
|
-
listen(
|
|
1566
|
-
handler: (args: {
|
|
1567
|
-
event: unknown;
|
|
1568
|
-
context: EventListenerContext;
|
|
1569
|
-
client: unknown;
|
|
1570
|
-
deps: TDeps;
|
|
1571
|
-
}) => MaybeAsync<void, BaseError>,
|
|
1572
|
-
subjectData?: Record<string, unknown>,
|
|
1573
|
-
opts?: EventOpts,
|
|
1574
|
-
): AsyncResult<void, ValidationError | UnexpectedError>;
|
|
1575
|
-
};
|
|
1576
|
-
|
|
1577
1396
|
function createServiceEventPublishFacade<TA extends TrellisAPI>(outbound: {
|
|
1578
1397
|
readonly api: TA;
|
|
1579
1398
|
prepare(
|
|
@@ -1708,7 +1527,8 @@ type OperationHandleFn<
|
|
|
1708
1527
|
InferSchemaType<TOwnedApi["operations"][O]["input"]>,
|
|
1709
1528
|
OperationProgressOf<TOwnedApi, O>,
|
|
1710
1529
|
OperationOutputOf<TOwnedApi, O>,
|
|
1711
|
-
OperationTransferContextOf<TOwnedApi, O
|
|
1530
|
+
OperationTransferContextOf<TOwnedApi, O>,
|
|
1531
|
+
OperationHandlerErrorOf<TOwnedApi, O>
|
|
1712
1532
|
>
|
|
1713
1533
|
& { client: Trellis<TTrellisApi, TKv, TJobs> },
|
|
1714
1534
|
) => unknown | Promise<unknown>,
|
|
@@ -1717,14 +1537,16 @@ type OperationHandleFn<
|
|
|
1717
1537
|
accept(args: { sessionKey: string }): AsyncResult<
|
|
1718
1538
|
AcceptedOperation<
|
|
1719
1539
|
OperationProgressOf<TOwnedApi, O>,
|
|
1720
|
-
OperationOutputOf<TOwnedApi, O
|
|
1540
|
+
OperationOutputOf<TOwnedApi, O>,
|
|
1541
|
+
OperationHandlerErrorOf<TOwnedApi, O>
|
|
1721
1542
|
>,
|
|
1722
1543
|
UnexpectedError
|
|
1723
1544
|
>;
|
|
1724
1545
|
control(operationId: string): AsyncResult<
|
|
1725
1546
|
OperationRuntimeHandle<
|
|
1726
1547
|
OperationProgressOf<TOwnedApi, O>,
|
|
1727
|
-
OperationOutputOf<TOwnedApi, O
|
|
1548
|
+
OperationOutputOf<TOwnedApi, O>,
|
|
1549
|
+
OperationHandlerErrorOf<TOwnedApi, O>
|
|
1728
1550
|
>,
|
|
1729
1551
|
BaseError
|
|
1730
1552
|
>;
|
|
@@ -1742,7 +1564,8 @@ export type OperationRegistration<
|
|
|
1742
1564
|
}): AsyncResult<
|
|
1743
1565
|
AcceptedOperation<
|
|
1744
1566
|
OperationProgressOf<TOwnedApi, O>,
|
|
1745
|
-
OperationOutputOf<TOwnedApi, O
|
|
1567
|
+
OperationOutputOf<TOwnedApi, O>,
|
|
1568
|
+
OperationHandlerErrorOf<TOwnedApi, O>
|
|
1746
1569
|
>,
|
|
1747
1570
|
UnexpectedError
|
|
1748
1571
|
>;
|
|
@@ -1755,7 +1578,8 @@ export type OperationRegistration<
|
|
|
1755
1578
|
): AsyncResult<
|
|
1756
1579
|
OperationRuntimeHandle<
|
|
1757
1580
|
OperationProgressOf<TOwnedApi, O>,
|
|
1758
|
-
OperationOutputOf<TOwnedApi, O
|
|
1581
|
+
OperationOutputOf<TOwnedApi, O>,
|
|
1582
|
+
OperationHandlerErrorOf<TOwnedApi, O>
|
|
1759
1583
|
>,
|
|
1760
1584
|
BaseError
|
|
1761
1585
|
>;
|
|
@@ -1766,7 +1590,8 @@ export type OperationRegistration<
|
|
|
1766
1590
|
InferSchemaType<TOwnedApi["operations"][O]["input"]>,
|
|
1767
1591
|
OperationProgressOf<TOwnedApi, O>,
|
|
1768
1592
|
OperationOutputOf<TOwnedApi, O>,
|
|
1769
|
-
OperationTransferContextOf<TOwnedApi, O
|
|
1593
|
+
OperationTransferContextOf<TOwnedApi, O>,
|
|
1594
|
+
OperationHandlerErrorOf<TOwnedApi, O>
|
|
1770
1595
|
>
|
|
1771
1596
|
& { client: Trellis<TTrellisApi, TKv, TJobs> },
|
|
1772
1597
|
) => unknown | Promise<unknown>,
|
|
@@ -1827,6 +1652,9 @@ export async function createConnectedService<
|
|
|
1827
1652
|
contractEventConsumers?: ContractEventConsumers;
|
|
1828
1653
|
server: TrellisServiceRuntimeCreateOpts<TOwnedApi, TTrellisApi>;
|
|
1829
1654
|
bindings: ResourceBindings;
|
|
1655
|
+
durableEventConsumerBeforeReadinessCheck?: TrellisServiceRuntimeDeps[
|
|
1656
|
+
"durableEventConsumerBeforeReadinessCheck"
|
|
1657
|
+
];
|
|
1830
1658
|
}): Promise<TrellisService<TOwnedApi, TTrellisApi, TJobs, TKv>> {
|
|
1831
1659
|
const resolvedLog = resolveServiceLogger(args.server.log);
|
|
1832
1660
|
const connection = observeNatsTrellisConnection({
|
|
@@ -1886,6 +1714,8 @@ export async function createConnectedService<
|
|
|
1886
1714
|
metadata: args.contractEventConsumers,
|
|
1887
1715
|
bindings: args.bindings.eventConsumers,
|
|
1888
1716
|
},
|
|
1717
|
+
durableEventConsumerBeforeReadinessCheck:
|
|
1718
|
+
args.durableEventConsumerBeforeReadinessCheck,
|
|
1889
1719
|
connection,
|
|
1890
1720
|
},
|
|
1891
1721
|
);
|
|
@@ -1922,6 +1752,7 @@ export async function createConnectedService<
|
|
|
1922
1752
|
prepare: (event, data) => outbound.prepare(event, data),
|
|
1923
1753
|
publish: (event, data) => outbound.publish(event, data),
|
|
1924
1754
|
publishPrepared: (event) => outbound.publishPrepared(event),
|
|
1755
|
+
stopEventListeners: () => outbound.stopEventListeners(),
|
|
1925
1756
|
get kv() {
|
|
1926
1757
|
return getHandlerResources().kv;
|
|
1927
1758
|
},
|
|
@@ -2054,6 +1885,190 @@ function toUnexpectedError(cause: unknown): UnexpectedError {
|
|
|
2054
1885
|
: new UnexpectedError({ cause });
|
|
2055
1886
|
}
|
|
2056
1887
|
|
|
1888
|
+
function resolveSqlOutboxTables(
|
|
1889
|
+
tables: Partial<SqlOutboxTables> | undefined,
|
|
1890
|
+
): SqlOutboxTables {
|
|
1891
|
+
return {
|
|
1892
|
+
outbox: tables?.outbox ?? defaultSqlOutboxTables.outbox,
|
|
1893
|
+
inbox: tables?.inbox ?? defaultSqlOutboxTables.inbox,
|
|
1894
|
+
};
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
function createSqlOutboxBaseExecutor<TTx>(
|
|
1898
|
+
options: TrellisServiceSqlOutboxOptions<TTx>,
|
|
1899
|
+
): SqlExecutor {
|
|
1900
|
+
return options.executor;
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
function createSqlOutboxTransactionRunner<TTx>(
|
|
1904
|
+
options: TrellisServiceSqlOutboxOptions<TTx>,
|
|
1905
|
+
): SqlOutboxTransactionRunner<TTx> {
|
|
1906
|
+
return options.transaction;
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
type SqlOutboxEventEnqueueLeaf = {
|
|
1910
|
+
enqueue(
|
|
1911
|
+
event: Record<string, unknown>,
|
|
1912
|
+
): AsyncResult<OutboxMessage, ValidationError | UnexpectedError>;
|
|
1913
|
+
};
|
|
1914
|
+
|
|
1915
|
+
function createSqlOutboxEventEnqueueFacade<TEventApi extends TrellisAPI>(args: {
|
|
1916
|
+
event: ActiveEventFacade<TEventApi>;
|
|
1917
|
+
repository: SqlOutboxRepository;
|
|
1918
|
+
onEnqueued(): void;
|
|
1919
|
+
}): SqlOutboxEventEnqueueFacade<TEventApi> {
|
|
1920
|
+
const facade: Record<string, Record<string, SqlOutboxEventEnqueueLeaf>> = {};
|
|
1921
|
+
const source = args.event as Record<
|
|
1922
|
+
string,
|
|
1923
|
+
Record<string, ServiceEventLeaf>
|
|
1924
|
+
>;
|
|
1925
|
+
|
|
1926
|
+
for (const [groupName, leaves] of Object.entries(source)) {
|
|
1927
|
+
const group: Record<string, SqlOutboxEventEnqueueLeaf> = {};
|
|
1928
|
+
for (const [leafName, leaf] of Object.entries(leaves)) {
|
|
1929
|
+
group[leafName] = {
|
|
1930
|
+
enqueue: (payload) =>
|
|
1931
|
+
AsyncResult.from((async () => {
|
|
1932
|
+
const prepared = leaf.prepare(payload).take();
|
|
1933
|
+
if (isErr(prepared)) return Result.err(prepared.error);
|
|
1934
|
+
const record = preparedTrellisEventToOutboxRecord(prepared);
|
|
1935
|
+
try {
|
|
1936
|
+
const message = await args.repository.enqueue(record);
|
|
1937
|
+
args.onEnqueued();
|
|
1938
|
+
return Result.ok(message);
|
|
1939
|
+
} catch (cause) {
|
|
1940
|
+
return Result.err(toUnexpectedError(cause));
|
|
1941
|
+
}
|
|
1942
|
+
})()),
|
|
1943
|
+
};
|
|
1944
|
+
}
|
|
1945
|
+
Object.defineProperty(facade, groupName, {
|
|
1946
|
+
value: group,
|
|
1947
|
+
enumerable: true,
|
|
1948
|
+
configurable: true,
|
|
1949
|
+
});
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
return facade as SqlOutboxEventEnqueueFacade<TEventApi>;
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
function createSqlOutboxJobEnqueueFacade<TJobs extends ContractJobsMetadata>(
|
|
1956
|
+
args: {
|
|
1957
|
+
serviceName: string;
|
|
1958
|
+
contractJobs: TJobs;
|
|
1959
|
+
jobsBinding?: ResourceBindingJobs;
|
|
1960
|
+
repository: SqlOutboxRepository;
|
|
1961
|
+
onEnqueued(): void;
|
|
1962
|
+
},
|
|
1963
|
+
): SqlOutboxJobEnqueueFacade<TJobs> {
|
|
1964
|
+
const facade: Record<string, Record<string, unknown>> = {};
|
|
1965
|
+
|
|
1966
|
+
for (
|
|
1967
|
+
const queueType of Object.keys(args.contractJobs as Record<string, unknown>)
|
|
1968
|
+
) {
|
|
1969
|
+
const queueBinding = args.jobsBinding?.queues[queueType];
|
|
1970
|
+
const queue = queueType;
|
|
1971
|
+
facade[queue] = {
|
|
1972
|
+
create: (payload: unknown) => {
|
|
1973
|
+
if (!queueBinding) {
|
|
1974
|
+
return AsyncResult.err(
|
|
1975
|
+
new ValidationError({
|
|
1976
|
+
errors: [{
|
|
1977
|
+
path: "/",
|
|
1978
|
+
message: `Jobs binding unavailable for queue '${queue}'`,
|
|
1979
|
+
}],
|
|
1980
|
+
}),
|
|
1981
|
+
);
|
|
1982
|
+
}
|
|
1983
|
+
const submissionId = ulid();
|
|
1984
|
+
const jobId = ulid();
|
|
1985
|
+
const context = createJobContext();
|
|
1986
|
+
|
|
1987
|
+
const record: PreparedOutboxRecord = {
|
|
1988
|
+
id: submissionId,
|
|
1989
|
+
kind: "job.create",
|
|
1990
|
+
name: queue,
|
|
1991
|
+
subject: `${queueBinding.publishPrefix}.${jobId}.created`,
|
|
1992
|
+
payload: JSON.stringify({
|
|
1993
|
+
...(payload as Record<string, unknown>),
|
|
1994
|
+
jobId,
|
|
1995
|
+
submissionId,
|
|
1996
|
+
}),
|
|
1997
|
+
headers: {
|
|
1998
|
+
"request-id": context.requestId,
|
|
1999
|
+
"traceparent": context.traceparent,
|
|
2000
|
+
...(context.tracestate ? { "tracestate": context.tracestate } : {}),
|
|
2001
|
+
},
|
|
2002
|
+
};
|
|
2003
|
+
|
|
2004
|
+
return AsyncResult.from((async () => {
|
|
2005
|
+
try {
|
|
2006
|
+
await args.repository.enqueue(record);
|
|
2007
|
+
args.onEnqueued();
|
|
2008
|
+
return Result.ok({
|
|
2009
|
+
submissionId,
|
|
2010
|
+
jobId,
|
|
2011
|
+
queue,
|
|
2012
|
+
mode: "create",
|
|
2013
|
+
});
|
|
2014
|
+
} catch (cause) {
|
|
2015
|
+
return Result.err(toUnexpectedError(cause));
|
|
2016
|
+
}
|
|
2017
|
+
})());
|
|
2018
|
+
},
|
|
2019
|
+
submit: (payload: unknown) => {
|
|
2020
|
+
if (!queueBinding) {
|
|
2021
|
+
return AsyncResult.err(
|
|
2022
|
+
new ValidationError({
|
|
2023
|
+
errors: [{
|
|
2024
|
+
path: "/",
|
|
2025
|
+
message: `Jobs binding unavailable for queue '${queue}'`,
|
|
2026
|
+
}],
|
|
2027
|
+
}),
|
|
2028
|
+
);
|
|
2029
|
+
}
|
|
2030
|
+
const submissionId = ulid();
|
|
2031
|
+
const jobId = ulid();
|
|
2032
|
+
const context = createJobContext();
|
|
2033
|
+
|
|
2034
|
+
const record: PreparedOutboxRecord = {
|
|
2035
|
+
id: submissionId,
|
|
2036
|
+
kind: "job.submit",
|
|
2037
|
+
name: queue,
|
|
2038
|
+
subject: `${queueBinding.publishPrefix}.${jobId}.created`,
|
|
2039
|
+
payload: JSON.stringify({
|
|
2040
|
+
...(payload as Record<string, unknown>),
|
|
2041
|
+
jobId,
|
|
2042
|
+
submissionId,
|
|
2043
|
+
}),
|
|
2044
|
+
headers: {
|
|
2045
|
+
"request-id": context.requestId,
|
|
2046
|
+
"traceparent": context.traceparent,
|
|
2047
|
+
...(context.tracestate ? { "tracestate": context.tracestate } : {}),
|
|
2048
|
+
},
|
|
2049
|
+
};
|
|
2050
|
+
|
|
2051
|
+
return AsyncResult.from((async () => {
|
|
2052
|
+
try {
|
|
2053
|
+
await args.repository.enqueue(record);
|
|
2054
|
+
args.onEnqueued();
|
|
2055
|
+
return Result.ok({
|
|
2056
|
+
submissionId,
|
|
2057
|
+
jobId,
|
|
2058
|
+
queue,
|
|
2059
|
+
mode: "submit",
|
|
2060
|
+
});
|
|
2061
|
+
} catch (cause) {
|
|
2062
|
+
return Result.err(toUnexpectedError(cause));
|
|
2063
|
+
}
|
|
2064
|
+
})());
|
|
2065
|
+
},
|
|
2066
|
+
};
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
return facade as SqlOutboxJobEnqueueFacade<TJobs>;
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2057
2072
|
function serializeJobHandlerError(error: BaseError): string {
|
|
2058
2073
|
try {
|
|
2059
2074
|
return JSON.stringify(error.toSerializable());
|
|
@@ -2081,7 +2096,8 @@ function isTerminalJobState(
|
|
|
2081
2096
|
state: string,
|
|
2082
2097
|
): state is TerminalJob<unknown, unknown>["state"] {
|
|
2083
2098
|
return state === "completed" || state === "failed" || state === "cancelled" ||
|
|
2084
|
-
state === "expired" || state === "
|
|
2099
|
+
state === "expired" || state === "skipped" || state === "stale" ||
|
|
2100
|
+
state === "dead" || state === "dismissed";
|
|
2085
2101
|
}
|
|
2086
2102
|
|
|
2087
2103
|
function isTerminalJobSnapshot<TPayload, TResult>(
|
|
@@ -2202,6 +2218,9 @@ function snapshotFromLifecycleEvent<TPayload, TResult>(
|
|
|
2202
2218
|
case "failed":
|
|
2203
2219
|
case "cancelled":
|
|
2204
2220
|
case "expired":
|
|
2221
|
+
case "skipped":
|
|
2222
|
+
case "stale":
|
|
2223
|
+
case "staleCompletionIgnored":
|
|
2205
2224
|
case "dead":
|
|
2206
2225
|
case "dismissed":
|
|
2207
2226
|
return event.error === undefined ? base : {
|
|
@@ -2359,8 +2378,8 @@ function createJobLifecycleTracker(nc: NatsConnection): JobLifecycleTracker {
|
|
|
2359
2378
|
function createJobRef<TPayload, TResult>(args: {
|
|
2360
2379
|
nc: NatsConnection;
|
|
2361
2380
|
queueType: string;
|
|
2362
|
-
jobsBinding:
|
|
2363
|
-
queueBinding:
|
|
2381
|
+
jobsBinding: JobsBinding;
|
|
2382
|
+
queueBinding: JobsQueueBinding;
|
|
2364
2383
|
seed: JobSnapshot<TPayload, TResult>;
|
|
2365
2384
|
lifecycle: JobLifecycleTracker;
|
|
2366
2385
|
}): JobRef<TPayload, TResult> {
|
|
@@ -2467,6 +2486,10 @@ function createJobsFacade<
|
|
|
2467
2486
|
const handlers = new Map<string, RegisteredJobHandler<unknown, unknown>>();
|
|
2468
2487
|
const jobsFacade: Record<string, unknown> = {};
|
|
2469
2488
|
const lifecycle = createJobLifecycleTracker(args.nc);
|
|
2489
|
+
const keyCoordinator = createNatsJobKeyCoordinator(args.nc);
|
|
2490
|
+
const jobsBinding = args.jobsBinding
|
|
2491
|
+
? normalizeResourceJobsBinding(args.jobsBinding)
|
|
2492
|
+
: undefined;
|
|
2470
2493
|
let activeHost: JobWorkerHostAdapter | undefined;
|
|
2471
2494
|
let startupPromise:
|
|
2472
2495
|
| Promise<Result<JobWorkerHostAdapter, BaseError>>
|
|
@@ -2474,14 +2497,13 @@ function createJobsFacade<
|
|
|
2474
2497
|
let stopPromise: Promise<Result<void, BaseError>> | undefined;
|
|
2475
2498
|
|
|
2476
2499
|
for (const queueType of Object.keys(args.contractJobs ?? {})) {
|
|
2477
|
-
const queueBinding =
|
|
2500
|
+
const queueBinding = jobsBinding?.queues[queueType];
|
|
2478
2501
|
if (queueBinding) lifecycle.watch(queueBinding);
|
|
2479
2502
|
|
|
2480
2503
|
jobsFacade[queueType] = {
|
|
2481
2504
|
create: (payload) =>
|
|
2482
2505
|
AsyncResult.from((async () => {
|
|
2483
2506
|
try {
|
|
2484
|
-
const jobsBinding = args.jobsBinding;
|
|
2485
2507
|
if (!jobsBinding) {
|
|
2486
2508
|
return Result.err(
|
|
2487
2509
|
toUnexpectedError(new Error("Jobs bindings are unavailable")),
|
|
@@ -2499,6 +2521,7 @@ function createJobsFacade<
|
|
|
2499
2521
|
const manager = new InternalJobManager<unknown, unknown>({
|
|
2500
2522
|
nc: args.nc,
|
|
2501
2523
|
jobs: jobsBinding,
|
|
2524
|
+
keyCoordinator,
|
|
2502
2525
|
});
|
|
2503
2526
|
await args.nc.flush();
|
|
2504
2527
|
const created = await manager.create(queueType, payload);
|
|
@@ -2510,6 +2533,67 @@ function createJobsFacade<
|
|
|
2510
2533
|
seed: created as JobSnapshot<unknown, unknown>,
|
|
2511
2534
|
lifecycle,
|
|
2512
2535
|
}));
|
|
2536
|
+
} catch (cause) {
|
|
2537
|
+
if (cause instanceof JobNotEnqueuedError) {
|
|
2538
|
+
return Result.err(cause);
|
|
2539
|
+
}
|
|
2540
|
+
return Result.err(toUnexpectedError(cause));
|
|
2541
|
+
}
|
|
2542
|
+
})()),
|
|
2543
|
+
submit: (payload) =>
|
|
2544
|
+
AsyncResult.from((async () => {
|
|
2545
|
+
try {
|
|
2546
|
+
if (!jobsBinding) {
|
|
2547
|
+
return Result.err(
|
|
2548
|
+
toUnexpectedError(new Error("Jobs bindings are unavailable")),
|
|
2549
|
+
);
|
|
2550
|
+
}
|
|
2551
|
+
const queueBinding = jobsBinding.queues[queueType];
|
|
2552
|
+
if (!queueBinding) {
|
|
2553
|
+
return Result.err(toUnexpectedError(
|
|
2554
|
+
new Error(
|
|
2555
|
+
`Jobs binding for queue '${queueType}' is unavailable`,
|
|
2556
|
+
),
|
|
2557
|
+
));
|
|
2558
|
+
}
|
|
2559
|
+
|
|
2560
|
+
const manager = new InternalJobManager<unknown, unknown>({
|
|
2561
|
+
nc: args.nc,
|
|
2562
|
+
jobs: jobsBinding,
|
|
2563
|
+
keyCoordinator,
|
|
2564
|
+
});
|
|
2565
|
+
await args.nc.flush();
|
|
2566
|
+
const outcome = await manager.submit(queueType, payload);
|
|
2567
|
+
if (outcome.kind === "accepted") {
|
|
2568
|
+
return Result.ok({
|
|
2569
|
+
kind: "accepted",
|
|
2570
|
+
key: outcome.key,
|
|
2571
|
+
ref: createJobRef({
|
|
2572
|
+
nc: args.nc,
|
|
2573
|
+
queueType,
|
|
2574
|
+
jobsBinding,
|
|
2575
|
+
queueBinding,
|
|
2576
|
+
seed: outcome.job as JobSnapshot<unknown, unknown>,
|
|
2577
|
+
lifecycle,
|
|
2578
|
+
}),
|
|
2579
|
+
});
|
|
2580
|
+
}
|
|
2581
|
+
if (outcome.kind === "replaced") {
|
|
2582
|
+
return Result.ok({
|
|
2583
|
+
kind: "replaced",
|
|
2584
|
+
key: outcome.key,
|
|
2585
|
+
replaced: outcome.replaced,
|
|
2586
|
+
ref: createJobRef({
|
|
2587
|
+
nc: args.nc,
|
|
2588
|
+
queueType,
|
|
2589
|
+
jobsBinding,
|
|
2590
|
+
queueBinding,
|
|
2591
|
+
seed: outcome.job as JobSnapshot<unknown, unknown>,
|
|
2592
|
+
lifecycle,
|
|
2593
|
+
}),
|
|
2594
|
+
});
|
|
2595
|
+
}
|
|
2596
|
+
return Result.ok(outcome);
|
|
2513
2597
|
} catch (cause) {
|
|
2514
2598
|
return Result.err(toUnexpectedError(cause));
|
|
2515
2599
|
}
|
|
@@ -2554,7 +2638,7 @@ function createJobsFacade<
|
|
|
2554
2638
|
return Result.ok(host);
|
|
2555
2639
|
}
|
|
2556
2640
|
|
|
2557
|
-
if (!
|
|
2641
|
+
if (!jobsBinding || !args.workStream) {
|
|
2558
2642
|
return Result.err(toUnexpectedError(
|
|
2559
2643
|
new Error(
|
|
2560
2644
|
"Jobs infrastructure bindings are unavailable for this service",
|
|
@@ -2562,7 +2646,6 @@ function createJobsFacade<
|
|
|
2562
2646
|
));
|
|
2563
2647
|
}
|
|
2564
2648
|
|
|
2565
|
-
const jobsBinding = args.jobsBinding;
|
|
2566
2649
|
const workStream = args.workStream;
|
|
2567
2650
|
|
|
2568
2651
|
const hosts = [] as Array<{ stop(): Promise<void> }>;
|
|
@@ -2582,6 +2665,7 @@ function createJobsFacade<
|
|
|
2582
2665
|
const manager = new InternalJobManager<unknown, unknown>({
|
|
2583
2666
|
nc: args.nc,
|
|
2584
2667
|
jobs: jobsBinding,
|
|
2668
|
+
keyCoordinator,
|
|
2585
2669
|
});
|
|
2586
2670
|
const host = await startNatsWorkerHostFromBinding<unknown>({
|
|
2587
2671
|
jobs: jobsBinding,
|
|
@@ -2591,6 +2675,7 @@ function createJobsFacade<
|
|
|
2591
2675
|
instanceId: `${args.serviceName}-worker`,
|
|
2592
2676
|
queueTypes: [queueType],
|
|
2593
2677
|
manager,
|
|
2678
|
+
heartbeatPublisher: args.nc,
|
|
2594
2679
|
getProjectedJob: async (job) => {
|
|
2595
2680
|
return lifecycle.get({
|
|
2596
2681
|
service: job.service,
|
|
@@ -2730,47 +2815,6 @@ function createJobsFacade<
|
|
|
2730
2815
|
return jobsFacade as ManagedJobsFacade<TJobs, TTrellisApi, TKv>;
|
|
2731
2816
|
}
|
|
2732
2817
|
|
|
2733
|
-
function createBoundJobsFacade<
|
|
2734
|
-
TJobs extends ContractJobsMetadata,
|
|
2735
|
-
TTrellisApi extends TrellisAPI,
|
|
2736
|
-
TKv extends ContractKvMetadata,
|
|
2737
|
-
TDeps,
|
|
2738
|
-
>(args: {
|
|
2739
|
-
jobs: JobsFacadeOf<TJobs, TTrellisApi, TKv>;
|
|
2740
|
-
deps: TDeps;
|
|
2741
|
-
}): BoundJobsFacadeOf<TJobs, TTrellisApi, TKv, TDeps> {
|
|
2742
|
-
const boundJobs: Record<string, unknown> = {};
|
|
2743
|
-
const jobs = args.jobs as Record<
|
|
2744
|
-
string,
|
|
2745
|
-
JobQueue<unknown, unknown, TTrellisApi, TKv, TJobs>
|
|
2746
|
-
>;
|
|
2747
|
-
|
|
2748
|
-
for (const queueType of Object.keys(jobs)) {
|
|
2749
|
-
const queue = jobs[queueType];
|
|
2750
|
-
if (!queue) continue;
|
|
2751
|
-
boundJobs[queueType] = {
|
|
2752
|
-
create: (payload) => queue.create(payload),
|
|
2753
|
-
handle: (handler) =>
|
|
2754
|
-
queue.handle(({ job, client }) =>
|
|
2755
|
-
handler({
|
|
2756
|
-
job,
|
|
2757
|
-
client,
|
|
2758
|
-
deps: args.deps,
|
|
2759
|
-
})
|
|
2760
|
-
),
|
|
2761
|
-
} satisfies BoundJobQueue<
|
|
2762
|
-
unknown,
|
|
2763
|
-
unknown,
|
|
2764
|
-
TTrellisApi,
|
|
2765
|
-
TKv,
|
|
2766
|
-
TJobs,
|
|
2767
|
-
TDeps
|
|
2768
|
-
>;
|
|
2769
|
-
}
|
|
2770
|
-
|
|
2771
|
-
return boundJobs as BoundJobsFacadeOf<TJobs, TTrellisApi, TKv, TDeps>;
|
|
2772
|
-
}
|
|
2773
|
-
|
|
2774
2818
|
/**
|
|
2775
2819
|
* Connects a service with caller-supplied runtime dependencies for tests and
|
|
2776
2820
|
* Trellis-owned internals. This helper is intentionally not re-exported from
|
|
@@ -2798,6 +2842,7 @@ export function connectTrellisServiceWithRuntimeDeps<
|
|
|
2798
2842
|
TransportError | UnexpectedError
|
|
2799
2843
|
> {
|
|
2800
2844
|
return AsyncResult.from((async () => {
|
|
2845
|
+
const totalStartedAt = performance.now();
|
|
2801
2846
|
try {
|
|
2802
2847
|
type TOwnedApi = ContractOwnedApi<TContract>;
|
|
2803
2848
|
type TTrellisApi = ContractTrellisApi<TContract>;
|
|
@@ -2811,6 +2856,7 @@ export function connectTrellisServiceWithRuntimeDeps<
|
|
|
2811
2856
|
}
|
|
2812
2857
|
const auth = await createAuth({ sessionKeySeed: args.sessionKeySeed });
|
|
2813
2858
|
const bootstrapLog = resolveServiceLogger(args.server?.log);
|
|
2859
|
+
const bootstrapStartedAt = performance.now();
|
|
2814
2860
|
const bootstrap = await fetchServiceBootstrapInfo({
|
|
2815
2861
|
trellisUrl: args.trellisUrl,
|
|
2816
2862
|
serviceName: args.name,
|
|
@@ -2820,6 +2866,15 @@ export function connectTrellisServiceWithRuntimeDeps<
|
|
|
2820
2866
|
auth,
|
|
2821
2867
|
log: bootstrapLog,
|
|
2822
2868
|
});
|
|
2869
|
+
recordTrellisDuration(
|
|
2870
|
+
"trellis.connect.duration",
|
|
2871
|
+
performance.now() - bootstrapStartedAt,
|
|
2872
|
+
{
|
|
2873
|
+
phase: "bootstrap",
|
|
2874
|
+
participantKind: "service",
|
|
2875
|
+
outcome: "ok",
|
|
2876
|
+
},
|
|
2877
|
+
);
|
|
2823
2878
|
const { authenticator: authTokenAuthenticator, inboxPrefix } = await auth
|
|
2824
2879
|
.natsConnectOptions({
|
|
2825
2880
|
contractDigest: args.contract.CONTRACT_DIGEST,
|
|
@@ -2827,6 +2882,7 @@ export function connectTrellisServiceWithRuntimeDeps<
|
|
|
2827
2882
|
|
|
2828
2883
|
let nc: NatsConnection;
|
|
2829
2884
|
try {
|
|
2885
|
+
const natsStartedAt = performance.now();
|
|
2830
2886
|
nc = await runtimeDeps.connect({
|
|
2831
2887
|
servers: selectRuntimeTransportServers(
|
|
2832
2888
|
bootstrap.connectInfo.transports,
|
|
@@ -2844,6 +2900,15 @@ export function connectTrellisServiceWithRuntimeDeps<
|
|
|
2844
2900
|
),
|
|
2845
2901
|
],
|
|
2846
2902
|
});
|
|
2903
|
+
recordTrellisDuration(
|
|
2904
|
+
"trellis.connect.duration",
|
|
2905
|
+
performance.now() - natsStartedAt,
|
|
2906
|
+
{
|
|
2907
|
+
phase: "nats_connect",
|
|
2908
|
+
participantKind: "service",
|
|
2909
|
+
outcome: "ok",
|
|
2910
|
+
},
|
|
2911
|
+
);
|
|
2847
2912
|
} catch (cause) {
|
|
2848
2913
|
throw new TransportError({
|
|
2849
2914
|
code: "trellis.runtime.connect_failed",
|
|
@@ -2871,31 +2936,41 @@ export function connectTrellisServiceWithRuntimeDeps<
|
|
|
2871
2936
|
api: args.contract.API.owned,
|
|
2872
2937
|
};
|
|
2873
2938
|
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2939
|
+
const service = await createConnectedService<
|
|
2940
|
+
TOwnedApi,
|
|
2941
|
+
TTrellisApi,
|
|
2942
|
+
ContractJobsOf<TContract>,
|
|
2943
|
+
ContractKvOf<TContract>
|
|
2944
|
+
>({
|
|
2945
|
+
name: args.name,
|
|
2946
|
+
auth,
|
|
2947
|
+
nc,
|
|
2948
|
+
contractId: args.contract.CONTRACT_ID,
|
|
2949
|
+
contractDigest: args.contract.CONTRACT_DIGEST,
|
|
2950
|
+
contractJobs:
|
|
2951
|
+
(args.contract[CONTRACT_JOBS_METADATA] ?? {}) as ContractJobsOf<
|
|
2952
|
+
TContract
|
|
2953
|
+
>,
|
|
2954
|
+
contractKv:
|
|
2955
|
+
(args.contract[CONTRACT_KV_METADATA] ?? {}) as ContractKvOf<
|
|
2956
|
+
TContract
|
|
2957
|
+
>,
|
|
2958
|
+
contractEventConsumers: args.contract.CONTRACT.eventConsumers,
|
|
2959
|
+
server,
|
|
2960
|
+
bindings: bootstrap.binding.resources,
|
|
2961
|
+
durableEventConsumerBeforeReadinessCheck:
|
|
2962
|
+
runtimeDeps.durableEventConsumerBeforeReadinessCheck,
|
|
2963
|
+
});
|
|
2964
|
+
recordTrellisDuration(
|
|
2965
|
+
"trellis.connect.duration",
|
|
2966
|
+
performance.now() - totalStartedAt,
|
|
2967
|
+
{
|
|
2968
|
+
phase: "total",
|
|
2969
|
+
participantKind: "service",
|
|
2970
|
+
outcome: "ok",
|
|
2971
|
+
},
|
|
2898
2972
|
);
|
|
2973
|
+
return Result.ok(service);
|
|
2899
2974
|
} catch (cause) {
|
|
2900
2975
|
await closeFailedServiceBootstrapConnection(nc);
|
|
2901
2976
|
throw cause;
|
|
@@ -2931,6 +3006,9 @@ export class TrellisService<
|
|
|
2931
3006
|
readonly #operationTransfer: ServiceTransfer;
|
|
2932
3007
|
readonly #stopHealthPublishing: () => Promise<void>;
|
|
2933
3008
|
readonly #managedJobWorkers: ManagedJobWorkers;
|
|
3009
|
+
readonly #contractJobs: TJobs;
|
|
3010
|
+
readonly #jobsBinding?: ResourceBindingJobs;
|
|
3011
|
+
readonly #ownedOutboxDispatchers = new Set<OutboxDispatcher>();
|
|
2934
3012
|
#waitPromise?: Promise<void>;
|
|
2935
3013
|
#stopPromise?: Promise<void>;
|
|
2936
3014
|
|
|
@@ -2987,6 +3065,8 @@ export class TrellisService<
|
|
|
2987
3065
|
});
|
|
2988
3066
|
this.jobs = jobs;
|
|
2989
3067
|
this.#managedJobWorkers = jobs[MANAGED_JOB_WORKERS];
|
|
3068
|
+
this.#contractJobs = contractJobs;
|
|
3069
|
+
this.#jobsBinding = bindings.jobs;
|
|
2990
3070
|
this.health = health;
|
|
2991
3071
|
this.handle = this.#createHandleFacade() as TypedServiceHandleFacade<
|
|
2992
3072
|
TOwnedApi,
|
|
@@ -2999,30 +3079,14 @@ export class TrellisService<
|
|
|
2999
3079
|
}
|
|
3000
3080
|
|
|
3001
3081
|
/**
|
|
3002
|
-
*
|
|
3003
|
-
*
|
|
3082
|
+
* Creates an explicit SQL outbox helper for service-owned transactions.
|
|
3083
|
+
* Services should create this at startup and close over it in handlers.
|
|
3004
3084
|
*/
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
):
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
auth: this.auth,
|
|
3011
|
-
event: this.#createBoundEventFacade(deps),
|
|
3012
|
-
kv: this.kv,
|
|
3013
|
-
store: this.store,
|
|
3014
|
-
jobs: createBoundJobsFacade({ jobs: this.jobs, deps }),
|
|
3015
|
-
health: this.#createBoundHealth(deps),
|
|
3016
|
-
handle: this.#createBoundHandleFacade(deps),
|
|
3017
|
-
connection: this.connection,
|
|
3018
|
-
createTransfer: (args) => this.createTransfer(args),
|
|
3019
|
-
completeOperation: (operationId, output) =>
|
|
3020
|
-
this.completeOperation(operationId, output),
|
|
3021
|
-
publishPrepared: (event) => this.publishPrepared(event),
|
|
3022
|
-
wait: () => this.wait(),
|
|
3023
|
-
stop: () => this.stop(),
|
|
3024
|
-
with: (nextDeps) => this.with(nextDeps),
|
|
3025
|
-
};
|
|
3085
|
+
createSqlOutbox<TTx>(
|
|
3086
|
+
options: TrellisServiceSqlOutboxExecutorOptions<TTx>,
|
|
3087
|
+
): SqlOutbox<TTx, TTrellisApi, TJobs> {
|
|
3088
|
+
const binding = this.#createSqlOutboxBinding(options);
|
|
3089
|
+
return this.#createSqlOutbox(binding);
|
|
3026
3090
|
}
|
|
3027
3091
|
|
|
3028
3092
|
/** Publishes a prepared event through the service runtime connection. */
|
|
@@ -3032,77 +3096,6 @@ export class TrellisService<
|
|
|
3032
3096
|
return this.#handlerTrellis.publishPrepared(event);
|
|
3033
3097
|
}
|
|
3034
3098
|
|
|
3035
|
-
#createBoundHealth<TDeps>(deps: TDeps): BoundServiceHealth<TDeps> {
|
|
3036
|
-
const health = this.health;
|
|
3037
|
-
return {
|
|
3038
|
-
serviceName: health.serviceName,
|
|
3039
|
-
kind: health.kind,
|
|
3040
|
-
instanceId: health.instanceId,
|
|
3041
|
-
contractId: health.contractId,
|
|
3042
|
-
contractDigest: health.contractDigest,
|
|
3043
|
-
startedAt: health.startedAt,
|
|
3044
|
-
publishIntervalMs: health.publishIntervalMs,
|
|
3045
|
-
setInfo(info) {
|
|
3046
|
-
if (typeof info !== "function") {
|
|
3047
|
-
health.setInfo(info);
|
|
3048
|
-
return;
|
|
3049
|
-
}
|
|
3050
|
-
|
|
3051
|
-
health.setInfo(() => info({ deps }));
|
|
3052
|
-
},
|
|
3053
|
-
add(name, check) {
|
|
3054
|
-
return health.add(name, () => check({ deps }));
|
|
3055
|
-
},
|
|
3056
|
-
checks: () => health.checks(),
|
|
3057
|
-
response: () => health.response(),
|
|
3058
|
-
heartbeat: () => health.heartbeat(),
|
|
3059
|
-
};
|
|
3060
|
-
}
|
|
3061
|
-
|
|
3062
|
-
#createBoundEventFacade<TDeps>(
|
|
3063
|
-
deps: TDeps,
|
|
3064
|
-
): BoundActiveEventFacade<TTrellisApi, TTrellisApi, TKv, TJobs, TDeps> {
|
|
3065
|
-
const event = {} as BoundActiveEventFacade<
|
|
3066
|
-
TTrellisApi,
|
|
3067
|
-
TTrellisApi,
|
|
3068
|
-
TKv,
|
|
3069
|
-
TJobs,
|
|
3070
|
-
TDeps
|
|
3071
|
-
>;
|
|
3072
|
-
const source = this.event as Record<
|
|
3073
|
-
string,
|
|
3074
|
-
Record<string, ServiceEventLeaf>
|
|
3075
|
-
>;
|
|
3076
|
-
for (const [groupName, leaves] of Object.entries(source)) {
|
|
3077
|
-
const group: Record<string, BoundServiceEventLeaf<TDeps>> = {};
|
|
3078
|
-
for (const [leafName, leaf] of Object.entries(leaves)) {
|
|
3079
|
-
group[leafName] = {
|
|
3080
|
-
prepare: (payload) => leaf.prepare(payload),
|
|
3081
|
-
publish: (payload) => leaf.publish(payload),
|
|
3082
|
-
listen: (handler, subjectData, opts) =>
|
|
3083
|
-
leaf.listen(
|
|
3084
|
-
(payload, context) =>
|
|
3085
|
-
handler({
|
|
3086
|
-
event: payload,
|
|
3087
|
-
context,
|
|
3088
|
-
client: this.#handlerTrellis,
|
|
3089
|
-
deps,
|
|
3090
|
-
}),
|
|
3091
|
-
subjectData,
|
|
3092
|
-
opts,
|
|
3093
|
-
),
|
|
3094
|
-
};
|
|
3095
|
-
}
|
|
3096
|
-
Object.defineProperty(event, groupName, {
|
|
3097
|
-
value: group,
|
|
3098
|
-
enumerable: true,
|
|
3099
|
-
configurable: true,
|
|
3100
|
-
});
|
|
3101
|
-
}
|
|
3102
|
-
|
|
3103
|
-
return event;
|
|
3104
|
-
}
|
|
3105
|
-
|
|
3106
3099
|
#createHandleFacade(): ServiceHandleFacade {
|
|
3107
3100
|
const rpc: ServiceHandleFacade["rpc"] = {};
|
|
3108
3101
|
for (const method of Object.keys(this.#server.api.rpc ?? {})) {
|
|
@@ -3168,80 +3161,100 @@ export class TrellisService<
|
|
|
3168
3161
|
return { rpc, feed, operation };
|
|
3169
3162
|
}
|
|
3170
3163
|
|
|
3171
|
-
#
|
|
3172
|
-
|
|
3173
|
-
):
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
client: this.#handlerTrellis,
|
|
3205
|
-
deps,
|
|
3206
|
-
})
|
|
3207
|
-
),
|
|
3208
|
-
);
|
|
3209
|
-
}
|
|
3210
|
-
|
|
3211
|
-
const operation: Record<
|
|
3212
|
-
string,
|
|
3213
|
-
Record<string, ServiceHandleOperationLeaf>
|
|
3214
|
-
> = {};
|
|
3215
|
-
for (
|
|
3216
|
-
const operationName of Object.keys(this.#server.api.operations ?? {})
|
|
3217
|
-
) {
|
|
3218
|
-
const registration = this.#operation(
|
|
3219
|
-
operationName as keyof TOwnedApi["operations"] & string,
|
|
3220
|
-
);
|
|
3221
|
-
const leaf = Object.assign(
|
|
3222
|
-
(handler: (context: unknown) => unknown) =>
|
|
3223
|
-
registration.handle((context) =>
|
|
3224
|
-
handler({
|
|
3225
|
-
...context,
|
|
3226
|
-
client: this.#handlerTrellis,
|
|
3227
|
-
deps,
|
|
3228
|
-
})
|
|
3229
|
-
),
|
|
3230
|
-
{
|
|
3231
|
-
accept: (args: { sessionKey: string }) => registration.accept(args),
|
|
3232
|
-
control: (operationId: string) => registration.control(operationId),
|
|
3233
|
-
},
|
|
3234
|
-
) as ServiceHandleOperationLeaf;
|
|
3235
|
-
addSurfaceLeaf(operation, operationName, leaf);
|
|
3236
|
-
}
|
|
3164
|
+
#createSqlOutboxBinding<TTx>(
|
|
3165
|
+
options: TrellisServiceSqlOutboxOptions<TTx>,
|
|
3166
|
+
): {
|
|
3167
|
+
readonly dialect: SqlDialect;
|
|
3168
|
+
readonly tables: SqlOutboxTables;
|
|
3169
|
+
readonly transaction: SqlOutboxTransactionRunner<TTx>;
|
|
3170
|
+
readonly dispatcher: OutboxDispatcher;
|
|
3171
|
+
} {
|
|
3172
|
+
const tables = resolveSqlOutboxTables(options.tables);
|
|
3173
|
+
const executor = createSqlOutboxBaseExecutor(options);
|
|
3174
|
+
const repository = new SqlOutboxRepository(
|
|
3175
|
+
executor,
|
|
3176
|
+
options.dialect,
|
|
3177
|
+
tables,
|
|
3178
|
+
);
|
|
3179
|
+
const dispatchRuntime: OutboxDispatchRuntime = {
|
|
3180
|
+
publishPreparedEvent: (event) =>
|
|
3181
|
+
this.#handlerTrellis.publishPrepared(event),
|
|
3182
|
+
// dispatchJobSubmission: stub — filled in by Phase 3
|
|
3183
|
+
};
|
|
3184
|
+
const dispatcher = new OutboxDispatcher(
|
|
3185
|
+
repository,
|
|
3186
|
+
dispatchRuntime,
|
|
3187
|
+
options.dispatcher,
|
|
3188
|
+
);
|
|
3189
|
+
this.#ownedOutboxDispatchers.add(dispatcher);
|
|
3190
|
+
return {
|
|
3191
|
+
dialect: options.dialect,
|
|
3192
|
+
tables,
|
|
3193
|
+
transaction: createSqlOutboxTransactionRunner(options),
|
|
3194
|
+
dispatcher,
|
|
3195
|
+
};
|
|
3196
|
+
}
|
|
3237
3197
|
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3198
|
+
#createSqlOutbox<TTx>(binding: {
|
|
3199
|
+
readonly dialect: SqlDialect;
|
|
3200
|
+
readonly tables: SqlOutboxTables;
|
|
3201
|
+
readonly transaction: SqlOutboxTransactionRunner<TTx>;
|
|
3202
|
+
readonly dispatcher: OutboxDispatcher;
|
|
3203
|
+
}): SqlOutbox<TTx, TTrellisApi, TJobs> {
|
|
3204
|
+
return {
|
|
3205
|
+
transaction: <TResult>(
|
|
3206
|
+
work: (
|
|
3207
|
+
context: SqlOutboxTransactionContext<TTx, TTrellisApi, TJobs>,
|
|
3208
|
+
) => Promise<TResult> | TResult,
|
|
3209
|
+
) =>
|
|
3210
|
+
AsyncResult.from((() => {
|
|
3211
|
+
let enqueued = 0;
|
|
3212
|
+
const toResultError = (
|
|
3213
|
+
cause: unknown,
|
|
3214
|
+
): Result<TResult, ValidationError | UnexpectedError> => {
|
|
3215
|
+
if (
|
|
3216
|
+
cause instanceof ValidationError ||
|
|
3217
|
+
cause instanceof UnexpectedError
|
|
3218
|
+
) {
|
|
3219
|
+
return Result.err(cause);
|
|
3220
|
+
}
|
|
3221
|
+
return Result.err(toUnexpectedError(cause));
|
|
3222
|
+
};
|
|
3223
|
+
let transaction: Promise<TResult>;
|
|
3224
|
+
try {
|
|
3225
|
+
transaction = binding.transaction<TResult>(({ tx, executor }) => {
|
|
3226
|
+
const repository = new SqlOutboxRepository(
|
|
3227
|
+
executor,
|
|
3228
|
+
binding.dialect,
|
|
3229
|
+
binding.tables,
|
|
3230
|
+
);
|
|
3231
|
+
const event = createSqlOutboxEventEnqueueFacade({
|
|
3232
|
+
event: this.event,
|
|
3233
|
+
repository,
|
|
3234
|
+
onEnqueued: () => {
|
|
3235
|
+
enqueued += 1;
|
|
3236
|
+
},
|
|
3237
|
+
});
|
|
3238
|
+
const job = createSqlOutboxJobEnqueueFacade({
|
|
3239
|
+
serviceName: this.name,
|
|
3240
|
+
contractJobs: this.#contractJobs,
|
|
3241
|
+
jobsBinding: this.#jobsBinding,
|
|
3242
|
+
repository,
|
|
3243
|
+
onEnqueued: () => {
|
|
3244
|
+
enqueued += 1;
|
|
3245
|
+
},
|
|
3246
|
+
});
|
|
3247
|
+
return work({ tx, event, job });
|
|
3248
|
+
});
|
|
3249
|
+
} catch (cause) {
|
|
3250
|
+
return Promise.resolve(toResultError(cause));
|
|
3251
|
+
}
|
|
3252
|
+
return transaction.then((result) => {
|
|
3253
|
+
if (enqueued > 0) binding.dispatcher.notify();
|
|
3254
|
+
return Result.ok(result);
|
|
3255
|
+
}, toResultError);
|
|
3256
|
+
})()),
|
|
3257
|
+
};
|
|
3245
3258
|
}
|
|
3246
3259
|
|
|
3247
3260
|
/**
|
|
@@ -3339,6 +3352,11 @@ export class TrellisService<
|
|
|
3339
3352
|
async stop(): Promise<void> {
|
|
3340
3353
|
this.#stopPromise ??= (async () => {
|
|
3341
3354
|
this.connection.stopObserving();
|
|
3355
|
+
this.#handlerTrellis.stopEventListeners();
|
|
3356
|
+
for (const dispatcher of this.#ownedOutboxDispatchers) {
|
|
3357
|
+
dispatcher.stop();
|
|
3358
|
+
}
|
|
3359
|
+
this.#ownedOutboxDispatchers.clear();
|
|
3342
3360
|
|
|
3343
3361
|
try {
|
|
3344
3362
|
await this.#stopHealthPublishing();
|
|
@@ -3373,7 +3391,8 @@ export class TrellisService<
|
|
|
3373
3391
|
InferSchemaType<TOwnedApi["operations"][O]["input"]>,
|
|
3374
3392
|
OperationProgressOf<TOwnedApi, O>,
|
|
3375
3393
|
OperationOutputOf<TOwnedApi, O>,
|
|
3376
|
-
OperationTransferContextOf<TOwnedApi, O
|
|
3394
|
+
OperationTransferContextOf<TOwnedApi, O>,
|
|
3395
|
+
BaseError
|
|
3377
3396
|
>;
|
|
3378
3397
|
|
|
3379
3398
|
return {
|
|
@@ -3386,7 +3405,8 @@ export class TrellisService<
|
|
|
3386
3405
|
InferSchemaType<TOwnedApi["operations"][O]["input"]>,
|
|
3387
3406
|
OperationProgressOf<TOwnedApi, O>,
|
|
3388
3407
|
OperationOutputOf<TOwnedApi, O>,
|
|
3389
|
-
OperationTransferContextOf<TOwnedApi, O
|
|
3408
|
+
OperationTransferContextOf<TOwnedApi, O>,
|
|
3409
|
+
BaseError
|
|
3390
3410
|
>
|
|
3391
3411
|
& { client: Trellis<TTrellisApi, TKv, TJobs> },
|
|
3392
3412
|
) => unknown | Promise<unknown>,
|