better-ship 0.25.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/auth-secondary-storage-b9ptwPSB.d.ts +37 -0
  2. package/dist/auth-secondary-storage-b9ptwPSB.d.ts.map +1 -0
  3. package/dist/better-auth/secondary-storage.d.ts +1 -1
  4. package/dist/bin/notify-ci.d.ts +1 -0
  5. package/dist/bin/notify-ci.js +24 -0
  6. package/dist/bin/notify-ci.js.map +1 -0
  7. package/dist/client-Dv1Ch7yY.js +20 -0
  8. package/dist/client-Dv1Ch7yY.js.map +1 -0
  9. package/dist/cloudflare/auth-secondary-storage.d.ts +2 -36
  10. package/dist/cloudflare/durable-object-client.js +1 -1
  11. package/dist/cloudflare/durable-object-errors.js +2 -47
  12. package/dist/cloudflare/kv-client.js +1 -1
  13. package/dist/cloudflare/kv-errors.js +2 -143
  14. package/dist/cloudflare/r2-client.js +1 -1
  15. package/dist/cloudflare/r2-errors.js +2 -89
  16. package/dist/durable-object-errors-DkMFXQnK.js +48 -0
  17. package/dist/durable-object-errors-DkMFXQnK.js.map +1 -0
  18. package/dist/kv-errors-BUTpFy0t.js +144 -0
  19. package/dist/kv-errors-BUTpFy0t.js.map +1 -0
  20. package/dist/metrics-CWv37yf9.js +30 -0
  21. package/dist/metrics-CWv37yf9.js.map +1 -0
  22. package/dist/metrics-cv9P5nvk.d.ts +19 -0
  23. package/dist/metrics-cv9P5nvk.d.ts.map +1 -0
  24. package/dist/payment-service-DUYd3Itt.d.ts +52 -0
  25. package/dist/payment-service-DUYd3Itt.d.ts.map +1 -0
  26. package/dist/payment-service.error-CqF7GkNp.js +30 -0
  27. package/dist/payment-service.error-CqF7GkNp.js.map +1 -0
  28. package/dist/posthog/metrics.d.ts +2 -18
  29. package/dist/posthog/metrics.js +2 -29
  30. package/dist/posthog/server.d.ts +1 -1
  31. package/dist/posthog/server.js +1 -1
  32. package/dist/r2-errors-rk7n7rvk.js +90 -0
  33. package/dist/r2-errors-rk7n7rvk.js.map +1 -0
  34. package/dist/request-4vJEWxyZ.js +21 -0
  35. package/dist/request-4vJEWxyZ.js.map +1 -0
  36. package/dist/slack-webhook-j1FCPeu8.js +270 -0
  37. package/dist/slack-webhook-j1FCPeu8.js.map +1 -0
  38. package/dist/slack.d.ts +102 -0
  39. package/dist/slack.d.ts.map +1 -0
  40. package/dist/slack.js +3 -0
  41. package/dist/stripe/client.js +2 -19
  42. package/dist/stripe/payment-service.d.ts +2 -51
  43. package/dist/stripe/payment-service.error.js +2 -29
  44. package/dist/stripe/payment-service.js +1 -1
  45. package/dist/stripe/request.js +2 -20
  46. package/dist/stripe-fixture-yFUknTJO.js +33 -0
  47. package/dist/stripe-fixture-yFUknTJO.js.map +1 -0
  48. package/dist/testing/payment-service.d.ts +1 -1
  49. package/dist/testing/payment-service.js +2 -2
  50. package/dist/testing/stripe.js +2 -32
  51. package/package.json +6 -1
  52. package/src/infrastructure/slack/ci-message.ts +276 -0
  53. package/src/infrastructure/slack/index.ts +10 -0
  54. package/src/infrastructure/slack/notify-ci.ts +22 -0
  55. package/src/infrastructure/slack/slack-webhook.ts +36 -0
  56. package/dist/cloudflare/auth-secondary-storage.d.ts.map +0 -1
  57. package/dist/cloudflare/durable-object-errors.js.map +0 -1
  58. package/dist/cloudflare/kv-errors.js.map +0 -1
  59. package/dist/cloudflare/r2-errors.js.map +0 -1
  60. package/dist/posthog/metrics.d.ts.map +0 -1
  61. package/dist/posthog/metrics.js.map +0 -1
  62. package/dist/stripe/client.js.map +0 -1
  63. package/dist/stripe/payment-service.d.ts.map +0 -1
  64. package/dist/stripe/payment-service.error.js.map +0 -1
  65. package/dist/stripe/request.js.map +0 -1
  66. package/dist/testing/stripe.js.map +0 -1
@@ -0,0 +1,37 @@
1
+ import { Xt as AppError } from "./index-DUFS0SJ_.js";
2
+ import { DurableObject } from "cloudflare:workers";
3
+ //#region src/infrastructure/cloudflare/auth-secondary-storage.d.ts
4
+ /** The supplied TTL cannot produce a valid expiry timestamp. Retrying the same input cannot fix it. */
5
+ declare class InvalidTtlError extends AppError {
6
+ readonly _tag = "InvalidTtlError";
7
+ readonly classification = "terminal";
8
+ constructor();
9
+ }
10
+ /** The stored value cannot be incremented safely. The failed operation leaves it unchanged. */
11
+ declare class InvalidCounterError extends AppError {
12
+ readonly _tag = "InvalidCounterError";
13
+ readonly classification = "terminal";
14
+ constructor();
15
+ }
16
+ /**
17
+ * Store one Better Auth secondary storage key per object. Export this class from the application's Worker entrypoint.
18
+ * Schedule cleanup at the caller's expiry; reads enforce expiry even when the alarm runs late.
19
+ * Requires compatibility date 2026-02-24 or later so deleteAll also removes the alarm atomically.
20
+ */
21
+ declare class AuthSecondaryStorage extends DurableObject {
22
+ /** Return null for absent or expired values, even when the cleanup alarm has not run. */
23
+ get(): Promise<string | null>;
24
+ /** Replace the value and its expiry. Omit ttl to keep the value until deletion. */
25
+ set(value: string, ttl?: number): Promise<void>;
26
+ /** Remove the value and its cleanup alarm before answering. */
27
+ delete(): Promise<void>;
28
+ /** Consume the value once. Concurrent callers receive null after the first successful consumption. */
29
+ getAndDelete(): Promise<string | null>;
30
+ /** Increment atomically. Apply ttl only when creating a counter or replacing an expired counter. */
31
+ increment(ttl: number): Promise<number>;
32
+ /** Delete expired data. An old alarm must not delete a value whose expiry was extended. */
33
+ alarm(): Promise<void>;
34
+ }
35
+ //#endregion
36
+ export { InvalidCounterError as n, InvalidTtlError as r, AuthSecondaryStorage as t };
37
+ //# sourceMappingURL=auth-secondary-storage-b9ptwPSB.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-secondary-storage-b9ptwPSB.d.ts","names":[],"sources":["../src/infrastructure/cloudflare/auth-secondary-storage.ts"],"mappings":";;;;cAWa,wBAAwB;WAC1B;WACA;EAET;;;cAMW,4BAA4B;WAC9B;WACA;EAET;;;;;;;cAUW,6BAA6B;;EAElC,OAAO;;EAMP,IAAI,eAAe,eAAe;;EAUlC,UAAU;;EAKV,gBAAgB;;EAQhB,UAAU,cAAc;;EAef,SAAS"}
@@ -1,4 +1,4 @@
1
- import { AuthSecondaryStorage } from "../cloudflare/auth-secondary-storage.js";
1
+ import { t as AuthSecondaryStorage } from "../auth-secondary-storage-b9ptwPSB.js";
2
2
  import { BetterAuthOptions } from "better-auth";
3
3
  //#region src/infrastructure/better-auth/secondary-storage.d.ts
4
4
  type SecondaryStorage = NonNullable<BetterAuthOptions['secondaryStorage']>;
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ import { a as parseCiRun, i as buildCiMessage, n as postSlackMessage } from "../slack-webhook-j1FCPeu8.js";
3
+ import { execFileSync } from "node:child_process";
4
+
5
+ //#region src/infrastructure/slack/notify-ci.ts
6
+ const webhookUrl = process.env.SLACK_WEBHOOK_URL;
7
+ if (webhookUrl === void 0 || webhookUrl === "") console.log("Slack notification skipped: SLACK_WEBHOOK_URL is unset.");
8
+ else {
9
+ const run = parseCiRun(process.env);
10
+ const git = (format) => execFileSync("git", [
11
+ "log",
12
+ "-1",
13
+ `--format=${format}`,
14
+ run.commitSha
15
+ ], { encoding: "utf8" }).trim();
16
+ await postSlackMessage(webhookUrl, buildCiMessage(run, {
17
+ subject: git("%s"),
18
+ author: git("%an")
19
+ }));
20
+ console.log("Slack notification sent.");
21
+ }
22
+
23
+ //#endregion
24
+ //# sourceMappingURL=notify-ci.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notify-ci.js","names":[],"sources":["../../src/infrastructure/slack/notify-ci.ts"],"sourcesContent":["#!/usr/bin/env node\n// Entry point of `better-ship-notify-ci`. Ported from suparelay and better-ship `.depot/scripts/notify-slack.sh`.\nimport { execFileSync } from 'node:child_process'\n\nimport { buildCiMessage, parseCiRun } from './ci-message.ts'\nimport { postSlackMessage } from './slack-webhook.ts'\n\nconst webhookUrl = process.env.SLACK_WEBHOOK_URL\nif (webhookUrl === undefined || webhookUrl === '') {\n console.log('Slack notification skipped: SLACK_WEBHOOK_URL is unset.')\n} else {\n const run = parseCiRun(process.env)\n const git = (format: string) =>\n execFileSync('git', ['log', '-1', `--format=${format}`, run.commitSha], {\n encoding: 'utf8',\n }).trim()\n await postSlackMessage(\n webhookUrl,\n buildCiMessage(run, { subject: git('%s'), author: git('%an') }),\n )\n console.log('Slack notification sent.')\n}\n"],"mappings":";;;;;AAOA,MAAM,aAAa,QAAQ,IAAI;AAC/B,IAAI,eAAe,UAAa,eAAe,IAC7C,QAAQ,IAAI,yDAAyD;KAChE;CACL,MAAM,MAAM,WAAW,QAAQ,GAAG;CAClC,MAAM,OAAO,WACX,aAAa,OAAO;EAAC;EAAO;EAAM,YAAY;EAAU,IAAI;CAAS,GAAG,EACtE,UAAU,OACZ,CAAC,CAAC,CAAC,KAAK;CACV,MAAM,iBACJ,YACA,eAAe,KAAK;EAAE,SAAS,IAAI,IAAI;EAAG,QAAQ,IAAI,KAAK;CAAE,CAAC,CAChE;CACA,QAAQ,IAAI,0BAA0B;AACxC"}
@@ -0,0 +1,20 @@
1
+ import Stripe from "stripe";
2
+
3
+ //#region src/infrastructure/stripe/client.ts
4
+ /** Default SDK retries; client configuration and individual requests can override this value. */
5
+ const STRIPE_REQUEST_OPTIONS = Object.freeze({ maxNetworkRetries: 2 });
6
+ /**
7
+ * Construct a native Stripe client with fetch transport and two SDK retries by default.
8
+ * Native configuration overrides these defaults. Inject the client into application services.
9
+ */
10
+ function createStripeClient(secretKey, options = {}) {
11
+ return new Stripe(secretKey, {
12
+ httpClient: Stripe.createFetchHttpClient(),
13
+ ...STRIPE_REQUEST_OPTIONS,
14
+ ...options
15
+ });
16
+ }
17
+
18
+ //#endregion
19
+ export { createStripeClient as n, STRIPE_REQUEST_OPTIONS as t };
20
+ //# sourceMappingURL=client-Dv1Ch7yY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-Dv1Ch7yY.js","names":[],"sources":["../src/infrastructure/stripe/client.ts"],"sourcesContent":["import Stripe from 'stripe'\n\n/** Default SDK retries; client configuration and individual requests can override this value. */\nexport const STRIPE_REQUEST_OPTIONS = Object.freeze({ maxNetworkRetries: 2 })\n\n/**\n * Construct a native Stripe client with fetch transport and two SDK retries by default.\n * Native configuration overrides these defaults. Inject the client into application services.\n */\nexport function createStripeClient(secretKey: string, options: Stripe.StripeConfig = {}): Stripe {\n return new Stripe(secretKey, {\n httpClient: Stripe.createFetchHttpClient(),\n ...STRIPE_REQUEST_OPTIONS,\n ...options,\n })\n}\n"],"mappings":";;;;AAGA,MAAa,yBAAyB,OAAO,OAAO,EAAE,mBAAmB,EAAE,CAAC;;;;;AAM5E,SAAgB,mBAAmB,WAAmB,UAA+B,CAAC,GAAW;CAC/F,OAAO,IAAI,OAAO,WAAW;EAC3B,YAAY,OAAO,sBAAsB;EACzC,GAAG;EACH,GAAG;CACL,CAAC;AACH"}
@@ -1,36 +1,2 @@
1
- import { Xt as AppError } from "../index-DUFS0SJ_.js";
2
- import { DurableObject } from "cloudflare:workers";
3
- //#region src/infrastructure/cloudflare/auth-secondary-storage.d.ts
4
- /** The supplied TTL cannot produce a valid expiry timestamp. Retrying the same input cannot fix it. */
5
- export declare class InvalidTtlError extends AppError {
6
- readonly _tag = "InvalidTtlError";
7
- readonly classification = "terminal";
8
- constructor();
9
- }
10
- /** The stored value cannot be incremented safely. The failed operation leaves it unchanged. */
11
- export declare class InvalidCounterError extends AppError {
12
- readonly _tag = "InvalidCounterError";
13
- readonly classification = "terminal";
14
- constructor();
15
- }
16
- /**
17
- * Store one Better Auth secondary storage key per object. Export this class from the application's Worker entrypoint.
18
- * Schedule cleanup at the caller's expiry; reads enforce expiry even when the alarm runs late.
19
- * Requires compatibility date 2026-02-24 or later so deleteAll also removes the alarm atomically.
20
- */
21
- export declare class AuthSecondaryStorage extends DurableObject {
22
- /** Return null for absent or expired values, even when the cleanup alarm has not run. */
23
- get(): Promise<string | null>;
24
- /** Replace the value and its expiry. Omit ttl to keep the value until deletion. */
25
- set(value: string, ttl?: number): Promise<void>;
26
- /** Remove the value and its cleanup alarm before answering. */
27
- delete(): Promise<void>;
28
- /** Consume the value once. Concurrent callers receive null after the first successful consumption. */
29
- getAndDelete(): Promise<string | null>;
30
- /** Increment atomically. Apply ttl only when creating a counter or replacing an expired counter. */
31
- increment(ttl: number): Promise<number>;
32
- /** Delete expired data. An old alarm must not delete a value whose expiry was extended. */
33
- alarm(): Promise<void>;
34
- }
35
- //#endregion
36
- //# sourceMappingURL=auth-secondary-storage.d.ts.map
1
+ import { n as InvalidCounterError, r as InvalidTtlError, t as AuthSecondaryStorage } from "../auth-secondary-storage-b9ptwPSB.js";
2
+ export { AuthSecondaryStorage, InvalidCounterError, InvalidTtlError };
@@ -1,5 +1,5 @@
1
1
  import { b as shouldRetryFailure, v as isAbortError } from "../core-BiN6S_cm.js";
2
- import { DurableObjectCallError, classifyDurableObjectError, isClassifiedDurableObjectError } from "./durable-object-errors.js";
2
+ import { i as isClassifiedDurableObjectError, n as DurableObjectCallError, r as classifyDurableObjectError } from "../durable-object-errors-DkMFXQnK.js";
3
3
  import { Result } from "better-result";
4
4
 
5
5
  //#region src/infrastructure/cloudflare/durable-object-client.ts
@@ -1,48 +1,3 @@
1
- import { _ as isTransientCloudflareRuntimeError, g as isCloudflareRetryableError, h as isCloudflareOverloadedError, nt as AppError, v as isAbortError, y as isTransientTransportError } from "../core-BiN6S_cm.js";
2
- import { z } from "zod";
1
+ import { a as isDurableObjectCallError, i as isClassifiedDurableObjectError, n as DurableObjectCallError, r as classifyDurableObjectError, t as DURABLE_OBJECT_CALL_ERROR } from "../durable-object-errors-DkMFXQnK.js";
3
2
 
4
- //#region src/infrastructure/cloudflare/durable-object-errors.ts
5
- /** Stable tag for a failed Durable Object call. */
6
- const DURABLE_OBJECT_CALL_ERROR = "DurableObjectCallError";
7
- const RemoteClassifiedErrorSchema = z.object({
8
- _tag: z.string(),
9
- classification: z.enum([
10
- "transient",
11
- "terminal",
12
- "unknown"
13
- ]),
14
- remote: z.literal(true)
15
- });
16
- const RemoteCallErrorSchema = RemoteClassifiedErrorSchema.extend({
17
- _tag: z.literal(DURABLE_OBJECT_CALL_ERROR),
18
- cause: z.unknown()
19
- });
20
- /** Recognize a judged local or remote error without admitting its tag to the public error table. */
21
- function isClassifiedDurableObjectError(cause) {
22
- return cause instanceof AppError || cause instanceof Error && RemoteClassifiedErrorSchema.safeParse(cause).success;
23
- }
24
- /** Classify the error itself. Its cause remains diagnostic evidence, not a second retry decision. */
25
- function classifyDurableObjectError(cause) {
26
- if (isAbortError(cause)) return "terminal";
27
- if (isClassifiedDurableObjectError(cause)) return cause.classification;
28
- if (isCloudflareOverloadedError(cause)) return "terminal";
29
- if (isCloudflareRetryableError(cause) || isTransientCloudflareRuntimeError(cause) || isTransientTransportError(cause)) return "transient";
30
- return "unknown";
31
- }
32
- /** Identify a local call error or its validated Workers RPC representation. */
33
- function isDurableObjectCallError(cause) {
34
- return cause instanceof DurableObjectCallError || cause instanceof Error && RemoteCallErrorSchema.safeParse(cause).success;
35
- }
36
- /** A call failed after retries stopped. Classification does not establish repeat safety. */
37
- var DurableObjectCallError = class extends AppError {
38
- _tag = DURABLE_OBJECT_CALL_ERROR;
39
- classification;
40
- constructor(args) {
41
- super("Durable Object call failed", { cause: args.cause });
42
- this.classification = classifyDurableObjectError(args.cause);
43
- }
44
- };
45
-
46
- //#endregion
47
- export { DURABLE_OBJECT_CALL_ERROR, DurableObjectCallError, classifyDurableObjectError, isClassifiedDurableObjectError, isDurableObjectCallError };
48
- //# sourceMappingURL=durable-object-errors.js.map
3
+ export { DURABLE_OBJECT_CALL_ERROR, DurableObjectCallError, classifyDurableObjectError, isClassifiedDurableObjectError, isDurableObjectCallError };
@@ -1,5 +1,5 @@
1
1
  import { l as spreadJitterMs, s as exponentialDelayCapMs } from "../core-BiN6S_cm.js";
2
- import { KVError, decideKVRetry, isKVPutRateLimitError } from "./kv-errors.js";
2
+ import { i as isKVPutRateLimitError, n as decideKVRetry, t as KVError } from "../kv-errors-BUTpFy0t.js";
3
3
  import { Result } from "better-result";
4
4
 
5
5
  //#region src/infrastructure/cloudflare/kv-client.ts
@@ -1,144 +1,3 @@
1
- import { g as isCloudflareRetryableError, h as isCloudflareOverloadedError, nt as AppError, y as isTransientTransportError } from "../core-BiN6S_cm.js";
1
+ import { a as isRetryableKVError, i as isKVPutRateLimitError, n as decideKVRetry, o as isTerminalKVError, r as extractKVMeta, s as isTransientKVError, t as KVError } from "../kv-errors-BUTpFy0t.js";
2
2
 
3
- //#region src/infrastructure/cloudflare/kv-errors.ts
4
- /** Extract Typist's verified KV message formats, including nested causes. */
5
- function extractKVMeta(cause) {
6
- const message = getDeepErrorMessage(cause);
7
- if (!message) return void 0;
8
- const statusMatch = message.match(/\bKV\s+(GET|PUT|DELETE|LIST)\s+failed:\s+(\d{3})\b/);
9
- const operation = statusMatch?.[1];
10
- const status = statusMatch ? Number(statusMatch[2]) : void 0;
11
- if (/Key name cannot be empty/i.test(message)) return {
12
- kind: "invalid_key",
13
- message,
14
- status,
15
- operation
16
- };
17
- if (/exceeds key length limit/i.test(message)) return {
18
- kind: "key_too_large",
19
- message,
20
- status,
21
- operation
22
- };
23
- if (/Invalid expiration_ttl/i.test(message)) return {
24
- kind: "invalid_expiration_ttl",
25
- message,
26
- status,
27
- operation
28
- };
29
- if (/Invalid expiration of/i.test(message)) return {
30
- kind: "invalid_expiration",
31
- message,
32
- status,
33
- operation
34
- };
35
- if (/Invalid cache_ttl/i.test(message)) return {
36
- kind: "invalid_cache_ttl",
37
- message,
38
- status,
39
- operation
40
- };
41
- if (/Invalid key_count_limit/i.test(message)) return {
42
- kind: "invalid_list_limit",
43
- message,
44
- status,
45
- operation
46
- };
47
- if (/value.*(too large|exceeds)|exceeds.*value/i.test(message)) return {
48
- kind: "value_too_large",
49
- message,
50
- status,
51
- operation
52
- };
53
- if (status === 429) return {
54
- kind: "rate_limited",
55
- message,
56
- status,
57
- operation
58
- };
59
- if (status !== void 0 && status >= 500) return {
60
- kind: "server_error",
61
- message,
62
- status,
63
- operation
64
- };
65
- if (status !== void 0 && status >= 400) return {
66
- kind: "client_error",
67
- message,
68
- status,
69
- operation
70
- };
71
- }
72
- /** Identify recognized KV failures that another attempt cannot resolve. */
73
- function isTerminalKVError(cause) {
74
- const meta = extractKVMeta(cause);
75
- if (!meta) return false;
76
- if (meta.kind === "rate_limited" || meta.kind === "server_error") return false;
77
- if (meta.status === 408) return false;
78
- return true;
79
- }
80
- /** Identify a PUT rate limit that requires at least one second before retrying. */
81
- function isKVPutRateLimitError(cause) {
82
- const meta = extractKVMeta(cause);
83
- return meta?.operation === "PUT" && meta.kind === "rate_limited";
84
- }
85
- /** Identify PUT rate limits, server failures, and request timeouts. */
86
- function isTransientKVError(cause) {
87
- const meta = extractKVMeta(cause);
88
- if (!meta) return false;
89
- return isKVPutRateLimitError(cause) || meta.kind === "server_error" || meta.status === 408;
90
- }
91
- /** Apply Typist's KV, overload, platform, and transport checks in their original order. */
92
- function isRetryableKVError(cause) {
93
- if (isTerminalKVError(cause)) return false;
94
- if (isTransientKVError(cause)) return true;
95
- if (isCloudflareOverloadedError(cause)) return false;
96
- return isCloudflareRetryableError(cause) || isTransientTransportError(cause);
97
- }
98
- /** Return the existing retry decision and minimum delay in milliseconds. */
99
- function decideKVRetry(cause) {
100
- if (!isRetryableKVError(cause)) return { retry: false };
101
- return {
102
- retry: true,
103
- retryAfterMs: isKVPutRateLimitError(cause) ? 1e3 : 0
104
- };
105
- }
106
- /** A final KV failure with Typist's diagnostics and Better Ship's error classification. */
107
- var KVError = class extends AppError {
108
- _tag = "KVError";
109
- classification;
110
- retryable;
111
- operation;
112
- key;
113
- cause;
114
- kv;
115
- context;
116
- constructor(args) {
117
- const kv = extractKVMeta(args.cause);
118
- const causeMessage = getDeepErrorMessage(args.cause) || String(args.cause);
119
- const keyContext = args.key === void 0 ? "" : ` for key "${args.key}"`;
120
- super(`KV ${args.operation} failed${keyContext}: ${causeMessage}`, { cause: args.cause });
121
- this.operation = args.operation;
122
- this.key = args.key;
123
- this.cause = args.cause;
124
- this.kv = kv;
125
- this.context = {
126
- operation: args.operation,
127
- key: args.key,
128
- kv
129
- };
130
- this.retryable = isRetryableKVError(args.cause);
131
- this.classification = isTerminalKVError(args.cause) ? "terminal" : this.retryable ? "transient" : "unknown";
132
- }
133
- };
134
- function getDeepErrorMessage(cause) {
135
- if (typeof cause === "string") return cause;
136
- if (cause === null || cause === void 0) return "";
137
- if (typeof cause === "number" || typeof cause === "boolean" || typeof cause === "bigint") return String(cause);
138
- if (typeof cause !== "object") return "";
139
- return `${"message" in cause && typeof cause.message === "string" ? cause.message : ""} ${"cause" in cause ? getDeepErrorMessage(cause.cause) : ""}`.trim();
140
- }
141
-
142
- //#endregion
143
- export { KVError, decideKVRetry, extractKVMeta, isKVPutRateLimitError, isRetryableKVError, isTerminalKVError, isTransientKVError };
144
- //# sourceMappingURL=kv-errors.js.map
3
+ export { KVError, decideKVRetry, extractKVMeta, isKVPutRateLimitError, isRetryableKVError, isTerminalKVError, isTransientKVError };
@@ -1,5 +1,5 @@
1
1
  import { nt as AppError, v as isAbortError } from "../core-BiN6S_cm.js";
2
- import { R2Error, R2JsonError } from "./r2-errors.js";
2
+ import { n as R2JsonError, t as R2Error } from "../r2-errors-rk7n7rvk.js";
3
3
 
4
4
  //#region src/infrastructure/cloudflare/r2-client.ts
5
5
  /**
@@ -1,90 +1,3 @@
1
- import { nt as AppError, u as classifyBoundaryError, v as isAbortError } from "../core-BiN6S_cm.js";
2
- import { z } from "zod";
1
+ import { n as R2JsonError, t as R2Error } from "../r2-errors-rk7n7rvk.js";
3
2
 
4
- //#region src/infrastructure/cloudflare/r2-errors.ts
5
- const R2FailureSchema = z.object({
6
- code: z.number().int().positive(),
7
- action: z.string().optional()
8
- });
9
- const classifications = /* @__PURE__ */ new Map([
10
- [10001, "transient"],
11
- [10043, "transient"],
12
- [10054, "transient"],
13
- [10058, "transient"],
14
- [10013, "transient"],
15
- [10002, "terminal"],
16
- [10003, "terminal"],
17
- [10018, "terminal"],
18
- [10035, "terminal"],
19
- [10042, "terminal"],
20
- [10005, "terminal"],
21
- [10006, "terminal"],
22
- [10008, "terminal"],
23
- [10009, "terminal"],
24
- [10073, "terminal"],
25
- [10007, "terminal"],
26
- [10020, "terminal"],
27
- [100100, "terminal"],
28
- [10012, "terminal"],
29
- [10069, "terminal"],
30
- [10033, "terminal"],
31
- [10014, "terminal"],
32
- [10037, "terminal"],
33
- [10039, "terminal"],
34
- [10031, "terminal"],
35
- [10011, "terminal"],
36
- [10024, "terminal"],
37
- [10025, "terminal"],
38
- [10048, "terminal"]
39
- ]);
40
- function extractR2Failure(cause) {
41
- if (!(cause instanceof Error)) return void 0;
42
- const parsed = R2FailureSchema.safeParse(cause);
43
- if (parsed.success) return parsed.data;
44
- const match = /\((\d{5,6})\)\s*$/.exec(cause.message);
45
- return match?.[1] === void 0 ? void 0 : { code: Number(match[1]) };
46
- }
47
- function classifyR2Code(cause) {
48
- const failure = extractR2Failure(cause);
49
- return failure === void 0 ? void 0 : classifications.get(failure.code) ?? "unknown";
50
- }
51
- function classifyR2Error(cause) {
52
- if (isAbortError(cause)) return "terminal";
53
- if (cause instanceof AppError) return cause.classification;
54
- return classifyBoundaryError({
55
- cause,
56
- classifyBoundary: classifyR2Code,
57
- includeCloudflare: true
58
- });
59
- }
60
- /** A raw binding failure after one attempt. Classification does not establish whether a mutation committed. */
61
- var R2Error = class extends AppError {
62
- _tag = "R2Error";
63
- classification;
64
- operation;
65
- r2;
66
- cause;
67
- constructor(input) {
68
- super(`R2 ${input.operation} failed`, { cause: input.cause });
69
- this.operation = input.operation;
70
- this.cause = input.cause;
71
- this.r2 = extractR2Failure(input.cause);
72
- this.classification = classifyR2Error(input.cause);
73
- }
74
- };
75
- /** JSON serialization or syntax failed. Binding and body-read failures use R2Error. */
76
- var R2JsonError = class extends AppError {
77
- _tag = "R2JsonError";
78
- classification = "terminal";
79
- operation;
80
- cause;
81
- constructor(input) {
82
- super(`R2 JSON ${input.operation} failed`, { cause: input.cause });
83
- this.operation = input.operation;
84
- this.cause = input.cause;
85
- }
86
- };
87
-
88
- //#endregion
89
- export { R2Error, R2JsonError };
90
- //# sourceMappingURL=r2-errors.js.map
3
+ export { R2Error, R2JsonError };
@@ -0,0 +1,48 @@
1
+ import { _ as isTransientCloudflareRuntimeError, g as isCloudflareRetryableError, h as isCloudflareOverloadedError, nt as AppError, v as isAbortError, y as isTransientTransportError } from "./core-BiN6S_cm.js";
2
+ import { z } from "zod";
3
+
4
+ //#region src/infrastructure/cloudflare/durable-object-errors.ts
5
+ /** Stable tag for a failed Durable Object call. */
6
+ const DURABLE_OBJECT_CALL_ERROR = "DurableObjectCallError";
7
+ const RemoteClassifiedErrorSchema = z.object({
8
+ _tag: z.string(),
9
+ classification: z.enum([
10
+ "transient",
11
+ "terminal",
12
+ "unknown"
13
+ ]),
14
+ remote: z.literal(true)
15
+ });
16
+ const RemoteCallErrorSchema = RemoteClassifiedErrorSchema.extend({
17
+ _tag: z.literal(DURABLE_OBJECT_CALL_ERROR),
18
+ cause: z.unknown()
19
+ });
20
+ /** Recognize a judged local or remote error without admitting its tag to the public error table. */
21
+ function isClassifiedDurableObjectError(cause) {
22
+ return cause instanceof AppError || cause instanceof Error && RemoteClassifiedErrorSchema.safeParse(cause).success;
23
+ }
24
+ /** Classify the error itself. Its cause remains diagnostic evidence, not a second retry decision. */
25
+ function classifyDurableObjectError(cause) {
26
+ if (isAbortError(cause)) return "terminal";
27
+ if (isClassifiedDurableObjectError(cause)) return cause.classification;
28
+ if (isCloudflareOverloadedError(cause)) return "terminal";
29
+ if (isCloudflareRetryableError(cause) || isTransientCloudflareRuntimeError(cause) || isTransientTransportError(cause)) return "transient";
30
+ return "unknown";
31
+ }
32
+ /** Identify a local call error or its validated Workers RPC representation. */
33
+ function isDurableObjectCallError(cause) {
34
+ return cause instanceof DurableObjectCallError || cause instanceof Error && RemoteCallErrorSchema.safeParse(cause).success;
35
+ }
36
+ /** A call failed after retries stopped. Classification does not establish repeat safety. */
37
+ var DurableObjectCallError = class extends AppError {
38
+ _tag = DURABLE_OBJECT_CALL_ERROR;
39
+ classification;
40
+ constructor(args) {
41
+ super("Durable Object call failed", { cause: args.cause });
42
+ this.classification = classifyDurableObjectError(args.cause);
43
+ }
44
+ };
45
+
46
+ //#endregion
47
+ export { isDurableObjectCallError as a, isClassifiedDurableObjectError as i, DurableObjectCallError as n, classifyDurableObjectError as r, DURABLE_OBJECT_CALL_ERROR as t };
48
+ //# sourceMappingURL=durable-object-errors-DkMFXQnK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"durable-object-errors-DkMFXQnK.js","names":[],"sources":["../src/infrastructure/cloudflare/durable-object-errors.ts"],"sourcesContent":["import { z } from 'zod'\n\nimport {\n AppError,\n isAbortError,\n isCloudflareOverloadedError,\n isCloudflareRetryableError,\n isTransientCloudflareRuntimeError,\n isTransientTransportError,\n type FailureClassification,\n} from '@/core'\n\n/** Stable tag for a failed Durable Object call. */\nexport const DURABLE_OBJECT_CALL_ERROR = 'DurableObjectCallError'\n\nconst RemoteClassifiedErrorSchema = z.object({\n _tag: z.string(),\n classification: z.enum(['transient', 'terminal', 'unknown']),\n remote: z.literal(true),\n})\n\nconst RemoteCallErrorSchema = RemoteClassifiedErrorSchema.extend({\n _tag: z.literal(DURABLE_OBJECT_CALL_ERROR),\n cause: z.unknown(),\n})\n\n/** Recognize a judged local or remote error without admitting its tag to the public error table. */\nexport function isClassifiedDurableObjectError(\n cause: unknown,\n): cause is AppError | (Error & z.output<typeof RemoteClassifiedErrorSchema>) {\n return (\n cause instanceof AppError ||\n (cause instanceof Error && RemoteClassifiedErrorSchema.safeParse(cause).success)\n )\n}\n\n/** Classify the error itself. Its cause remains diagnostic evidence, not a second retry decision. */\nexport function classifyDurableObjectError(cause: unknown): FailureClassification {\n if (isAbortError(cause)) return 'terminal'\n if (isClassifiedDurableObjectError(cause)) return cause.classification\n if (isCloudflareOverloadedError(cause)) return 'terminal'\n if (\n isCloudflareRetryableError(cause) ||\n isTransientCloudflareRuntimeError(cause) ||\n isTransientTransportError(cause)\n )\n return 'transient'\n return 'unknown'\n}\n\n/** Identify a local call error or its validated Workers RPC representation. */\nexport function isDurableObjectCallError(\n cause: unknown,\n): cause is DurableObjectCallError | (Error & z.output<typeof RemoteCallErrorSchema>) {\n return (\n cause instanceof DurableObjectCallError ||\n (cause instanceof Error && RemoteCallErrorSchema.safeParse(cause).success)\n )\n}\n\n/** A call failed after retries stopped. Classification does not establish repeat safety. */\nexport class DurableObjectCallError extends AppError {\n readonly _tag = DURABLE_OBJECT_CALL_ERROR\n readonly classification: FailureClassification\n\n constructor(args: { readonly cause: unknown }) {\n super('Durable Object call failed', { cause: args.cause })\n this.classification = classifyDurableObjectError(args.cause)\n }\n}\n"],"mappings":";;;;;AAaA,MAAa,4BAA4B;AAEzC,MAAM,8BAA8B,EAAE,OAAO;CAC3C,MAAM,EAAE,OAAO;CACf,gBAAgB,EAAE,KAAK;EAAC;EAAa;EAAY;CAAS,CAAC;CAC3D,QAAQ,EAAE,QAAQ,IAAI;AACxB,CAAC;AAED,MAAM,wBAAwB,4BAA4B,OAAO;CAC/D,MAAM,EAAE,QAAQ,yBAAyB;CACzC,OAAO,EAAE,QAAQ;AACnB,CAAC;;AAGD,SAAgB,+BACd,OAC4E;CAC5E,OACE,iBAAiB,YAChB,iBAAiB,SAAS,4BAA4B,UAAU,KAAK,CAAC,CAAC;AAE5E;;AAGA,SAAgB,2BAA2B,OAAuC;CAChF,IAAI,aAAa,KAAK,GAAG,OAAO;CAChC,IAAI,+BAA+B,KAAK,GAAG,OAAO,MAAM;CACxD,IAAI,4BAA4B,KAAK,GAAG,OAAO;CAC/C,IACE,2BAA2B,KAAK,KAChC,kCAAkC,KAAK,KACvC,0BAA0B,KAAK,GAE/B,OAAO;CACT,OAAO;AACT;;AAGA,SAAgB,yBACd,OACoF;CACpF,OACE,iBAAiB,0BAChB,iBAAiB,SAAS,sBAAsB,UAAU,KAAK,CAAC,CAAC;AAEtE;;AAGA,IAAa,yBAAb,cAA4C,SAAS;CACnD,AAAS,OAAO;CAChB,AAAS;CAET,YAAY,MAAmC;EAC7C,MAAM,8BAA8B,EAAE,OAAO,KAAK,MAAM,CAAC;EACzD,KAAK,iBAAiB,2BAA2B,KAAK,KAAK;CAC7D;AACF"}
@@ -0,0 +1,144 @@
1
+ import { g as isCloudflareRetryableError, h as isCloudflareOverloadedError, nt as AppError, y as isTransientTransportError } from "./core-BiN6S_cm.js";
2
+
3
+ //#region src/infrastructure/cloudflare/kv-errors.ts
4
+ /** Extract Typist's verified KV message formats, including nested causes. */
5
+ function extractKVMeta(cause) {
6
+ const message = getDeepErrorMessage(cause);
7
+ if (!message) return void 0;
8
+ const statusMatch = message.match(/\bKV\s+(GET|PUT|DELETE|LIST)\s+failed:\s+(\d{3})\b/);
9
+ const operation = statusMatch?.[1];
10
+ const status = statusMatch ? Number(statusMatch[2]) : void 0;
11
+ if (/Key name cannot be empty/i.test(message)) return {
12
+ kind: "invalid_key",
13
+ message,
14
+ status,
15
+ operation
16
+ };
17
+ if (/exceeds key length limit/i.test(message)) return {
18
+ kind: "key_too_large",
19
+ message,
20
+ status,
21
+ operation
22
+ };
23
+ if (/Invalid expiration_ttl/i.test(message)) return {
24
+ kind: "invalid_expiration_ttl",
25
+ message,
26
+ status,
27
+ operation
28
+ };
29
+ if (/Invalid expiration of/i.test(message)) return {
30
+ kind: "invalid_expiration",
31
+ message,
32
+ status,
33
+ operation
34
+ };
35
+ if (/Invalid cache_ttl/i.test(message)) return {
36
+ kind: "invalid_cache_ttl",
37
+ message,
38
+ status,
39
+ operation
40
+ };
41
+ if (/Invalid key_count_limit/i.test(message)) return {
42
+ kind: "invalid_list_limit",
43
+ message,
44
+ status,
45
+ operation
46
+ };
47
+ if (/value.*(too large|exceeds)|exceeds.*value/i.test(message)) return {
48
+ kind: "value_too_large",
49
+ message,
50
+ status,
51
+ operation
52
+ };
53
+ if (status === 429) return {
54
+ kind: "rate_limited",
55
+ message,
56
+ status,
57
+ operation
58
+ };
59
+ if (status !== void 0 && status >= 500) return {
60
+ kind: "server_error",
61
+ message,
62
+ status,
63
+ operation
64
+ };
65
+ if (status !== void 0 && status >= 400) return {
66
+ kind: "client_error",
67
+ message,
68
+ status,
69
+ operation
70
+ };
71
+ }
72
+ /** Identify recognized KV failures that another attempt cannot resolve. */
73
+ function isTerminalKVError(cause) {
74
+ const meta = extractKVMeta(cause);
75
+ if (!meta) return false;
76
+ if (meta.kind === "rate_limited" || meta.kind === "server_error") return false;
77
+ if (meta.status === 408) return false;
78
+ return true;
79
+ }
80
+ /** Identify a PUT rate limit that requires at least one second before retrying. */
81
+ function isKVPutRateLimitError(cause) {
82
+ const meta = extractKVMeta(cause);
83
+ return meta?.operation === "PUT" && meta.kind === "rate_limited";
84
+ }
85
+ /** Identify PUT rate limits, server failures, and request timeouts. */
86
+ function isTransientKVError(cause) {
87
+ const meta = extractKVMeta(cause);
88
+ if (!meta) return false;
89
+ return isKVPutRateLimitError(cause) || meta.kind === "server_error" || meta.status === 408;
90
+ }
91
+ /** Apply Typist's KV, overload, platform, and transport checks in their original order. */
92
+ function isRetryableKVError(cause) {
93
+ if (isTerminalKVError(cause)) return false;
94
+ if (isTransientKVError(cause)) return true;
95
+ if (isCloudflareOverloadedError(cause)) return false;
96
+ return isCloudflareRetryableError(cause) || isTransientTransportError(cause);
97
+ }
98
+ /** Return the existing retry decision and minimum delay in milliseconds. */
99
+ function decideKVRetry(cause) {
100
+ if (!isRetryableKVError(cause)) return { retry: false };
101
+ return {
102
+ retry: true,
103
+ retryAfterMs: isKVPutRateLimitError(cause) ? 1e3 : 0
104
+ };
105
+ }
106
+ /** A final KV failure with Typist's diagnostics and Better Ship's error classification. */
107
+ var KVError = class extends AppError {
108
+ _tag = "KVError";
109
+ classification;
110
+ retryable;
111
+ operation;
112
+ key;
113
+ cause;
114
+ kv;
115
+ context;
116
+ constructor(args) {
117
+ const kv = extractKVMeta(args.cause);
118
+ const causeMessage = getDeepErrorMessage(args.cause) || String(args.cause);
119
+ const keyContext = args.key === void 0 ? "" : ` for key "${args.key}"`;
120
+ super(`KV ${args.operation} failed${keyContext}: ${causeMessage}`, { cause: args.cause });
121
+ this.operation = args.operation;
122
+ this.key = args.key;
123
+ this.cause = args.cause;
124
+ this.kv = kv;
125
+ this.context = {
126
+ operation: args.operation,
127
+ key: args.key,
128
+ kv
129
+ };
130
+ this.retryable = isRetryableKVError(args.cause);
131
+ this.classification = isTerminalKVError(args.cause) ? "terminal" : this.retryable ? "transient" : "unknown";
132
+ }
133
+ };
134
+ function getDeepErrorMessage(cause) {
135
+ if (typeof cause === "string") return cause;
136
+ if (cause === null || cause === void 0) return "";
137
+ if (typeof cause === "number" || typeof cause === "boolean" || typeof cause === "bigint") return String(cause);
138
+ if (typeof cause !== "object") return "";
139
+ return `${"message" in cause && typeof cause.message === "string" ? cause.message : ""} ${"cause" in cause ? getDeepErrorMessage(cause.cause) : ""}`.trim();
140
+ }
141
+
142
+ //#endregion
143
+ export { isRetryableKVError as a, isKVPutRateLimitError as i, decideKVRetry as n, isTerminalKVError as o, extractKVMeta as r, isTransientKVError as s, KVError as t };
144
+ //# sourceMappingURL=kv-errors-BUTpFy0t.js.map