@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
|
@@ -1,14 +1,31 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type AsyncResult,
|
|
3
|
+
type BaseError,
|
|
4
|
+
isErr,
|
|
5
|
+
UnexpectedError,
|
|
6
|
+
} from "@qlever-llc/result";
|
|
2
7
|
import { type StaticDecode, Type } from "typebox";
|
|
3
|
-
import type { PreparedTrellisEvent
|
|
8
|
+
import type { PreparedTrellisEvent } from "../trellis.js";
|
|
4
9
|
import { TypedKV } from "../kv.js";
|
|
5
10
|
import { recordTrellisError } from "../telemetry/mod.js";
|
|
6
11
|
|
|
7
12
|
export type OutboxMessageState = "pending" | "dispatched" | "failed";
|
|
8
13
|
|
|
14
|
+
export type OutboxRecordKind = "event.publish" | "job.create" | "job.submit";
|
|
15
|
+
|
|
16
|
+
export type PreparedOutboxRecord = {
|
|
17
|
+
readonly id: string;
|
|
18
|
+
readonly kind: OutboxRecordKind;
|
|
19
|
+
readonly name: string;
|
|
20
|
+
readonly subject: string;
|
|
21
|
+
readonly payload: string;
|
|
22
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
23
|
+
};
|
|
24
|
+
|
|
9
25
|
export type OutboxMessage = {
|
|
10
26
|
id: string;
|
|
11
|
-
|
|
27
|
+
kind: OutboxRecordKind;
|
|
28
|
+
name: string;
|
|
12
29
|
subject: string;
|
|
13
30
|
payload: string;
|
|
14
31
|
headers: Record<string, string>;
|
|
@@ -18,6 +35,7 @@ export type OutboxMessage = {
|
|
|
18
35
|
updatedAt: string;
|
|
19
36
|
nextAttemptAt?: string;
|
|
20
37
|
lastError?: string;
|
|
38
|
+
outcome?: unknown;
|
|
21
39
|
};
|
|
22
40
|
|
|
23
41
|
export type OutboxDispatchResult = {
|
|
@@ -39,10 +57,21 @@ export type OutboxDispatcherOptions = {
|
|
|
39
57
|
onError?: (error: unknown) => void;
|
|
40
58
|
};
|
|
41
59
|
|
|
60
|
+
export type OutboxDispatchRuntime = {
|
|
61
|
+
/** Dispatches an event-publish outbox record. Returns Void on success, err on transient failure. */
|
|
62
|
+
publishPreparedEvent(
|
|
63
|
+
event: PreparedTrellisEvent,
|
|
64
|
+
): AsyncResult<void, UnexpectedError>;
|
|
65
|
+
/** Dispatches a job create/submit outbox record. Stub until Phase 3. */
|
|
66
|
+
dispatchJobSubmission?(
|
|
67
|
+
message: OutboxMessage,
|
|
68
|
+
): AsyncResult<void, BaseError>;
|
|
69
|
+
};
|
|
70
|
+
|
|
42
71
|
export type OutboxRepository = {
|
|
43
|
-
enqueue(
|
|
72
|
+
enqueue(record: PreparedOutboxRecord): Promise<OutboxMessage>;
|
|
44
73
|
claimDue(limit: number, now: Date): Promise<OutboxMessage[]>;
|
|
45
|
-
markDispatched(id: string, now: Date): Promise<void>;
|
|
74
|
+
markDispatched(id: string, now: Date, outcome?: unknown): Promise<void>;
|
|
46
75
|
markFailed(
|
|
47
76
|
id: string,
|
|
48
77
|
failure: { error: string; nextAttemptAt: Date; now: Date },
|
|
@@ -53,26 +82,55 @@ export type InboxRepository = {
|
|
|
53
82
|
record(messageId: string, now?: Date): Promise<boolean>;
|
|
54
83
|
};
|
|
55
84
|
|
|
85
|
+
/** SQL dialects supported by Trellis SQL outbox helpers. */
|
|
56
86
|
export type SqlDialect = "sqlite" | "postgres";
|
|
57
87
|
|
|
88
|
+
/** Table names used by Trellis SQL outbox and inbox helper tables. */
|
|
58
89
|
export type SqlOutboxTables = {
|
|
59
90
|
outbox: string;
|
|
60
91
|
inbox: string;
|
|
61
92
|
};
|
|
62
93
|
|
|
94
|
+
/** Minimal SQL execution surface used by Trellis SQL outbox repositories. */
|
|
63
95
|
export type SqlExecutor = {
|
|
64
96
|
query(sql: string, params: readonly unknown[]): Promise<readonly SqlRow[]>;
|
|
65
97
|
execute(sql: string, params: readonly unknown[]): Promise<void>;
|
|
66
98
|
};
|
|
67
99
|
|
|
100
|
+
/** Row shape returned by a {@link SqlExecutor}. */
|
|
68
101
|
export type SqlRow = Record<string, unknown>;
|
|
69
102
|
|
|
103
|
+
/** SQL outbox adapter bundle over caller-owned SQL execution. */
|
|
70
104
|
export type SqlOutboxAdapter = {
|
|
71
105
|
outbox: SqlOutboxRepository;
|
|
72
106
|
inbox: SqlInboxRepository;
|
|
73
107
|
ddl: readonly string[];
|
|
74
108
|
};
|
|
75
109
|
|
|
110
|
+
/** Versioned Trellis-owned SQL outbox/inbox migration artifact. */
|
|
111
|
+
export type SqlOutboxMigration = {
|
|
112
|
+
/** Stable migration identifier for the dialect and schema version. */
|
|
113
|
+
readonly id: string;
|
|
114
|
+
/** Monotonic Trellis helper-table schema version. */
|
|
115
|
+
readonly version: number;
|
|
116
|
+
/** SQL dialect targeted by this migration artifact. */
|
|
117
|
+
readonly dialect: SqlDialect;
|
|
118
|
+
/** SQL statements that apply this migration. */
|
|
119
|
+
readonly up: readonly string[];
|
|
120
|
+
/** SQL statements that revert this migration when supported by the runner. */
|
|
121
|
+
readonly down?: readonly string[];
|
|
122
|
+
/** Deterministic checksum over the canonical migration SQL. */
|
|
123
|
+
readonly checksum: string;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/** Options for generating Trellis SQL outbox/inbox migration artifacts. */
|
|
127
|
+
export type SqlOutboxMigrationOptions = {
|
|
128
|
+
/** SQL dialect targeted by the generated migration artifacts. */
|
|
129
|
+
readonly dialect: SqlDialect;
|
|
130
|
+
/** Optional helper-table names; omitted names use Trellis defaults. */
|
|
131
|
+
readonly tables?: Partial<SqlOutboxTables>;
|
|
132
|
+
};
|
|
133
|
+
|
|
76
134
|
type KvAsyncResult<T> = Pick<AsyncResult<T, BaseError>, "take">;
|
|
77
135
|
|
|
78
136
|
export type OutboxKvEntry = {
|
|
@@ -88,41 +146,70 @@ export type OutboxKvStore = {
|
|
|
88
146
|
keys(filter?: string | string[]): KvAsyncResult<AsyncIterable<string>>;
|
|
89
147
|
};
|
|
90
148
|
|
|
149
|
+
/** Default Trellis helper-table names for SQL outbox and inbox storage. */
|
|
91
150
|
export const defaultSqlOutboxTables: SqlOutboxTables = Object.freeze({
|
|
92
151
|
outbox: "trellis_outbox",
|
|
93
152
|
inbox: "trellis_inbox",
|
|
94
153
|
});
|
|
95
154
|
|
|
155
|
+
const sqlIdentifierPattern = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
156
|
+
|
|
96
157
|
/**
|
|
97
|
-
* Creates SQL outbox/inbox repositories plus
|
|
158
|
+
* Creates SQL outbox/inbox repositories plus current helper-table DDL.
|
|
98
159
|
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
* equivalent Drizzle table definition using these table and column names, in
|
|
102
|
-
* their own Drizzle-managed migration flow.
|
|
160
|
+
* Use {@link getSqlOutboxMigrations} when services need versioned Trellis-owned
|
|
161
|
+
* migration artifacts for their normal database migration tooling.
|
|
103
162
|
*/
|
|
104
163
|
export function createSqlOutboxAdapter(
|
|
105
164
|
executor: SqlExecutor,
|
|
106
165
|
dialect: SqlDialect,
|
|
107
166
|
tables: SqlOutboxTables = defaultSqlOutboxTables,
|
|
108
167
|
): SqlOutboxAdapter {
|
|
168
|
+
const sqlTables = validateSqlOutboxTables(tables);
|
|
109
169
|
return {
|
|
110
|
-
outbox: new SqlOutboxRepository(executor, dialect,
|
|
111
|
-
inbox: new SqlInboxRepository(executor, dialect,
|
|
170
|
+
outbox: new SqlOutboxRepository(executor, dialect, sqlTables),
|
|
171
|
+
inbox: new SqlInboxRepository(executor, dialect, sqlTables),
|
|
112
172
|
ddl: dialect === "postgres"
|
|
113
|
-
? createPostgresOutboxSchema(
|
|
114
|
-
: createSqliteOutboxSchema(
|
|
173
|
+
? createPostgresOutboxSchema(sqlTables)
|
|
174
|
+
: createSqliteOutboxSchema(sqlTables),
|
|
115
175
|
};
|
|
116
176
|
}
|
|
117
177
|
|
|
178
|
+
/**
|
|
179
|
+
* Returns Trellis-owned SQL outbox/inbox migration artifacts for a dialect.
|
|
180
|
+
*
|
|
181
|
+
* Trellis generates the helper-table SQL, but services remain responsible for
|
|
182
|
+
* running these artifacts through their normal database migration tooling.
|
|
183
|
+
*/
|
|
184
|
+
export function getSqlOutboxMigrations(
|
|
185
|
+
options: SqlOutboxMigrationOptions,
|
|
186
|
+
): readonly SqlOutboxMigration[] {
|
|
187
|
+
const tables = resolveSqlOutboxTables(options.tables);
|
|
188
|
+
const up = options.dialect === "postgres"
|
|
189
|
+
? createPostgresOutboxSchema(tables)
|
|
190
|
+
: createSqliteOutboxSchema(tables);
|
|
191
|
+
const down = createSqlOutboxDownSchema(tables);
|
|
192
|
+
return Object.freeze([
|
|
193
|
+
Object.freeze({
|
|
194
|
+
id: `trellis_sql_outbox_inbox_${options.dialect}_v1`,
|
|
195
|
+
version: 1,
|
|
196
|
+
dialect: options.dialect,
|
|
197
|
+
up,
|
|
198
|
+
down,
|
|
199
|
+
checksum: checksumMigrationSql(up, down),
|
|
200
|
+
}),
|
|
201
|
+
]);
|
|
202
|
+
}
|
|
203
|
+
|
|
118
204
|
/** Returns SQLite DDL for Trellis outbox and inbox tables. */
|
|
119
205
|
export function createSqliteOutboxSchema(
|
|
120
206
|
tables: SqlOutboxTables = defaultSqlOutboxTables,
|
|
121
207
|
): readonly string[] {
|
|
208
|
+
const sqlTables = validateSqlOutboxTables(tables);
|
|
122
209
|
return [
|
|
123
|
-
`CREATE TABLE IF NOT EXISTS ${
|
|
124
|
-
`CREATE INDEX IF NOT EXISTS ${
|
|
125
|
-
`CREATE TABLE IF NOT EXISTS ${
|
|
210
|
+
`CREATE TABLE IF NOT EXISTS ${sqlTables.outbox} (id TEXT PRIMARY KEY, kind TEXT NOT NULL, name TEXT NOT NULL, subject TEXT NOT NULL, payload TEXT NOT NULL, headers TEXT NOT NULL, state TEXT NOT NULL, attempts INTEGER NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, next_attempt_at TEXT, last_error TEXT, outcome TEXT)`,
|
|
211
|
+
`CREATE INDEX IF NOT EXISTS ${sqlTables.outbox}_due_idx ON ${sqlTables.outbox} (state, next_attempt_at)`,
|
|
212
|
+
`CREATE TABLE IF NOT EXISTS ${sqlTables.inbox} (message_id TEXT PRIMARY KEY, received_at TEXT NOT NULL)`,
|
|
126
213
|
];
|
|
127
214
|
}
|
|
128
215
|
|
|
@@ -130,28 +217,91 @@ export function createSqliteOutboxSchema(
|
|
|
130
217
|
export function createPostgresOutboxSchema(
|
|
131
218
|
tables: SqlOutboxTables = defaultSqlOutboxTables,
|
|
132
219
|
): readonly string[] {
|
|
220
|
+
const sqlTables = validateSqlOutboxTables(tables);
|
|
221
|
+
return [
|
|
222
|
+
`CREATE TABLE IF NOT EXISTS ${sqlTables.outbox} (id text PRIMARY KEY, kind text NOT NULL, name text NOT NULL, subject text NOT NULL, payload text NOT NULL, headers jsonb NOT NULL, state text NOT NULL, attempts integer NOT NULL, created_at timestamptz NOT NULL, updated_at timestamptz NOT NULL, next_attempt_at timestamptz, last_error text, outcome jsonb)`,
|
|
223
|
+
`CREATE INDEX IF NOT EXISTS ${sqlTables.outbox}_due_idx ON ${sqlTables.outbox} (state, next_attempt_at)`,
|
|
224
|
+
`CREATE TABLE IF NOT EXISTS ${sqlTables.inbox} (message_id text PRIMARY KEY, received_at timestamptz NOT NULL)`,
|
|
225
|
+
];
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function createSqlOutboxDownSchema(
|
|
229
|
+
tables: SqlOutboxTables,
|
|
230
|
+
): readonly string[] {
|
|
231
|
+
const sqlTables = validateSqlOutboxTables(tables);
|
|
133
232
|
return [
|
|
134
|
-
`
|
|
135
|
-
`
|
|
136
|
-
`
|
|
233
|
+
`DROP INDEX IF EXISTS ${sqlTables.outbox}_due_idx`,
|
|
234
|
+
`DROP TABLE IF EXISTS ${sqlTables.inbox}`,
|
|
235
|
+
`DROP TABLE IF EXISTS ${sqlTables.outbox}`,
|
|
137
236
|
];
|
|
138
237
|
}
|
|
139
238
|
|
|
239
|
+
function resolveSqlOutboxTables(
|
|
240
|
+
tables: Partial<SqlOutboxTables> | undefined,
|
|
241
|
+
): SqlOutboxTables {
|
|
242
|
+
return validateSqlOutboxTables({
|
|
243
|
+
outbox: tables?.outbox ?? defaultSqlOutboxTables.outbox,
|
|
244
|
+
inbox: tables?.inbox ?? defaultSqlOutboxTables.inbox,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function validateSqlOutboxTables(tables: SqlOutboxTables): SqlOutboxTables {
|
|
249
|
+
validateSqlIdentifier("outbox", tables.outbox);
|
|
250
|
+
validateSqlIdentifier("inbox", tables.inbox);
|
|
251
|
+
return Object.freeze({ outbox: tables.outbox, inbox: tables.inbox });
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function validateSqlIdentifier(
|
|
255
|
+
kind: keyof SqlOutboxTables,
|
|
256
|
+
name: string,
|
|
257
|
+
): void {
|
|
258
|
+
if (!sqlIdentifierPattern.test(name)) {
|
|
259
|
+
throw new Error(
|
|
260
|
+
`Invalid SQL ${kind} table name "${name}". Use a simple identifier matching ${sqlIdentifierPattern.source}.`,
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function checksumMigrationSql(
|
|
266
|
+
up: readonly string[],
|
|
267
|
+
down: readonly string[],
|
|
268
|
+
): string {
|
|
269
|
+
return `fnv1a64:${
|
|
270
|
+
fnv1a64Hex([
|
|
271
|
+
"-- up",
|
|
272
|
+
...up,
|
|
273
|
+
"-- down",
|
|
274
|
+
...down,
|
|
275
|
+
].join("\n"))
|
|
276
|
+
}`;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function fnv1a64Hex(input: string): string {
|
|
280
|
+
let hash = 0xcbf29ce484222325n;
|
|
281
|
+
const prime = 0x100000001b3n;
|
|
282
|
+
for (const character of input) {
|
|
283
|
+
hash ^= BigInt(character.codePointAt(0) ?? 0);
|
|
284
|
+
hash = BigInt.asUintN(64, hash * prime);
|
|
285
|
+
}
|
|
286
|
+
return hash.toString(16).padStart(16, "0");
|
|
287
|
+
}
|
|
288
|
+
|
|
140
289
|
/** In-memory outbox repository intended for tests and local process adapters. */
|
|
141
290
|
export class MemoryOutboxRepository implements OutboxRepository {
|
|
142
291
|
#messages = new Map<string, OutboxMessage>();
|
|
143
292
|
|
|
144
|
-
async enqueue(
|
|
293
|
+
async enqueue(record: PreparedOutboxRecord): Promise<OutboxMessage> {
|
|
145
294
|
const now = new Date().toISOString();
|
|
146
|
-
const id =
|
|
295
|
+
const id = record.id;
|
|
147
296
|
const existing = this.#messages.get(id);
|
|
148
297
|
if (existing) return existing;
|
|
149
298
|
const message: OutboxMessage = {
|
|
150
299
|
id,
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
300
|
+
kind: record.kind,
|
|
301
|
+
name: record.name,
|
|
302
|
+
subject: record.subject,
|
|
303
|
+
payload: record.payload,
|
|
304
|
+
headers: { ...record.headers },
|
|
155
305
|
state: "pending",
|
|
156
306
|
attempts: 0,
|
|
157
307
|
createdAt: now,
|
|
@@ -177,7 +327,11 @@ export class MemoryOutboxRepository implements OutboxRepository {
|
|
|
177
327
|
return claimed;
|
|
178
328
|
}
|
|
179
329
|
|
|
180
|
-
async markDispatched(
|
|
330
|
+
async markDispatched(
|
|
331
|
+
id: string,
|
|
332
|
+
now: Date,
|
|
333
|
+
outcome?: unknown,
|
|
334
|
+
): Promise<void> {
|
|
181
335
|
const message = this.#messages.get(id);
|
|
182
336
|
if (!message) return;
|
|
183
337
|
this.#messages.set(id, {
|
|
@@ -186,6 +340,7 @@ export class MemoryOutboxRepository implements OutboxRepository {
|
|
|
186
340
|
updatedAt: now.toISOString(),
|
|
187
341
|
nextAttemptAt: undefined,
|
|
188
342
|
lastError: undefined,
|
|
343
|
+
outcome: outcome ?? message.outcome,
|
|
189
344
|
});
|
|
190
345
|
}
|
|
191
346
|
|
|
@@ -226,20 +381,25 @@ export class MemoryInboxRepository implements InboxRepository {
|
|
|
226
381
|
|
|
227
382
|
/** SQL-backed outbox repository over a caller-owned executor. */
|
|
228
383
|
export class SqlOutboxRepository implements OutboxRepository {
|
|
384
|
+
readonly tables: SqlOutboxTables;
|
|
385
|
+
|
|
229
386
|
constructor(
|
|
230
387
|
readonly executor: SqlExecutor,
|
|
231
388
|
readonly dialect: SqlDialect,
|
|
232
|
-
|
|
233
|
-
) {
|
|
389
|
+
tables: SqlOutboxTables = defaultSqlOutboxTables,
|
|
390
|
+
) {
|
|
391
|
+
this.tables = validateSqlOutboxTables(tables);
|
|
392
|
+
}
|
|
234
393
|
|
|
235
|
-
async enqueue(
|
|
394
|
+
async enqueue(record: PreparedOutboxRecord): Promise<OutboxMessage> {
|
|
236
395
|
const now = new Date().toISOString();
|
|
237
396
|
const message: OutboxMessage = {
|
|
238
|
-
id:
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
397
|
+
id: record.id,
|
|
398
|
+
kind: record.kind,
|
|
399
|
+
name: record.name,
|
|
400
|
+
subject: record.subject,
|
|
401
|
+
payload: record.payload,
|
|
402
|
+
headers: { ...record.headers },
|
|
243
403
|
state: "pending",
|
|
244
404
|
attempts: 0,
|
|
245
405
|
createdAt: now,
|
|
@@ -250,12 +410,13 @@ export class SqlOutboxRepository implements OutboxRepository {
|
|
|
250
410
|
? "ON CONFLICT (id) DO NOTHING"
|
|
251
411
|
: "ON CONFLICT(id) DO NOTHING";
|
|
252
412
|
await this.executor.execute(
|
|
253
|
-
`INSERT INTO ${this.tables.outbox} (id,
|
|
254
|
-
placeholders(this.dialect,
|
|
413
|
+
`INSERT INTO ${this.tables.outbox} (id, kind, name, subject, payload, headers, state, attempts, created_at, updated_at, next_attempt_at, last_error, outcome) VALUES (${
|
|
414
|
+
placeholders(this.dialect, 13)
|
|
255
415
|
}) ${conflict}`,
|
|
256
416
|
[
|
|
257
417
|
message.id,
|
|
258
|
-
message.
|
|
418
|
+
message.kind,
|
|
419
|
+
message.name,
|
|
259
420
|
message.subject,
|
|
260
421
|
message.payload,
|
|
261
422
|
headers,
|
|
@@ -265,6 +426,7 @@ export class SqlOutboxRepository implements OutboxRepository {
|
|
|
265
426
|
message.updatedAt,
|
|
266
427
|
null,
|
|
267
428
|
null,
|
|
429
|
+
null,
|
|
268
430
|
],
|
|
269
431
|
);
|
|
270
432
|
return message;
|
|
@@ -272,7 +434,7 @@ export class SqlOutboxRepository implements OutboxRepository {
|
|
|
272
434
|
|
|
273
435
|
async claimDue(limit: number, now: Date): Promise<OutboxMessage[]> {
|
|
274
436
|
const rows = await this.executor.query(
|
|
275
|
-
`SELECT id,
|
|
437
|
+
`SELECT id, kind, name, subject, payload, headers, state, attempts, created_at, updated_at, next_attempt_at, last_error, outcome FROM ${this.tables.outbox} WHERE state != ${
|
|
276
438
|
placeholder(this.dialect, 1)
|
|
277
439
|
} AND (next_attempt_at IS NULL OR next_attempt_at <= ${
|
|
278
440
|
placeholder(this.dialect, 2)
|
|
@@ -282,16 +444,21 @@ export class SqlOutboxRepository implements OutboxRepository {
|
|
|
282
444
|
return rows.map(rowToOutboxMessage);
|
|
283
445
|
}
|
|
284
446
|
|
|
285
|
-
async markDispatched(
|
|
447
|
+
async markDispatched(
|
|
448
|
+
id: string,
|
|
449
|
+
now: Date,
|
|
450
|
+
outcome?: unknown,
|
|
451
|
+
): Promise<void> {
|
|
452
|
+
const outcomeJson = outcome !== undefined ? JSON.stringify(outcome) : null;
|
|
286
453
|
await this.executor.execute(
|
|
287
454
|
`UPDATE ${this.tables.outbox} SET state = ${
|
|
288
455
|
placeholder(this.dialect, 1)
|
|
289
456
|
}, updated_at = ${
|
|
290
457
|
placeholder(this.dialect, 2)
|
|
291
|
-
}, next_attempt_at = NULL, last_error = NULL
|
|
458
|
+
}, next_attempt_at = NULL, last_error = NULL, outcome = ${
|
|
292
459
|
placeholder(this.dialect, 3)
|
|
293
|
-
}`,
|
|
294
|
-
["dispatched", now.toISOString(), id],
|
|
460
|
+
} WHERE id = ${placeholder(this.dialect, 4)}`,
|
|
461
|
+
["dispatched", now.toISOString(), outcomeJson, id],
|
|
295
462
|
);
|
|
296
463
|
}
|
|
297
464
|
|
|
@@ -320,11 +487,15 @@ export class SqlOutboxRepository implements OutboxRepository {
|
|
|
320
487
|
|
|
321
488
|
/** SQL-backed inbox repository over a caller-owned executor. */
|
|
322
489
|
export class SqlInboxRepository implements InboxRepository {
|
|
490
|
+
readonly tables: SqlOutboxTables;
|
|
491
|
+
|
|
323
492
|
constructor(
|
|
324
493
|
readonly executor: SqlExecutor,
|
|
325
494
|
readonly dialect: SqlDialect,
|
|
326
|
-
|
|
327
|
-
) {
|
|
495
|
+
tables: SqlOutboxTables = defaultSqlOutboxTables,
|
|
496
|
+
) {
|
|
497
|
+
this.tables = validateSqlOutboxTables(tables);
|
|
498
|
+
}
|
|
328
499
|
|
|
329
500
|
async record(messageId: string, now: Date = new Date()): Promise<boolean> {
|
|
330
501
|
try {
|
|
@@ -363,7 +534,8 @@ type KvInboxRecord = StaticDecode<typeof KvInboxRecordSchema>;
|
|
|
363
534
|
|
|
364
535
|
export const KvOutboxRecordSchema = Type.Object({
|
|
365
536
|
id: Type.String(),
|
|
366
|
-
|
|
537
|
+
kind: Type.String(),
|
|
538
|
+
name: Type.String(),
|
|
367
539
|
subject: Type.String(),
|
|
368
540
|
payload: Type.String(),
|
|
369
541
|
headers: Type.Record(Type.String(), Type.String()),
|
|
@@ -378,6 +550,7 @@ export const KvOutboxRecordSchema = Type.Object({
|
|
|
378
550
|
updatedAt: Type.String(),
|
|
379
551
|
nextAttemptAt: Type.Optional(Type.String()),
|
|
380
552
|
lastError: Type.Optional(Type.String()),
|
|
553
|
+
outcome: Type.Optional(Type.String()),
|
|
381
554
|
});
|
|
382
555
|
|
|
383
556
|
export type KvOutboxRecord = StaticDecode<typeof KvOutboxRecordSchema>;
|
|
@@ -386,14 +559,15 @@ export type KvOutboxRecord = StaticDecode<typeof KvOutboxRecordSchema>;
|
|
|
386
559
|
export class NatsKvOutboxRepository implements OutboxRepository {
|
|
387
560
|
constructor(readonly kv: OutboxKvStore) {}
|
|
388
561
|
|
|
389
|
-
async enqueue(
|
|
562
|
+
async enqueue(rec: PreparedOutboxRecord): Promise<OutboxMessage> {
|
|
390
563
|
const now = new Date().toISOString();
|
|
391
564
|
const record: KvOutboxRecord = {
|
|
392
|
-
id:
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
565
|
+
id: rec.id,
|
|
566
|
+
kind: rec.kind,
|
|
567
|
+
name: rec.name,
|
|
568
|
+
subject: rec.subject,
|
|
569
|
+
payload: rec.payload,
|
|
570
|
+
headers: { ...rec.headers },
|
|
397
571
|
state: "pending",
|
|
398
572
|
attempts: 0,
|
|
399
573
|
createdAt: now,
|
|
@@ -446,18 +620,26 @@ export class NatsKvOutboxRepository implements OutboxRepository {
|
|
|
446
620
|
return claimed;
|
|
447
621
|
}
|
|
448
622
|
|
|
449
|
-
async markDispatched(
|
|
623
|
+
async markDispatched(
|
|
624
|
+
id: string,
|
|
625
|
+
now: Date,
|
|
626
|
+
outcome?: unknown,
|
|
627
|
+
): Promise<void> {
|
|
450
628
|
const loaded = await this.kv.get(id).take();
|
|
451
629
|
if (isErr(loaded)) {
|
|
452
630
|
if (hasKvReason(loaded.error, "not found")) return;
|
|
453
631
|
throw loaded.error;
|
|
454
632
|
}
|
|
633
|
+
const outcomeStr = outcome !== undefined
|
|
634
|
+
? JSON.stringify(outcome)
|
|
635
|
+
: undefined;
|
|
455
636
|
const stored = await loaded.put({
|
|
456
637
|
...loaded.value,
|
|
457
638
|
state: "dispatched",
|
|
458
639
|
updatedAt: now.toISOString(),
|
|
459
640
|
nextAttemptAt: undefined,
|
|
460
641
|
lastError: undefined,
|
|
642
|
+
outcome: outcomeStr ?? loaded.value.outcome,
|
|
461
643
|
}, true).take();
|
|
462
644
|
if (isErr(stored)) throw stored.error;
|
|
463
645
|
}
|
|
@@ -512,7 +694,7 @@ export class NatsKvInboxRepository implements InboxRepository {
|
|
|
512
694
|
*/
|
|
513
695
|
export class OutboxDispatcher {
|
|
514
696
|
readonly #repository: OutboxRepository;
|
|
515
|
-
readonly #runtime:
|
|
697
|
+
readonly #runtime: OutboxDispatchRuntime;
|
|
516
698
|
readonly #options: OutboxDispatcherOptions;
|
|
517
699
|
#wakeTimer: ReturnType<typeof setTimeout> | undefined;
|
|
518
700
|
#retryTimer: ReturnType<typeof setTimeout> | undefined;
|
|
@@ -525,7 +707,7 @@ export class OutboxDispatcher {
|
|
|
525
707
|
/** Creates a dispatcher over an existing outbox repository and runtime. */
|
|
526
708
|
constructor(
|
|
527
709
|
repository: OutboxRepository,
|
|
528
|
-
runtime:
|
|
710
|
+
runtime: OutboxDispatchRuntime,
|
|
529
711
|
options: OutboxDispatcherOptions = {},
|
|
530
712
|
) {
|
|
531
713
|
this.#repository = repository;
|
|
@@ -656,10 +838,10 @@ export class OutboxDispatcher {
|
|
|
656
838
|
}
|
|
657
839
|
}
|
|
658
840
|
|
|
659
|
-
/** Dispatches due outbox messages through a
|
|
841
|
+
/** Dispatches due outbox messages through a dispatch runtime. */
|
|
660
842
|
export async function dispatchOutbox(
|
|
661
843
|
repository: OutboxRepository,
|
|
662
|
-
runtime:
|
|
844
|
+
runtime: OutboxDispatchRuntime,
|
|
663
845
|
options: { limit?: number; now?: Date; retryDelayMs?: number } = {},
|
|
664
846
|
): Promise<OutboxDispatchResult> {
|
|
665
847
|
const now = options.now ?? new Date();
|
|
@@ -668,58 +850,128 @@ export async function dispatchOutbox(
|
|
|
668
850
|
let dispatched = 0;
|
|
669
851
|
let failed = 0;
|
|
670
852
|
for (const message of messages) {
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
853
|
+
if (message.kind === "event.publish") {
|
|
854
|
+
const result = await runtime.publishPreparedEvent(
|
|
855
|
+
outboxMessageToPreparedEvent(message),
|
|
856
|
+
);
|
|
857
|
+
const value = result.take();
|
|
858
|
+
if (isErr(value)) {
|
|
859
|
+
recordTrellisError(value.error, {
|
|
860
|
+
surface: "outbox",
|
|
861
|
+
direction: "dispatcher",
|
|
862
|
+
operation: message.name,
|
|
863
|
+
phase: "publish",
|
|
864
|
+
messagingSystem: "nats",
|
|
865
|
+
});
|
|
866
|
+
failed += 1;
|
|
867
|
+
await repository.markFailed(message.id, {
|
|
868
|
+
error: value.error.message,
|
|
869
|
+
nextAttemptAt: new Date(now.getTime() + retryDelayMs),
|
|
870
|
+
now,
|
|
871
|
+
});
|
|
872
|
+
continue;
|
|
873
|
+
}
|
|
874
|
+
dispatched += 1;
|
|
875
|
+
await repository.markDispatched(message.id, now);
|
|
876
|
+
} else if (
|
|
877
|
+
message.kind === "job.create" || message.kind === "job.submit"
|
|
878
|
+
) {
|
|
879
|
+
if (runtime.dispatchJobSubmission) {
|
|
880
|
+
const result = await runtime.dispatchJobSubmission(message);
|
|
881
|
+
const value = result.take();
|
|
882
|
+
if (isErr(value)) {
|
|
883
|
+
recordTrellisError(value.error, {
|
|
884
|
+
surface: "outbox",
|
|
885
|
+
direction: "dispatcher",
|
|
886
|
+
operation: message.name,
|
|
887
|
+
phase: "job.dispatch",
|
|
888
|
+
});
|
|
889
|
+
failed += 1;
|
|
890
|
+
await repository.markFailed(message.id, {
|
|
891
|
+
error: value.error.message,
|
|
892
|
+
nextAttemptAt: new Date(now.getTime() + retryDelayMs),
|
|
893
|
+
now,
|
|
894
|
+
});
|
|
895
|
+
continue;
|
|
896
|
+
}
|
|
897
|
+
dispatched += 1;
|
|
898
|
+
await repository.markDispatched(message.id, now);
|
|
899
|
+
} else {
|
|
900
|
+
recordTrellisError(new Error(`job dispatch not supported`), {
|
|
901
|
+
surface: "outbox",
|
|
902
|
+
direction: "dispatcher",
|
|
903
|
+
operation: message.name,
|
|
904
|
+
phase: "dispatch",
|
|
905
|
+
});
|
|
906
|
+
await repository.markDispatched(message.id, now, {
|
|
907
|
+
error: "job dispatch not supported",
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
} else {
|
|
911
|
+
const error = `Unknown outbox record kind: ${message.kind}`;
|
|
912
|
+
recordTrellisError(new Error(error), {
|
|
677
913
|
surface: "outbox",
|
|
678
914
|
direction: "dispatcher",
|
|
679
|
-
operation: message.
|
|
680
|
-
phase: "
|
|
681
|
-
messagingSystem: "nats",
|
|
682
|
-
});
|
|
683
|
-
failed += 1;
|
|
684
|
-
await repository.markFailed(message.id, {
|
|
685
|
-
error: value.error.message,
|
|
686
|
-
nextAttemptAt: new Date(now.getTime() + retryDelayMs),
|
|
687
|
-
now,
|
|
915
|
+
operation: message.name,
|
|
916
|
+
phase: "dispatch",
|
|
688
917
|
});
|
|
689
|
-
|
|
918
|
+
await repository.markDispatched(message.id, now, { error });
|
|
690
919
|
}
|
|
691
|
-
dispatched += 1;
|
|
692
|
-
await repository.markDispatched(message.id, now);
|
|
693
920
|
}
|
|
694
921
|
return { dispatched, failed };
|
|
695
922
|
}
|
|
696
923
|
|
|
697
|
-
/** Rehydrates a persisted outbox row into a prepared event. */
|
|
698
|
-
export function
|
|
924
|
+
/** Rehydrates a persisted outbox event row into a prepared event. */
|
|
925
|
+
export function outboxMessageToPreparedEvent(
|
|
699
926
|
message: OutboxMessage,
|
|
700
927
|
): PreparedTrellisEvent {
|
|
928
|
+
if (message.kind !== "event.publish") {
|
|
929
|
+
throw new Error(
|
|
930
|
+
`Expected event.publish kind, got ${message.kind}`,
|
|
931
|
+
);
|
|
932
|
+
}
|
|
933
|
+
const payload = JSON.parse(message.payload) as Record<string, unknown>;
|
|
934
|
+
const header = eventHeaderFromMessage(message.headers);
|
|
701
935
|
return Object.freeze({
|
|
702
|
-
event: message.
|
|
936
|
+
event: message.name,
|
|
703
937
|
subject: message.subject,
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
header: { id: string; time: string };
|
|
707
|
-
},
|
|
708
|
-
),
|
|
938
|
+
header: Object.freeze(header),
|
|
939
|
+
payload: Object.freeze(payload),
|
|
709
940
|
encodedPayload: message.payload,
|
|
710
941
|
headers: Object.freeze({ ...message.headers }),
|
|
711
942
|
});
|
|
712
943
|
}
|
|
713
944
|
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
945
|
+
/** Adapts a prepared Trellis event into a generic outbox record. */
|
|
946
|
+
export function preparedTrellisEventToOutboxRecord(
|
|
947
|
+
event: PreparedTrellisEvent,
|
|
948
|
+
): PreparedOutboxRecord {
|
|
949
|
+
return {
|
|
950
|
+
id: event.headers["Nats-Msg-Id"] ?? event.header.id,
|
|
951
|
+
kind: "event.publish",
|
|
952
|
+
name: event.event,
|
|
953
|
+
subject: event.subject,
|
|
954
|
+
payload: event.encodedPayload,
|
|
955
|
+
headers: event.headers,
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
function eventHeaderFromMessage(
|
|
960
|
+
headers: Record<string, string>,
|
|
961
|
+
): { id: string; time: string } {
|
|
962
|
+
const id = headers["Nats-Msg-Id"] ?? headers["nats-msg-id"];
|
|
963
|
+
const time = headers["Trellis-Event-Time"] ?? headers["trellis-event-time"];
|
|
964
|
+
return {
|
|
965
|
+
id: typeof id === "string" ? id : "",
|
|
966
|
+
time: typeof time === "string" ? time : new Date(0).toISOString(),
|
|
967
|
+
};
|
|
717
968
|
}
|
|
718
969
|
|
|
719
970
|
function rowToOutboxMessage(row: SqlRow): OutboxMessage {
|
|
720
971
|
return {
|
|
721
972
|
id: stringField(row, "id"),
|
|
722
|
-
|
|
973
|
+
kind: kindField(row, "kind"),
|
|
974
|
+
name: stringField(row, "name"),
|
|
723
975
|
subject: stringField(row, "subject"),
|
|
724
976
|
payload: stringField(row, "payload"),
|
|
725
977
|
headers: parseHeaders(row["headers"]),
|
|
@@ -729,13 +981,15 @@ function rowToOutboxMessage(row: SqlRow): OutboxMessage {
|
|
|
729
981
|
updatedAt: stringField(row, "updated_at"),
|
|
730
982
|
nextAttemptAt: optionalStringField(row, "next_attempt_at"),
|
|
731
983
|
lastError: optionalStringField(row, "last_error"),
|
|
984
|
+
outcome: optionalStringField(row, "outcome"),
|
|
732
985
|
};
|
|
733
986
|
}
|
|
734
987
|
|
|
735
988
|
function kvRecordToOutboxMessage(record: KvOutboxRecord): OutboxMessage {
|
|
736
989
|
return {
|
|
737
990
|
id: record.id,
|
|
738
|
-
|
|
991
|
+
kind: record.kind as OutboxRecordKind,
|
|
992
|
+
name: record.name,
|
|
739
993
|
subject: record.subject,
|
|
740
994
|
payload: record.payload,
|
|
741
995
|
headers: { ...record.headers },
|
|
@@ -745,6 +999,7 @@ function kvRecordToOutboxMessage(record: KvOutboxRecord): OutboxMessage {
|
|
|
745
999
|
updatedAt: record.updatedAt,
|
|
746
1000
|
nextAttemptAt: record.nextAttemptAt,
|
|
747
1001
|
lastError: record.lastError,
|
|
1002
|
+
outcome: record.outcome,
|
|
748
1003
|
};
|
|
749
1004
|
}
|
|
750
1005
|
|
|
@@ -810,6 +1065,17 @@ function numberField(row: SqlRow, field: string): number {
|
|
|
810
1065
|
return value;
|
|
811
1066
|
}
|
|
812
1067
|
|
|
1068
|
+
function kindField(row: SqlRow, field: string): OutboxRecordKind {
|
|
1069
|
+
const value = stringField(row, field);
|
|
1070
|
+
if (
|
|
1071
|
+
value === "event.publish" || value === "job.create" ||
|
|
1072
|
+
value === "job.submit"
|
|
1073
|
+
) {
|
|
1074
|
+
return value;
|
|
1075
|
+
}
|
|
1076
|
+
throw new Error(`Expected SQL field ${field} to be an outbox record kind`);
|
|
1077
|
+
}
|
|
1078
|
+
|
|
813
1079
|
function stateField(row: SqlRow, field: string): OutboxMessageState {
|
|
814
1080
|
const value = stringField(row, field);
|
|
815
1081
|
if (value === "pending" || value === "dispatched" || value === "failed") {
|