@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.
Files changed (241) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +77 -0
  3. package/dist/authority.d.ts +217 -0
  4. package/dist/authority.js +261 -0
  5. package/dist/callback-ownership.d.ts +58 -0
  6. package/dist/callback-ownership.js +71 -0
  7. package/dist/callback-safety.d.ts +38 -0
  8. package/dist/callback-safety.js +403 -0
  9. package/dist/compatibility.d.ts +82 -0
  10. package/dist/compatibility.js +115 -0
  11. package/dist/delivery.d.ts +401 -0
  12. package/dist/delivery.js +311 -0
  13. package/dist/endpoints.d.ts +305 -0
  14. package/dist/endpoints.js +179 -0
  15. package/dist/events.d.ts +62 -0
  16. package/dist/events.js +38 -0
  17. package/dist/first-value-journey.d.ts +305 -0
  18. package/dist/first-value-journey.js +466 -0
  19. package/dist/forecast.d.ts +443 -0
  20. package/dist/forecast.js +729 -0
  21. package/dist/identifiers.d.ts +109 -0
  22. package/dist/identifiers.js +131 -0
  23. package/dist/identity.d.ts +196 -0
  24. package/dist/identity.js +182 -0
  25. package/dist/index.d.ts +27 -0
  26. package/dist/index.js +27 -0
  27. package/dist/mcp.d.ts +51 -0
  28. package/dist/mcp.js +56 -0
  29. package/dist/oauth.d.ts +796 -0
  30. package/dist/oauth.js +708 -0
  31. package/dist/operations.d.ts +3985 -0
  32. package/dist/operations.js +1837 -0
  33. package/dist/partition.d.ts +771 -0
  34. package/dist/partition.js +206 -0
  35. package/dist/policy.d.ts +93 -0
  36. package/dist/policy.js +180 -0
  37. package/dist/problems.d.ts +158 -0
  38. package/dist/problems.js +104 -0
  39. package/dist/protocol.d.ts +308 -0
  40. package/dist/protocol.js +515 -0
  41. package/dist/scheduling-records.d.ts +395 -0
  42. package/dist/scheduling-records.js +284 -0
  43. package/dist/scheduling.d.ts +1098 -0
  44. package/dist/scheduling.js +1116 -0
  45. package/dist/secrets.d.ts +383 -0
  46. package/dist/secrets.js +272 -0
  47. package/dist/signatures.d.ts +704 -0
  48. package/dist/signatures.js +867 -0
  49. package/dist/tenancy.d.ts +178 -0
  50. package/dist/tenancy.js +145 -0
  51. package/dist/test-sink.d.ts +986 -0
  52. package/dist/test-sink.js +755 -0
  53. package/dist/time.d.ts +45 -0
  54. package/dist/time.js +128 -0
  55. package/dist/usage.d.ts +121 -0
  56. package/dist/usage.js +200 -0
  57. package/dist/webhooks.d.ts +116 -0
  58. package/dist/webhooks.js +161 -0
  59. package/dist/workspace.d.ts +92 -0
  60. package/dist/workspace.js +59 -0
  61. package/generated/v1/callback-delivery-signature.json +467 -0
  62. package/generated/v1/callback-endpoint-verification.json +59 -0
  63. package/generated/v1/callback-receiver.json +241 -0
  64. package/generated/v1/examples/attempts.list.response.json +24 -0
  65. package/generated/v1/examples/attempts.read.response.json +20 -0
  66. package/generated/v1/examples/credentials.create.request.json +5 -0
  67. package/generated/v1/examples/credentials.create.response.json +10 -0
  68. package/generated/v1/examples/credentials.list.response.json +17 -0
  69. package/generated/v1/examples/credentials.revoke.response.json +11 -0
  70. package/generated/v1/examples/credentials.rotate.request.json +3 -0
  71. package/generated/v1/examples/credentials.rotate.response.json +22 -0
  72. package/generated/v1/examples/endpoints.create.request.json +3 -0
  73. package/generated/v1/examples/endpoints.create.response.json +16 -0
  74. package/generated/v1/examples/endpoints.disable.response.json +15 -0
  75. package/generated/v1/examples/endpoints.list.response.json +21 -0
  76. package/generated/v1/examples/endpoints.read.response.json +14 -0
  77. package/generated/v1/examples/endpoints.rotateSigningKey.request.json +3 -0
  78. package/generated/v1/examples/endpoints.rotateSigningKey.response.json +23 -0
  79. package/generated/v1/examples/endpoints.update.request.json +3 -0
  80. package/generated/v1/examples/endpoints.update.response.json +13 -0
  81. package/generated/v1/examples/endpoints.verify.response.json +15 -0
  82. package/generated/v1/examples/forecast.read.response.json +28 -0
  83. package/generated/v1/examples/forecast.windows.response.json +21 -0
  84. package/generated/v1/examples/grants.list.response.json +17 -0
  85. package/generated/v1/examples/grants.revoke.response.json +16 -0
  86. package/generated/v1/examples/meta.scaffold.response.json +5 -0
  87. package/generated/v1/examples/meta.version.response.json +5 -0
  88. package/generated/v1/examples/occurrences.list.response.json +17 -0
  89. package/generated/v1/examples/occurrences.read.response.json +15 -0
  90. package/generated/v1/examples/occurrences.redeliver.response.json +16 -0
  91. package/generated/v1/examples/projects.create.request.json +3 -0
  92. package/generated/v1/examples/projects.create.response.json +5 -0
  93. package/generated/v1/examples/projects.list.response.json +9 -0
  94. package/generated/v1/examples/schedules.archive.response.json +25 -0
  95. package/generated/v1/examples/schedules.cancel.response.json +24 -0
  96. package/generated/v1/examples/schedules.create.request.json +9 -0
  97. package/generated/v1/examples/schedules.create.response.json +25 -0
  98. package/generated/v1/examples/schedules.list.response.json +23 -0
  99. package/generated/v1/examples/schedules.pause.response.json +23 -0
  100. package/generated/v1/examples/schedules.read.response.json +25 -0
  101. package/generated/v1/examples/schedules.replace.request.json +8 -0
  102. package/generated/v1/examples/schedules.replace.response.json +25 -0
  103. package/generated/v1/examples/schedules.resume.response.json +23 -0
  104. package/generated/v1/examples/sinkdeliveries.list.response.json +19 -0
  105. package/generated/v1/examples/sinkdeliveries.read.response.json +16 -0
  106. package/generated/v1/examples/testsinks.configure.request.json +3 -0
  107. package/generated/v1/examples/testsinks.configure.response.json +13 -0
  108. package/generated/v1/examples/testsinks.create.request.json +8 -0
  109. package/generated/v1/examples/testsinks.create.response.json +13 -0
  110. package/generated/v1/examples/testsinks.delete.response.json +13 -0
  111. package/generated/v1/examples/testsinks.list.response.json +20 -0
  112. package/generated/v1/examples/testsinks.read.response.json +14 -0
  113. package/generated/v1/examples/usage.read.response.json +20 -0
  114. package/generated/v1/first-value-journey.json +436 -0
  115. package/generated/v1/manifest.json +1037 -0
  116. package/generated/v1/openapi.json +23665 -0
  117. package/generated/v1/policy-matrix.json +305 -0
  118. package/generated/v1/schemas/attempts.list.query.json +16 -0
  119. package/generated/v1/schemas/attempts.list.response.json +175 -0
  120. package/generated/v1/schemas/attempts.read.response.json +168 -0
  121. package/generated/v1/schemas/credentials.create.request.json +39 -0
  122. package/generated/v1/schemas/credentials.create.response.json +88 -0
  123. package/generated/v1/schemas/credentials.list.query.json +16 -0
  124. package/generated/v1/schemas/credentials.list.response.json +124 -0
  125. package/generated/v1/schemas/credentials.revoke.response.json +84 -0
  126. package/generated/v1/schemas/credentials.rotate.request.json +11 -0
  127. package/generated/v1/schemas/credentials.rotate.response.json +171 -0
  128. package/generated/v1/schemas/endpoints.create.request.json +12 -0
  129. package/generated/v1/schemas/endpoints.create.response.json +115 -0
  130. package/generated/v1/schemas/endpoints.disable.response-headers.json +11 -0
  131. package/generated/v1/schemas/endpoints.disable.response.json +100 -0
  132. package/generated/v1/schemas/endpoints.list.query.json +16 -0
  133. package/generated/v1/schemas/endpoints.list.response.json +146 -0
  134. package/generated/v1/schemas/endpoints.read.response-headers.json +11 -0
  135. package/generated/v1/schemas/endpoints.read.response.json +100 -0
  136. package/generated/v1/schemas/endpoints.rotateSigningKey.request-headers.json +10 -0
  137. package/generated/v1/schemas/endpoints.rotateSigningKey.request.json +11 -0
  138. package/generated/v1/schemas/endpoints.rotateSigningKey.response-headers.json +11 -0
  139. package/generated/v1/schemas/endpoints.rotateSigningKey.response.json +118 -0
  140. package/generated/v1/schemas/endpoints.update.request-headers.json +10 -0
  141. package/generated/v1/schemas/endpoints.update.request.json +12 -0
  142. package/generated/v1/schemas/endpoints.update.response-headers.json +11 -0
  143. package/generated/v1/schemas/endpoints.update.response.json +100 -0
  144. package/generated/v1/schemas/endpoints.verify.request-headers.json +10 -0
  145. package/generated/v1/schemas/endpoints.verify.response-headers.json +11 -0
  146. package/generated/v1/schemas/endpoints.verify.response.json +100 -0
  147. package/generated/v1/schemas/forecast.read.query.json +26 -0
  148. package/generated/v1/schemas/forecast.read.response.json +100 -0
  149. package/generated/v1/schemas/forecast.windows.query.json +26 -0
  150. package/generated/v1/schemas/forecast.windows.response.json +94 -0
  151. package/generated/v1/schemas/grants.list.response.json +98 -0
  152. package/generated/v1/schemas/grants.revoke.response.json +95 -0
  153. package/generated/v1/schemas/meta.scaffold.response.json +36 -0
  154. package/generated/v1/schemas/meta.version.response.json +19 -0
  155. package/generated/v1/schemas/occurrences.list.query.json +16 -0
  156. package/generated/v1/schemas/occurrences.list.response.json +83 -0
  157. package/generated/v1/schemas/occurrences.read.response.json +76 -0
  158. package/generated/v1/schemas/occurrences.redeliver.request-headers.json +13 -0
  159. package/generated/v1/schemas/occurrences.redeliver.response-headers.json +10 -0
  160. package/generated/v1/schemas/occurrences.redeliver.response.json +168 -0
  161. package/generated/v1/schemas/projects.create.request.json +12 -0
  162. package/generated/v1/schemas/projects.create.response.json +25 -0
  163. package/generated/v1/schemas/projects.list.response.json +35 -0
  164. package/generated/v1/schemas/schedules.archive.request-headers.json +10 -0
  165. package/generated/v1/schemas/schedules.archive.response-headers.json +11 -0
  166. package/generated/v1/schemas/schedules.archive.response.json +189 -0
  167. package/generated/v1/schemas/schedules.cancel.request-headers.json +10 -0
  168. package/generated/v1/schemas/schedules.cancel.response-headers.json +11 -0
  169. package/generated/v1/schemas/schedules.cancel.response.json +189 -0
  170. package/generated/v1/schemas/schedules.create.request-headers.json +12 -0
  171. package/generated/v1/schemas/schedules.create.request.json +99 -0
  172. package/generated/v1/schemas/schedules.create.response-headers.json +15 -0
  173. package/generated/v1/schemas/schedules.create.response.json +189 -0
  174. package/generated/v1/schemas/schedules.list.query.json +16 -0
  175. package/generated/v1/schemas/schedules.list.response.json +170 -0
  176. package/generated/v1/schemas/schedules.pause.request-headers.json +10 -0
  177. package/generated/v1/schemas/schedules.pause.response-headers.json +11 -0
  178. package/generated/v1/schemas/schedules.pause.response.json +189 -0
  179. package/generated/v1/schemas/schedules.read.response-headers.json +11 -0
  180. package/generated/v1/schemas/schedules.read.response.json +189 -0
  181. package/generated/v1/schemas/schedules.replace.request-headers.json +10 -0
  182. package/generated/v1/schemas/schedules.replace.request.json +99 -0
  183. package/generated/v1/schemas/schedules.replace.response-headers.json +11 -0
  184. package/generated/v1/schemas/schedules.replace.response.json +189 -0
  185. package/generated/v1/schemas/schedules.resume.request-headers.json +10 -0
  186. package/generated/v1/schemas/schedules.resume.response-headers.json +11 -0
  187. package/generated/v1/schemas/schedules.resume.response.json +189 -0
  188. package/generated/v1/schemas/sinkdeliveries.list.query.json +16 -0
  189. package/generated/v1/schemas/sinkdeliveries.list.response.json +126 -0
  190. package/generated/v1/schemas/sinkdeliveries.read.response.json +98 -0
  191. package/generated/v1/schemas/testsinks.configure.request.json +66 -0
  192. package/generated/v1/schemas/testsinks.configure.response.json +115 -0
  193. package/generated/v1/schemas/testsinks.create.request.json +76 -0
  194. package/generated/v1/schemas/testsinks.create.response.json +115 -0
  195. package/generated/v1/schemas/testsinks.delete.response.json +115 -0
  196. package/generated/v1/schemas/testsinks.list.query.json +16 -0
  197. package/generated/v1/schemas/testsinks.list.response.json +161 -0
  198. package/generated/v1/schemas/testsinks.read.response.json +115 -0
  199. package/generated/v1/schemas/usage.read.query.json +15 -0
  200. package/generated/v1/schemas/usage.read.response.json +71 -0
  201. package/generated/v1/snippets/attempts.list.json +5 -0
  202. package/generated/v1/snippets/attempts.read.json +5 -0
  203. package/generated/v1/snippets/credentials.create.json +5 -0
  204. package/generated/v1/snippets/credentials.list.json +5 -0
  205. package/generated/v1/snippets/credentials.revoke.json +5 -0
  206. package/generated/v1/snippets/credentials.rotate.json +5 -0
  207. package/generated/v1/snippets/endpoints.create.json +5 -0
  208. package/generated/v1/snippets/endpoints.disable.json +5 -0
  209. package/generated/v1/snippets/endpoints.list.json +5 -0
  210. package/generated/v1/snippets/endpoints.read.json +5 -0
  211. package/generated/v1/snippets/endpoints.rotateSigningKey.json +5 -0
  212. package/generated/v1/snippets/endpoints.update.json +5 -0
  213. package/generated/v1/snippets/endpoints.verify.json +5 -0
  214. package/generated/v1/snippets/forecast.read.json +5 -0
  215. package/generated/v1/snippets/forecast.windows.json +5 -0
  216. package/generated/v1/snippets/grants.list.json +5 -0
  217. package/generated/v1/snippets/grants.revoke.json +5 -0
  218. package/generated/v1/snippets/meta.scaffold.json +5 -0
  219. package/generated/v1/snippets/meta.version.json +5 -0
  220. package/generated/v1/snippets/occurrences.list.json +5 -0
  221. package/generated/v1/snippets/occurrences.read.json +5 -0
  222. package/generated/v1/snippets/occurrences.redeliver.json +5 -0
  223. package/generated/v1/snippets/projects.create.json +5 -0
  224. package/generated/v1/snippets/projects.list.json +5 -0
  225. package/generated/v1/snippets/schedules.archive.json +5 -0
  226. package/generated/v1/snippets/schedules.cancel.json +5 -0
  227. package/generated/v1/snippets/schedules.create.json +5 -0
  228. package/generated/v1/snippets/schedules.list.json +5 -0
  229. package/generated/v1/snippets/schedules.pause.json +5 -0
  230. package/generated/v1/snippets/schedules.read.json +5 -0
  231. package/generated/v1/snippets/schedules.replace.json +5 -0
  232. package/generated/v1/snippets/schedules.resume.json +5 -0
  233. package/generated/v1/snippets/sinkdeliveries.list.json +5 -0
  234. package/generated/v1/snippets/sinkdeliveries.read.json +5 -0
  235. package/generated/v1/snippets/testsinks.configure.json +5 -0
  236. package/generated/v1/snippets/testsinks.create.json +5 -0
  237. package/generated/v1/snippets/testsinks.delete.json +5 -0
  238. package/generated/v1/snippets/testsinks.list.json +5 -0
  239. package/generated/v1/snippets/testsinks.read.json +5 -0
  240. package/generated/v1/snippets/usage.read.json +5 -0
  241. package/package.json +153 -0
@@ -0,0 +1,986 @@
1
+ import { z } from "zod";
2
+ import { type CallbackCompletionOutcome } from "./delivery.js";
3
+ /**
4
+ * The deterministic test sink and the receiver contract it and the shipped
5
+ * middleware both implement.
6
+ *
7
+ * An integrator cannot rehearse a delivery failure against their own receiver
8
+ * without first building one that fails on demand, and the failures that matter
9
+ * — a dropped connection, a receiver that answers three minutes late, a 429
10
+ * carrying a delay hint — are exactly the ones hardest to produce by hand. The
11
+ * sink produces them on request, in test environments only.
12
+ *
13
+ * Two properties are load-bearing and are the reason this vocabulary lives in
14
+ * the contract package rather than inside the API Worker.
15
+ *
16
+ * **The sink and the SDK middleware must agree.** The sink is a reference
17
+ * receiver built on the middleware this story ships, so an integrator who
18
+ * compares the two is comparing one implementation against itself. That is only
19
+ * worth anything if the status a receiver returns for each outcome is published
20
+ * rather than chosen twice — hence `CALLBACK_RECEIVER_STATUS` below.
21
+ *
22
+ * **A sink is a receiver, and the only thing it ever sends is a report about a
23
+ * delivery it has already received.** That sentence used to read "never a
24
+ * sender", and the deferring behaviors added on 2026-08-23 are why it does not:
25
+ * a receiver that defers is one that answers `202` now and reports later, and a
26
+ * sink that could not report later could rehearse the first half of that
27
+ * exchange and none of the half where it goes wrong.
28
+ *
29
+ * What has not changed is that **there is deliberately no field anywhere in
30
+ * this file that names a place to send to.** A completion report goes to the
31
+ * credential inside the signed envelope of the delivery in hand, sent to the
32
+ * one address the stage is configured with, and
33
+ * nowhere else: no program, no control operation and no request header names a
34
+ * destination, so a sink still cannot be asked to forward, redirect or fetch,
35
+ * and still cannot be configured into an egress proxy. A delivery whose
36
+ * envelope carries no completion material cannot defer at all.
37
+ */
38
+ /**
39
+ * What a sink does with a delivery. Exhaustive, and each entry corresponds to a
40
+ * distinct sender-visible outcome class rather than to a distinct status code.
41
+ *
42
+ * `timeout` and `disconnect` are both ambiguous to the sender, and that is the
43
+ * point rather than a defect in the simulation: a sender genuinely cannot tell
44
+ * them apart, which is why the architecture records ambiguity honestly instead
45
+ * of guessing. They differ in what the *receiver* did — see
46
+ * `TEST_SINK_BEHAVIOR_FACTS`.
47
+ *
48
+ * **The three `defer_*` behaviors are three entries rather than one entry with
49
+ * an option**, for exactly the reason stated above: they produce three
50
+ * different sender-visible outcomes. All three answer the same `202` and the
51
+ * same pending marker, so a list that collapsed them would be a list keyed on
52
+ * the status a receiver returns — which is the thing this list is deliberately
53
+ * not keyed on. What separates them happens *after* the acknowledgement, and it
54
+ * is the whole of what an integrator rehearsing deferral needs to see.
55
+ */
56
+ export declare const TEST_SINK_BEHAVIORS: readonly ["client_error", "defer_abandon", "defer_fail", "defer_succeed", "delay", "disconnect", "duplicate", "invalid_signature", "rate_limited", "server_error", "success", "timeout"];
57
+ export declare const testSinkBehaviorSchema: z.ZodEnum<{
58
+ rate_limited: "rate_limited";
59
+ success: "success";
60
+ server_error: "server_error";
61
+ client_error: "client_error";
62
+ defer_abandon: "defer_abandon";
63
+ defer_fail: "defer_fail";
64
+ defer_succeed: "defer_succeed";
65
+ delay: "delay";
66
+ disconnect: "disconnect";
67
+ duplicate: "duplicate";
68
+ invalid_signature: "invalid_signature";
69
+ timeout: "timeout";
70
+ }>;
71
+ export type TestSinkBehavior = z.infer<typeof testSinkBehaviorSchema>;
72
+ /**
73
+ * Bounds on what a sink may be asked to do.
74
+ *
75
+ * Every one of these exists because the sink runs inside the API Worker's
76
+ * request budget on the product's own account. A test facility that can be
77
+ * asked to hold a request open indefinitely, or to accumulate history without
78
+ * limit, is a denial-of-service surface wearing a test badge.
79
+ */
80
+ export declare const TEST_SINK_BOUNDS: {
81
+ /**
82
+ * Longest a sink will wait between acknowledging a deferral and reporting
83
+ * completion.
84
+ *
85
+ * Bounded for the same reason `maxDelayMs` is, and the reason survives the
86
+ * response having already been sent: the report is performed on the same
87
+ * invocation, so a sink asked to report an hour later would be asking the API
88
+ * Worker to stay alive for an hour on the product's own account. Smaller than
89
+ * `maxDelayMs` because nothing is waiting on this wait — a rehearsal of
90
+ * deferral is a rehearsal of the *shape* of the exchange, and any integrator
91
+ * who needs to watch a real deadline expire has `defer_abandon`, which costs
92
+ * nothing at all.
93
+ */
94
+ readonly maxCompletionDelayMs: 5000;
95
+ /** Longest `delay` a sink will honour before answering. */
96
+ readonly maxDelayMs: 10000;
97
+ /** Longest a `timeout` or `disconnect` holds a request open. */
98
+ readonly maxHoldMs: 45000;
99
+ /** Retained delivery receipts per sink; the oldest are evicted. */
100
+ readonly maxRetainedDeliveries: 50;
101
+ /** Sinks per project/environment. */
102
+ readonly maxSinksPerEnvironment: 10;
103
+ /** Largest request body a sink will read. Larger is refused unread. */
104
+ readonly maxRequestBodyBytes: 65536;
105
+ /** Captured body bytes retained on a receipt; beyond this it is truncated. */
106
+ readonly maxRetainedBodyBytes: 4096;
107
+ };
108
+ /**
109
+ * Selected 4xx a sink may answer with.
110
+ *
111
+ * **They are no longer all terminal to the sender, and which are is not this
112
+ * file's decision.** FXO-1596 moved the refusals — a receiver declining *this
113
+ * delivery now* rather than objecting to the message — onto the receiver's
114
+ * ordinary five-failure budget. `TEST_SINK_REFUSAL_STATUSES` below is the
115
+ * subset of this list that the sender will retry, and `test-sink.test.ts`
116
+ * holds it equal to what `classifyStatus` actually says, so a status moved in
117
+ * `CALLBACK_REFUSAL_STATUSES` and not here fails a test rather than teaching an
118
+ * integrator the wrong lesson.
119
+ *
120
+ * Nothing is removed from what a caller may request: every status here was
121
+ * accepted before and still is.
122
+ */
123
+ export declare const TEST_SINK_CLIENT_ERROR_STATUSES: readonly [400, 401, 403, 404, 409, 410, 422];
124
+ /**
125
+ * The statuses above that the sender retries rather than settling on.
126
+ *
127
+ * Exists so an integrator simulating a rotated credential (401) or a WAF rule
128
+ * (403) can see five attempts arrive, and one simulating a malformed body (400)
129
+ * or a duplicate (409) can see exactly one.
130
+ */
131
+ export declare const TEST_SINK_REFUSAL_STATUSES: readonly [401, 403, 404];
132
+ /** Selected 5xx a sink may answer with. Each is retryable to the sender. */
133
+ export declare const TEST_SINK_SERVER_ERROR_STATUSES: readonly [500, 502, 503, 504];
134
+ /**
135
+ * A deterministic behavior program.
136
+ *
137
+ * `behavior` applies to the first `forDeliveries` deliveries and `then` to
138
+ * every one after that, so "fail twice, then succeed" is one declaration rather
139
+ * than a sequence of control calls racing the schedule that is firing. Leaving
140
+ * `forDeliveries` out makes `behavior` permanent, which is what a single
141
+ * failure mode under test wants.
142
+ *
143
+ * The count is over deliveries this sink has *received*, not over a particular
144
+ * occurrence's attempts: a sink is a destination and does not get to know which
145
+ * occurrence the sender considers current.
146
+ */
147
+ export declare const testSinkProgramSchema: z.ZodObject<{
148
+ behavior: z.ZodEnum<{
149
+ rate_limited: "rate_limited";
150
+ success: "success";
151
+ server_error: "server_error";
152
+ client_error: "client_error";
153
+ defer_abandon: "defer_abandon";
154
+ defer_fail: "defer_fail";
155
+ defer_succeed: "defer_succeed";
156
+ delay: "delay";
157
+ disconnect: "disconnect";
158
+ duplicate: "duplicate";
159
+ invalid_signature: "invalid_signature";
160
+ timeout: "timeout";
161
+ }>;
162
+ completionDelayMs: z.ZodOptional<z.ZodNumber>;
163
+ delayMs: z.ZodOptional<z.ZodNumber>;
164
+ forDeliveries: z.ZodOptional<z.ZodNumber>;
165
+ retryAfterSeconds: z.ZodOptional<z.ZodNumber>;
166
+ status: z.ZodOptional<z.ZodNumber>;
167
+ then: z.ZodOptional<z.ZodEnum<{
168
+ rate_limited: "rate_limited";
169
+ success: "success";
170
+ server_error: "server_error";
171
+ client_error: "client_error";
172
+ defer_abandon: "defer_abandon";
173
+ defer_fail: "defer_fail";
174
+ defer_succeed: "defer_succeed";
175
+ delay: "delay";
176
+ disconnect: "disconnect";
177
+ duplicate: "duplicate";
178
+ invalid_signature: "invalid_signature";
179
+ timeout: "timeout";
180
+ }>>;
181
+ }, z.core.$strip>;
182
+ export type TestSinkProgram = z.infer<typeof testSinkProgramSchema>;
183
+ /**
184
+ * What each behavior does, as data rather than prose, so the published artifact
185
+ * and the implementation cannot drift and a receiver author can read the whole
186
+ * simulation without running it.
187
+ *
188
+ * `senderOutcome` is stated in the vocabulary of `classifyOutcome`, which is
189
+ * what the delivery path actually applies. `receiverActed` is the honest answer
190
+ * to "may the receiver have processed this?" — the question a sender cannot
191
+ * answer for itself and must therefore record as ambiguity.
192
+ *
193
+ * `deferredReport` is present on the behaviors that acknowledge now and report
194
+ * later, and it is the reason `senderOutcome` cannot be derived from
195
+ * `defaultStatus` for those three: all three answer the same `202`, which
196
+ * `classifyStatus` reads as an ordinary success, and what the sender finally
197
+ * records comes from the report — or, for `never`, from the deadline passing
198
+ * with no report at all.
199
+ */
200
+ export declare const TEST_SINK_BEHAVIOR_FACTS: {
201
+ readonly client_error: {
202
+ readonly defaultStatus: 422;
203
+ readonly deferredReport: undefined;
204
+ readonly receiverActed: false;
205
+ readonly senderOutcome: "terminal";
206
+ readonly summary: "Answers a selected 4xx. The sender records a customer refusal, and retries it only when the status is one the receiver may recover from — 401, 403 and 404, listed as `clientErrorStatusesRetried`. The default, 422, is not one of those, so it settles on the first attempt.";
207
+ };
208
+ readonly defer_abandon: {
209
+ readonly defaultStatus: 202;
210
+ readonly deferredReport: "never";
211
+ readonly receiverActed: true;
212
+ readonly senderOutcome: "ambiguous";
213
+ readonly summary: "Acknowledges the delivery and then never reports at all. The sender waits until the deadline in the envelope and records ambiguity, because a receiver that took the work and went quiet may well have done it. This is the case the completion deadline exists to bound.";
214
+ };
215
+ readonly defer_fail: {
216
+ readonly defaultStatus: 202;
217
+ readonly deferredReport: "failed";
218
+ readonly receiverActed: true;
219
+ readonly senderOutcome: "retryable";
220
+ readonly summary: "Acknowledges the delivery, then reports `failed` with the credential in its signed envelope. The occurrence goes back on the ordinary retry ladder — the same place a 500 puts it — which is how a receiver says its job died rather than that it vanished.";
221
+ };
222
+ readonly defer_succeed: {
223
+ readonly defaultStatus: 202;
224
+ readonly deferredReport: "succeeded";
225
+ readonly receiverActed: true;
226
+ readonly senderOutcome: "success";
227
+ readonly summary: "Acknowledges the delivery with 202 and the pending marker, keeps the work, then reports `succeeded` with the credential in its signed envelope. The whole deferred exchange, without writing a receiver.";
228
+ };
229
+ readonly delay: {
230
+ readonly defaultStatus: 200;
231
+ readonly deferredReport: undefined;
232
+ readonly receiverActed: true;
233
+ readonly senderOutcome: "success";
234
+ readonly summary: "Waits a bounded interval, then accepts. Exercises a slow receiver that still succeeds.";
235
+ };
236
+ readonly disconnect: {
237
+ readonly defaultStatus: undefined;
238
+ readonly deferredReport: undefined;
239
+ readonly receiverActed: false;
240
+ readonly senderOutcome: "ambiguous";
241
+ readonly summary: "Authenticates the delivery and then drops the connection without processing it, holding until the sender gives up. Nothing was claimed and nothing ran, but the sender cannot know that.";
242
+ };
243
+ readonly duplicate: {
244
+ readonly defaultStatus: 200;
245
+ readonly deferredReport: undefined;
246
+ readonly receiverActed: false;
247
+ readonly senderOutcome: "success";
248
+ readonly summary: "Accepts and reports the occurrence as already handled, without processing it again. What occurrence idempotency looks like from the sender's side.";
249
+ };
250
+ readonly invalid_signature: {
251
+ readonly defaultStatus: 401;
252
+ readonly deferredReport: undefined;
253
+ readonly receiverActed: false;
254
+ readonly senderOutcome: "retryable";
255
+ readonly summary: "Rejects the delivery as unauthenticated regardless of whether the signature verified. Exercises the key-mismatch path without forging a signature. The sender retries on the receiver's ordinary budget, because the usual cause is a credential the receiver is about to redeploy.";
256
+ };
257
+ readonly rate_limited: {
258
+ readonly defaultStatus: 429;
259
+ readonly deferredReport: undefined;
260
+ readonly receiverActed: false;
261
+ readonly senderOutcome: "retryable";
262
+ readonly summary: "Answers 429 with a bounded Retry-After. The sender honours the hint and does not spend its give-up budget.";
263
+ };
264
+ readonly server_error: {
265
+ readonly defaultStatus: 503;
266
+ readonly deferredReport: undefined;
267
+ readonly receiverActed: false;
268
+ readonly senderOutcome: "retryable";
269
+ readonly summary: "Answers a selected 5xx. The sender retries with backoff.";
270
+ };
271
+ readonly success: {
272
+ readonly defaultStatus: 200;
273
+ readonly deferredReport: undefined;
274
+ readonly receiverActed: true;
275
+ readonly senderOutcome: "success";
276
+ readonly summary: "Verifies, records, and accepts.";
277
+ };
278
+ readonly timeout: {
279
+ readonly defaultStatus: undefined;
280
+ readonly deferredReport: undefined;
281
+ readonly receiverActed: true;
282
+ readonly senderOutcome: "ambiguous";
283
+ readonly summary: "Verifies and records the delivery, then never answers. The receiver acted and the sender cannot know it.";
284
+ };
285
+ };
286
+ /**
287
+ * The exact body a deferring sink answers with.
288
+ *
289
+ * Built from the published discriminator rather than written out, so a sink
290
+ * cannot emit a marker the sender does not recognise. Deliberately without
291
+ * `completeBy`: the sink's report follows within `completionDelayMs`, and an
292
+ * acknowledgement that named an instant that close would be asking the sender
293
+ * to stop waiting at roughly the moment the report arrives — a race the
294
+ * rehearsal would lose intermittently and blame on the protocol. A receiver
295
+ * that wants to bring its deadline forward is exercising
296
+ * `effectiveCompletionDeadlineMs`, which is the delivery path's own test rather
297
+ * than this simulation's.
298
+ */
299
+ export declare const TEST_SINK_PENDING_ACKNOWLEDGEMENT: string;
300
+ /**
301
+ * What the sink should do with one delivery.
302
+ *
303
+ * `hold` means no response is ever produced.
304
+ *
305
+ * `claims` says whether the receiver's idempotency ledgers are the durable
306
+ * ones. It is false only for `disconnect`, which models a connection that died
307
+ * before the receiver processed anything: the delivery is still authenticated —
308
+ * a sink that held an *unsigned* request open would be a way for anyone holding
309
+ * a sink url to pin the Worker's request budget, not a simulation of a customer
310
+ * receiver — but no occurrence is claimed and no work is recorded as done, so
311
+ * `receiverActed: false` stays true.
312
+ */
313
+ export interface TestSinkAction {
314
+ /**
315
+ * The exact acknowledgement bytes a deferral answers with, present exactly
316
+ * when `defers` is true.
317
+ *
318
+ * Carried here rather than composed by the runtime because these bytes are
319
+ * the protocol: a sink that assembled its own marker could assemble one the
320
+ * sender does not recognise, and the failure would be a delivery quietly
321
+ * settled `succeeded` while the receiver was still working — the one thing
322
+ * the whole deferral protocol exists to prevent.
323
+ */
324
+ readonly acknowledgement?: string;
325
+ readonly behavior: TestSinkBehavior;
326
+ /** Whether the occurrence and delivery ledgers this delivery touches persist. */
327
+ readonly claims: boolean;
328
+ /**
329
+ * Milliseconds between the acknowledgement and the report. Zero when there
330
+ * is no report to send, the way `delayMs` is zero when there is no delay.
331
+ */
332
+ readonly completionDelayMs: number;
333
+ /**
334
+ * What to report, with the credential in the delivery's signed envelope.
335
+ *
336
+ * Absent means "report nothing", which is `defer_abandon` and every behavior
337
+ * that does not defer. The two are told apart by `defers`, never by this.
338
+ */
339
+ readonly completionOutcome?: CallbackCompletionOutcome;
340
+ /** Whether to acknowledge the work and answer later instead of now. */
341
+ readonly defers: boolean;
342
+ readonly delayMs: number;
343
+ readonly hold: boolean;
344
+ readonly retryAfterSeconds?: number;
345
+ readonly status?: number;
346
+ }
347
+ /**
348
+ * Resolve a program against how many deliveries this sink has already taken.
349
+ *
350
+ * Pure and total: the same `(program, deliveryIndex)` always produces the same
351
+ * action, which is what "deterministic" means here and what lets the same
352
+ * engine drive both the hosted sink and an offline conformance run.
353
+ *
354
+ * `deliveryIndex` is zero-based and counts every delivery the sink accepted for
355
+ * processing, including refused ones — a receiver that answers 401 has still
356
+ * received a request, and a program that said "fail the first two" must not be
357
+ * satisfiable by failing the same delivery twice.
358
+ */
359
+ export declare function resolveTestSinkAction(program: TestSinkProgram, deliveryIndex: number): TestSinkAction;
360
+ /**
361
+ * Every way a receiver can dispose of one delivery.
362
+ *
363
+ * `duplicate` and `replay` are both "already handled" and both succeed, and
364
+ * separating them is what makes an integrator's logs legible: `replay` is the
365
+ * same delivery identity arriving twice — a duplicated request — while
366
+ * `duplicate` is a *different* delivery for an occurrence already processed,
367
+ * which is what a retry after an ambiguous attempt, and what a customer-ordered
368
+ * redelivery, both look like on the wire.
369
+ *
370
+ * `in_flight` is the concurrent case: another worker holds the occurrence and
371
+ * has not finished. Answering success there would claim an outcome nobody has
372
+ * yet produced.
373
+ */
374
+ export declare const CALLBACK_RECEIVER_DISPOSITIONS: readonly ["body_too_large", "duplicate", "handler_failed", "in_flight", "invalid_envelope", "invalid_headers", "invalid_signature", "processed", "replay", "stale_timestamp"];
375
+ export declare const callbackReceiverDispositionSchema: z.ZodEnum<{
376
+ duplicate: "duplicate";
377
+ invalid_signature: "invalid_signature";
378
+ body_too_large: "body_too_large";
379
+ handler_failed: "handler_failed";
380
+ in_flight: "in_flight";
381
+ invalid_envelope: "invalid_envelope";
382
+ invalid_headers: "invalid_headers";
383
+ processed: "processed";
384
+ replay: "replay";
385
+ stale_timestamp: "stale_timestamp";
386
+ }>;
387
+ export type CallbackReceiverDisposition = z.infer<typeof callbackReceiverDispositionSchema>;
388
+ /**
389
+ * What a sink recorded, which is one value wider than what a receiver can
390
+ * answer with.
391
+ *
392
+ * `connection_dropped` has no status because the point of it is that there was
393
+ * none: the sink modelled a connection that died before the receiver read
394
+ * anything, so nothing was authenticated and nothing ran. Widening
395
+ * `CALLBACK_RECEIVER_DISPOSITIONS` to hold it would break the property that
396
+ * makes that list useful — every member maps to exactly one status.
397
+ */
398
+ export declare const TEST_SINK_DISPOSITIONS: readonly ["body_too_large", "duplicate", "handler_failed", "in_flight", "invalid_envelope", "invalid_headers", "invalid_signature", "processed", "replay", "stale_timestamp", "connection_dropped"];
399
+ export declare const testSinkDispositionSchema: z.ZodEnum<{
400
+ duplicate: "duplicate";
401
+ invalid_signature: "invalid_signature";
402
+ body_too_large: "body_too_large";
403
+ handler_failed: "handler_failed";
404
+ in_flight: "in_flight";
405
+ invalid_envelope: "invalid_envelope";
406
+ invalid_headers: "invalid_headers";
407
+ processed: "processed";
408
+ replay: "replay";
409
+ stale_timestamp: "stale_timestamp";
410
+ connection_dropped: "connection_dropped";
411
+ }>;
412
+ export type TestSinkDisposition = z.infer<typeof testSinkDispositionSchema>;
413
+ /**
414
+ * The HTTP status a conforming receiver returns for each disposition.
415
+ *
416
+ * Published, and published *here*, because the sender classifies on the status
417
+ * and the classification decides whether an occurrence retries, settles, or is
418
+ * abandoned. A receiver that picks its own numbers can be perfectly correct
419
+ * about what it did and still tell the sender the opposite.
420
+ *
421
+ * Each choice is answerable against `classifyStatus`:
422
+ *
423
+ * - `processed`, `duplicate`, `replay` — 200. All three mean the occurrence is
424
+ * handled. A replayed or duplicated delivery answering 4xx would make the
425
+ * sender record a terminal failure for work that succeeded, which is the
426
+ * single most damaging thing a receiver can do.
427
+ * - `stale_timestamp` — 408, which is retryable. A clock skew wide enough to
428
+ * fail tolerance is an operator problem no retry fixes, but a request held on
429
+ * the network for six minutes is not, and every attempt re-signs with a fresh
430
+ * timestamp, so the transient case self-heals and the permanent case exhausts
431
+ * the attempt bound honestly.
432
+ * - `invalid_signature`, `invalid_headers` — 401, **retryable since
433
+ * FXO-1596**, and this entry used to say the opposite. The reasoning it
434
+ * replaces was that retrying with the same key cannot succeed and a retryable
435
+ * answer would spend the give-up budget hiding a misconfiguration. That is
436
+ * true of a mismatch in *our* signing and false of the population that
437
+ * actually reaches a 401: a receiver holding a credential it has rotated,
438
+ * expired, or not yet deployed. Those recover on the receiver's next release,
439
+ * and the delivery after it carries a fresh signature. The misconfiguration
440
+ * is not hidden either — five identical 401s settle the occurrence 82 seconds
441
+ * later with the same `inspect_receiver` advice, five rows instead of one.
442
+ * - `invalid_envelope` — 400, terminal. The bytes authenticated and did not
443
+ * parse, so the sender and receiver disagree about the contract.
444
+ * - `body_too_large` — 413, terminal, and matched to the `payload_too_large`
445
+ * problem code the API surface already publishes.
446
+ * - `in_flight` — 503, retryable. Honest: someone else holds the occurrence.
447
+ * - `handler_failed` — 500, retryable. The receiver's own fault and worth
448
+ * another attempt.
449
+ */
450
+ export declare const CALLBACK_RECEIVER_STATUS: {
451
+ readonly body_too_large: 413;
452
+ readonly duplicate: 200;
453
+ readonly handler_failed: 500;
454
+ readonly in_flight: 503;
455
+ readonly invalid_envelope: 400;
456
+ readonly invalid_headers: 401;
457
+ readonly invalid_signature: 401;
458
+ readonly processed: 200;
459
+ readonly replay: 200;
460
+ readonly stale_timestamp: 408;
461
+ };
462
+ /**
463
+ * The response body a conforming receiver returns.
464
+ *
465
+ * Deliberately tiny and deliberately free of prose: a receiver's response is
466
+ * read by a machine, and a body that carries an exception message is how a
467
+ * receiver leaks its internals to whoever can provoke an error.
468
+ */
469
+ export declare const callbackReceiverAcknowledgementSchema: z.ZodObject<{
470
+ disposition: z.ZodEnum<{
471
+ duplicate: "duplicate";
472
+ invalid_signature: "invalid_signature";
473
+ body_too_large: "body_too_large";
474
+ handler_failed: "handler_failed";
475
+ in_flight: "in_flight";
476
+ invalid_envelope: "invalid_envelope";
477
+ invalid_headers: "invalid_headers";
478
+ processed: "processed";
479
+ replay: "replay";
480
+ stale_timestamp: "stale_timestamp";
481
+ }>;
482
+ occurrenceId: z.ZodOptional<z.ZodString>;
483
+ }, z.core.$strip>;
484
+ export type CallbackReceiverAcknowledgement = z.infer<typeof callbackReceiverAcknowledgementSchema>;
485
+ export declare const testSinkSchema: z.ZodObject<{
486
+ createdAt: z.ZodString;
487
+ deliveryCount: z.ZodNumber;
488
+ endpoint: z.ZodString;
489
+ environmentKind: z.ZodLiteral<"test">;
490
+ id: z.ZodString;
491
+ program: z.ZodObject<{
492
+ behavior: z.ZodEnum<{
493
+ rate_limited: "rate_limited";
494
+ success: "success";
495
+ server_error: "server_error";
496
+ client_error: "client_error";
497
+ defer_abandon: "defer_abandon";
498
+ defer_fail: "defer_fail";
499
+ defer_succeed: "defer_succeed";
500
+ delay: "delay";
501
+ disconnect: "disconnect";
502
+ duplicate: "duplicate";
503
+ invalid_signature: "invalid_signature";
504
+ timeout: "timeout";
505
+ }>;
506
+ completionDelayMs: z.ZodOptional<z.ZodNumber>;
507
+ delayMs: z.ZodOptional<z.ZodNumber>;
508
+ forDeliveries: z.ZodOptional<z.ZodNumber>;
509
+ retryAfterSeconds: z.ZodOptional<z.ZodNumber>;
510
+ status: z.ZodOptional<z.ZodNumber>;
511
+ then: z.ZodOptional<z.ZodEnum<{
512
+ rate_limited: "rate_limited";
513
+ success: "success";
514
+ server_error: "server_error";
515
+ client_error: "client_error";
516
+ defer_abandon: "defer_abandon";
517
+ defer_fail: "defer_fail";
518
+ defer_succeed: "defer_succeed";
519
+ delay: "delay";
520
+ disconnect: "disconnect";
521
+ duplicate: "duplicate";
522
+ invalid_signature: "invalid_signature";
523
+ timeout: "timeout";
524
+ }>>;
525
+ }, z.core.$strip>;
526
+ project: z.ZodString;
527
+ updatedAt: z.ZodString;
528
+ url: z.ZodURL;
529
+ }, z.core.$strip>;
530
+ export type TestSink = z.infer<typeof testSinkSchema>;
531
+ export declare const testSinkPageSchema: z.ZodObject<{
532
+ data: z.ZodArray<z.ZodObject<{
533
+ createdAt: z.ZodString;
534
+ deliveryCount: z.ZodNumber;
535
+ endpoint: z.ZodString;
536
+ environmentKind: z.ZodLiteral<"test">;
537
+ id: z.ZodString;
538
+ program: z.ZodObject<{
539
+ behavior: z.ZodEnum<{
540
+ rate_limited: "rate_limited";
541
+ success: "success";
542
+ server_error: "server_error";
543
+ client_error: "client_error";
544
+ defer_abandon: "defer_abandon";
545
+ defer_fail: "defer_fail";
546
+ defer_succeed: "defer_succeed";
547
+ delay: "delay";
548
+ disconnect: "disconnect";
549
+ duplicate: "duplicate";
550
+ invalid_signature: "invalid_signature";
551
+ timeout: "timeout";
552
+ }>;
553
+ completionDelayMs: z.ZodOptional<z.ZodNumber>;
554
+ delayMs: z.ZodOptional<z.ZodNumber>;
555
+ forDeliveries: z.ZodOptional<z.ZodNumber>;
556
+ retryAfterSeconds: z.ZodOptional<z.ZodNumber>;
557
+ status: z.ZodOptional<z.ZodNumber>;
558
+ then: z.ZodOptional<z.ZodEnum<{
559
+ rate_limited: "rate_limited";
560
+ success: "success";
561
+ server_error: "server_error";
562
+ client_error: "client_error";
563
+ defer_abandon: "defer_abandon";
564
+ defer_fail: "defer_fail";
565
+ defer_succeed: "defer_succeed";
566
+ delay: "delay";
567
+ disconnect: "disconnect";
568
+ duplicate: "duplicate";
569
+ invalid_signature: "invalid_signature";
570
+ timeout: "timeout";
571
+ }>>;
572
+ }, z.core.$strip>;
573
+ project: z.ZodString;
574
+ updatedAt: z.ZodString;
575
+ url: z.ZodURL;
576
+ }, z.core.$strip>>;
577
+ freshness: z.ZodDiscriminatedUnion<[z.ZodObject<{
578
+ kind: z.ZodLiteral<"authoritative">;
579
+ }, z.core.$strip>, z.ZodObject<{
580
+ asOf: z.ZodString;
581
+ kind: z.ZodLiteral<"projected">;
582
+ lagSeconds: z.ZodNumber;
583
+ }, z.core.$strip>], "kind">;
584
+ nextCursor: z.ZodOptional<z.ZodString>;
585
+ }, z.core.$strip>;
586
+ export type TestSinkPage = z.infer<typeof testSinkPageSchema>;
587
+ export declare const testSinkCreateRequestSchema: z.ZodObject<{
588
+ endpoint: z.ZodString;
589
+ program: z.ZodOptional<z.ZodObject<{
590
+ behavior: z.ZodEnum<{
591
+ rate_limited: "rate_limited";
592
+ success: "success";
593
+ server_error: "server_error";
594
+ client_error: "client_error";
595
+ defer_abandon: "defer_abandon";
596
+ defer_fail: "defer_fail";
597
+ defer_succeed: "defer_succeed";
598
+ delay: "delay";
599
+ disconnect: "disconnect";
600
+ duplicate: "duplicate";
601
+ invalid_signature: "invalid_signature";
602
+ timeout: "timeout";
603
+ }>;
604
+ completionDelayMs: z.ZodOptional<z.ZodNumber>;
605
+ delayMs: z.ZodOptional<z.ZodNumber>;
606
+ forDeliveries: z.ZodOptional<z.ZodNumber>;
607
+ retryAfterSeconds: z.ZodOptional<z.ZodNumber>;
608
+ status: z.ZodOptional<z.ZodNumber>;
609
+ then: z.ZodOptional<z.ZodEnum<{
610
+ rate_limited: "rate_limited";
611
+ success: "success";
612
+ server_error: "server_error";
613
+ client_error: "client_error";
614
+ defer_abandon: "defer_abandon";
615
+ defer_fail: "defer_fail";
616
+ defer_succeed: "defer_succeed";
617
+ delay: "delay";
618
+ disconnect: "disconnect";
619
+ duplicate: "duplicate";
620
+ invalid_signature: "invalid_signature";
621
+ timeout: "timeout";
622
+ }>>;
623
+ }, z.core.$strip>>;
624
+ }, z.core.$strip>;
625
+ export type TestSinkCreateRequest = z.infer<typeof testSinkCreateRequestSchema>;
626
+ /**
627
+ * One received delivery, as the sink recorded it.
628
+ *
629
+ * The identifiers, the key that signed it, and the disposition are the whole of
630
+ * what a *list* returns. The exact body is available only from the single-record
631
+ * read, capped, and only in a test environment — see `testSinkDeliveryBodySchema`.
632
+ */
633
+ export declare const testSinkDeliverySchema: z.ZodObject<{
634
+ behavior: z.ZodEnum<{
635
+ rate_limited: "rate_limited";
636
+ success: "success";
637
+ server_error: "server_error";
638
+ client_error: "client_error";
639
+ defer_abandon: "defer_abandon";
640
+ defer_fail: "defer_fail";
641
+ defer_succeed: "defer_succeed";
642
+ delay: "delay";
643
+ disconnect: "disconnect";
644
+ duplicate: "duplicate";
645
+ invalid_signature: "invalid_signature";
646
+ timeout: "timeout";
647
+ }>;
648
+ disposition: z.ZodEnum<{
649
+ duplicate: "duplicate";
650
+ invalid_signature: "invalid_signature";
651
+ body_too_large: "body_too_large";
652
+ handler_failed: "handler_failed";
653
+ in_flight: "in_flight";
654
+ invalid_envelope: "invalid_envelope";
655
+ invalid_headers: "invalid_headers";
656
+ processed: "processed";
657
+ replay: "replay";
658
+ stale_timestamp: "stale_timestamp";
659
+ connection_dropped: "connection_dropped";
660
+ }>;
661
+ delivery: z.ZodString;
662
+ id: z.ZodString;
663
+ keyId: z.ZodOptional<z.ZodString>;
664
+ occurrenceId: z.ZodOptional<z.ZodString>;
665
+ receivedAt: z.ZodString;
666
+ respondedWith: z.ZodOptional<z.ZodNumber>;
667
+ scheduleId: z.ZodOptional<z.ZodString>;
668
+ sink: z.ZodString;
669
+ }, z.core.$strip>;
670
+ export type TestSinkDelivery = z.infer<typeof testSinkDeliverySchema>;
671
+ export declare const testSinkDeliveryPageSchema: z.ZodObject<{
672
+ data: z.ZodArray<z.ZodObject<{
673
+ behavior: z.ZodEnum<{
674
+ rate_limited: "rate_limited";
675
+ success: "success";
676
+ server_error: "server_error";
677
+ client_error: "client_error";
678
+ defer_abandon: "defer_abandon";
679
+ defer_fail: "defer_fail";
680
+ defer_succeed: "defer_succeed";
681
+ delay: "delay";
682
+ disconnect: "disconnect";
683
+ duplicate: "duplicate";
684
+ invalid_signature: "invalid_signature";
685
+ timeout: "timeout";
686
+ }>;
687
+ disposition: z.ZodEnum<{
688
+ duplicate: "duplicate";
689
+ invalid_signature: "invalid_signature";
690
+ body_too_large: "body_too_large";
691
+ handler_failed: "handler_failed";
692
+ in_flight: "in_flight";
693
+ invalid_envelope: "invalid_envelope";
694
+ invalid_headers: "invalid_headers";
695
+ processed: "processed";
696
+ replay: "replay";
697
+ stale_timestamp: "stale_timestamp";
698
+ connection_dropped: "connection_dropped";
699
+ }>;
700
+ delivery: z.ZodString;
701
+ id: z.ZodString;
702
+ keyId: z.ZodOptional<z.ZodString>;
703
+ occurrenceId: z.ZodOptional<z.ZodString>;
704
+ receivedAt: z.ZodString;
705
+ respondedWith: z.ZodOptional<z.ZodNumber>;
706
+ scheduleId: z.ZodOptional<z.ZodString>;
707
+ sink: z.ZodString;
708
+ }, z.core.$strip>>;
709
+ freshness: z.ZodDiscriminatedUnion<[z.ZodObject<{
710
+ kind: z.ZodLiteral<"authoritative">;
711
+ }, z.core.$strip>, z.ZodObject<{
712
+ asOf: z.ZodString;
713
+ kind: z.ZodLiteral<"projected">;
714
+ lagSeconds: z.ZodNumber;
715
+ }, z.core.$strip>], "kind">;
716
+ nextCursor: z.ZodOptional<z.ZodString>;
717
+ }, z.core.$strip>;
718
+ export type TestSinkDeliveryPage = z.infer<typeof testSinkDeliveryPageSchema>;
719
+ /**
720
+ * One received delivery including the bytes that arrived.
721
+ *
722
+ * **The one deliberate exception to "callback bodies never leave the delivery
723
+ * path".** The rule exists because a payload is customer content and a log,
724
+ * metric, or support index is the wrong place for it. A test sink is none of
725
+ * those: it is a destination the customer created, in their own test
726
+ * environment, to look at what their receiver would have been sent, and
727
+ * withholding it would leave the sink unable to answer the first question
728
+ * anybody asks it.
729
+ *
730
+ * The exception is kept narrow rather than assumed safe. The body is available
731
+ * only from the single-delivery read and never from a listing, only under the
732
+ * creating authority, only in test, capped at
733
+ * `TEST_SINK_BOUNDS.maxRetainedBodyBytes` with the truncation stated, and never
734
+ * written to a log. `signature` is included because a MAC is not secret and
735
+ * comparing it against a locally computed one is how a receiver author finds a
736
+ * body-mutation bug; no signing key has a field anywhere in this file.
737
+ */
738
+ export declare const testSinkDeliveryBodySchema: z.ZodObject<{
739
+ behavior: z.ZodEnum<{
740
+ rate_limited: "rate_limited";
741
+ success: "success";
742
+ server_error: "server_error";
743
+ client_error: "client_error";
744
+ defer_abandon: "defer_abandon";
745
+ defer_fail: "defer_fail";
746
+ defer_succeed: "defer_succeed";
747
+ delay: "delay";
748
+ disconnect: "disconnect";
749
+ duplicate: "duplicate";
750
+ invalid_signature: "invalid_signature";
751
+ timeout: "timeout";
752
+ }>;
753
+ disposition: z.ZodEnum<{
754
+ duplicate: "duplicate";
755
+ invalid_signature: "invalid_signature";
756
+ body_too_large: "body_too_large";
757
+ handler_failed: "handler_failed";
758
+ in_flight: "in_flight";
759
+ invalid_envelope: "invalid_envelope";
760
+ invalid_headers: "invalid_headers";
761
+ processed: "processed";
762
+ replay: "replay";
763
+ stale_timestamp: "stale_timestamp";
764
+ connection_dropped: "connection_dropped";
765
+ }>;
766
+ delivery: z.ZodString;
767
+ id: z.ZodString;
768
+ keyId: z.ZodOptional<z.ZodString>;
769
+ occurrenceId: z.ZodOptional<z.ZodString>;
770
+ receivedAt: z.ZodString;
771
+ respondedWith: z.ZodOptional<z.ZodNumber>;
772
+ scheduleId: z.ZodOptional<z.ZodString>;
773
+ sink: z.ZodString;
774
+ body: z.ZodString;
775
+ bodyTruncated: z.ZodBoolean;
776
+ receivedBytes: z.ZodNumber;
777
+ signature: z.ZodOptional<z.ZodString>;
778
+ }, z.core.$strip>;
779
+ export type TestSinkDeliveryBody = z.infer<typeof testSinkDeliveryBodySchema>;
780
+ /**
781
+ * Dated, and moved on 2026-08-23 because the sink gained the three deferring
782
+ * behaviors and the bound on how long one may wait before reporting. A reader
783
+ * holding the older artifact has a complete and correct description of a sink
784
+ * that could not defer.
785
+ */
786
+ export declare const TEST_SINK_PROTOCOL_VERSION = "2026-08-23";
787
+ /**
788
+ * The language-neutral receiver contract.
789
+ *
790
+ * A receiver written in any stack can be held to this: verify these bytes,
791
+ * answer these statuses, dedupe on this identity. It is generated beside
792
+ * OpenAPI so an integrator does not have to read TypeScript to write a Go
793
+ * receiver.
794
+ */
795
+ export declare const CALLBACK_RECEIVER_PROTOCOL: {
796
+ readonly idempotency: {
797
+ /**
798
+ * Stated plainly because it is the property most often got wrong, and the
799
+ * one the contract vocabulary already implies without saying so anywhere a
800
+ * receiver author would look.
801
+ */
802
+ readonly occurrence: {
803
+ readonly identity: "Callback-Occurrence-Id";
804
+ readonly note: "Every automatic delivery of one occurrence — the first attempt and every retry the system chose — is at most one execution. A receiver claims the occurrence atomically and answers 200 for every later automatic delivery of it. This is what makes at-least-once delivery safe; a receiver that needs a second execution asks for a second occurrence, or a person orders a redelivery.";
805
+ readonly requirement: "claim the occurrence atomically before running the handler; release the claim if the handler fails so a later attempt can retry it";
806
+ };
807
+ /**
808
+ * The one case where an occurrence runs twice, and the reason it is safe.
809
+ *
810
+ * Until the envelope carried `trigger` this could not be expressed: a retry
811
+ * nobody asked for and a redelivery a person ordered arrived identically, so
812
+ * the rule safe against the first was applied to both and pressing
813
+ * "redeliver" changed nothing. The unit of idempotency is therefore not the
814
+ * occurrence alone but the occurrence *and what asked for it*.
815
+ */
816
+ readonly redelivery: {
817
+ readonly identity: "Callback-Occurrence-Id together with the signed attempt number, which is the redelivery's ordinal";
818
+ readonly note: "A redelivery is a deliberate, authorized instruction to do the work again. Suppressing it as a duplicate answers a person with silence, which is what a customer pressing redeliver after fixing their endpoint experiences as the attempt succeeding while nothing happens.";
819
+ readonly optOut: "A receiver that genuinely requires strict once-only per occurrence chooses it explicitly and keeps answering duplicate. It is an opt-out, not the default, because a default of suppression makes the product lie to the person who pressed the button.";
820
+ readonly ordering: "attempt only ever increases within an occurrence and every copy of one redelivery carries the same value, so a receiver keeps the highest redelivery it has run to completion and refuses anything at or below it. That is what recognises a copy delayed in a proxy, a queue or an abandoned connection, however late it arrives and however many redeliveries have completed since. Remembering only the most recent instruction cannot do this and will run a customer's work twice; remembering every instruction can, at the cost of a store that grows without bound. sourceAttemptId names the attempt the person chose to follow and is there to show them, not to decide whether the work runs.";
821
+ readonly requirement: "run the handler again for a redelivery whose attempt is above the highest this receiver has run to completion for the occurrence, and suppress every redelivery at or below that high-water mark";
822
+ readonly strictness: "A receiver that chose strict once-only suppresses every delivery for an occurrence once anything has finished, whatever caused it. Reading strictness as a rule about redeliveries alone leaves a hole: a strict receiver that processed a redelivery first would then admit a delayed automatic delivery and run a payment twice.";
823
+ readonly warranty: "Each accepted redelivery is one logical run. Repeats are suppressed once the receiver has durably completed that run — not from the moment it starts one. A handler that performs its external effect and then dies before its claim is settled will be run again when the lease expires, which is the honest limit of what any at-least-once receiver can promise. A customer whose effect must happen exactly once commits the occurrence and ordinal alongside their own state, or passes them downstream as an idempotency key.";
824
+ };
825
+ readonly replay: {
826
+ readonly identity: "Callback-Delivery-Id";
827
+ readonly requirement: "reject a previously accepted delivery identity before parsing, and answer 200 — a duplicated request is not a failure";
828
+ };
829
+ };
830
+ /**
831
+ * How a receiver's occurrence store answers one claim, in order.
832
+ *
833
+ * Published as data because a receiver author outside JavaScript had nowhere
834
+ * to read it: `order` covers authenticating a request, and the prose guide is
835
+ * TypeScript-shaped and not machine-readable. It drifted from the code three
836
+ * times during W3-08 — and the third time it published a rule that would have
837
+ * had a customer take a payment twice — so the ordered list now lives beside
838
+ * the rules it describes and the guide is checked against it.
839
+ *
840
+ * Rule 2 is deliberately about what has *finished* rather than what *arrived*.
841
+ * Splitting it — suppress an automatic delivery when the automatic run
842
+ * finished, apply strictness only to redeliveries — is the hole: a strict
843
+ * receiver that legitimately saw an ordered redelivery first would then admit
844
+ * a delayed automatic copy and run the work again.
845
+ */
846
+ readonly claimRules: readonly ["another delivery holds an unexpired lease, whatever the run, so an ordered redelivery never runs alongside the attempt it follows -> in_flight", "the delivery may not retake a settled occurrence and anything at all has finished for it -> settled. This covers every automatic delivery and every delivery at a receiver that chose strict once-only", "the delivery is a redelivery whose ordinal is at or below the highest this receiver has run to completion -> settled", "otherwise take it, recording the holder and the run -> claimed"];
847
+ readonly order: readonly ["read the exact request bytes before any body parser sees them", "reject a body over the receiver's byte bound without buffering it", "validate the five Callback-* headers and the content type", "reject a timestamp outside tolerance", "select the signing key by Callback-Key-Id within the receiver's own endpoint, project, and environment", "compare the HMAC in constant time over <timestamp>.<delivery-id>. plus the exact bytes", "compare the canonical envelope identifiers with the occurrence header", "claim the delivery identity atomically", "only now parse the body, and read the trigger that says why this delivery exists", "claim the occurrence identity atomically, for the run the signed body names — the occurrence for an automatic delivery, and the occurrence with the redelivery's attempt ordinal for one a person ordered", "run the application handler"];
848
+ readonly responses: {
849
+ readonly body_too_large: 413;
850
+ readonly duplicate: 200;
851
+ readonly handler_failed: 500;
852
+ readonly in_flight: 503;
853
+ readonly invalid_envelope: 400;
854
+ readonly invalid_headers: 401;
855
+ readonly invalid_signature: 401;
856
+ readonly processed: 200;
857
+ readonly replay: 200;
858
+ readonly stale_timestamp: 408;
859
+ };
860
+ readonly sink: {
861
+ readonly behaviors: {
862
+ readonly client_error: {
863
+ readonly defaultStatus: 422;
864
+ readonly deferredReport: undefined;
865
+ readonly receiverActed: false;
866
+ readonly senderOutcome: "terminal";
867
+ readonly summary: "Answers a selected 4xx. The sender records a customer refusal, and retries it only when the status is one the receiver may recover from — 401, 403 and 404, listed as `clientErrorStatusesRetried`. The default, 422, is not one of those, so it settles on the first attempt.";
868
+ };
869
+ readonly defer_abandon: {
870
+ readonly defaultStatus: 202;
871
+ readonly deferredReport: "never";
872
+ readonly receiverActed: true;
873
+ readonly senderOutcome: "ambiguous";
874
+ readonly summary: "Acknowledges the delivery and then never reports at all. The sender waits until the deadline in the envelope and records ambiguity, because a receiver that took the work and went quiet may well have done it. This is the case the completion deadline exists to bound.";
875
+ };
876
+ readonly defer_fail: {
877
+ readonly defaultStatus: 202;
878
+ readonly deferredReport: "failed";
879
+ readonly receiverActed: true;
880
+ readonly senderOutcome: "retryable";
881
+ readonly summary: "Acknowledges the delivery, then reports `failed` with the credential in its signed envelope. The occurrence goes back on the ordinary retry ladder — the same place a 500 puts it — which is how a receiver says its job died rather than that it vanished.";
882
+ };
883
+ readonly defer_succeed: {
884
+ readonly defaultStatus: 202;
885
+ readonly deferredReport: "succeeded";
886
+ readonly receiverActed: true;
887
+ readonly senderOutcome: "success";
888
+ readonly summary: "Acknowledges the delivery with 202 and the pending marker, keeps the work, then reports `succeeded` with the credential in its signed envelope. The whole deferred exchange, without writing a receiver.";
889
+ };
890
+ readonly delay: {
891
+ readonly defaultStatus: 200;
892
+ readonly deferredReport: undefined;
893
+ readonly receiverActed: true;
894
+ readonly senderOutcome: "success";
895
+ readonly summary: "Waits a bounded interval, then accepts. Exercises a slow receiver that still succeeds.";
896
+ };
897
+ readonly disconnect: {
898
+ readonly defaultStatus: undefined;
899
+ readonly deferredReport: undefined;
900
+ readonly receiverActed: false;
901
+ readonly senderOutcome: "ambiguous";
902
+ readonly summary: "Authenticates the delivery and then drops the connection without processing it, holding until the sender gives up. Nothing was claimed and nothing ran, but the sender cannot know that.";
903
+ };
904
+ readonly duplicate: {
905
+ readonly defaultStatus: 200;
906
+ readonly deferredReport: undefined;
907
+ readonly receiverActed: false;
908
+ readonly senderOutcome: "success";
909
+ readonly summary: "Accepts and reports the occurrence as already handled, without processing it again. What occurrence idempotency looks like from the sender's side.";
910
+ };
911
+ readonly invalid_signature: {
912
+ readonly defaultStatus: 401;
913
+ readonly deferredReport: undefined;
914
+ readonly receiverActed: false;
915
+ readonly senderOutcome: "retryable";
916
+ readonly summary: "Rejects the delivery as unauthenticated regardless of whether the signature verified. Exercises the key-mismatch path without forging a signature. The sender retries on the receiver's ordinary budget, because the usual cause is a credential the receiver is about to redeploy.";
917
+ };
918
+ readonly rate_limited: {
919
+ readonly defaultStatus: 429;
920
+ readonly deferredReport: undefined;
921
+ readonly receiverActed: false;
922
+ readonly senderOutcome: "retryable";
923
+ readonly summary: "Answers 429 with a bounded Retry-After. The sender honours the hint and does not spend its give-up budget.";
924
+ };
925
+ readonly server_error: {
926
+ readonly defaultStatus: 503;
927
+ readonly deferredReport: undefined;
928
+ readonly receiverActed: false;
929
+ readonly senderOutcome: "retryable";
930
+ readonly summary: "Answers a selected 5xx. The sender retries with backoff.";
931
+ };
932
+ readonly success: {
933
+ readonly defaultStatus: 200;
934
+ readonly deferredReport: undefined;
935
+ readonly receiverActed: true;
936
+ readonly senderOutcome: "success";
937
+ readonly summary: "Verifies, records, and accepts.";
938
+ };
939
+ readonly timeout: {
940
+ readonly defaultStatus: undefined;
941
+ readonly deferredReport: undefined;
942
+ readonly receiverActed: true;
943
+ readonly senderOutcome: "ambiguous";
944
+ readonly summary: "Verifies and records the delivery, then never answers. The receiver acted and the sender cannot know it.";
945
+ };
946
+ };
947
+ readonly bounds: {
948
+ /**
949
+ * Longest a sink will wait between acknowledging a deferral and reporting
950
+ * completion.
951
+ *
952
+ * Bounded for the same reason `maxDelayMs` is, and the reason survives the
953
+ * response having already been sent: the report is performed on the same
954
+ * invocation, so a sink asked to report an hour later would be asking the API
955
+ * Worker to stay alive for an hour on the product's own account. Smaller than
956
+ * `maxDelayMs` because nothing is waiting on this wait — a rehearsal of
957
+ * deferral is a rehearsal of the *shape* of the exchange, and any integrator
958
+ * who needs to watch a real deadline expire has `defer_abandon`, which costs
959
+ * nothing at all.
960
+ */
961
+ readonly maxCompletionDelayMs: 5000;
962
+ /** Longest `delay` a sink will honour before answering. */
963
+ readonly maxDelayMs: 10000;
964
+ /** Longest a `timeout` or `disconnect` holds a request open. */
965
+ readonly maxHoldMs: 45000;
966
+ /** Retained delivery receipts per sink; the oldest are evicted. */
967
+ readonly maxRetainedDeliveries: 50;
968
+ /** Sinks per project/environment. */
969
+ readonly maxSinksPerEnvironment: 10;
970
+ /** Largest request body a sink will read. Larger is refused unread. */
971
+ readonly maxRequestBodyBytes: 65536;
972
+ /** Captured body bytes retained on a receipt; beyond this it is truncated. */
973
+ readonly maxRetainedBodyBytes: 4096;
974
+ };
975
+ readonly clientErrorStatuses: readonly [400, 401, 403, 404, 409, 410, 422];
976
+ readonly clientErrorStatusesRetried: readonly [401, 403, 404];
977
+ readonly deferral: {
978
+ readonly acknowledgement: string;
979
+ readonly note: "A deferring behavior answers this exact body with this status, then reports with the bearer credential from the delivery's own signed envelope — to the one completion address the stage is configured with, which the envelope's own URL must match. A delivery whose envelope carries no completion material, or names a different address, cannot defer, and the sink answers it as an ordinary success instead.";
980
+ readonly status: 202;
981
+ };
982
+ readonly egress: "A test sink has no destination field: no program, no control operation and no request header names anywhere for it to send. The one request it performs is a deferred-completion report for a delivery it has already received and authenticated, to the URL and credential carried inside that delivery's signed envelope. It cannot forward or redirect a delivery.";
983
+ readonly visibility: "Test environments only. Organization, project, environment, and credential kind come from the verified principal; no request may widen them.";
984
+ };
985
+ readonly version: "2026-08-23";
986
+ };