@pithy-sh/cloudflare 0.1.5 → 0.1.7

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.
@@ -1 +1 @@
1
- {"version":3,"file":"assetSeeder.d.ts","sourceRoot":"","sources":["../../src/media/assetSeeder.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAC7D,OAAO,EAAE,KAAK,UAAU,EAAsB,MAAM,aAAa,CAAC;AAClE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAyB/D;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,sBAAsB,EAC/B,KAAK,EAAE,UAAU,GAAG,IAAI,EACxB,IAAI,EAAE;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAC7D,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,CAMzB;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,uBAAuB,EAChC,KAAK,EAAE,UAAU,GAAG,IAAI,EACxB,IAAI,EAAE;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1F,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAwB1B"}
1
+ {"version":3,"file":"assetSeeder.d.ts","sourceRoot":"","sources":["../../src/media/assetSeeder.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAC7D,OAAO,EAAE,KAAK,UAAU,EAAsB,MAAM,aAAa,CAAC;AAClE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAyB/D;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,sBAAsB,EAC/B,KAAK,EAAE,UAAU,GAAG,IAAI,EACxB,IAAI,EAAE;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAC7D,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,CAMzB;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,uBAAuB,EAChC,KAAK,EAAE,UAAU,GAAG,IAAI,EACxB,IAAI,EAAE;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1F,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAiC1B"}
@@ -1,6 +1,6 @@
1
1
  // SPDX-FileCopyrightText: 2026 Pithy
2
2
  // SPDX-License-Identifier: MIT
3
- import { CloudflareRequestError, cloudflareRequest, decodeResponse } from "../client/errors.js";
3
+ import { cloudflareApiErrors, cloudflareRefusal, cloudflareRequest, decodeResponse } from "../client/errors.js";
4
4
  import { withAssetOwnership } from "./ownership.js";
5
5
  import { z } from "zod";
6
6
  //#region src/media/assetSeeder.ts
@@ -61,12 +61,26 @@ async function uploadStreamBytes(manager, bytes, opts) {
61
61
  method: "POST",
62
62
  body: form
63
63
  });
64
- if (!response.ok) throw new CloudflareRequestError({
65
- message: "The Stream direct upload failed.",
66
- detail: `Stream direct upload returned ${response.status}: ${await response.text()}`
67
- });
64
+ if (!response.ok) {
65
+ const body = await response.text();
66
+ throw cloudflareRefusal({
67
+ problem: "The Stream direct upload failed.",
68
+ apiErrors: cloudflareApiErrors(parseEnvelope(body)),
69
+ status: response.status,
70
+ permission: "Stream",
71
+ detail: `Stream direct upload returned ${response.status}: ${body}`.slice(0, 2e3)
72
+ });
73
+ }
68
74
  });
69
75
  return { uid: mint.uid };
70
76
  }
77
+ /** A CF response body as an object, or `undefined` when it is not JSON — `cloudflareApiErrors` reads either. */
78
+ function parseEnvelope(body) {
79
+ try {
80
+ return JSON.parse(body);
81
+ } catch {
82
+ return;
83
+ }
84
+ }
71
85
  //#endregion
72
86
  export { uploadImageBytes, uploadStreamBytes };
@@ -1 +1 @@
1
- {"version":3,"file":"r2Credentials.d.ts","sourceRoot":"","sources":["../../src/r2/r2Credentials.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;GAIG;AACH,eAAO,MAAM,aAAa;;;iBAKiE,CAAC;AAC5F,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC"}
1
+ {"version":3,"file":"r2Credentials.d.ts","sourceRoot":"","sources":["../../src/r2/r2Credentials.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;GAIG;AACH,eAAO,MAAM,aAAa;;;iBASiE,CAAC;AAC5F,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC"}
@@ -8,8 +8,8 @@ import { z } from "zod";
8
8
  * `@pithy-sh/secrets` validates the persisted credential envelope against it.
9
9
  */
10
10
  const R2Credentials = z.object({
11
- accessKeyId: z.string().min(1).describe("The R2 S3 access key id."),
12
- secretAccessKey: z.string().min(1).describe("The R2 S3 secret access key. Never logged or serialized to clients.")
11
+ accessKeyId: z.string().min(1).meta({ multiline: false }).describe("The R2 S3 access key id."),
12
+ secretAccessKey: z.string().min(1).meta({ multiline: false }).describe("The R2 S3 secret access key. Never logged or serialized to clients.")
13
13
  }).describe("The S3-compatible access-key/secret-key pair R2 uses to sign presigned URLs.");
14
14
  //#endregion
15
15
  export { R2Credentials };
@@ -1 +1 @@
1
- {"version":3,"file":"emailRoutingRules.d.ts","sourceRoot":"","sources":["../../src/test-utils/emailRoutingRules.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAkClD,iEAAiE;AACjE,eAAO,MAAM,mBAAmB;;;;iBAMoB,CAAC;AAErD,yEAAyE;AACzE,eAAO,MAAM,kBAAkB;;;iBAKoB,CAAC;AAEpD,qDAAqD;AACrD,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;iBAYsC,CAAC;AAEpE,qDAAqD;AACrD,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAiBjE,+DAA+D;AAC/D,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAuBhH;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,gBAAgB,EAAE,GAAG,gBAAgB,EAAE,CAEjF"}
1
+ {"version":3,"file":"emailRoutingRules.d.ts","sourceRoot":"","sources":["../../src/test-utils/emailRoutingRules.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAkClD,iEAAiE;AACjE,eAAO,MAAM,mBAAmB;;;;iBAMoB,CAAC;AAErD,yEAAyE;AACzE,eAAO,MAAM,kBAAkB;;;iBAKoB,CAAC;AAEpD,qDAAqD;AACrD,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;iBAYsC,CAAC;AAEpE,qDAAqD;AACrD,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAiBjE,+DAA+D;AAC/D,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CA4BhH;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,gBAAgB,EAAE,GAAG,gBAAgB,EAAE,CAEjF"}
@@ -1,6 +1,6 @@
1
1
  // SPDX-FileCopyrightText: 2026 Pithy
2
2
  // SPDX-License-Identifier: MIT
3
- import { CloudflareRequestError } from "../client/errors.js";
3
+ import { cloudflareApiErrors, cloudflareRefusal } from "../client/errors.js";
4
4
  import { z } from "zod";
5
5
  //#region src/test-utils/emailRoutingRules.ts
6
6
  /**
@@ -67,11 +67,16 @@ async function listEmailRoutingRules(creds, zoneId) {
67
67
  for (let page = 1; page <= MAX_RULE_PAGES; page += 1) {
68
68
  const url = `https://api.cloudflare.com/client/v4/zones/${zoneId}/email/routing/rules?page=${page}&per_page=${RULES_PER_PAGE}`;
69
69
  const response = await fetch(url, { headers: { Authorization: `Bearer ${creds.apiToken}` } });
70
- if (!response.ok) throw new CloudflareRequestError({
71
- message: "Could not read the zone's Email Routing rules.",
72
- action: "Check the token carries Email Routing Rules: Read on this zone.",
73
- detail: `Email Routing rule list returned ${response.status}.`
74
- });
70
+ if (!response.ok) {
71
+ const body = await response.text();
72
+ throw cloudflareRefusal({
73
+ problem: "Could not read the zone's Email Routing rules.",
74
+ apiErrors: cloudflareApiErrors(parseEnvelope(body)),
75
+ status: response.status,
76
+ permission: "Email Routing Rules",
77
+ detail: `Email Routing rule list returned ${response.status}: ${body}`.slice(0, 2e3)
78
+ });
79
+ }
75
80
  const envelope = RuleListEnvelope.parse(await response.json());
76
81
  const batch = envelope.result ?? [];
77
82
  rules.push(...batch);
@@ -87,5 +92,13 @@ async function listEmailRoutingRules(creds, zoneId) {
87
92
  function namedRules(rules) {
88
93
  return rules.filter((rule) => rule.name !== "");
89
94
  }
95
+ /** A CF response body as an object, or `undefined` when it is not JSON — `cloudflareApiErrors` reads either. */
96
+ function parseEnvelope(body) {
97
+ try {
98
+ return JSON.parse(body);
99
+ } catch {
100
+ return;
101
+ }
102
+ }
90
103
  //#endregion
91
104
  export { EmailRoutingAction, EmailRoutingMatcher, EmailRoutingRule, listEmailRoutingRules, namedRules };
@@ -1 +1 @@
1
- {"version":3,"file":"inboundRecorder.d.ts","sourceRoot":"","sources":["../../src/test-utils/inboundRecorder.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAC1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,kDAAkD;AAClD,eAAO,MAAM,wBAAwB,aAAa,CAAC;AAEnD,4GAA4G;AAC5G,eAAO,MAAM,oBAAoB,kBAAkB,CAAC;AAapD;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,myCAmCnC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,eAAe;;;;;;;;iBAgBqD,CAAC;AAElF,6BAA6B;AAC7B,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,eAAe,CAAC,CAAC;AAE/D,8FAA8F;AAC9F,eAAO,MAAM,0BAA0B,2CAA2C,CAAC;AAEnF,yEAAyE;AACzE,eAAO,MAAM,mBAAmB,eAAe,CAAC;AAEhD;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CAAC,OAAO,EAAE;IACnD,OAAO,EAAE,wBAAwB,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,2GAA2G;IAC3G,iBAAiB,EAAE,MAAM,CAAC;CAC3B,GAAG,OAAO,CAAC,IAAI,CAAC,CAQhB;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE;IACjD,KAAK,EAAE,gBAAgB,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAuBlC"}
1
+ {"version":3,"file":"inboundRecorder.d.ts","sourceRoot":"","sources":["../../src/test-utils/inboundRecorder.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAC1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,kDAAkD;AAClD,eAAO,MAAM,wBAAwB,aAAa,CAAC;AAEnD,4GAA4G;AAC5G,eAAO,MAAM,oBAAoB,kBAAkB,CAAC;AAapD;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,myCAmCnC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,eAAe;;;;;;;;iBAgBqD,CAAC;AAElF,6BAA6B;AAC7B,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,eAAe,CAAC,CAAC;AAE/D,8FAA8F;AAC9F,eAAO,MAAM,0BAA0B,2CAA2C,CAAC;AAEnF,yEAAyE;AACzE,eAAO,MAAM,mBAAmB,eAAe,CAAC;AAEhD;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CAAC,OAAO,EAAE;IACnD,OAAO,EAAE,wBAAwB,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,2GAA2G;IAC3G,iBAAiB,EAAE,MAAM,CAAC;CAC3B,GAAG,OAAO,CAAC,IAAI,CAAC,CAQhB;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE;IACjD,KAAK,EAAE,gBAAgB,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CA4BlC"}
@@ -1,6 +1,6 @@
1
1
  // SPDX-FileCopyrightText: 2026 Pithy
2
2
  // SPDX-License-Identifier: MIT
3
- import { CloudflareInvalidResponseError, CloudflareRequestError } from "../client/errors.js";
3
+ import { CloudflareInvalidResponseError, cloudflareApiErrors, cloudflareRefusal } from "../client/errors.js";
4
4
  import { z } from "zod";
5
5
  //#region src/test-utils/inboundRecorder.ts
6
6
  /**
@@ -127,11 +127,16 @@ async function readObservedInbound(options) {
127
127
  const url = `https://api.cloudflare.com/client/v4/accounts/${creds.accountId}/storage/kv/namespaces/${namespaceId}/values/${encodeURIComponent(nonce)}`;
128
128
  const response = await fetch(url, { headers: { Authorization: `Bearer ${creds.apiToken}` } });
129
129
  if (response.status === 404) return null;
130
- if (!response.ok) throw new CloudflareRequestError({
131
- message: "Could not read the recorded inbound message.",
132
- action: "Check the token carries Workers KV Storage: Edit on this account.",
133
- detail: `KV read for the inbound record returned ${response.status}.`
134
- });
130
+ if (!response.ok) {
131
+ const failure = await response.text();
132
+ throw cloudflareRefusal({
133
+ problem: "Could not read the recorded inbound message.",
134
+ apiErrors: cloudflareApiErrors(parseEnvelope(failure)),
135
+ status: response.status,
136
+ permission: "Workers KV Storage",
137
+ detail: `KV read for the inbound record returned ${response.status}: ${failure}`.slice(0, 2e3)
138
+ });
139
+ }
135
140
  const body = JSON.parse(await response.text());
136
141
  if (typeof body === "object" && body !== null && "failure" in body) throw new CloudflareInvalidResponseError({
137
142
  message: "The inbound recorder could not read the message it was handed.",
@@ -139,5 +144,13 @@ async function readObservedInbound(options) {
139
144
  });
140
145
  return ObservedInbound.parse(body);
141
146
  }
147
+ /** A CF response body as an object, or `undefined` when it is not JSON — `cloudflareApiErrors` reads either. */
148
+ function parseEnvelope(body) {
149
+ try {
150
+ return JSON.parse(body);
151
+ } catch {
152
+ return;
153
+ }
154
+ }
142
155
  //#endregion
143
156
  export { INBOUND_MODULE_NAME, INBOUND_NONCE_HEADER, INBOUND_RECORDER_BINDING, INBOUND_RECORDER_MODULE, ObservedInbound, PLACEHOLDER_INBOUND_MODULE, deployInboundRecorder, readObservedInbound };
@@ -1 +1 @@
1
- {"version":3,"file":"accountTokensManager.d.ts","sourceRoot":"","sources":["../../src/tokens/accountTokensManager.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AASxB,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1D;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,kHAAkH;IAClH,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,kGAAkG;IAClG,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,2EAA2E;IAC3E,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CAC3B;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEzE;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB;;;iBAK8E,CAAC;AAC7G,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAEnE;;;;GAIG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;iBAO6D,CAAC;AAC7F,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAErE;;;;GAIG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;iBAMuD,CAAC;AACxF,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEvE;;;;;;;;;GASG;AACH,qBAAa,8BAA+B,SAAQ,iBAAiB;IACnE,cAAc,IAAI,MAAM,CAEvB;IAED,qGAAqG;IAC/F,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC,CAO9C;IAED;;;;;;;OAOG;IACG,WAAW,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAKhD;IAED;;;;;;;;;;;;OAYG;IACG,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAW1D;IAED,qGAAqG;IAC/F,oBAAoB,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAWzD;IAED;;;;;;OAMG;IACG,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAE7E;IAED;;;;;;;OAOG;IACG,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA2BzF;IAED;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAqB3B,2GAA2G;IACrG,UAAU,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC,CASjD;IAED,oGAAoG;IAC9F,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAQvE;IAED,qCAAqC;IAC/B,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhD;IAED;;;;OAIG;IACG,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAYtD;IAED;;;;;OAKG;IACG,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAKrD;IAED;;;;;;OAMG;IACG,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAKzF;CACF"}
1
+ {"version":3,"file":"accountTokensManager.d.ts","sourceRoot":"","sources":["../../src/tokens/accountTokensManager.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAYxB,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAkB1D;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,kHAAkH;IAClH,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,kGAAkG;IAClG,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,2EAA2E;IAC3E,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CAC3B;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEzE;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB;;;iBAK8E,CAAC;AAC7G,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAEnE;;;;GAIG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;iBAO6D,CAAC;AAC7F,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAErE;;;;GAIG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;iBAMuD,CAAC;AACxF,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEvE;;;;;;;;;GASG;AACH,qBAAa,8BAA+B,SAAQ,iBAAiB;IACnE,cAAc,IAAI,MAAM,CAEvB;IAED,qGAAqG;IAC/F,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC,CAO9C;IAED;;;;;;;OAOG;IACG,WAAW,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAKhD;IAED;;;;;;;;;;;;OAYG;IACG,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAe1D;IAED,qGAAqG;IAC/F,oBAAoB,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAezD;IAED;;;;;;OAMG;IACG,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAE7E;IAED;;;;;;;OAOG;IACG,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAgDzF;IAED;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAqB3B,2GAA2G;IACrG,UAAU,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAajD;IAED,oGAAoG;IAC9F,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAYvE;IAED,qCAAqC;IAC/B,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMhD;IAED;;;;OAIG;IACG,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgBtD;IAED;;;;;OAKG;IACG,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAOrD;IAED;;;;;;OAMG;IACG,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAKzF;CACF"}
@@ -1,11 +1,26 @@
1
1
  // SPDX-FileCopyrightText: 2026 Pithy
2
2
  // SPDX-License-Identifier: MIT
3
- import { CloudflareNotConfiguredError, CloudflareRequestError, cloudflareRequest, decodeResponse, isAuthorizationError, messageOf } from "../client/errors.js";
3
+ import { CloudflareNotConfiguredError, cloudflareApiErrors, cloudflareRefusal, cloudflareRequest, cloudflareSaid, decodeResponse, isAuthorizationError, messageOf, statusOf } from "../client/errors.js";
4
4
  import { CloudflareManager } from "../client/manager.js";
5
5
  import { CfTokenVerification } from "../user/userManager.js";
6
6
  import { z } from "zod";
7
7
  //#region src/tokens/accountTokensManager.ts
8
8
  /**
9
+ * The account permission group behind every endpoint in this file, handed to `cloudflareRequest` so an
10
+ * auth-class refusal names it.
11
+ *
12
+ * It has to be stated rather than derived, because Cloudflare's `documentation_url` for
13
+ * `accounts/tokens` yields `accounts` — a segment `ACCOUNT_PERMISSIONS` deliberately does not map, since
14
+ * account-scoped endpoints span every product. And it has to be on **every** call here rather than only
15
+ * on the mint: `pithy token mint` is `rollToken → findTokenByName → listTokens`, then
16
+ * `listPermissionGroups`, and every one of those runs before `tokens.create` on the same credential. A
17
+ * hint reachable only from the mint is a hint an under-scoped bootstrap token never reaches (#534).
18
+ *
19
+ * `verifyToken` is the deliberate omission: any account token may call `/tokens/verify` with no
20
+ * permission at all, so a refusal there is a dead token or the wrong account, never a missing grant.
21
+ */
22
+ const API_TOKENS_ENDPOINT = { permission: "API Tokens" };
23
+ /**
9
24
  * The whole-account resource scope for a token policy: `{ "com.cloudflare.api.account.<id>": "*" }`.
10
25
  * Account-level permission groups (Secrets Store, D1, Workers, …) scope to this. One helper so the
11
26
  * exact resource key format lives in a single place instead of being hand-spelled per use case.
@@ -106,7 +121,7 @@ var CloudflareAccountTokensManager = class extends CloudflareManager {
106
121
  if (isAuthorizationError(error)) return null;
107
122
  throw error;
108
123
  }
109
- });
124
+ }, API_TOKENS_ENDPOINT);
110
125
  const parsed = TokenName.safeParse(raw);
111
126
  return parsed.success ? parsed.data.name : null;
112
127
  }
@@ -119,7 +134,7 @@ var CloudflareAccountTokensManager = class extends CloudflareManager {
119
134
  if (parsed.success) out.push(parsed.data);
120
135
  }
121
136
  return out;
122
- });
137
+ }, API_TOKENS_ENDPOINT);
123
138
  }
124
139
  /**
125
140
  * Resolve permission-group names to their `{ id }` references against the account's live group list.
@@ -154,15 +169,23 @@ var CloudflareAccountTokensManager = class extends CloudflareManager {
154
169
  policies
155
170
  });
156
171
  } catch (error) {
157
- if (isAuthorizationError(error)) throw new CloudflareNotConfiguredError({
158
- message: "The Cloudflare API token is not allowed to create account tokens.",
159
- action: "Grant it 'Account API Tokens Write' (Account → API Tokens → Edit), then re-run.",
160
- detail: `mint account token '${name}': ${messageOf(error)}`
161
- }, { cause: error });
162
- throw new CloudflareRequestError({
163
- message: `Failed to mint account token '${name}'.`,
164
- detail: messageOf(error)
165
- }, { cause: error });
172
+ if (isAuthorizationError(error)) {
173
+ const said = cloudflareSaid("The Cloudflare API token is not allowed to create account tokens.", cloudflareApiErrors(error));
174
+ throw new CloudflareNotConfiguredError({
175
+ message: said.message,
176
+ action: "Grant it 'Account API Tokens Write' (Account → API Tokens → Edit), then re-run.",
177
+ params: said.params,
178
+ detail: `mint account token '${name}': ${messageOf(error)}`
179
+ }, { cause: error });
180
+ }
181
+ throw cloudflareRefusal({
182
+ problem: `Failed to mint account token '${name}'.`,
183
+ apiErrors: cloudflareApiErrors(error),
184
+ status: statusOf(error),
185
+ permission: "API Tokens",
186
+ detail: messageOf(error),
187
+ cause: error
188
+ });
166
189
  }
167
190
  return decodeResponse(MintedAccountToken, raw, "account token create");
168
191
  }
@@ -196,7 +219,7 @@ var CloudflareAccountTokensManager = class extends CloudflareManager {
196
219
  if (parsed.success) out.push(parsed.data);
197
220
  }
198
221
  return out;
199
- });
222
+ }, API_TOKENS_ENDPOINT);
200
223
  }
201
224
  /** Find an account token by exact name, or `null` — for idempotent re-mint (SDK auto-paginates). */
202
225
  async findTokenByName(name) {
@@ -206,11 +229,11 @@ var CloudflareAccountTokensManager = class extends CloudflareManager {
206
229
  if (parsed.success && parsed.data.name === name) return parsed.data;
207
230
  }
208
231
  return null;
209
- });
232
+ }, API_TOKENS_ENDPOINT);
210
233
  }
211
234
  /** Delete an account token by id. */
212
235
  async deleteToken(tokenId) {
213
- await cloudflareRequest(`delete account token ${tokenId}`, () => this.getClient().accounts.tokens.delete(tokenId, { account_id: this.accountId }));
236
+ await cloudflareRequest(`delete account token ${tokenId}`, () => this.getClient().accounts.tokens.delete(tokenId, { account_id: this.accountId }), API_TOKENS_ENDPOINT);
214
237
  }
215
238
  /**
216
239
  * Delete every account token with the given name; returns how many were removed. Names are not
@@ -226,7 +249,7 @@ var CloudflareAccountTokensManager = class extends CloudflareManager {
226
249
  }
227
250
  for (const id of ids) await this.getClient().accounts.tokens.delete(id, { account_id: this.accountId });
228
251
  return ids.length;
229
- });
252
+ }, API_TOKENS_ENDPOINT);
230
253
  }
231
254
  /**
232
255
  * Roll an existing token's secret: regenerate its value **in place**, keeping the same token id,
@@ -238,7 +261,7 @@ var CloudflareAccountTokensManager = class extends CloudflareManager {
238
261
  const raw = await cloudflareRequest(`roll account token value ${tokenId}`, () => this.getClient().accounts.tokens.value.update(tokenId, {
239
262
  account_id: this.accountId,
240
263
  body: {}
241
- }));
264
+ }), API_TOKENS_ENDPOINT);
242
265
  return decodeResponse(RolledTokenValue, raw, "account token value roll");
243
266
  }
244
267
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"buildsManager.d.ts","sourceRoot":"","sources":["../../src/workers/buildsManager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"buildsManager.d.ts","sourceRoot":"","sources":["../../src/workers/buildsManager.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  // SPDX-FileCopyrightText: 2026 Pithy
2
2
  // SPDX-License-Identifier: MIT
3
- import { CloudflareInvalidResponseError, CloudflareRequestError, cloudflareRequest, decodeResponse, messageOf } from "../client/errors.js";
3
+ import { CloudflareInvalidResponseError, CloudflareRequestError, cloudflareApiErrors, cloudflareRefusal, cloudflareRequest, decodeResponse, messageOf } from "../client/errors.js";
4
4
  import { CloudflareManager } from "../client/manager.js";
5
5
  import { CfBuild, CfBuildLog, CfBuildToken, CfBuildTrigger, CfRepoConnection } from "./buildsTypes.js";
6
6
  import { z } from "zod";
@@ -17,7 +17,8 @@ const CfEnvelope = z.object({
17
17
  success: z.boolean().optional(),
18
18
  errors: z.array(z.object({
19
19
  code: z.number().optional(),
20
- message: z.string().optional()
20
+ message: z.string().optional(),
21
+ documentation_url: z.string().optional()
21
22
  }).loose()).optional()
22
23
  });
23
24
  /**
@@ -61,10 +62,13 @@ var CloudflareBuildsManager = class extends CloudflareManager {
61
62
  const envelope = CfEnvelope.safeParse(parsed);
62
63
  const apiReportedFailure = envelope.success && envelope.data.success === false;
63
64
  if (!response.ok || apiReportedFailure) {
64
- const codes = envelope.success ? (envelope.data.errors ?? []).map((e) => e.code).filter((c) => c !== void 0) : [];
65
+ const errors = envelope.success ? envelope.data.errors ?? [] : [];
66
+ const codes = errors.map((e) => e.code).filter((c) => c !== void 0);
65
67
  const marker = codes.length > 0 ? `[cf-codes:${codes.join(",")}] ` : "";
66
- throw new CloudflareRequestError({
67
- message: `Cloudflare Builds returned ${response.status}.`,
68
+ throw cloudflareRefusal({
69
+ problem: `Cloudflare Builds returned ${response.status}.`,
70
+ apiErrors: cloudflareApiErrors(errors),
71
+ status: response.status,
68
72
  detail: `${marker}${method} ${path} → ${response.status}: ${text}`.slice(0, 2e3)
69
73
  });
70
74
  }
@@ -170,6 +170,34 @@ export declare const WORKFLOW_FAILED_STATUS: 500;
170
170
  * the boundary — and nothing derived from the step's own `detail` is in it, because the step's
171
171
  * `detail` never left the step. The only fields promoted from the far side are `message` and `action`,
172
172
  * both already public, both already proved kit-authored by `kitSentence`.
173
+ *
174
+ * ## `params` did not cross either, and a recovered code still has to keep their promise
175
+ *
176
+ * `core`'s `GUARANTEED_ERROR_PARAMS` states the names a code's throw sites pass on **every** path, and
177
+ * a locale writes `{apiAnswer}` into its sentence against that statement. Re-raising a recovered code
178
+ * here is a throw site like any other — it just happens to be one whose inputs came over a wire that
179
+ * carries three fields. So every guaranteed name is supplied, as the empty string: `interpolate` leaves
180
+ * an unsupplied placeholder written out, and `{apiAnswer}` on a Spanish reader's screen is the exact
181
+ * defect the declaration exists to prevent. Empty is also the honest value — it is what the catalogs
182
+ * define as *no answer to quote*, and the answer genuinely did not cross.
183
+ *
184
+ * **One reader does lose something by it, and it is worth naming rather than glossing.** An earlier
185
+ * version of this comment said nothing was lost, on the grounds that the API's words are already inside
186
+ * the promoted English `message`. That is true of the operator's terminal, the `--json` line and any
187
+ * client with no catalog — but the contract is `t.maybe(payload.code, payload.params) ?? payload.message`
188
+ * (`core/src/i18n/translator.ts`), and `maybe` returns null only on a catalog **miss**. `es` has an entry
189
+ * for this code, so a Spanish reader gets the translated sentence with an empty `{apiAnswer}` rather
190
+ * than falling through to the English one that carries the answer:
191
+ *
192
+ * ```
193
+ * at the CLI No se ha podido completar la llamada a Cloudflare: 10000 Authentication error.
194
+ * across a step No se ha podido completar la llamada a Cloudflare.
195
+ * ```
196
+ *
197
+ * The step channel carries three fields and `params` is not one of them, so closing this means widening
198
+ * the channel or re-parsing the promoted sentence — the first is a change to a durable format, the
199
+ * second reconstructs data from prose. Neither belongs in a re-raise, so the gap stands and is written
200
+ * down here instead of being claimed away.
173
201
  */
174
202
  export declare function terminalWorkflowError(args: {
175
203
  /** What the instance's steps said, or `null` when no step reported a failure. */
@@ -1 +1 @@
1
- {"version":3,"file":"stepFailure.d.ts","sourceRoot":"","sources":["../../src/workflows/stepFailure.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AAMjE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,gHAAgH;AAChH,eAAO,MAAM,qBAAqB;;;iBAQwD,CAAC;AAC3F,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAE3E,qGAAqG;AACrG,eAAO,MAAM,mBAAmB;;;;;;iBAK0D,CAAC;AAC3F,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEvE;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;iBAQ+D,CAAC;AAC/F,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAErE,gGAAgG;AAChG,MAAM,WAAW,mBAAmB;IAClC,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yGAAyG;IACzG,GAAG,EAAE,MAAM,CAAC;IACZ,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sFAAsF;IACtF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAmBD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAqBnG;AAcD;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,GAAG,SAAS,GAAG,mBAAmB,GAAG,IAAI,CAoB7F;AAED;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,EAAG,sBAA+B,CAAC;AAEpE,oGAAoG;AACpG,eAAO,MAAM,sBAAsB,EAAG,GAAY,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAC1C,iFAAiF;IACjF,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACpC,8FAA8F;IAC9F,eAAe,EAAE,MAAM,CAAC;IACxB,uGAAuG;IACvG,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,UAAU,CAmBb"}
1
+ {"version":3,"file":"stepFailure.d.ts","sourceRoot":"","sources":["../../src/workflows/stepFailure.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AAOjE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,gHAAgH;AAChH,eAAO,MAAM,qBAAqB;;;iBAQwD,CAAC;AAC3F,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAE3E,qGAAqG;AACrG,eAAO,MAAM,mBAAmB;;;;;;iBAK0D,CAAC;AAC3F,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEvE;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;iBAQ+D,CAAC;AAC/F,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAErE,gGAAgG;AAChG,MAAM,WAAW,mBAAmB;IAClC,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yGAAyG;IACzG,GAAG,EAAE,MAAM,CAAC;IACZ,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sFAAsF;IACtF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAmBD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAqBnG;AAcD;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,GAAG,SAAS,GAAG,mBAAmB,GAAG,IAAI,CAoB7F;AAED;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,EAAG,sBAA+B,CAAC;AAEpE,oGAAoG;AACpG,eAAO,MAAM,sBAAsB,EAAG,GAAY,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoEG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAC1C,iFAAiF;IACjF,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACpC,8FAA8F;IAC9F,eAAe,EAAE,MAAM,CAAC;IACxB,uGAAuG;IACvG,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,UAAU,CAmBb"}
@@ -2,8 +2,9 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  import { z } from "zod";
4
4
  import { PithyError } from "@pithy-sh/core/src/error/pithyError";
5
- import { ErrorPayload, kitErrorStatus } from "@pithy-sh/core/src/error/payload";
6
5
  import { MAX_WORKFLOW_STEP_TEXT, decodeWorkflowStepMessage, splitWorkflowStepCode } from "@pithy-sh/core/src/workflow/stepMessage";
6
+ import { guaranteedErrorParams } from "@pithy-sh/core/src/error/messageParams";
7
+ import { ErrorPayload, kitErrorStatus } from "@pithy-sh/core/src/error/payload";
7
8
  //#region src/workflows/stepFailure.ts
8
9
  /**
9
10
  * **The step's sentence, recovered from an instance the engine has already written its own over.**
@@ -200,6 +201,34 @@ const WORKFLOW_FAILED_STATUS = 500;
200
201
  * the boundary — and nothing derived from the step's own `detail` is in it, because the step's
201
202
  * `detail` never left the step. The only fields promoted from the far side are `message` and `action`,
202
203
  * both already public, both already proved kit-authored by `kitSentence`.
204
+ *
205
+ * ## `params` did not cross either, and a recovered code still has to keep their promise
206
+ *
207
+ * `core`'s `GUARANTEED_ERROR_PARAMS` states the names a code's throw sites pass on **every** path, and
208
+ * a locale writes `{apiAnswer}` into its sentence against that statement. Re-raising a recovered code
209
+ * here is a throw site like any other — it just happens to be one whose inputs came over a wire that
210
+ * carries three fields. So every guaranteed name is supplied, as the empty string: `interpolate` leaves
211
+ * an unsupplied placeholder written out, and `{apiAnswer}` on a Spanish reader's screen is the exact
212
+ * defect the declaration exists to prevent. Empty is also the honest value — it is what the catalogs
213
+ * define as *no answer to quote*, and the answer genuinely did not cross.
214
+ *
215
+ * **One reader does lose something by it, and it is worth naming rather than glossing.** An earlier
216
+ * version of this comment said nothing was lost, on the grounds that the API's words are already inside
217
+ * the promoted English `message`. That is true of the operator's terminal, the `--json` line and any
218
+ * client with no catalog — but the contract is `t.maybe(payload.code, payload.params) ?? payload.message`
219
+ * (`core/src/i18n/translator.ts`), and `maybe` returns null only on a catalog **miss**. `es` has an entry
220
+ * for this code, so a Spanish reader gets the translated sentence with an empty `{apiAnswer}` rather
221
+ * than falling through to the English one that carries the answer:
222
+ *
223
+ * ```
224
+ * at the CLI No se ha podido completar la llamada a Cloudflare: 10000 Authentication error.
225
+ * across a step No se ha podido completar la llamada a Cloudflare.
226
+ * ```
227
+ *
228
+ * The step channel carries three fields and `params` is not one of them, so closing this means widening
229
+ * the channel or re-parsing the promoted sentence — the first is a change to a durable format, the
230
+ * second reconstructs data from prose. Neither belongs in a re-raise, so the gap stands and is written
231
+ * down here instead of being claimed away.
203
232
  */
204
233
  function terminalWorkflowError(args) {
205
234
  const { failure, fallbackMessage, detail } = args;
@@ -212,7 +241,8 @@ function terminalWorkflowError(args) {
212
241
  status,
213
242
  message,
214
243
  action,
215
- detail
244
+ detail,
245
+ params: crossedParams(failure.code)
216
246
  };
217
247
  const parsed = ErrorPayload.safeParse(candidate);
218
248
  if (parsed.success) return new PithyError(parsed.data);
@@ -225,5 +255,17 @@ function terminalWorkflowError(args) {
225
255
  detail
226
256
  });
227
257
  }
258
+ /**
259
+ * The `params` a re-raised code owes a translating client: every name its throw sites guarantee, empty.
260
+ *
261
+ * `undefined` when the code guarantees none, so a payload for the other 60-odd codes is byte-identical
262
+ * to what it was — an empty `params` object on every recovered error would be a new field on the wire
263
+ * for no reader.
264
+ */
265
+ function crossedParams(code) {
266
+ const names = guaranteedErrorParams(code);
267
+ if (names.length === 0) return void 0;
268
+ return Object.fromEntries(names.map((name) => [name, ""]));
269
+ }
228
270
  //#endregion
229
271
  export { WORKFLOW_FAILED_CODE, WORKFLOW_FAILED_STATUS, WorkflowFailureDetail, WorkflowStepAttempt, WorkflowStepRecord, kitSentence, stepFailure, terminalWorkflowError };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pithy-sh/cloudflare",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -36,7 +36,7 @@
36
36
  "@aws-sdk/client-s3": "^3.1111.0",
37
37
  "@aws-sdk/s3-request-presigner": "^3.1111.0",
38
38
  "@cloudflare/workers-types": "^5.20260729.1",
39
- "@pithy-sh/core": "^0.2.0",
39
+ "@pithy-sh/core": "^0.3.1",
40
40
  "cloudflare": "^7.0.0",
41
41
  "js-base64": "^3.9.2"
42
42
  },