@lostgradient/weft 0.23.1 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -4
- package/dist/cli/codegen-emit-dedup.d.ts +81 -0
- package/dist/cli/codegen-emit-dedup.js +45 -0
- package/dist/cli/codegen-emit-registry.d.ts +90 -0
- package/dist/cli/codegen-emit-registry.js +50 -0
- package/dist/cli/codegen-emit.d.ts +26 -39
- package/dist/cli/codegen-emit.js +4 -27
- package/dist/cli/codegen-validate.d.ts +49 -0
- package/dist/cli/codegen-validate.js +186 -0
- package/dist/cli/codegen.js +6 -87
- package/dist/cli/generated/operation-catalog.snapshot.json +287 -3
- package/dist/cli/generated/operation-client.generated.d.ts +324 -10
- package/dist/cli/generated/operation-client.generated.js +12 -0
- package/dist/cli/output.js +1 -1
- package/dist/cli/storage-factory.js +2 -0
- package/dist/cli/workflow-commands.js +2 -0
- package/dist/cli-main.js +1 -1
- package/dist/core/application-payload-digest.d.ts +42 -0
- package/dist/core/application-payload-digest.js +76 -0
- package/dist/core/application-primitive-abort.d.ts +50 -0
- package/dist/core/application-primitive-abort.js +40 -0
- package/dist/core/application-primitive-attempt-registry.d.ts +74 -0
- package/dist/core/application-primitive-attempt-registry.js +73 -0
- package/dist/core/application-primitive-codec.d.ts +36 -0
- package/dist/core/application-primitive-codec.js +56 -0
- package/dist/core/application-primitive-commit.d.ts +67 -0
- package/dist/core/application-primitive-commit.js +66 -0
- package/dist/core/application-primitive-guards.d.ts +62 -0
- package/dist/core/application-primitive-guards.js +89 -0
- package/dist/core/application-primitive-payload.d.ts +29 -0
- package/dist/core/application-primitive-payload.js +65 -0
- package/dist/core/application-primitive-timing.d.ts +18 -0
- package/dist/core/application-primitive-timing.js +22 -0
- package/dist/core/atomic-state.js +2 -0
- package/dist/core/catalog/codec.d.ts +40 -0
- package/dist/core/catalog/codec.js +45 -0
- package/dist/core/catalog/errors.d.ts +104 -0
- package/dist/core/catalog/errors.js +41 -0
- package/dist/core/catalog/index.d.ts +25 -0
- package/dist/core/catalog/index.js +15 -0
- package/dist/core/catalog/reference-counts.d.ts +105 -0
- package/dist/core/catalog/reference-counts.js +28 -0
- package/dist/core/catalog/removal.d.ts +52 -0
- package/dist/core/catalog/removal.js +14 -0
- package/dist/core/catalog/storage-io.d.ts +78 -0
- package/dist/core/catalog/storage-io.js +106 -0
- package/dist/core/catalog/types.d.ts +108 -0
- package/dist/core/catalog/types.js +0 -0
- package/dist/core/catalog/workflow-catalog.d.ts +165 -0
- package/dist/core/catalog/workflow-catalog.js +198 -0
- package/dist/core/compare-codepoint.d.ts +13 -0
- package/dist/core/compare-codepoint.js +7 -0
- package/dist/core/compression.js +2 -0
- package/dist/core/context/attributes.d.ts +1 -1
- package/dist/core/context/durable-operations.d.ts +2 -2
- package/dist/core/context/index.js +5 -3
- package/dist/core/context/state-namespace.d.ts +1 -1
- package/dist/core/contract/build.d.ts +64 -0
- package/dist/core/contract/build.js +99 -0
- package/dist/core/contract/compatibility.d.ts +187 -0
- package/dist/core/contract/compatibility.js +37 -0
- package/dist/core/contract/failure.d.ts +64 -0
- package/dist/core/contract/failure.js +4 -0
- package/dist/core/contract/hash.d.ts +92 -0
- package/dist/core/contract/hash.js +34 -0
- package/dist/core/contract/index.d.ts +21 -0
- package/dist/core/contract/index.js +22 -0
- package/dist/core/contract/limits.d.ts +74 -0
- package/dist/core/contract/limits.js +1 -0
- package/dist/core/contract/manifest-parse-schema.d.ts +37 -0
- package/dist/core/contract/manifest-parse-schema.js +106 -0
- package/dist/core/contract/manifest-parse.d.ts +76 -0
- package/dist/core/contract/manifest-parse.js +189 -0
- package/dist/core/contract/manifest.d.ts +56 -0
- package/dist/core/contract/manifest.js +31 -0
- package/dist/core/contract/normalize.d.ts +97 -0
- package/dist/core/contract/normalize.js +127 -0
- package/dist/core/contract/revision.d.ts +29 -0
- package/dist/core/contract/revision.js +5 -0
- package/dist/core/contract/types.d.ts +274 -0
- package/dist/core/contract/types.js +1 -0
- package/dist/core/engine/catalog-activation.d.ts +40 -0
- package/dist/core/engine/catalog-activation.js +17 -0
- package/dist/core/engine/catalog-events.d.ts +36 -0
- package/dist/core/engine/catalog-events.js +15 -0
- package/dist/core/engine/catalog-readiness.d.ts +49 -0
- package/dist/core/engine/catalog-readiness.js +59 -0
- package/dist/core/engine/catalog-removal.d.ts +132 -0
- package/dist/core/engine/catalog-removal.js +70 -0
- package/dist/core/engine/construction.d.ts +1 -1
- package/dist/core/engine/construction.js +4 -2
- package/dist/core/engine/disposal.js +9 -0
- package/dist/core/engine/engine-workflows-namespace.d.ts +130 -0
- package/dist/core/engine/engine-workflows-namespace.js +49 -0
- package/dist/core/engine/finalizer-metadata.d.ts +22 -0
- package/dist/core/engine/finalizer-metadata.js +12 -0
- package/dist/core/engine/handle-result.js +2 -0
- package/dist/core/engine/index.d.ts +83 -0
- package/dist/core/engine/index.js +61 -0
- package/dist/core/engine/internals.d.ts +44 -1
- package/dist/core/engine/internals.js +6 -0
- package/dist/core/engine/lifecycle/start.js +3 -0
- package/dist/core/engine/registration.d.ts +76 -0
- package/dist/core/engine/registration.js +12 -6
- package/dist/core/engine/source-registration.d.ts +42 -0
- package/dist/core/engine/source-registration.js +36 -0
- package/dist/core/engine/source-resolution.d.ts +85 -0
- package/dist/core/engine/source-resolution.js +119 -0
- package/dist/core/engine/validation.js +2 -2
- package/dist/core/events/catalog-events.d.ts +117 -0
- package/dist/core/events/catalog-events.js +62 -0
- package/dist/core/events/event-map.d.ts +6 -0
- package/dist/core/events/index.d.ts +1 -0
- package/dist/core/events/index.js +1 -0
- package/dist/core/mailbox-admission.d.ts +26 -0
- package/dist/core/mailbox-admission.js +130 -0
- package/dist/core/mailbox-claims.d.ts +251 -0
- package/dist/core/mailbox-claims.js +0 -0
- package/dist/core/mailbox-codec.d.ts +32 -0
- package/dist/core/mailbox-codec.js +221 -0
- package/dist/core/mailbox-contract.d.ts +290 -0
- package/dist/core/mailbox-contract.js +0 -0
- package/dist/core/mailbox-delivery.d.ts +37 -0
- package/dist/core/mailbox-delivery.js +193 -0
- package/dist/core/mailbox-guards.d.ts +78 -0
- package/dist/core/mailbox-guards.js +58 -0
- package/dist/core/mailbox-index-codec.d.ts +41 -0
- package/dist/core/mailbox-index-codec.js +71 -0
- package/dist/core/mailbox-internals.d.ts +156 -0
- package/dist/core/mailbox-internals.js +154 -0
- package/dist/core/mailbox-maintenance.d.ts +29 -0
- package/dist/core/mailbox-maintenance.js +196 -0
- package/dist/core/mailbox-settlement.d.ts +69 -0
- package/dist/core/mailbox-settlement.js +206 -0
- package/dist/core/mailbox-storage.d.ts +117 -0
- package/dist/core/mailbox-storage.js +115 -0
- package/dist/core/mailbox-transition-helpers.d.ts +70 -0
- package/dist/core/mailbox-transition-helpers.js +43 -0
- package/dist/core/mailbox-transitions-recovery.d.ts +28 -0
- package/dist/core/mailbox-transitions-recovery.js +64 -0
- package/dist/core/mailbox-transitions.d.ts +138 -0
- package/dist/core/mailbox-transitions.js +180 -0
- package/dist/core/mailbox-types.d.ts +406 -0
- package/dist/core/mailbox-types.js +16 -0
- package/dist/core/mailbox-validation.d.ts +65 -0
- package/dist/core/mailbox-validation.js +73 -0
- package/dist/core/mailbox-waits.d.ts +50 -0
- package/dist/core/mailbox-waits.js +97 -0
- package/dist/core/mailbox.d.ts +163 -0
- package/dist/core/mailbox.js +211 -0
- package/dist/core/outbox-claims.d.ts +307 -0
- package/dist/core/outbox-claims.js +0 -0
- package/dist/core/outbox-codec.d.ts +24 -0
- package/dist/core/outbox-codec.js +257 -0
- package/dist/core/outbox-contract.d.ts +346 -0
- package/dist/core/outbox-contract.js +0 -0
- package/dist/core/outbox-delivery.d.ts +34 -0
- package/dist/core/outbox-delivery.js +149 -0
- package/dist/core/outbox-drain.d.ts +29 -0
- package/dist/core/outbox-drain.js +158 -0
- package/dist/core/outbox-enqueue.d.ts +24 -0
- package/dist/core/outbox-enqueue.js +115 -0
- package/dist/core/outbox-guards.d.ts +76 -0
- package/dist/core/outbox-guards.js +60 -0
- package/dist/core/outbox-index-codec.d.ts +33 -0
- package/dist/core/outbox-index-codec.js +71 -0
- package/dist/core/outbox-internals.d.ts +99 -0
- package/dist/core/outbox-internals.js +159 -0
- package/dist/core/outbox-listing.d.ts +12 -0
- package/dist/core/outbox-listing.js +38 -0
- package/dist/core/outbox-maintenance.d.ts +28 -0
- package/dist/core/outbox-maintenance.js +191 -0
- package/dist/core/outbox-operations.d.ts +18 -0
- package/dist/core/outbox-operations.js +46 -0
- package/dist/core/outbox-runner.d.ts +30 -0
- package/dist/core/outbox-runner.js +217 -0
- package/dist/core/outbox-settlement.d.ts +57 -0
- package/dist/core/outbox-settlement.js +183 -0
- package/dist/core/outbox-storage.d.ts +107 -0
- package/dist/core/outbox-storage.js +127 -0
- package/dist/core/outbox-transition-helpers.d.ts +83 -0
- package/dist/core/outbox-transition-helpers.js +63 -0
- package/dist/core/outbox-transitions-recovery.d.ts +30 -0
- package/dist/core/outbox-transitions-recovery.js +42 -0
- package/dist/core/outbox-transitions.d.ts +145 -0
- package/dist/core/outbox-transitions.js +236 -0
- package/dist/core/outbox-types.d.ts +400 -0
- package/dist/core/outbox-types.js +20 -0
- package/dist/core/outbox-validation.d.ts +101 -0
- package/dist/core/outbox-validation.js +127 -0
- package/dist/core/outbox-waits.d.ts +39 -0
- package/dist/core/outbox-waits.js +88 -0
- package/dist/core/outbox.d.ts +156 -0
- package/dist/core/outbox.js +234 -0
- package/dist/core/registry-limits.d.ts +49 -0
- package/dist/core/registry-limits.js +10 -0
- package/dist/core/registry-schema-conversion.d.ts +20 -0
- package/dist/core/registry-schema-conversion.js +22 -0
- package/dist/core/registry-snapshot.d.ts +91 -42
- package/dist/core/registry-snapshot.js +39 -77
- package/dist/core/registry-workflow-contract-draft.d.ts +32 -0
- package/dist/core/registry-workflow-contract-draft.js +28 -0
- package/dist/core/registry-workflow-manifest.d.ts +122 -0
- package/dist/core/registry-workflow-manifest.js +92 -0
- package/dist/core/source/errors.d.ts +55 -0
- package/dist/core/source/errors.js +13 -0
- package/dist/core/source/index.d.ts +19 -0
- package/dist/core/source/index.js +4 -0
- package/dist/core/source/resolvers.d.ts +45 -0
- package/dist/core/source/resolvers.js +9 -0
- package/dist/core/source/types.d.ts +211 -0
- package/dist/core/source/types.js +0 -0
- package/dist/core/source/validate.d.ts +91 -0
- package/dist/core/source/validate.js +62 -0
- package/dist/core/source/workflow-source.d.ts +43 -0
- package/dist/core/source/workflow-source.js +16 -0
- package/dist/core/types/workflow-registries.d.ts +7 -0
- package/dist/core/types/workflow-registry.d.ts +12 -0
- package/dist/core/versioning.js +2 -0
- package/dist/core/weft-error.d.ts +1 -1
- package/dist/core/weft-error.js +9 -1
- package/dist/http.js +2 -2
- package/dist/index.d.ts +23 -2
- package/dist/index.js +50 -0
- package/dist/indexeddb.js +1 -1
- package/dist/json-schema.js +3 -3
- package/dist/mcp/resources.js +2 -0
- package/dist/runtime/portable.d.ts +9 -1
- package/dist/server/fault-to-json-rpc.js +3 -1
- package/dist/server/handler/auth-context-principal.js +4 -0
- package/dist/server/json-rpc-http.js +2 -0
- package/dist/server/json-rpc-websocket.js +6 -4
- package/dist/server/operation-catalog/index.d.ts +1 -1
- package/dist/server/operation-catalog/pipeline-helpers.js +2 -0
- package/dist/server/operation-catalog/types.d.ts +17 -0
- package/dist/server/operation-fault.d.ts +3 -0
- package/dist/server/operation-fault.js +3 -1
- package/dist/server/operations/activate-workflow-revision.d.ts +30 -0
- package/dist/server/operations/activate-workflow-revision.js +69 -0
- package/dist/server/operations/fleet-events-subscription.js +1 -1
- package/dist/server/operations/fork-workflow.js +1 -1
- package/dist/server/operations/get-active-workflow-revision.d.ts +32 -0
- package/dist/server/operations/get-active-workflow-revision.js +46 -0
- package/dist/server/operations/get-catalog-diagnostics.d.ts +85 -0
- package/dist/server/operations/get-catalog-diagnostics.js +57 -0
- package/dist/server/operations/get-registry.js +17 -4
- package/dist/server/operations/get-workflow-revision.d.ts +25 -0
- package/dist/server/operations/get-workflow-revision.js +50 -0
- package/dist/server/operations/install-workflow-revision.d.ts +27 -0
- package/dist/server/operations/install-workflow-revision.js +46 -0
- package/dist/server/operations/list-workflow-revisions.d.ts +20 -0
- package/dist/server/operations/list-workflow-revisions.js +40 -0
- package/dist/server/operations/start-or-signal-workflow.js +1 -1
- package/dist/server/operations/start-workflow.js +1 -1
- package/dist/server/operations/static-registrations.js +36 -0
- package/dist/server/operations/storage.js +2 -2
- package/dist/server/operations/submit-review-decision.js +1 -1
- package/dist/server/operations/workflow-catalog-operation-helpers.d.ts +59 -0
- package/dist/server/operations/workflow-catalog-operation-helpers.js +135 -0
- package/dist/server/runtime/websocket-upgrade.js +2 -0
- package/dist/storage/bun-sql.js +49 -6
- package/dist/storage/catalog-keys.d.ts +45 -0
- package/dist/storage/catalog-keys.js +6 -0
- package/dist/storage/compressed-storage.js +1 -1
- package/dist/storage/derived-operations.js +1 -1
- package/dist/storage/index.d.ts +26 -1
- package/dist/storage/interface.d.ts +26 -1
- package/dist/storage/interface.js +1 -1
- package/dist/storage/key-encoding.d.ts +15 -0
- package/dist/storage/key-encoding.js +3 -0
- package/dist/storage/key-prefixes.d.ts +1 -1
- package/dist/storage/key-prefixes.js +15 -0
- package/dist/storage/lmdb.d.ts +26 -1
- package/dist/storage/lmdb.js +1 -1
- package/dist/storage/mailbox-keys.d.ts +80 -0
- package/dist/storage/mailbox-keys.js +14 -0
- package/dist/storage/memory.js +1 -1
- package/dist/storage/neon.js +2 -2
- package/dist/storage/node-sqlite-loader.d.ts +1 -1
- package/dist/storage/node-sqlite.js +49 -6
- package/dist/storage/outbox-keys.d.ts +77 -0
- package/dist/storage/outbox-keys.js +14 -0
- package/dist/storage/postgres.js +2 -2
- package/dist/storage/resolve.js +1 -1
- package/dist/storage/scoped-storage.js +1 -1
- package/dist/storage/storage-configuration.d.ts +6 -0
- package/dist/storage/testing.js +1 -1
- package/dist/storage/turso.js +2 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/web-extension.js +1 -1
- package/dist/worker/manifest/registry-contract-builder.d.ts +21 -3
- package/dist/worker/manifest/registry-contract-builder.js +40 -31
- package/package.json +1 -1
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import {
|
|
2
|
+
raceAbort,
|
|
3
|
+
raceAbortWithin,
|
|
4
|
+
WaitBudgetElapsedError
|
|
5
|
+
} from "./application-primitive-abort.js";
|
|
6
|
+
import { claimNextDelivery } from "./outbox-delivery.js";
|
|
7
|
+
import { ApplicationDeliveryValidationError, boundFailureMessage } from "./outbox-guards.js";
|
|
8
|
+
import {
|
|
9
|
+
releaseAttemptController,
|
|
10
|
+
toApplicationDeliveryReceipt
|
|
11
|
+
} from "./outbox-internals.js";
|
|
12
|
+
import { beginAttempt, heartbeatAttempt, settleAttempt } from "./outbox-settlement.js";
|
|
13
|
+
import { loadDelivery } from "./outbox-storage.js";
|
|
14
|
+
import { validateOutcome } from "./outbox-validation.js";
|
|
15
|
+
async function sendOnce(runtime, adapter, claim, requestSignal) {
|
|
16
|
+
const budget = claim.attemptDeadlineAt - runtime.now();
|
|
17
|
+
if (claim.signal.aborted || requestSignal?.aborted === !0 || budget <= 0)
|
|
18
|
+
return {
|
|
19
|
+
status: "retryable",
|
|
20
|
+
failure: {
|
|
21
|
+
reason: "retryable",
|
|
22
|
+
message: "The attempt ended before the transport was called."
|
|
23
|
+
},
|
|
24
|
+
retryAfterMs: void 0
|
|
25
|
+
};
|
|
26
|
+
const raced = await raceAbortWithin(async () => {
|
|
27
|
+
try {
|
|
28
|
+
return {
|
|
29
|
+
ok: !0,
|
|
30
|
+
outcome: validateOutcome(await adapter.send({
|
|
31
|
+
delivery: claim.receipt,
|
|
32
|
+
payload: claim.payload,
|
|
33
|
+
credentialRef: claim.credentialRef,
|
|
34
|
+
attemptToken: claim.attemptToken,
|
|
35
|
+
signal: claim.signal
|
|
36
|
+
}))
|
|
37
|
+
};
|
|
38
|
+
} catch (error) {
|
|
39
|
+
return { ok: !1, error };
|
|
40
|
+
}
|
|
41
|
+
}, budget, claim.signal, requestSignal);
|
|
42
|
+
if (raced.aborted)
|
|
43
|
+
return unknownAfterLostRace(runtime, claim, raced.reason);
|
|
44
|
+
if (raced.value.ok)
|
|
45
|
+
return raced.value.outcome;
|
|
46
|
+
return {
|
|
47
|
+
status: "unknown",
|
|
48
|
+
failure: {
|
|
49
|
+
reason: "unknown-outcome",
|
|
50
|
+
message: boundFailureMessage(`The transport adapter threw: ${describeError(raced.value.error)}`)
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function unknownAfterLostRace(runtime, claim, reason) {
|
|
55
|
+
const deadlineWon = reason instanceof WaitBudgetElapsedError, message = deadlineWon ? "The attempt deadline passed while the transport call was in flight." : "The attempt was aborted while the transport call was in flight.";
|
|
56
|
+
if (deadlineWon) {
|
|
57
|
+
const registration = runtime.attemptControllers.get(claim.attemptToken);
|
|
58
|
+
if (registration !== void 0 && !registration.controller.signal.aborted)
|
|
59
|
+
registration.controller.abort(Error(message));
|
|
60
|
+
}
|
|
61
|
+
return { status: "unknown", failure: { reason: "unknown-outcome", message } };
|
|
62
|
+
}
|
|
63
|
+
function describeError(error) {
|
|
64
|
+
try {
|
|
65
|
+
const message = error instanceof Error ? error.message : error;
|
|
66
|
+
return typeof message === "string" ? message : String(message);
|
|
67
|
+
} catch {
|
|
68
|
+
return "a value that could not be converted to a string";
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export async function deliverNext(runtime, options) {
|
|
72
|
+
const adapter = requireAdapter(runtime), claimed = await claimNextDelivery(runtime, options);
|
|
73
|
+
if (claimed.status !== "claimed")
|
|
74
|
+
return claimed;
|
|
75
|
+
const { claim } = claimed, deliveryId = claim.receipt.deliveryId;
|
|
76
|
+
if (options?.signal?.aborted === !0 || claim.signal.aborted || runtime.disposal.aborted)
|
|
77
|
+
return abandonBeforeSend(runtime, claim);
|
|
78
|
+
const begun = await beginAttempt(runtime, { deliveryId, attemptToken: claim.attemptToken });
|
|
79
|
+
if (begun.status !== "settled")
|
|
80
|
+
return {
|
|
81
|
+
status: "settled",
|
|
82
|
+
receipt: await currentReceipt(runtime, deliveryId, begun, claim.receipt),
|
|
83
|
+
committed: !1
|
|
84
|
+
};
|
|
85
|
+
const attempting = {
|
|
86
|
+
...claim,
|
|
87
|
+
receipt: begun.receipt,
|
|
88
|
+
visibilityExpiresAt: begun.receipt.visibilityExpiresAt ?? claim.visibilityExpiresAt
|
|
89
|
+
};
|
|
90
|
+
return sendAndSettle(runtime, adapter, attempting, options?.signal);
|
|
91
|
+
}
|
|
92
|
+
async function abandonBeforeSend(runtime, claim) {
|
|
93
|
+
const deliveryId = claim.receipt.deliveryId;
|
|
94
|
+
releaseAttemptController(runtime, claim.attemptToken, "The delivery request was aborted before the send began.", deliveryId);
|
|
95
|
+
return { status: "settled", receipt: runtime.disposal.aborted ? claim.receipt : await currentReceipt(runtime, deliveryId, { status: "aborted" }, claim.receipt), committed: !1 };
|
|
96
|
+
}
|
|
97
|
+
async function sendAndSettle(runtime, adapter, attempting, requestSignal) {
|
|
98
|
+
const deliveryId = attempting.receipt.deliveryId, stopRenewing = keepLeaseAlive(runtime, attempting), stopForwarding = forwardAbort(runtime, attempting.attemptToken, requestSignal);
|
|
99
|
+
let outcome;
|
|
100
|
+
try {
|
|
101
|
+
outcome = await sendOnce(runtime, adapter, attempting, requestSignal);
|
|
102
|
+
} finally {
|
|
103
|
+
stopForwarding();
|
|
104
|
+
}
|
|
105
|
+
if (runtime.disposal.aborted) {
|
|
106
|
+
stopRenewing();
|
|
107
|
+
return { status: "settled", receipt: attempting.receipt, committed: !1 };
|
|
108
|
+
}
|
|
109
|
+
const settling = settleAttempt(runtime, {
|
|
110
|
+
deliveryId,
|
|
111
|
+
attemptToken: attempting.attemptToken,
|
|
112
|
+
outcome
|
|
113
|
+
}).finally(() => {
|
|
114
|
+
stopRenewing();
|
|
115
|
+
releaseAttemptController(runtime, attempting.attemptToken, "The application outbox finished this attempt.", deliveryId);
|
|
116
|
+
}), raced = await raceAbort(() => settling, requestSignal);
|
|
117
|
+
if (raced.aborted) {
|
|
118
|
+
settling.catch(() => {
|
|
119
|
+
return;
|
|
120
|
+
});
|
|
121
|
+
return { status: "settled", receipt: attempting.receipt, committed: !1 };
|
|
122
|
+
}
|
|
123
|
+
const settled = raced.value;
|
|
124
|
+
return {
|
|
125
|
+
status: "settled",
|
|
126
|
+
receipt: await currentReceipt(runtime, deliveryId, settled, attempting.receipt),
|
|
127
|
+
committed: settled.status === "settled" || settled.status === "retrying"
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
export function requireAdapter(runtime) {
|
|
131
|
+
if (runtime.adapter === void 0)
|
|
132
|
+
throw new ApplicationDeliveryValidationError("deliverNext() and drain() require an adapter; construct the outbox with one or drive claims directly.");
|
|
133
|
+
return runtime.adapter;
|
|
134
|
+
}
|
|
135
|
+
function forwardAbort(runtime, attemptToken, requestSignal) {
|
|
136
|
+
if (requestSignal === void 0)
|
|
137
|
+
return () => {
|
|
138
|
+
return;
|
|
139
|
+
};
|
|
140
|
+
const onAbort = () => {
|
|
141
|
+
const registration = runtime.attemptControllers.get(attemptToken);
|
|
142
|
+
if (registration !== void 0 && !registration.controller.signal.aborted)
|
|
143
|
+
registration.controller.abort(requestSignal.reason);
|
|
144
|
+
};
|
|
145
|
+
if (requestSignal.aborted) {
|
|
146
|
+
onAbort();
|
|
147
|
+
return () => {
|
|
148
|
+
return;
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
requestSignal.addEventListener("abort", onAbort, { once: !0 });
|
|
152
|
+
return () => {
|
|
153
|
+
requestSignal.removeEventListener("abort", onAbort);
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function keepLeaseAlive(runtime, claim) {
|
|
157
|
+
const deliveryId = claim.receipt.deliveryId;
|
|
158
|
+
let timer = null, stopped = !1, confirmedExpiresAt = claim.visibilityExpiresAt;
|
|
159
|
+
const abort = (reason) => {
|
|
160
|
+
const registration = runtime.attemptControllers.get(claim.attemptToken);
|
|
161
|
+
if (registration !== void 0 && !registration.controller.signal.aborted)
|
|
162
|
+
registration.controller.abort(Error(reason));
|
|
163
|
+
}, arm = () => {
|
|
164
|
+
if (stopped)
|
|
165
|
+
return;
|
|
166
|
+
const remaining = confirmedExpiresAt - runtime.now();
|
|
167
|
+
if (remaining <= 0) {
|
|
168
|
+
abort("The attempt lease expired before its renewal could be confirmed.");
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
timer = setTimeout(() => {
|
|
172
|
+
timer = null;
|
|
173
|
+
renew();
|
|
174
|
+
}, Math.max(1, Math.floor(remaining / 2)));
|
|
175
|
+
timer.unref?.();
|
|
176
|
+
}, renew = async () => {
|
|
177
|
+
if (stopped)
|
|
178
|
+
return;
|
|
179
|
+
const remaining = confirmedExpiresAt - runtime.now();
|
|
180
|
+
if (remaining <= 0) {
|
|
181
|
+
abort("The attempt lease expired before its renewal could be confirmed.");
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const raced = await raceAbortWithin(() => heartbeatAttempt(runtime, { deliveryId, attemptToken: claim.attemptToken }), remaining).catch(() => null);
|
|
185
|
+
if (stopped)
|
|
186
|
+
return;
|
|
187
|
+
if (raced === null) {
|
|
188
|
+
arm();
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (raced.aborted) {
|
|
192
|
+
abort("The attempt lease expired before its renewal could be confirmed.");
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const result = raced.value;
|
|
196
|
+
if (result.status !== "renewed")
|
|
197
|
+
return;
|
|
198
|
+
confirmedExpiresAt = result.visibilityExpiresAt;
|
|
199
|
+
if (result.cancellationRequested) {
|
|
200
|
+
abort("The application outbox cancelled this delivery.");
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
arm();
|
|
204
|
+
};
|
|
205
|
+
arm();
|
|
206
|
+
return () => {
|
|
207
|
+
stopped = !0;
|
|
208
|
+
if (timer !== null)
|
|
209
|
+
clearTimeout(timer);
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
async function currentReceipt(runtime, deliveryId, result, lastObserved) {
|
|
213
|
+
if (result.receipt !== void 0)
|
|
214
|
+
return result.receipt;
|
|
215
|
+
const loaded = await loadDelivery(runtime.storage, runtime.keys, deliveryId);
|
|
216
|
+
return loaded === null ? lastObserved : toApplicationDeliveryReceipt(loaded.record);
|
|
217
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attempt-fenced settlement for the application delivery outbox (WFT-85):
|
|
3
|
+
* marking an attempt begun, heartbeat, reporting the transport outcome,
|
|
4
|
+
* cancellation, and the cleanup-state read.
|
|
5
|
+
*
|
|
6
|
+
* Every function proves the caller holds the current attempt before it writes
|
|
7
|
+
* anything, and re-reads durable state after a lost compare-and-swap rather
|
|
8
|
+
* than retrying with stale bytes. A stale claimant therefore cannot begin,
|
|
9
|
+
* heartbeat, settle, or cancel a newer attempt.
|
|
10
|
+
*
|
|
11
|
+
* Heartbeat is the one mutation that emits no fleet event: it is liveness
|
|
12
|
+
* evidence, not a disposition.
|
|
13
|
+
*
|
|
14
|
+
* @module core/outbox-settlement
|
|
15
|
+
*/
|
|
16
|
+
import type { JSONValue } from './json.ts';
|
|
17
|
+
import type { ApplicationDeliveryCancellationResult, ApplicationDeliveryCleanupResult, ApplicationDeliveryHeartbeatResult, ApplicationDeliverySettleResult } from './outbox-contract.ts';
|
|
18
|
+
import { type OutboxRuntime } from './outbox-internals.ts';
|
|
19
|
+
import type { ValidatedOutcome } from './outbox-validation.ts';
|
|
20
|
+
/**
|
|
21
|
+
* Durably mark that the current attempt is about to call the transport.
|
|
22
|
+
* `settled` here means the `attempting` record committed; the receipt says so.
|
|
23
|
+
*/
|
|
24
|
+
export declare function beginAttempt(runtime: OutboxRuntime, options: {
|
|
25
|
+
readonly deliveryId: string;
|
|
26
|
+
readonly attemptToken: string;
|
|
27
|
+
}): Promise<ApplicationDeliverySettleResult>;
|
|
28
|
+
/**
|
|
29
|
+
* Record liveness and extend visibility for the current attempt, clamped to
|
|
30
|
+
* the fixed attempt deadline. The returned `cancellationRequested` flag is the
|
|
31
|
+
* cross-process cancellation channel.
|
|
32
|
+
*/
|
|
33
|
+
export declare function heartbeatAttempt(runtime: OutboxRuntime, options: {
|
|
34
|
+
readonly deliveryId: string;
|
|
35
|
+
readonly attemptToken: string;
|
|
36
|
+
readonly transportActivity?: JSONValue | undefined;
|
|
37
|
+
}): Promise<ApplicationDeliveryHeartbeatResult>;
|
|
38
|
+
/** Settle the current attempt on a validated transport outcome. */
|
|
39
|
+
export declare function settleAttempt(runtime: OutboxRuntime, options: {
|
|
40
|
+
readonly deliveryId: string;
|
|
41
|
+
readonly attemptToken: string;
|
|
42
|
+
readonly outcome: ValidatedOutcome;
|
|
43
|
+
}): Promise<ApplicationDeliverySettleResult>;
|
|
44
|
+
/**
|
|
45
|
+
* Record a durable cancellation request and, when the claimant is in this
|
|
46
|
+
* process, abort its attempt-scoped signal after the record commits.
|
|
47
|
+
*/
|
|
48
|
+
export declare function requestCancellation(runtime: OutboxRuntime, options: {
|
|
49
|
+
readonly deliveryId: string;
|
|
50
|
+
readonly reason?: string | undefined;
|
|
51
|
+
}): Promise<ApplicationDeliveryCancellationResult>;
|
|
52
|
+
/**
|
|
53
|
+
* Read whether a cancelled delivery's attempt has finished. `pending` means
|
|
54
|
+
* the outbox has not seen the attempt settle, never that the transport
|
|
55
|
+
* stopped.
|
|
56
|
+
*/
|
|
57
|
+
export declare function readCleanupState(runtime: OutboxRuntime, deliveryId: string): Promise<ApplicationDeliveryCleanupResult>;
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { leaseCommitSerial } from "./application-primitive-attempt-registry.js";
|
|
2
|
+
import { ApplicationDeliveryValidationError } from "./outbox-guards.js";
|
|
3
|
+
import {
|
|
4
|
+
commitDeliveryTransition,
|
|
5
|
+
MAX_OUTBOX_TRANSITION_ATTEMPTS,
|
|
6
|
+
OutboxContentionError,
|
|
7
|
+
releaseAttemptController,
|
|
8
|
+
releaseAttemptsForDelivery,
|
|
9
|
+
toApplicationDeliveryReceipt
|
|
10
|
+
} from "./outbox-internals.js";
|
|
11
|
+
import { commitOutboxTransition, loadDelivery, planDeliveryTransition } from "./outbox-storage.js";
|
|
12
|
+
import {
|
|
13
|
+
isTerminalDeliveryRecord
|
|
14
|
+
} from "./outbox-transition-helpers.js";
|
|
15
|
+
import {
|
|
16
|
+
beginDeliveryAttempt,
|
|
17
|
+
heartbeatDeliveryAttempt,
|
|
18
|
+
requestDeliveryCancellation,
|
|
19
|
+
settleDeliveryAttempt
|
|
20
|
+
} from "./outbox-transitions.js";
|
|
21
|
+
import { isApplicationDeliveryLeased } from "./outbox-types.js";
|
|
22
|
+
function refusal(reason, record) {
|
|
23
|
+
return {
|
|
24
|
+
status: reason === "deadline-exceeded" ? "deadline-exceeded" : "stale",
|
|
25
|
+
receipt: toApplicationDeliveryReceipt(record)
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function releaseRefusedAttempt(runtime, deliveryId, attemptToken) {
|
|
29
|
+
releaseAttemptController(runtime, attemptToken, "This attempt is no longer current: its request was refused.", deliveryId);
|
|
30
|
+
}
|
|
31
|
+
function refuse(runtime, deliveryId, attemptToken, reason, record) {
|
|
32
|
+
if (reason === "not-applicable")
|
|
33
|
+
return { status: "settled", receipt: toApplicationDeliveryReceipt(record) };
|
|
34
|
+
if (reason === "not-attempting")
|
|
35
|
+
return { status: "stale", receipt: toApplicationDeliveryReceipt(record) };
|
|
36
|
+
releaseRefusedAttempt(runtime, deliveryId, attemptToken);
|
|
37
|
+
return refusal(reason, record);
|
|
38
|
+
}
|
|
39
|
+
async function fenced(runtime, deliveryId, attemptToken, operation, decide) {
|
|
40
|
+
for (let attempt = 1;attempt <= MAX_OUTBOX_TRANSITION_ATTEMPTS; attempt += 1) {
|
|
41
|
+
const loaded = await loadDelivery(runtime.storage, runtime.keys, deliveryId);
|
|
42
|
+
if (loaded === null) {
|
|
43
|
+
releaseRefusedAttempt(runtime, deliveryId, attemptToken);
|
|
44
|
+
return { status: "unknown" };
|
|
45
|
+
}
|
|
46
|
+
if (runtime.disposal.aborted)
|
|
47
|
+
return { status: "stale", receipt: toApplicationDeliveryReceipt(loaded.record) };
|
|
48
|
+
const now = runtime.now(), transition = decide(loaded.record, now);
|
|
49
|
+
if (!transition.ok)
|
|
50
|
+
return refuse(runtime, deliveryId, attemptToken, transition.reason, loaded.record);
|
|
51
|
+
if (!await commitDeliveryTransition(runtime, {
|
|
52
|
+
previous: loaded.record,
|
|
53
|
+
expectedBytes: loaded.bytes,
|
|
54
|
+
next: transition.next,
|
|
55
|
+
now
|
|
56
|
+
}))
|
|
57
|
+
continue;
|
|
58
|
+
if (isTerminalDeliveryRecord(transition.next) || !isApplicationDeliveryLeased(transition.next)) {
|
|
59
|
+
releaseAttemptController(runtime, attemptToken, "The application outbox released this attempt when the delivery settled.");
|
|
60
|
+
return {
|
|
61
|
+
status: isTerminalDeliveryRecord(transition.next) ? "settled" : "retrying",
|
|
62
|
+
receipt: toApplicationDeliveryReceipt(transition.next)
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return { status: "settled", receipt: toApplicationDeliveryReceipt(transition.next) };
|
|
66
|
+
}
|
|
67
|
+
throw new OutboxContentionError(operation, deliveryId);
|
|
68
|
+
}
|
|
69
|
+
export function beginAttempt(runtime, options) {
|
|
70
|
+
return fenced(runtime, options.deliveryId, options.attemptToken, "begin", (record, now) => beginDeliveryAttempt(record, { attemptToken: options.attemptToken, now }));
|
|
71
|
+
}
|
|
72
|
+
export async function heartbeatAttempt(runtime, options) {
|
|
73
|
+
for (let attempt = 1;attempt <= MAX_OUTBOX_TRANSITION_ATTEMPTS; attempt += 1) {
|
|
74
|
+
const loaded = await loadDelivery(runtime.storage, runtime.keys, options.deliveryId);
|
|
75
|
+
if (loaded === null) {
|
|
76
|
+
releaseRefusedAttempt(runtime, options.deliveryId, options.attemptToken);
|
|
77
|
+
return { status: "unknown" };
|
|
78
|
+
}
|
|
79
|
+
if (runtime.disposal.aborted)
|
|
80
|
+
return { status: "stale", receipt: toApplicationDeliveryReceipt(loaded.record) };
|
|
81
|
+
const now = runtime.now(), transition = heartbeatDeliveryAttempt(loaded.record, {
|
|
82
|
+
attemptToken: options.attemptToken,
|
|
83
|
+
now,
|
|
84
|
+
transportActivity: options.transportActivity
|
|
85
|
+
});
|
|
86
|
+
if (!transition.ok) {
|
|
87
|
+
releaseRefusedAttempt(runtime, options.deliveryId, options.attemptToken);
|
|
88
|
+
return refusal(transition.reason, loaded.record);
|
|
89
|
+
}
|
|
90
|
+
if (!await commitOutboxTransition(runtime.storage, runtime.events, planDeliveryTransition(runtime.keys, {
|
|
91
|
+
previous: loaded.record,
|
|
92
|
+
expectedBytes: loaded.bytes,
|
|
93
|
+
next: transition.next,
|
|
94
|
+
event: null,
|
|
95
|
+
now
|
|
96
|
+
})))
|
|
97
|
+
continue;
|
|
98
|
+
if (runtime.now() >= transition.next.attemptDeadlineAt) {
|
|
99
|
+
releaseRefusedAttempt(runtime, options.deliveryId, options.attemptToken);
|
|
100
|
+
return refusal("deadline-exceeded", transition.next);
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
status: "renewed",
|
|
104
|
+
visibilityExpiresAt: transition.next.visibilityExpiresAt,
|
|
105
|
+
attemptDeadlineAt: transition.next.attemptDeadlineAt,
|
|
106
|
+
cancellationRequested: transition.next.state === "cancellation-requested",
|
|
107
|
+
receipt: toApplicationDeliveryReceipt(transition.next)
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
throw new OutboxContentionError("heartbeat", options.deliveryId);
|
|
111
|
+
}
|
|
112
|
+
export function settleAttempt(runtime, options) {
|
|
113
|
+
return fenced(runtime, options.deliveryId, options.attemptToken, "settle", (record, now) => settleDeliveryAttempt(record, {
|
|
114
|
+
attemptToken: options.attemptToken,
|
|
115
|
+
now,
|
|
116
|
+
outcome: options.outcome,
|
|
117
|
+
retryBackoffMs: runtime.policy.retryBackoffMs,
|
|
118
|
+
maxRetryBackoffMs: runtime.policy.maxRetryBackoffMs
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
function abortLocalClaimant(runtime, attemptToken) {
|
|
122
|
+
const registration = runtime.attemptControllers.get(attemptToken);
|
|
123
|
+
if (registration !== void 0 && !registration.controller.signal.aborted)
|
|
124
|
+
registration.controller.abort(Error("The application outbox cancelled this delivery."));
|
|
125
|
+
}
|
|
126
|
+
function releaseTerminalAttempts(runtime, deliveryId, observedAt) {
|
|
127
|
+
releaseAttemptsForDelivery(runtime, deliveryId, "This delivery is terminal; its attempt is over.", void 0, observedAt);
|
|
128
|
+
}
|
|
129
|
+
export async function requestCancellation(runtime, options) {
|
|
130
|
+
for (let attempt = 1;attempt <= MAX_OUTBOX_TRANSITION_ATTEMPTS; attempt += 1) {
|
|
131
|
+
const observedAt = leaseCommitSerial(), loaded = await loadDelivery(runtime.storage, runtime.keys, options.deliveryId);
|
|
132
|
+
if (loaded === null) {
|
|
133
|
+
releaseTerminalAttempts(runtime, options.deliveryId, observedAt);
|
|
134
|
+
return { status: "unknown" };
|
|
135
|
+
}
|
|
136
|
+
if (runtime.disposal.aborted)
|
|
137
|
+
throw new ApplicationDeliveryValidationError("This application outbox has been disposed.");
|
|
138
|
+
const now = runtime.now(), transition = requestDeliveryCancellation(loaded.record, { now, reason: options.reason });
|
|
139
|
+
if (!transition.ok) {
|
|
140
|
+
if (transition.reason === "not-leased" && isApplicationDeliveryLeased(loaded.record)) {
|
|
141
|
+
abortLocalClaimant(runtime, loaded.record.attemptToken);
|
|
142
|
+
return {
|
|
143
|
+
status: "requested",
|
|
144
|
+
receipt: toApplicationDeliveryReceipt(loaded.record),
|
|
145
|
+
cleanupPending: !0
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
releaseTerminalAttempts(runtime, options.deliveryId, observedAt);
|
|
149
|
+
return { status: "already-terminal", receipt: toApplicationDeliveryReceipt(loaded.record) };
|
|
150
|
+
}
|
|
151
|
+
if (!await commitDeliveryTransition(runtime, {
|
|
152
|
+
previous: loaded.record,
|
|
153
|
+
expectedBytes: loaded.bytes,
|
|
154
|
+
next: transition.next,
|
|
155
|
+
now
|
|
156
|
+
}))
|
|
157
|
+
continue;
|
|
158
|
+
if (transition.next.state === "cancellation-requested") {
|
|
159
|
+
abortLocalClaimant(runtime, transition.next.attemptToken);
|
|
160
|
+
return {
|
|
161
|
+
status: "requested",
|
|
162
|
+
receipt: toApplicationDeliveryReceipt(transition.next),
|
|
163
|
+
cleanupPending: !0
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
if (isApplicationDeliveryLeased(loaded.record))
|
|
167
|
+
releaseAttemptController(runtime, loaded.record.attemptToken, "The application outbox cancelled this delivery before its send began.");
|
|
168
|
+
return { status: "cancelled", receipt: toApplicationDeliveryReceipt(transition.next) };
|
|
169
|
+
}
|
|
170
|
+
throw new OutboxContentionError("cancel", options.deliveryId);
|
|
171
|
+
}
|
|
172
|
+
export async function readCleanupState(runtime, deliveryId) {
|
|
173
|
+
const observedAt = leaseCommitSerial(), loaded = await loadDelivery(runtime.storage, runtime.keys, deliveryId);
|
|
174
|
+
if (loaded === null) {
|
|
175
|
+
releaseAttemptsForDelivery(runtime, deliveryId, "This delivery no longer exists; its receipt was retired.", void 0, observedAt);
|
|
176
|
+
return { status: "unknown" };
|
|
177
|
+
}
|
|
178
|
+
releaseAttemptsForDelivery(runtime, deliveryId, "This attempt is no longer the current lease on its delivery.", isApplicationDeliveryLeased(loaded.record) ? loaded.record.attemptToken : void 0, observedAt);
|
|
179
|
+
const receipt = toApplicationDeliveryReceipt(loaded.record);
|
|
180
|
+
if (receipt.terminalAt !== void 0 && receipt.cleanupPending !== !0)
|
|
181
|
+
return { status: "settled", receipt };
|
|
182
|
+
return { status: "pending", receipt };
|
|
183
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage access for the application delivery outbox (WFT-85): bound keys,
|
|
3
|
+
* record loads that keep the exact bytes for compare-and-swap, index
|
|
4
|
+
* maintenance, and the transition plan the shared commit executes.
|
|
5
|
+
*
|
|
6
|
+
* @module core/outbox-storage
|
|
7
|
+
*/
|
|
8
|
+
import type { BatchOperation, ConditionalBatchCondition, Storage } from '../storage/interface.ts';
|
|
9
|
+
import { type ApplicationCommitPlan, type ApplicationEventSink } from './application-primitive-commit.ts';
|
|
10
|
+
import type { LoadedDeliveryRecord } from './outbox-contract.ts';
|
|
11
|
+
import { type ApplicationDeliveryIdempotencyRecord, type ApplicationDeliveryRecord, type OutboxRecord } from './outbox-types.ts';
|
|
12
|
+
/** Every key builder for one `(namespace, ownerId)` outbox, bound once. */
|
|
13
|
+
export type OutboxKeys = Readonly<{
|
|
14
|
+
header: string;
|
|
15
|
+
sinkProbe: (nonce: string) => string;
|
|
16
|
+
deliveryPrefix: string;
|
|
17
|
+
delivery: (deliveryId: string) => string;
|
|
18
|
+
duePrefix: string;
|
|
19
|
+
due: (availableAt: number, deliveryId: string) => string;
|
|
20
|
+
bySequencePrefix: string;
|
|
21
|
+
bySequence: (sequence: number) => string;
|
|
22
|
+
idempotency: (key: string) => string;
|
|
23
|
+
terminalPrefix: string;
|
|
24
|
+
terminal: (terminalAt: number, deliveryId: string) => string;
|
|
25
|
+
}>;
|
|
26
|
+
/** Bind every outbox storage key to one namespace and owner. */
|
|
27
|
+
export declare function createOutboxKeys(namespace: string, ownerId: string): OutboxKeys;
|
|
28
|
+
/** The empty header an outbox starts from. */
|
|
29
|
+
export declare function emptyOutboxRecord(namespace: string, ownerId: string): OutboxRecord;
|
|
30
|
+
/** A header read together with the exact bytes it decoded from. */
|
|
31
|
+
export type LoadedOutboxRecord = {
|
|
32
|
+
readonly record: OutboxRecord;
|
|
33
|
+
/** `null` when the outbox has never been written. */
|
|
34
|
+
readonly bytes: Uint8Array | null;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Read the per-outbox header, treating an absent key as a fresh outbox.
|
|
38
|
+
*
|
|
39
|
+
* @throws {PersistedDataCorruptError} When the stored header is malformed.
|
|
40
|
+
*/
|
|
41
|
+
export declare function loadOutboxHeader(storage: Storage, keys: OutboxKeys, namespace: string, ownerId: string): Promise<LoadedOutboxRecord>;
|
|
42
|
+
/**
|
|
43
|
+
* Read one delivery record with the exact bytes it decoded from.
|
|
44
|
+
*
|
|
45
|
+
* @throws {PersistedDataCorruptError} When the stored record is malformed.
|
|
46
|
+
*/
|
|
47
|
+
export declare function loadDelivery(storage: Storage, keys: OutboxKeys, deliveryId: string): Promise<LoadedDeliveryRecord | null>;
|
|
48
|
+
/**
|
|
49
|
+
* Read the idempotency binding for a retry key.
|
|
50
|
+
*
|
|
51
|
+
* @throws {PersistedDataCorruptError} When the stored binding is malformed.
|
|
52
|
+
*/
|
|
53
|
+
export declare function loadDeliveryIdempotencyBinding(storage: Storage, keys: OutboxKeys, idempotencyKey: string): Promise<{
|
|
54
|
+
readonly record: ApplicationDeliveryIdempotencyRecord;
|
|
55
|
+
readonly bytes: Uint8Array;
|
|
56
|
+
} | null>;
|
|
57
|
+
/** One entry of the time-keyed due index. */
|
|
58
|
+
export type DueEntry = {
|
|
59
|
+
readonly key: string;
|
|
60
|
+
readonly bytes: Uint8Array;
|
|
61
|
+
readonly deliveryId: string;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Read the earliest entries of the due index.
|
|
65
|
+
*
|
|
66
|
+
* The index is keyed by `availableAt`, so the first entry is the earliest
|
|
67
|
+
* delivery, due or not. The caller decides whether it is claimable now.
|
|
68
|
+
*
|
|
69
|
+
* @throws {PersistedDataCorruptError} When an index entry is malformed, or names a delivery other than the one its key names.
|
|
70
|
+
*/
|
|
71
|
+
export declare function loadDueHead(storage: Storage, keys: OutboxKeys, limit: number): Promise<DueEntry[]>;
|
|
72
|
+
/**
|
|
73
|
+
* Put/delete operations that keep the due and terminal indexes consistent
|
|
74
|
+
* with a record's new state.
|
|
75
|
+
*
|
|
76
|
+
* Unlike the mailbox's FIFO index, the due key embeds `availableAt`, which a
|
|
77
|
+
* reschedule changes: the entry under the previous instant is deleted and one
|
|
78
|
+
* under the new instant is written. A terminal record moving between terminal
|
|
79
|
+
* dispositions (an operator dead-letter of a parked delivery) likewise moves
|
|
80
|
+
* its retention entry to its new `terminalAt`.
|
|
81
|
+
*/
|
|
82
|
+
export declare function indexOperationsFor(keys: OutboxKeys, previous: ApplicationDeliveryRecord | null, next: ApplicationDeliveryRecord): BatchOperation[];
|
|
83
|
+
/**
|
|
84
|
+
* Commit one outbox transition, atomically with its fleet event when a sink is
|
|
85
|
+
* configured. The outbox's binding of the shared commit.
|
|
86
|
+
*/
|
|
87
|
+
export declare function commitOutboxTransition(storage: Storage, events: ApplicationEventSink | undefined, plan: ApplicationCommitPlan): Promise<boolean>;
|
|
88
|
+
/**
|
|
89
|
+
* Persist a delivery record plus its index maintenance as one plan.
|
|
90
|
+
*
|
|
91
|
+
* `expectedBytes` must be the exact bytes the record was read as, never a
|
|
92
|
+
* re-encoding of the decoded value.
|
|
93
|
+
*/
|
|
94
|
+
export declare function planDeliveryTransition(keys: OutboxKeys, options: {
|
|
95
|
+
readonly previous: ApplicationDeliveryRecord | null;
|
|
96
|
+
readonly expectedBytes: Uint8Array | null;
|
|
97
|
+
readonly next: ApplicationDeliveryRecord;
|
|
98
|
+
readonly event: {
|
|
99
|
+
readonly kind: string;
|
|
100
|
+
readonly payload: unknown;
|
|
101
|
+
} | null;
|
|
102
|
+
readonly now: number;
|
|
103
|
+
readonly extraConditions?: readonly ConditionalBatchCondition[] | undefined;
|
|
104
|
+
readonly extraOperations?: readonly BatchOperation[] | undefined;
|
|
105
|
+
}): ApplicationCommitPlan;
|
|
106
|
+
/** The put operation that advances the outbox header. */
|
|
107
|
+
export declare function headerOperation(keys: OutboxKeys, record: OutboxRecord): BatchOperation;
|