@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,104 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Refusals.
4
+ *
5
+ * Every non-2xx response is an RFC 9457 problem document. It carries a stable
6
+ * machine-readable `code`, a human `title`, and — where the caller can act on
7
+ * it — a `retry` hint and per-field `errors`. The legacy scheduler returned
8
+ * `{ "error": "invalid_cron" }` with no category, no correlation, and no way
9
+ * for a client to tell "retry this" from "never retry this"; every adapter and
10
+ * SDK invented its own interpretation.
11
+ *
12
+ * `code` is the contract. `title` and `detail` are prose and may change without
13
+ * being a breaking change; clients must never branch on them.
14
+ */
15
+ export const PROBLEM_CATEGORIES = [
16
+ "authentication",
17
+ "authorization",
18
+ "conflict",
19
+ "invalid-request",
20
+ "not-found",
21
+ "rate-limited",
22
+ "unavailable",
23
+ ];
24
+ export const problemCategorySchema = z.enum(PROBLEM_CATEGORIES);
25
+ /**
26
+ * The stable refusal catalog. Adding a code is additive; removing one, or
27
+ * changing the category or status of an existing one, is breaking.
28
+ */
29
+ export const PROBLEM_CODES = {
30
+ authentication_required: { category: "authentication", status: 401 },
31
+ // Separate from `credential_revoked` because the two mean different things to
32
+ // whoever is reading them. An expiry is the deadline a rotation already told
33
+ // you about; a revocation is somebody deciding this credential stops working
34
+ // now. One code for both would make a missed rotation window indistinguishable
35
+ // from a security event, and the client's remedy differs — rotate again
36
+ // versus find out who revoked it.
37
+ credential_expired: { category: "authentication", status: 401 },
38
+ credential_revoked: { category: "authentication", status: 401 },
39
+ endpoint_unverified: { category: "invalid-request", status: 422 },
40
+ idempotency_key_reused: { category: "conflict", status: 409 },
41
+ invalid_request: { category: "invalid-request", status: 400 },
42
+ not_found: { category: "not-found", status: 404 },
43
+ payload_too_large: { category: "invalid-request", status: 413 },
44
+ plan_limit_reached: { category: "conflict", status: 409 },
45
+ rate_limited: { category: "rate-limited", status: 429 },
46
+ scope_missing: { category: "authorization", status: 403 },
47
+ service_unavailable: { category: "unavailable", status: 503 },
48
+ // The resource is in a state that does not permit what was asked — resuming a
49
+ // cancelled schedule, archiving an active one. A conflict rather than an
50
+ // invalid request: the request is well formed and would succeed against the
51
+ // same resource in another state, so telling the caller to fix their request
52
+ // would send them looking in the wrong place. Distinct from `version_conflict`,
53
+ // which says somebody else got there first; this one says nobody did and the
54
+ // transition is still not available.
55
+ state_conflict: { category: "conflict", status: 409 },
56
+ test_to_live_escalation: { category: "authorization", status: 403 },
57
+ version_conflict: { category: "conflict", status: 409 },
58
+ };
59
+ export const problemCodeSchema = z.enum(Object.keys(PROBLEM_CODES));
60
+ /** Whether repeating the identical request could succeed later. */
61
+ export function isRetryable(code) {
62
+ const category = PROBLEM_CODES[code].category;
63
+ return category === "rate-limited" || category === "unavailable";
64
+ }
65
+ export const fieldErrorSchema = z.object({
66
+ /** JSON Pointer into the request body, e.g. `/schedule/expression`. */
67
+ pointer: z.string().startsWith("/"),
68
+ reason: z.string().min(1),
69
+ });
70
+ export const problemSchema = z.object({
71
+ code: problemCodeSchema,
72
+ /** Prose. Never branch on it. */
73
+ detail: z.string().min(1).optional(),
74
+ errors: z.array(fieldErrorSchema).optional(),
75
+ /** Seconds to wait before repeating the request; only on retryable codes. */
76
+ retryAfterSeconds: z.number().int().positive().optional(),
77
+ status: z.number().int().min(400).max(599),
78
+ /** Prose. Never branch on it. */
79
+ title: z.string().min(1),
80
+ /** Echoes the request correlation id so a report can be traced. */
81
+ traceId: z.string().min(1).optional(),
82
+ type: z.literal("about:blank"),
83
+ });
84
+ /**
85
+ * Builds a problem whose status and category cannot contradict its code, and
86
+ * which cannot carry a retry hint on a code that is not retryable.
87
+ */
88
+ export function problem(code, options) {
89
+ if (options.retryAfterSeconds !== undefined && !isRetryable(code)) {
90
+ throw new RangeError(`${code} is not retryable, so it must not carry retryAfterSeconds`);
91
+ }
92
+ return problemSchema.parse({
93
+ code,
94
+ status: PROBLEM_CODES[code].status,
95
+ type: "about:blank",
96
+ ...(options.detail === undefined ? {} : { detail: options.detail }),
97
+ ...(options.errors === undefined ? {} : { errors: [...options.errors] }),
98
+ ...(options.retryAfterSeconds === undefined
99
+ ? {}
100
+ : { retryAfterSeconds: options.retryAfterSeconds }),
101
+ title: options.title,
102
+ ...(options.traceId === undefined ? {} : { traceId: options.traceId }),
103
+ });
104
+ }
@@ -0,0 +1,308 @@
1
+ import { z } from "zod";
2
+ import type { Authority } from "./authority.js";
3
+ import type { FieldError } from "./problems.js";
4
+ /**
5
+ * Request and collection mechanics: paging, optimistic concurrency, and
6
+ * idempotent writes.
7
+ */
8
+ /**
9
+ * Cursor paging only. Offsets drift when rows are inserted mid-scan, and a
10
+ * scheduling book changes constantly. The cursor is opaque: clients pass back
11
+ * exactly what they were given.
12
+ */
13
+ export declare const PAGE_SIZE: {
14
+ readonly default: 25;
15
+ readonly max: 100;
16
+ readonly min: 1;
17
+ };
18
+ export declare const pageRequestSchema: z.ZodObject<{
19
+ cursor: z.ZodOptional<z.ZodString>;
20
+ limit: z.ZodDefault<z.ZodNumber>;
21
+ }, z.core.$strip>;
22
+ export type PageRequest = z.infer<typeof pageRequestSchema>;
23
+ /**
24
+ * A page of results.
25
+ *
26
+ * `freshness` is deliberately part of the contract rather than an
27
+ * implementation detail: cross-partition listings are served from a projection
28
+ * that may lag, and a client that cannot tell fresh from stale will present
29
+ * stale data as fact. `authoritative` means the answer came from the owning
30
+ * partition; `projected` means it may lag and carries the age.
31
+ */
32
+ export declare const freshnessSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
33
+ kind: z.ZodLiteral<"authoritative">;
34
+ }, z.core.$strip>, z.ZodObject<{
35
+ asOf: z.ZodString;
36
+ kind: z.ZodLiteral<"projected">;
37
+ lagSeconds: z.ZodNumber;
38
+ }, z.core.$strip>], "kind">;
39
+ export type Freshness = z.infer<typeof freshnessSchema>;
40
+ /**
41
+ * The `projected` arm alone, for a response that is never authoritative.
42
+ *
43
+ * **A comment saying "always projected" is a claim; this is a check.** The
44
+ * forecast's own schema documented that arm as the only one it ever sends and
45
+ * then admitted both, so every reader had to handle a case the service cannot
46
+ * produce — and the two most useful fields, the age and the instant it was
47
+ * taken, were unreachable without narrowing that no consumer could justify.
48
+ * Naming the narrower shape moves the claim into the type.
49
+ */
50
+ export declare const projectedFreshnessSchema: z.ZodObject<{
51
+ asOf: z.ZodString;
52
+ kind: z.ZodLiteral<"projected">;
53
+ lagSeconds: z.ZodNumber;
54
+ }, z.core.$strip>;
55
+ export type ProjectedFreshness = z.infer<typeof projectedFreshnessSchema>;
56
+ export declare function pageSchema<TItem extends z.ZodTypeAny>(item: TItem): z.ZodObject<{
57
+ data: z.ZodArray<TItem>;
58
+ freshness: z.ZodDiscriminatedUnion<[z.ZodObject<{
59
+ kind: z.ZodLiteral<"authoritative">;
60
+ }, z.core.$strip>, z.ZodObject<{
61
+ asOf: z.ZodString;
62
+ kind: z.ZodLiteral<"projected">;
63
+ lagSeconds: z.ZodNumber;
64
+ }, z.core.$strip>], "kind">;
65
+ nextCursor: z.ZodOptional<z.ZodString>;
66
+ }, z.core.$strip>;
67
+ /**
68
+ * Where a page stopped.
69
+ *
70
+ * Every collection in this product is ordered by the instant a row was created
71
+ * and then by its identifier, so one position type serves all of them. The
72
+ * identifier is the tiebreak rather than decoration: two credentials issued in
73
+ * the same millisecond are ordinary, and an order that does not break the tie
74
+ * can show one of them twice and the other never.
75
+ */
76
+ export interface PagePosition {
77
+ /** The creation instant of the last item on the page just served. */
78
+ readonly createdAtMs: number;
79
+ /** That item's identifier. */
80
+ readonly id: string;
81
+ }
82
+ /**
83
+ * The cursor a client is handed for the next page.
84
+ *
85
+ * Opaque by construction: it is a position and a tenancy fingerprint, and
86
+ * `docs/contracts/v1-conventions.md` says clients pass back exactly what they
87
+ * were given. It is deliberately *not* signed. A signature would imply the value
88
+ * carries authority, and it carries none — every listing is scoped by the
89
+ * principal before a cursor is even looked at.
90
+ */
91
+ export declare function encodeCursor(authority: Authority, position: PagePosition): string;
92
+ /**
93
+ * The position a cursor names, or nothing.
94
+ *
95
+ * Nothing covers every way a cursor can fail to be one this listing issued:
96
+ * not base64url, not JSON, the wrong shape, or minted under a different
97
+ * authority. They collapse into one answer on purpose — telling a caller *how*
98
+ * their cursor is wrong tells them what a valid one looks like, and the caller
99
+ * never constructs one anyway.
100
+ */
101
+ export declare function decodeCursor(authority: Authority, cursor: string): PagePosition | undefined;
102
+ export type PageRequestParse = {
103
+ readonly ok: true;
104
+ readonly page: PageRequest;
105
+ } | {
106
+ readonly ok: false;
107
+ readonly errors: readonly FieldError[];
108
+ };
109
+ /**
110
+ * A page request, read from a URL's query string.
111
+ *
112
+ * Lives here rather than in a route so REST and MCP cannot disagree about what
113
+ * `limit=0`, `limit=abc` or a repeated parameter mean — the bounds and the
114
+ * default come from `pageRequestSchema` and are stated once.
115
+ *
116
+ * A **repeated** parameter is refused rather than resolved by taking the first
117
+ * or the last. `?limit=5&limit=500` is a caller who does not agree with
118
+ * themselves, and silently picking one makes their bug look like ours.
119
+ *
120
+ * Unknown parameters are ignored. A client written against a later version of
121
+ * this contract must not start failing against an earlier deployment, and that
122
+ * is the direction compatibility has to hold in.
123
+ */
124
+ export declare function parsePageRequest(query: URLSearchParams): PageRequestParse;
125
+ /**
126
+ * Every mutable resource carries a monotonically increasing `version`, surfaced
127
+ * as a strong ETag. A conditional write that loses the race is refused with
128
+ * `version_conflict` rather than silently overwriting — a cancel racing a
129
+ * replace must not resolve by luck.
130
+ */
131
+ export declare const versionSchema: z.ZodNumber;
132
+ export type Version = z.infer<typeof versionSchema>;
133
+ export declare function toETag(version: Version): string;
134
+ /** Parses a strong ETag, returning undefined for anything malformed or weak. */
135
+ export declare function fromETag(etag: string): Version | undefined;
136
+ /**
137
+ * The same rule as a schema, and deliberately the *same* rule: the refinement
138
+ * calls the parser rather than restating what it accepts, so a value the
139
+ * document says is valid cannot be one the service then refuses.
140
+ *
141
+ * The `regex` is kept beside the refinement because only the regex survives
142
+ * JSON Schema conversion — the published document carries the pattern, and the
143
+ * refinement is the extra strictness the OpenAPI preamble already discloses
144
+ * that a described schema does not capture.
145
+ */
146
+ export declare const etagSchema: z.ZodString;
147
+ /**
148
+ * Idempotency keys make an unsafe write safe to retry.
149
+ *
150
+ * The key is scoped to the authority tuple and the operation, so two projects
151
+ * may use the same key without colliding. A replay of the *same* request
152
+ * returns the original result with `replayed: true`. Reusing a key with a
153
+ * *different* request body is a `idempotency_key_reused` conflict, never a
154
+ * silent second write — that distinction is the whole point, and the legacy
155
+ * scheduler had no equivalent.
156
+ */
157
+ export declare const idempotencyKeySchema: z.ZodString;
158
+ export type IdempotencyKey = z.infer<typeof idempotencyKeySchema>;
159
+ /**
160
+ * The scope a key was used in. Carried on the receipt rather than assumed,
161
+ * because an implementation that keys its receipt store on the idempotency key
162
+ * alone would return one tenant's result to another whenever both happened to
163
+ * send the same key. The scope is part of the record so that mistake cannot be
164
+ * made silently.
165
+ */
166
+ export declare const idempotencyScopeSchema: z.ZodObject<{
167
+ environment: z.ZodString;
168
+ operation: z.ZodString;
169
+ organization: z.ZodString;
170
+ project: z.ZodString;
171
+ }, z.core.$strip>;
172
+ export type IdempotencyScope = z.infer<typeof idempotencyScopeSchema>;
173
+ export declare const idempotencyReceiptSchema: z.ZodObject<{
174
+ requestFingerprint: z.ZodString;
175
+ replayed: z.ZodBoolean;
176
+ scope: z.ZodObject<{
177
+ environment: z.ZodString;
178
+ operation: z.ZodString;
179
+ organization: z.ZodString;
180
+ project: z.ZodString;
181
+ }, z.core.$strip>;
182
+ storedAt: z.ZodString;
183
+ }, z.core.$strip>;
184
+ export type IdempotencyReceipt = z.infer<typeof idempotencyReceiptSchema>;
185
+ /**
186
+ * The canonical form of a request, as the receipt's `requestFingerprint`
187
+ * describes it: the operation name, then the body with object keys sorted and
188
+ * no insignificant whitespace.
189
+ *
190
+ * Here rather than in each adapter because the receipt schema says every
191
+ * adapter must compute it the same way, and a rule stated in a doc comment and
192
+ * implemented twice is a rule that holds until the second implementation. REST
193
+ * and MCP send the same create differently — one as a JSON body, one as tool
194
+ * arguments — and this is what makes "the same request" mean the same thing to
195
+ * both.
196
+ *
197
+ * Keys are ordered by UTF-16 code unit rather than by `localeCompare`, which is
198
+ * locale-dependent: a canonical form that depends on the machine's locale is
199
+ * not canonical. `undefined`-valued keys are dropped, because `JSON.stringify`
200
+ * drops them too and a caller who omitted a field and one who set it to
201
+ * `undefined` sent the same bytes.
202
+ */
203
+ export declare function canonicalRequest(operation: string, body: unknown): string;
204
+ /**
205
+ * The fingerprint stored on a receipt: SHA-256 of the canonical request, hex.
206
+ *
207
+ * Hashed rather than stored whole because a body may carry a payload up to
208
+ * `SCHEDULING_BOUNDS.payloadBytes`, and a receipt store that kept every request
209
+ * verbatim would cost the same as the schedules it protects. A cryptographic
210
+ * digest rather than the cheap hash `encodeCursor` uses: a collision there
211
+ * costs a confusing refusal, and a collision here would replay one request's
212
+ * result for a different request, which is the exact failure the receipt exists
213
+ * to prevent.
214
+ *
215
+ * Web Crypto, for the reason `identifiers.ts` gives for `getRandomValues`: it
216
+ * is a standard rather than a Cloudflare extension, so one implementation
217
+ * serves every runtime this package is read from.
218
+ */
219
+ export declare function requestFingerprint(operation: string, body: unknown): Promise<string>;
220
+ export type IdempotencyOutcome = {
221
+ readonly kind: "accepted";
222
+ } | {
223
+ readonly kind: "replayed";
224
+ } | {
225
+ readonly kind: "conflict";
226
+ };
227
+ /**
228
+ * Decides how a write carrying an idempotency key must be treated. Pure, so
229
+ * both the API and the MCP adapter reach the same answer.
230
+ *
231
+ * A receipt from a different scope is not this caller's key at all — it is
232
+ * accepted as a first use, never replayed and never a conflict.
233
+ */
234
+ export declare function classifyIdempotency(incoming: {
235
+ readonly fingerprint: string;
236
+ readonly scope: IdempotencyScope;
237
+ }, stored: IdempotencyReceipt | undefined): IdempotencyOutcome;
238
+ /**
239
+ * The headers the mechanics above are carried in, as object schemas whose
240
+ * properties are the header names.
241
+ *
242
+ * The same shape `OperationDefinition.query` uses, for the same reason: a
243
+ * header a route reads and the published document does not carry is drift, and
244
+ * the way to prevent it is for both to derive from one schema. Names are
245
+ * standard where a standard exists — `If-Match` and `ETag` are RFC 9110's, and
246
+ * a conditional write invented a private spelling of them would be a
247
+ * conditional write no HTTP cache, proxy or client library understands.
248
+ *
249
+ * `Idempotency-Key` has no RFC yet; the name is the one the payments industry
250
+ * settled on and the one the IETF draft uses, which is what an integrator will
251
+ * already have a client for.
252
+ */
253
+ export declare const CONCURRENCY_HEADERS: {
254
+ readonly etag: "ETag";
255
+ readonly idempotencyKey: "Idempotency-Key";
256
+ readonly ifMatch: "If-Match";
257
+ readonly replayed: "Idempotency-Replayed";
258
+ };
259
+ export declare const idempotencyRequestHeaderSchema: z.ZodObject<{
260
+ "Idempotency-Key": z.ZodOptional<z.ZodString>;
261
+ }, z.core.$strip>;
262
+ /**
263
+ * A write for which idempotency is mandatory, rather than an optional
264
+ * protection a caller may opt into.
265
+ *
266
+ * Redelivery uses this shape because retrying an ambiguous redelivery request
267
+ * without the same key could send a second callback. Keeping it distinct from
268
+ * `idempotencyRequestHeaderSchema` lets schedule creation retain its published
269
+ * optional behavior without weakening the redelivery promise.
270
+ */
271
+ export declare const requiredIdempotencyRequestHeaderSchema: z.ZodObject<{
272
+ "Idempotency-Key": z.ZodString;
273
+ }, z.core.$strip>;
274
+ export declare const conditionalRequestHeaderSchema: z.ZodObject<{
275
+ "If-Match": z.ZodOptional<z.ZodString>;
276
+ }, z.core.$strip>;
277
+ /**
278
+ * Required rather than optional, deliberately: an operation that declares this
279
+ * promises the header on every success, and a client that has to test for its
280
+ * presence before making a conditional write will write the untested branch
281
+ * wrong.
282
+ */
283
+ export declare const versionResponseHeaderSchema: z.ZodObject<{
284
+ ETag: z.ZodString;
285
+ }, z.core.$strip>;
286
+ /**
287
+ * What a replayed write answers with, on top of the version.
288
+ *
289
+ * `replayed: true` is a promise `docs/contracts/v1-conventions.md` and
290
+ * `idempotencyReceiptSchema` have both made since W0-02 — the receipt's own
291
+ * field is documented as "true when **this response** replays an earlier
292
+ * identical request" — and until W2-02's review nothing carried it on the wire.
293
+ * A caller could not tell a replay from a first create, which makes the
294
+ * difference between "my retry worked" and "I created a second one" unanswerable
295
+ * from the response.
296
+ *
297
+ * Present only on a replay rather than `false` on every create: an absent
298
+ * header is the ordinary case, and a client that has to read a header to learn
299
+ * nothing happened is a client that will not read it.
300
+ */
301
+ export declare const replayedResponseHeaderSchema: z.ZodObject<{
302
+ ETag: z.ZodString;
303
+ "Idempotency-Replayed": z.ZodOptional<z.ZodLiteral<"true">>;
304
+ }, z.core.$strip>;
305
+ /** Replay marker for an idempotent create whose result has no ETag. */
306
+ export declare const idempotencyReplayResponseHeaderSchema: z.ZodObject<{
307
+ "Idempotency-Replayed": z.ZodOptional<z.ZodLiteral<"true">>;
308
+ }, z.core.$strip>;