@greatwait/contracts 0.1.0-alpha.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/LICENSE +21 -0
- package/README.md +77 -0
- package/dist/authority.d.ts +217 -0
- package/dist/authority.js +261 -0
- package/dist/callback-ownership.d.ts +58 -0
- package/dist/callback-ownership.js +71 -0
- package/dist/callback-safety.d.ts +38 -0
- package/dist/callback-safety.js +403 -0
- package/dist/compatibility.d.ts +82 -0
- package/dist/compatibility.js +115 -0
- package/dist/delivery.d.ts +401 -0
- package/dist/delivery.js +311 -0
- package/dist/endpoints.d.ts +305 -0
- package/dist/endpoints.js +179 -0
- package/dist/events.d.ts +62 -0
- package/dist/events.js +38 -0
- package/dist/first-value-journey.d.ts +305 -0
- package/dist/first-value-journey.js +466 -0
- package/dist/forecast.d.ts +443 -0
- package/dist/forecast.js +729 -0
- package/dist/identifiers.d.ts +109 -0
- package/dist/identifiers.js +131 -0
- package/dist/identity.d.ts +196 -0
- package/dist/identity.js +182 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +27 -0
- package/dist/mcp.d.ts +51 -0
- package/dist/mcp.js +56 -0
- package/dist/oauth.d.ts +796 -0
- package/dist/oauth.js +708 -0
- package/dist/operations.d.ts +3985 -0
- package/dist/operations.js +1837 -0
- package/dist/partition.d.ts +771 -0
- package/dist/partition.js +206 -0
- package/dist/policy.d.ts +93 -0
- package/dist/policy.js +180 -0
- package/dist/problems.d.ts +158 -0
- package/dist/problems.js +104 -0
- package/dist/protocol.d.ts +308 -0
- package/dist/protocol.js +515 -0
- package/dist/scheduling-records.d.ts +395 -0
- package/dist/scheduling-records.js +284 -0
- package/dist/scheduling.d.ts +1098 -0
- package/dist/scheduling.js +1116 -0
- package/dist/secrets.d.ts +383 -0
- package/dist/secrets.js +272 -0
- package/dist/signatures.d.ts +704 -0
- package/dist/signatures.js +867 -0
- package/dist/tenancy.d.ts +178 -0
- package/dist/tenancy.js +145 -0
- package/dist/test-sink.d.ts +986 -0
- package/dist/test-sink.js +755 -0
- package/dist/time.d.ts +45 -0
- package/dist/time.js +128 -0
- package/dist/usage.d.ts +121 -0
- package/dist/usage.js +200 -0
- package/dist/webhooks.d.ts +116 -0
- package/dist/webhooks.js +161 -0
- package/dist/workspace.d.ts +92 -0
- package/dist/workspace.js +59 -0
- package/generated/v1/callback-delivery-signature.json +467 -0
- package/generated/v1/callback-endpoint-verification.json +59 -0
- package/generated/v1/callback-receiver.json +241 -0
- package/generated/v1/examples/attempts.list.response.json +24 -0
- package/generated/v1/examples/attempts.read.response.json +20 -0
- package/generated/v1/examples/credentials.create.request.json +5 -0
- package/generated/v1/examples/credentials.create.response.json +10 -0
- package/generated/v1/examples/credentials.list.response.json +17 -0
- package/generated/v1/examples/credentials.revoke.response.json +11 -0
- package/generated/v1/examples/credentials.rotate.request.json +3 -0
- package/generated/v1/examples/credentials.rotate.response.json +22 -0
- package/generated/v1/examples/endpoints.create.request.json +3 -0
- package/generated/v1/examples/endpoints.create.response.json +16 -0
- package/generated/v1/examples/endpoints.disable.response.json +15 -0
- package/generated/v1/examples/endpoints.list.response.json +21 -0
- package/generated/v1/examples/endpoints.read.response.json +14 -0
- package/generated/v1/examples/endpoints.rotateSigningKey.request.json +3 -0
- package/generated/v1/examples/endpoints.rotateSigningKey.response.json +23 -0
- package/generated/v1/examples/endpoints.update.request.json +3 -0
- package/generated/v1/examples/endpoints.update.response.json +13 -0
- package/generated/v1/examples/endpoints.verify.response.json +15 -0
- package/generated/v1/examples/forecast.read.response.json +28 -0
- package/generated/v1/examples/forecast.windows.response.json +21 -0
- package/generated/v1/examples/grants.list.response.json +17 -0
- package/generated/v1/examples/grants.revoke.response.json +16 -0
- package/generated/v1/examples/meta.scaffold.response.json +5 -0
- package/generated/v1/examples/meta.version.response.json +5 -0
- package/generated/v1/examples/occurrences.list.response.json +17 -0
- package/generated/v1/examples/occurrences.read.response.json +15 -0
- package/generated/v1/examples/occurrences.redeliver.response.json +16 -0
- package/generated/v1/examples/projects.create.request.json +3 -0
- package/generated/v1/examples/projects.create.response.json +5 -0
- package/generated/v1/examples/projects.list.response.json +9 -0
- package/generated/v1/examples/schedules.archive.response.json +25 -0
- package/generated/v1/examples/schedules.cancel.response.json +24 -0
- package/generated/v1/examples/schedules.create.request.json +9 -0
- package/generated/v1/examples/schedules.create.response.json +25 -0
- package/generated/v1/examples/schedules.list.response.json +23 -0
- package/generated/v1/examples/schedules.pause.response.json +23 -0
- package/generated/v1/examples/schedules.read.response.json +25 -0
- package/generated/v1/examples/schedules.replace.request.json +8 -0
- package/generated/v1/examples/schedules.replace.response.json +25 -0
- package/generated/v1/examples/schedules.resume.response.json +23 -0
- package/generated/v1/examples/sinkdeliveries.list.response.json +19 -0
- package/generated/v1/examples/sinkdeliveries.read.response.json +16 -0
- package/generated/v1/examples/testsinks.configure.request.json +3 -0
- package/generated/v1/examples/testsinks.configure.response.json +13 -0
- package/generated/v1/examples/testsinks.create.request.json +8 -0
- package/generated/v1/examples/testsinks.create.response.json +13 -0
- package/generated/v1/examples/testsinks.delete.response.json +13 -0
- package/generated/v1/examples/testsinks.list.response.json +20 -0
- package/generated/v1/examples/testsinks.read.response.json +14 -0
- package/generated/v1/examples/usage.read.response.json +20 -0
- package/generated/v1/first-value-journey.json +436 -0
- package/generated/v1/manifest.json +1037 -0
- package/generated/v1/openapi.json +23665 -0
- package/generated/v1/policy-matrix.json +305 -0
- package/generated/v1/schemas/attempts.list.query.json +16 -0
- package/generated/v1/schemas/attempts.list.response.json +175 -0
- package/generated/v1/schemas/attempts.read.response.json +168 -0
- package/generated/v1/schemas/credentials.create.request.json +39 -0
- package/generated/v1/schemas/credentials.create.response.json +88 -0
- package/generated/v1/schemas/credentials.list.query.json +16 -0
- package/generated/v1/schemas/credentials.list.response.json +124 -0
- package/generated/v1/schemas/credentials.revoke.response.json +84 -0
- package/generated/v1/schemas/credentials.rotate.request.json +11 -0
- package/generated/v1/schemas/credentials.rotate.response.json +171 -0
- package/generated/v1/schemas/endpoints.create.request.json +12 -0
- package/generated/v1/schemas/endpoints.create.response.json +115 -0
- package/generated/v1/schemas/endpoints.disable.response-headers.json +11 -0
- package/generated/v1/schemas/endpoints.disable.response.json +100 -0
- package/generated/v1/schemas/endpoints.list.query.json +16 -0
- package/generated/v1/schemas/endpoints.list.response.json +146 -0
- package/generated/v1/schemas/endpoints.read.response-headers.json +11 -0
- package/generated/v1/schemas/endpoints.read.response.json +100 -0
- package/generated/v1/schemas/endpoints.rotateSigningKey.request-headers.json +10 -0
- package/generated/v1/schemas/endpoints.rotateSigningKey.request.json +11 -0
- package/generated/v1/schemas/endpoints.rotateSigningKey.response-headers.json +11 -0
- package/generated/v1/schemas/endpoints.rotateSigningKey.response.json +118 -0
- package/generated/v1/schemas/endpoints.update.request-headers.json +10 -0
- package/generated/v1/schemas/endpoints.update.request.json +12 -0
- package/generated/v1/schemas/endpoints.update.response-headers.json +11 -0
- package/generated/v1/schemas/endpoints.update.response.json +100 -0
- package/generated/v1/schemas/endpoints.verify.request-headers.json +10 -0
- package/generated/v1/schemas/endpoints.verify.response-headers.json +11 -0
- package/generated/v1/schemas/endpoints.verify.response.json +100 -0
- package/generated/v1/schemas/forecast.read.query.json +26 -0
- package/generated/v1/schemas/forecast.read.response.json +100 -0
- package/generated/v1/schemas/forecast.windows.query.json +26 -0
- package/generated/v1/schemas/forecast.windows.response.json +94 -0
- package/generated/v1/schemas/grants.list.response.json +98 -0
- package/generated/v1/schemas/grants.revoke.response.json +95 -0
- package/generated/v1/schemas/meta.scaffold.response.json +36 -0
- package/generated/v1/schemas/meta.version.response.json +19 -0
- package/generated/v1/schemas/occurrences.list.query.json +16 -0
- package/generated/v1/schemas/occurrences.list.response.json +83 -0
- package/generated/v1/schemas/occurrences.read.response.json +76 -0
- package/generated/v1/schemas/occurrences.redeliver.request-headers.json +13 -0
- package/generated/v1/schemas/occurrences.redeliver.response-headers.json +10 -0
- package/generated/v1/schemas/occurrences.redeliver.response.json +168 -0
- package/generated/v1/schemas/projects.create.request.json +12 -0
- package/generated/v1/schemas/projects.create.response.json +25 -0
- package/generated/v1/schemas/projects.list.response.json +35 -0
- package/generated/v1/schemas/schedules.archive.request-headers.json +10 -0
- package/generated/v1/schemas/schedules.archive.response-headers.json +11 -0
- package/generated/v1/schemas/schedules.archive.response.json +189 -0
- package/generated/v1/schemas/schedules.cancel.request-headers.json +10 -0
- package/generated/v1/schemas/schedules.cancel.response-headers.json +11 -0
- package/generated/v1/schemas/schedules.cancel.response.json +189 -0
- package/generated/v1/schemas/schedules.create.request-headers.json +12 -0
- package/generated/v1/schemas/schedules.create.request.json +99 -0
- package/generated/v1/schemas/schedules.create.response-headers.json +15 -0
- package/generated/v1/schemas/schedules.create.response.json +189 -0
- package/generated/v1/schemas/schedules.list.query.json +16 -0
- package/generated/v1/schemas/schedules.list.response.json +170 -0
- package/generated/v1/schemas/schedules.pause.request-headers.json +10 -0
- package/generated/v1/schemas/schedules.pause.response-headers.json +11 -0
- package/generated/v1/schemas/schedules.pause.response.json +189 -0
- package/generated/v1/schemas/schedules.read.response-headers.json +11 -0
- package/generated/v1/schemas/schedules.read.response.json +189 -0
- package/generated/v1/schemas/schedules.replace.request-headers.json +10 -0
- package/generated/v1/schemas/schedules.replace.request.json +99 -0
- package/generated/v1/schemas/schedules.replace.response-headers.json +11 -0
- package/generated/v1/schemas/schedules.replace.response.json +189 -0
- package/generated/v1/schemas/schedules.resume.request-headers.json +10 -0
- package/generated/v1/schemas/schedules.resume.response-headers.json +11 -0
- package/generated/v1/schemas/schedules.resume.response.json +189 -0
- package/generated/v1/schemas/sinkdeliveries.list.query.json +16 -0
- package/generated/v1/schemas/sinkdeliveries.list.response.json +126 -0
- package/generated/v1/schemas/sinkdeliveries.read.response.json +98 -0
- package/generated/v1/schemas/testsinks.configure.request.json +66 -0
- package/generated/v1/schemas/testsinks.configure.response.json +115 -0
- package/generated/v1/schemas/testsinks.create.request.json +76 -0
- package/generated/v1/schemas/testsinks.create.response.json +115 -0
- package/generated/v1/schemas/testsinks.delete.response.json +115 -0
- package/generated/v1/schemas/testsinks.list.query.json +16 -0
- package/generated/v1/schemas/testsinks.list.response.json +161 -0
- package/generated/v1/schemas/testsinks.read.response.json +115 -0
- package/generated/v1/schemas/usage.read.query.json +15 -0
- package/generated/v1/schemas/usage.read.response.json +71 -0
- package/generated/v1/snippets/attempts.list.json +5 -0
- package/generated/v1/snippets/attempts.read.json +5 -0
- package/generated/v1/snippets/credentials.create.json +5 -0
- package/generated/v1/snippets/credentials.list.json +5 -0
- package/generated/v1/snippets/credentials.revoke.json +5 -0
- package/generated/v1/snippets/credentials.rotate.json +5 -0
- package/generated/v1/snippets/endpoints.create.json +5 -0
- package/generated/v1/snippets/endpoints.disable.json +5 -0
- package/generated/v1/snippets/endpoints.list.json +5 -0
- package/generated/v1/snippets/endpoints.read.json +5 -0
- package/generated/v1/snippets/endpoints.rotateSigningKey.json +5 -0
- package/generated/v1/snippets/endpoints.update.json +5 -0
- package/generated/v1/snippets/endpoints.verify.json +5 -0
- package/generated/v1/snippets/forecast.read.json +5 -0
- package/generated/v1/snippets/forecast.windows.json +5 -0
- package/generated/v1/snippets/grants.list.json +5 -0
- package/generated/v1/snippets/grants.revoke.json +5 -0
- package/generated/v1/snippets/meta.scaffold.json +5 -0
- package/generated/v1/snippets/meta.version.json +5 -0
- package/generated/v1/snippets/occurrences.list.json +5 -0
- package/generated/v1/snippets/occurrences.read.json +5 -0
- package/generated/v1/snippets/occurrences.redeliver.json +5 -0
- package/generated/v1/snippets/projects.create.json +5 -0
- package/generated/v1/snippets/projects.list.json +5 -0
- package/generated/v1/snippets/schedules.archive.json +5 -0
- package/generated/v1/snippets/schedules.cancel.json +5 -0
- package/generated/v1/snippets/schedules.create.json +5 -0
- package/generated/v1/snippets/schedules.list.json +5 -0
- package/generated/v1/snippets/schedules.pause.json +5 -0
- package/generated/v1/snippets/schedules.read.json +5 -0
- package/generated/v1/snippets/schedules.replace.json +5 -0
- package/generated/v1/snippets/schedules.resume.json +5 -0
- package/generated/v1/snippets/sinkdeliveries.list.json +5 -0
- package/generated/v1/snippets/sinkdeliveries.read.json +5 -0
- package/generated/v1/snippets/testsinks.configure.json +5 -0
- package/generated/v1/snippets/testsinks.create.json +5 -0
- package/generated/v1/snippets/testsinks.delete.json +5 -0
- package/generated/v1/snippets/testsinks.list.json +5 -0
- package/generated/v1/snippets/testsinks.read.json +5 -0
- package/generated/v1/snippets/usage.read.json +5 -0
- package/package.json +153 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* What may change, and what that costs.
|
|
4
|
+
*
|
|
5
|
+
* The contract version is dated and moves independently of any deployment. A
|
|
6
|
+
* release that ships twice a day must not imply the contract changed twice a
|
|
7
|
+
* day, and a contract change must be visible even when no code deployed.
|
|
8
|
+
*/
|
|
9
|
+
export const CONTRACT_VERSION = "2026-08-22";
|
|
10
|
+
export const contractVersionSchema = z.literal(CONTRACT_VERSION);
|
|
11
|
+
/**
|
|
12
|
+
* Three kinds of change, and only the first is free.
|
|
13
|
+
*
|
|
14
|
+
* - `additive`: existing clients keep working untouched. New optional request
|
|
15
|
+
* field, new response field, new enum member a client may *send*, new
|
|
16
|
+
* endpoint, new scope, new problem code, new member of a vocabulary the
|
|
17
|
+
* artifact publishes open. A new member a client must *receive* from a closed
|
|
18
|
+
* list is breaking — it can break exhaustive parsing.
|
|
19
|
+
* - `breaking`: some conforming client stops working. Removing or renaming a
|
|
20
|
+
* field, making an optional request field required, narrowing a type,
|
|
21
|
+
* removing an enum member a client may send, changing a problem code's
|
|
22
|
+
* status or category, changing what is signed.
|
|
23
|
+
* - `deployment-only`: no wire effect at all. Internal refactor, added index,
|
|
24
|
+
* new log field, prose in `title` or `detail`. Prose is `internal`: `title`
|
|
25
|
+
* and `detail` are explicitly not part of the contract, so editing them is
|
|
26
|
+
* not a change to a response field.
|
|
27
|
+
*
|
|
28
|
+
* The distinction is enforceable rather than advisory: `classifyChange` is
|
|
29
|
+
* exercised by fixtures, so "we thought it was additive" is a testable claim.
|
|
30
|
+
*/
|
|
31
|
+
export const CHANGE_KINDS = [
|
|
32
|
+
"additive",
|
|
33
|
+
"breaking",
|
|
34
|
+
"deployment-only",
|
|
35
|
+
];
|
|
36
|
+
export const changeKindSchema = z.enum(CHANGE_KINDS);
|
|
37
|
+
/**
|
|
38
|
+
* Declared as runtime arrays, not bare type unions, so a test can iterate the
|
|
39
|
+
* product and prove every combination is classified.
|
|
40
|
+
*
|
|
41
|
+
* A hand-written table of cases cannot do that: a combination nobody thought
|
|
42
|
+
* of is untested, and a subject or operation added later is untested by
|
|
43
|
+
* default — in a function whose whole job is to tell a caller whether a change
|
|
44
|
+
* will break customers.
|
|
45
|
+
*/
|
|
46
|
+
export const CHANGE_SUBJECTS = [
|
|
47
|
+
"endpoint",
|
|
48
|
+
"enum-member",
|
|
49
|
+
"internal",
|
|
50
|
+
"problem-code",
|
|
51
|
+
"request-field",
|
|
52
|
+
"response-field",
|
|
53
|
+
"scope",
|
|
54
|
+
"signature-input",
|
|
55
|
+
];
|
|
56
|
+
export const CHANGE_OPERATIONS = [
|
|
57
|
+
/** The meaning, status, or category of something existing was altered. */
|
|
58
|
+
"changed",
|
|
59
|
+
"added",
|
|
60
|
+
"narrowed",
|
|
61
|
+
"removed",
|
|
62
|
+
"required",
|
|
63
|
+
"widened",
|
|
64
|
+
];
|
|
65
|
+
/**
|
|
66
|
+
* Classifies a proposed change. Deliberately conservative: anything not
|
|
67
|
+
* demonstrably safe is `breaking`, because the cost of wrongly calling a
|
|
68
|
+
* breaking change additive is borne by customers.
|
|
69
|
+
*/
|
|
70
|
+
export function classifyChange(change) {
|
|
71
|
+
if (change.subject === "internal") {
|
|
72
|
+
return "deployment-only";
|
|
73
|
+
}
|
|
74
|
+
if (change.subject === "signature-input") {
|
|
75
|
+
// Any change to what is signed invalidates every receiver's verifier.
|
|
76
|
+
return "breaking";
|
|
77
|
+
}
|
|
78
|
+
if (change.operation === "removed" || change.operation === "changed") {
|
|
79
|
+
// "changed" covers altering a problem code's status or category, which the
|
|
80
|
+
// conventions document names as breaking.
|
|
81
|
+
return "breaking";
|
|
82
|
+
}
|
|
83
|
+
if (change.operation === "narrowed") {
|
|
84
|
+
// Accepting less than before breaks senders; returning less breaks readers.
|
|
85
|
+
return "breaking";
|
|
86
|
+
}
|
|
87
|
+
if (change.operation === "required") {
|
|
88
|
+
// Making a request field required breaks clients that omit it. Marking a
|
|
89
|
+
// response field always-present is merely a promise we now keep.
|
|
90
|
+
return change.subject === "request-field" ? "breaking" : "additive";
|
|
91
|
+
}
|
|
92
|
+
if (change.operation === "added") {
|
|
93
|
+
if (change.subject === "request-field") {
|
|
94
|
+
// Safe only while optional; `required` is handled above.
|
|
95
|
+
return "additive";
|
|
96
|
+
}
|
|
97
|
+
if (change.subject === "enum-member") {
|
|
98
|
+
// A new member a client may *send* is additive; a new member a client
|
|
99
|
+
// must *handle* on the wire can break exhaustive parsing — unless the
|
|
100
|
+
// artifact never published a list for it to parse exhaustively against.
|
|
101
|
+
return change.clientSupplied === true || change.openVocabulary === true
|
|
102
|
+
? "additive"
|
|
103
|
+
: "breaking";
|
|
104
|
+
}
|
|
105
|
+
return "additive";
|
|
106
|
+
}
|
|
107
|
+
// `widened`. Accepting more on the way in never breaks a sender. Returning
|
|
108
|
+
// more on the way out does: a client parsing `string` breaks the day it
|
|
109
|
+
// starts receiving `string | number`.
|
|
110
|
+
return change.subject === "response-field" ? "breaking" : "additive";
|
|
111
|
+
}
|
|
112
|
+
export function assess(change) {
|
|
113
|
+
const kind = classifyChange(change);
|
|
114
|
+
return { kind, requiresNewContractVersion: kind === "breaking" };
|
|
115
|
+
}
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import type { Authority } from "./authority.js";
|
|
2
|
+
import type { AttemptId, DeliveryId, EndpointId, OccurrenceId, ScheduleId } from "./identifiers.js";
|
|
3
|
+
import type { CallbackTrigger as AttemptTrigger } from "./signatures.js";
|
|
4
|
+
/**
|
|
5
|
+
* Versioned associated-data contract for encrypted schedule payloads.
|
|
6
|
+
*
|
|
7
|
+
* The value is persisted beside the envelope by the portable scheduling
|
|
8
|
+
* record contract. Changing it requires an explicit reader for the old value;
|
|
9
|
+
* silently trying another value would turn an authentication failure into a
|
|
10
|
+
* decryption oracle.
|
|
11
|
+
*/
|
|
12
|
+
export declare const SCHEDULE_PAYLOAD_AAD_VERSION: "greatwait.schedule-payload.v1";
|
|
13
|
+
/**
|
|
14
|
+
* The encrypted customer data carried from its authoritative partition to the
|
|
15
|
+
* network boundary. This is an internal Durable Object RPC shape, not a public
|
|
16
|
+
* schema. Its application-encryption key is deliberately distinct from the key
|
|
17
|
+
* which wraps endpoint signing secrets.
|
|
18
|
+
*/
|
|
19
|
+
export interface EncryptedSchedulePayload {
|
|
20
|
+
readonly ciphertext: string;
|
|
21
|
+
readonly keyId: string;
|
|
22
|
+
readonly nonce: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* One immutable delivery attempt, minted by the Scheduler Partition from its
|
|
26
|
+
* verified authority. No member of this tuple comes from callback request
|
|
27
|
+
* input. A logical retry mints a new attempt and delivery identity; duplicate
|
|
28
|
+
* dispatch/recovery of this same attempt keeps both identities stable.
|
|
29
|
+
*/
|
|
30
|
+
export interface CallbackAttemptDispatch {
|
|
31
|
+
readonly authority: Authority;
|
|
32
|
+
readonly attemptId: AttemptId;
|
|
33
|
+
readonly attemptNumber: number;
|
|
34
|
+
/**
|
|
35
|
+
* How this receiver says it is finished: by answering the POST, or by calling
|
|
36
|
+
* the completion URL afterwards.
|
|
37
|
+
*
|
|
38
|
+
* Carried for the same reason `maxDurationSeconds` is — the partition holds
|
|
39
|
+
* the schedule — and load-bearing twice over. It decides whether the delivery
|
|
40
|
+
* holds its socket open or is free to park on an acknowledgement, and
|
|
41
|
+
* together with the duration it decides whether this delivery occupies one of
|
|
42
|
+
* the partition's six in-flight slots at all.
|
|
43
|
+
*/
|
|
44
|
+
readonly completion: "response" | "callback";
|
|
45
|
+
readonly deliveryId: DeliveryId;
|
|
46
|
+
/** Monotonic partition fence for duplicate/late dispatch and report RPCs. */
|
|
47
|
+
readonly dispatchEpoch: number;
|
|
48
|
+
readonly encryptedPayloadEnvelope: EncryptedSchedulePayload;
|
|
49
|
+
readonly endpointId: EndpointId;
|
|
50
|
+
readonly latencyClass: "realtime" | "bulk";
|
|
51
|
+
/**
|
|
52
|
+
* How long the executor may wait for this receiver, in seconds.
|
|
53
|
+
*
|
|
54
|
+
* **Carried on the dispatch rather than resolved by the executor**, which is
|
|
55
|
+
* the rule `trigger` follows a few fields down and for the same reason: the
|
|
56
|
+
* partition is what holds the schedule, and a value the executor looked up
|
|
57
|
+
* separately would be a second opinion about the customer's own setting. It is
|
|
58
|
+
* also what makes a recovery honest — a redispatch of an attempt admitted
|
|
59
|
+
* under a five-minute setting is still that attempt, even if the schedule has
|
|
60
|
+
* been replaced with a five-second one since.
|
|
61
|
+
*/
|
|
62
|
+
readonly maxDurationSeconds: number;
|
|
63
|
+
/**
|
|
64
|
+
* Internal lost-result recovery policy. `recovery` may perform the original
|
|
65
|
+
* same-identity POST only when the executor has no ledger row;
|
|
66
|
+
* `recover_only` may read the ledger but must never perform network I/O.
|
|
67
|
+
*/
|
|
68
|
+
readonly mode: "attempt" | "recovery" | "recover_only";
|
|
69
|
+
readonly occurrenceId: OccurrenceId;
|
|
70
|
+
readonly plannedForMs: number;
|
|
71
|
+
readonly routingUrl: string;
|
|
72
|
+
readonly scheduleId: ScheduleId;
|
|
73
|
+
/** The verified project/environment partition lane used for host fairness. */
|
|
74
|
+
readonly shard: string;
|
|
75
|
+
/**
|
|
76
|
+
* Why this attempt exists. Unlike `mode` and `dispatchEpoch`, this crosses
|
|
77
|
+
* the network: it is serialized into the signed callback envelope, and it is
|
|
78
|
+
* what lets a receiver run a customer-ordered redelivery again while still
|
|
79
|
+
* suppressing a retry nobody asked for.
|
|
80
|
+
*/
|
|
81
|
+
readonly trigger: AttemptTrigger;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* What a receiver says about work it acknowledged and then finished.
|
|
85
|
+
*
|
|
86
|
+
* Here rather than beside the envelope schema because the scheduler Worker's
|
|
87
|
+
* completion route reads it and that bundle deliberately carries no Zod — this
|
|
88
|
+
* module is the Zod-free subpath. `signatures.ts` builds its enum from this
|
|
89
|
+
* list, so the vocabulary exists once.
|
|
90
|
+
*
|
|
91
|
+
* `succeeded` is what an empty body means: the overwhelmingly common report is
|
|
92
|
+
* "done", and a receiver should not have to compose a document to say it.
|
|
93
|
+
* `failed` puts the occurrence back on the ordinary retry ladder — the same
|
|
94
|
+
* place a 500 puts it — so a receiver can distinguish "my job died" from "I
|
|
95
|
+
* vanished".
|
|
96
|
+
*/
|
|
97
|
+
export declare const CALLBACK_COMPLETION_OUTCOMES: readonly ["succeeded", "failed"];
|
|
98
|
+
export type CallbackCompletionOutcome = (typeof CALLBACK_COMPLETION_OUTCOMES)[number];
|
|
99
|
+
export type CallbackAttemptClassification = "succeeded" | "retryable_failure" | "terminal_failure" | "ambiguous";
|
|
100
|
+
/**
|
|
101
|
+
* The one status that can mean "I have the work and I am not finished".
|
|
102
|
+
*
|
|
103
|
+
* Exactly `202`, and the narrowness is the point. Every other 2xx keeps the
|
|
104
|
+
* meaning it has had since the first delivery this product sent — success,
|
|
105
|
+
* now — so a receiver that has never heard of deferral cannot be parked by
|
|
106
|
+
* accident. A `200` carrying the marker is success, and so is a `204`, because
|
|
107
|
+
* the discriminator is not the whole rule: the status and the body have to
|
|
108
|
+
* agree, and only this pair opts in.
|
|
109
|
+
*/
|
|
110
|
+
export declare const CALLBACK_DEFERRAL_STATUS = 202;
|
|
111
|
+
/**
|
|
112
|
+
* How much of an acknowledgement body is read before the answer is decided.
|
|
113
|
+
*
|
|
114
|
+
* One kibibyte. An acknowledgement is a fixed object of two or three short
|
|
115
|
+
* members, so the bound is roughly twenty times what a legitimate one needs
|
|
116
|
+
* and is not a limit a receiver can meet by accident. What it stops is a
|
|
117
|
+
* receiver — or something in front of one — answering `202` with a body that
|
|
118
|
+
* is expensive to read: the delivery path reads this many bytes, decides, and
|
|
119
|
+
* cancels the rest of the stream without buffering it.
|
|
120
|
+
*
|
|
121
|
+
* **Exceeding it is not pending.** A body over the bound cannot be shown to
|
|
122
|
+
* carry the discriminator, and the safe reading of "cannot be shown" is the
|
|
123
|
+
* one that settles the attempt now rather than the one that parks a customer's
|
|
124
|
+
* occurrence on a receiver whose answer we did not finish reading.
|
|
125
|
+
*/
|
|
126
|
+
export declare const CALLBACK_ACKNOWLEDGEMENT_MAX_BYTES = 1024;
|
|
127
|
+
/**
|
|
128
|
+
* The media types an acknowledgement may be carried in.
|
|
129
|
+
*
|
|
130
|
+
* JSON, in the two spellings a receiver actually sends. A `202` whose body is
|
|
131
|
+
* HTML or plain text is an ordinary success, because whatever it says it is
|
|
132
|
+
* not this protocol.
|
|
133
|
+
*/
|
|
134
|
+
export declare const CALLBACK_ACKNOWLEDGEMENT_CONTENT_TYPES: readonly ["application/json", "application/problem+json"];
|
|
135
|
+
/**
|
|
136
|
+
* What a receiver answers with to keep the work and report later.
|
|
137
|
+
*
|
|
138
|
+
* ```json
|
|
139
|
+
* { "greatwait": { "status": "pending", "completeBy": "2026-08-23T18:00:00Z" } }
|
|
140
|
+
* ```
|
|
141
|
+
*
|
|
142
|
+
* **Namespaced under one key on purpose.** A receiver's `202` body is its own
|
|
143
|
+
* document — a job id, a queue position, a status URL — and a bare
|
|
144
|
+
* `{"status":"pending"}` would collide with the most ordinary word in that
|
|
145
|
+
* vocabulary. Nesting it means an existing receiver has to have chosen the key
|
|
146
|
+
* `greatwait` for something else before this can misfire.
|
|
147
|
+
*/
|
|
148
|
+
export declare const CALLBACK_PENDING_DISCRIMINATOR: {
|
|
149
|
+
readonly key: "greatwait";
|
|
150
|
+
readonly status: "pending";
|
|
151
|
+
};
|
|
152
|
+
/**
|
|
153
|
+
* Whether an acknowledgement asked to defer, and until when if it named an
|
|
154
|
+
* instant.
|
|
155
|
+
*
|
|
156
|
+
* `completeByMs` is what the receiver *asked for*, before clamping. Turning it
|
|
157
|
+
* into the deadline that is stored is
|
|
158
|
+
* {@link effectiveCompletionDeadlineMs}'s job, and the two are separate
|
|
159
|
+
* because only one of them is allowed to be an opinion.
|
|
160
|
+
*/
|
|
161
|
+
export type CallbackAcknowledgement = {
|
|
162
|
+
readonly pending: false;
|
|
163
|
+
} | {
|
|
164
|
+
readonly completeByMs?: number;
|
|
165
|
+
readonly pending: true;
|
|
166
|
+
};
|
|
167
|
+
/**
|
|
168
|
+
* Decide whether one receiver answer is the deferral acknowledgement.
|
|
169
|
+
*
|
|
170
|
+
* Every term is required and each one is refused on its own: the status is
|
|
171
|
+
* exactly {@link CALLBACK_DEFERRAL_STATUS}, the media type is JSON, the body
|
|
172
|
+
* fits {@link CALLBACK_ACKNOWLEDGEMENT_MAX_BYTES}, it parses, it is a JSON
|
|
173
|
+
* object rather than an array or a scalar, and it carries the discriminator.
|
|
174
|
+
* Anything that fails one of them is not pending, which for a 2xx means
|
|
175
|
+
* ordinary immediate success.
|
|
176
|
+
*
|
|
177
|
+
* **It never decides a non-2xx.** The caller classifies the response first and
|
|
178
|
+
* only asks this about a success; a `500` carrying a perfectly formed pending
|
|
179
|
+
* marker is a `500`, because a receiver that is failing does not get to park
|
|
180
|
+
* the occurrence it is failing to handle.
|
|
181
|
+
*
|
|
182
|
+
* **There is no `Array.isArray` guard, and there was one.** Mutation testing
|
|
183
|
+
* showed both — over the parsed body and over the marker — surviving deletion
|
|
184
|
+
* with every case green, and the reason is that they cannot decide anything: a
|
|
185
|
+
* JSON array has no named members, so reading the discriminator key off one is
|
|
186
|
+
* `undefined` and the very next check refuses it. A rule no input can reach
|
|
187
|
+
* reads like protection and is not, so it is gone and this paragraph is what
|
|
188
|
+
* it left behind. The array cases in the suite stay — they pin the outcome a
|
|
189
|
+
* receiver sees, which is unchanged — but they hold by construction rather
|
|
190
|
+
* than by a guard.
|
|
191
|
+
*/
|
|
192
|
+
export declare function readCallbackAcknowledgement(response: {
|
|
193
|
+
/** The decoded prefix that was read, at most the bound plus nothing. */
|
|
194
|
+
readonly body: string;
|
|
195
|
+
readonly contentType: string | null;
|
|
196
|
+
readonly status: number;
|
|
197
|
+
/** Whether the body had more bytes than the bound allowed reading. */
|
|
198
|
+
readonly truncated: boolean;
|
|
199
|
+
}): CallbackAcknowledgement;
|
|
200
|
+
/**
|
|
201
|
+
* The deadline this deferral actually gets, in epoch milliseconds.
|
|
202
|
+
*
|
|
203
|
+
* The receiver may bring it forward and may not push it out. `maximumMs` is
|
|
204
|
+
* the deadline already inside the signed envelope — the schedule's own
|
|
205
|
+
* duration allowance measured from the attempt — so a receiver asking for
|
|
206
|
+
* longer is asking to change what its customer bought, and a receiver asking
|
|
207
|
+
* for an instant that has already passed is asking for something no wait can
|
|
208
|
+
* satisfy.
|
|
209
|
+
*
|
|
210
|
+
* **All three refusals fall back to the maximum rather than to immediate
|
|
211
|
+
* success.** The acknowledgement itself is what took the work; a malformed
|
|
212
|
+
* hint about *when* does not unmake that, and settling such an attempt
|
|
213
|
+
* `succeeded` would record a result the receiver never reported.
|
|
214
|
+
*/
|
|
215
|
+
export declare function effectiveCompletionDeadlineMs(requestedMs: number | undefined, maximumMs: number, nowMs: number): number;
|
|
216
|
+
/** Safe, bounded diagnostics. Never contains a URL, payload, or secret. */
|
|
217
|
+
/**
|
|
218
|
+
* Safe delivery diagnostics shared by the executor result and the public
|
|
219
|
+
* attempt timeline.
|
|
220
|
+
*
|
|
221
|
+
* Values are deliberately short categories. A URL, response body, payload,
|
|
222
|
+
* exception message, signing key, or transport trace has no representation in
|
|
223
|
+
* this vocabulary, so the durable history cannot accidentally turn private
|
|
224
|
+
* callback material into a public diagnostic.
|
|
225
|
+
*/
|
|
226
|
+
export declare const CALLBACK_ATTEMPT_DIAGNOSTICS: readonly ["completion_timed_out", "delivered", "duplicate_inflight", "endpoint_unavailable", "endpoint_unsafe", "http_refused", "http_retryable", "http_terminal", "identity_conflict", "network_ambiguous", "payload_unavailable", "rate_limited"];
|
|
227
|
+
export type CallbackAttemptDiagnostic = (typeof CALLBACK_ATTEMPT_DIAGNOSTICS)[number];
|
|
228
|
+
/**
|
|
229
|
+
* Statuses where the receiver is refusing *this delivery, right now* rather
|
|
230
|
+
* than objecting to the message it carries.
|
|
231
|
+
*
|
|
232
|
+
* **The line is whether resending the identical bytes could ever get a
|
|
233
|
+
* different answer.** Every callback attempt re-sends the same signed envelope,
|
|
234
|
+
* so a receiver that rejects the message itself — malformed, too large, wrong
|
|
235
|
+
* media type, gone, refused for legal reasons — will reject it identically for
|
|
236
|
+
* as long as the occurrence lives, and retrying is pure cost on the population
|
|
237
|
+
* least likely to recover. A receiver that refuses *us* has a cause someone
|
|
238
|
+
* fixes: an expired or rotated credential, a WAF rule that started matching, a
|
|
239
|
+
* deploy window where the route is briefly absent.
|
|
240
|
+
*
|
|
241
|
+
* | Status | Why it is here |
|
|
242
|
+
* | ------ | -------------- |
|
|
243
|
+
* | 401 | credential expired, rotated, or not yet deployed |
|
|
244
|
+
* | 402 | receiver's own service suspended, restored on payment |
|
|
245
|
+
* | 403 | WAF or bot rule, revoked permission, IP block |
|
|
246
|
+
* | 404 | route absent during a deploy or a partial rollout |
|
|
247
|
+
* | 405 | method absent during a deploy or a partial rollout |
|
|
248
|
+
* | 407 | an egress proxy in front of the receiver wants credentials |
|
|
249
|
+
* | 421 | misdirected during a routing or certificate change |
|
|
250
|
+
* | 423 | the target resource is temporarily locked |
|
|
251
|
+
* | 424 | the receiver's own dependency failed |
|
|
252
|
+
* | 425 | the receiver asked us not to risk a replay yet — literally "retry later" |
|
|
253
|
+
*
|
|
254
|
+
* **409 is deliberately not here**, though it is a refusal in the plain
|
|
255
|
+
* reading. From a callback receiver a conflict overwhelmingly means "I already
|
|
256
|
+
* have this delivery", which the identical retry cannot resolve and which is
|
|
257
|
+
* the receiver's idempotency working rather than failing.
|
|
258
|
+
*
|
|
259
|
+
* **An unrecognised 4xx is not here either.** RFC 7231 says an unknown 4xx must
|
|
260
|
+
* be treated as a 400, so the default is terminal and this list is the whole of
|
|
261
|
+
* the exception. Adding to it is a product decision, not a lookup.
|
|
262
|
+
*/
|
|
263
|
+
export declare const CALLBACK_REFUSAL_STATUSES: readonly [401, 402, 403, 404, 405, 407, 421, 423, 424, 425];
|
|
264
|
+
/** Whether a receiver refused this delivery in a way that may recover. */
|
|
265
|
+
export declare function isCallbackRefusalStatus(status: number): boolean;
|
|
266
|
+
/** Stable outcome of the one network attempt an executor invocation may run. */
|
|
267
|
+
export interface CallbackAttemptResult {
|
|
268
|
+
readonly classification: CallbackAttemptClassification;
|
|
269
|
+
readonly diagnostic: CallbackAttemptDiagnostic;
|
|
270
|
+
/** Receiver status when a response was obtained. */
|
|
271
|
+
readonly status?: number;
|
|
272
|
+
/** Bounded receiver hint, in milliseconds, for a retryable 429. */
|
|
273
|
+
readonly retryAfterMs?: number;
|
|
274
|
+
/** `tenant` applies only to this verified partition lane; `global` to host. */
|
|
275
|
+
readonly rateLimitScope?: "tenant" | "global";
|
|
276
|
+
/**
|
|
277
|
+
* Bounded, non-secret evidence about the response itself.
|
|
278
|
+
*
|
|
279
|
+
* Present whenever a response was obtained at all, including a success —
|
|
280
|
+
* status, duration and classification are kept for every attempt. The
|
|
281
|
+
* customer-content half of it, {@link CallbackResponseEvidence.snippet}, is
|
|
282
|
+
* present only where the policy allows one.
|
|
283
|
+
*/
|
|
284
|
+
readonly evidence?: CallbackResponseEvidence;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Response headers worth keeping, in the order one is chosen.
|
|
288
|
+
*
|
|
289
|
+
* **A closed list, and short on purpose.** A response header is receiver-
|
|
290
|
+
* controlled text that lands in durable history and on a support screen, so
|
|
291
|
+
* the question is not "is this one dangerous" but "does this one earn its
|
|
292
|
+
* place". Content type explains what the snippet is, `Retry-After` explains a
|
|
293
|
+
* pacing decision the customer can see, and one receiver-side identifier is
|
|
294
|
+
* what turns "your receiver refused this" into something an integrator can
|
|
295
|
+
* look up in their own logs.
|
|
296
|
+
*
|
|
297
|
+
* Everything else is absent by construction rather than by filtering: cookies,
|
|
298
|
+
* `Authorization`, `WWW-Authenticate`, `Set-Cookie` and every vendor header
|
|
299
|
+
* have no entry here, so no future edit can retain one by forgetting to add a
|
|
300
|
+
* denial.
|
|
301
|
+
*/
|
|
302
|
+
export declare const CALLBACK_RESPONSE_HEADER_ALLOWLIST: readonly ["content-type", "retry-after"];
|
|
303
|
+
/**
|
|
304
|
+
* Receiver-side identifiers, most specific first. **At most one is kept.**
|
|
305
|
+
*
|
|
306
|
+
* The order is which name a receiver most likely means as "look this up in my
|
|
307
|
+
* logs". Keeping several would be keeping a header set rather than an
|
|
308
|
+
* identifier, which is the thing the allowlist above exists to refuse.
|
|
309
|
+
*/
|
|
310
|
+
export declare const CALLBACK_RESPONSE_REQUEST_ID_HEADERS: readonly ["x-request-id", "request-id", "x-correlation-id", "x-amzn-requestid", "cf-ray"];
|
|
311
|
+
/** How much of any retained header value is kept, in bytes. */
|
|
312
|
+
export declare const CALLBACK_RESPONSE_HEADER_MAX_BYTES = 256;
|
|
313
|
+
/**
|
|
314
|
+
* How much of a failed response body may be retained, in bytes.
|
|
315
|
+
*
|
|
316
|
+
* Four kibibytes of the *start* of the body, read as a bounded prefix and with
|
|
317
|
+
* the rest of the stream cancelled rather than buffered. It is enough for a
|
|
318
|
+
* stack trace's first frames or a validation error list, which is what makes a
|
|
319
|
+
* failing receiver diagnosable without turning this product into a log store.
|
|
320
|
+
*/
|
|
321
|
+
export declare const CALLBACK_FAILURE_SNIPPET_MAX_BYTES = 4096;
|
|
322
|
+
/**
|
|
323
|
+
* Media types whose body may be retained as a failure snippet.
|
|
324
|
+
*
|
|
325
|
+
* Text and JSON only. A binary body has nothing a person reads in its first
|
|
326
|
+
* four kibibytes, and retaining one would mean storing an arbitrary fragment
|
|
327
|
+
* of an arbitrary file because a receiver set a header.
|
|
328
|
+
*/
|
|
329
|
+
export declare const CALLBACK_FAILURE_SNIPPET_CONTENT_TYPES: readonly ["application/json", "application/problem+json", "application/xml", "text/"];
|
|
330
|
+
/** One retained response header, already bounded and lowercased by name. */
|
|
331
|
+
export interface CallbackResponseHeader {
|
|
332
|
+
readonly name: string;
|
|
333
|
+
readonly value: string;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* What a failing receiver said, bounded and destined for encryption.
|
|
337
|
+
*
|
|
338
|
+
* `truncated` is carried rather than inferred from the length, because a body
|
|
339
|
+
* that is exactly the bound and a body that was cut at the bound are different
|
|
340
|
+
* facts and only one of them should show a customer an ellipsis.
|
|
341
|
+
*/
|
|
342
|
+
export interface CallbackFailureSnippet {
|
|
343
|
+
readonly contentType: string;
|
|
344
|
+
readonly text: string;
|
|
345
|
+
readonly truncated: boolean;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Bounded evidence about one HTTP response.
|
|
349
|
+
*
|
|
350
|
+
* Deliberately not "the response": there is no body for a success, no header
|
|
351
|
+
* outside the allowlist, and no representation at all for a transport failure
|
|
352
|
+
* that produced no response.
|
|
353
|
+
*/
|
|
354
|
+
export interface CallbackResponseEvidence {
|
|
355
|
+
readonly headers: readonly CallbackResponseHeader[];
|
|
356
|
+
/** Absent for a success, a pending acknowledgement, and a non-text body. */
|
|
357
|
+
readonly snippet?: CallbackFailureSnippet;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* What one network attempt did, including the answer that is not an outcome.
|
|
361
|
+
*
|
|
362
|
+
* **`deferred` is a third thing beside success and failure**, and it is a
|
|
363
|
+
* union member rather than a flag on {@link CallbackAttemptResult} so that
|
|
364
|
+
* every caller has to say what it does about it. A boolean on the result would
|
|
365
|
+
* have let the existing carriers keep compiling while silently settling a
|
|
366
|
+
* deferred attempt `succeeded` — which is precisely the wrong answer, and the
|
|
367
|
+
* one nothing would have reported.
|
|
368
|
+
*/
|
|
369
|
+
export type CallbackAttemptOutcome = CallbackAttemptResult | {
|
|
370
|
+
/** The instant the receiver asked for, before clamping. */
|
|
371
|
+
readonly completeByMs?: number;
|
|
372
|
+
readonly deferred: true;
|
|
373
|
+
readonly evidence: CallbackResponseEvidence;
|
|
374
|
+
readonly status: number;
|
|
375
|
+
};
|
|
376
|
+
/** Whether one network attempt asked to finish later. */
|
|
377
|
+
export declare function isDeferredAttempt(outcome: CallbackAttemptOutcome): outcome is Extract<CallbackAttemptOutcome, {
|
|
378
|
+
readonly deferred: true;
|
|
379
|
+
}>;
|
|
380
|
+
/**
|
|
381
|
+
* Host admission and delivery result. A reroute is not a delivery attempt: no
|
|
382
|
+
* request was sent and the partition may resolve the returned URL to the right
|
|
383
|
+
* host executor once, outside its transaction. Backpressure likewise consumes
|
|
384
|
+
* no receiver attempt.
|
|
385
|
+
*/
|
|
386
|
+
export type HostExecutorAttemptResult = {
|
|
387
|
+
readonly accepted: false;
|
|
388
|
+
readonly disposition: "backpressure";
|
|
389
|
+
readonly reason: "paused" | "max_connections" | "partition_cap" | "recovery_probe" | "shard_transition" | "tokens" | "realtime_share";
|
|
390
|
+
readonly retryAfterMs: number;
|
|
391
|
+
} | {
|
|
392
|
+
readonly accepted: false;
|
|
393
|
+
readonly disposition: "reroute";
|
|
394
|
+
readonly routingUrl: string;
|
|
395
|
+
} | {
|
|
396
|
+
readonly accepted: true;
|
|
397
|
+
readonly durationMs: number;
|
|
398
|
+
readonly result: CallbackAttemptResult;
|
|
399
|
+
readonly shard: number;
|
|
400
|
+
readonly shardCount: number;
|
|
401
|
+
};
|