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.
- package/dist/auth-secondary-storage-b9ptwPSB.d.ts +37 -0
- package/dist/auth-secondary-storage-b9ptwPSB.d.ts.map +1 -0
- package/dist/better-auth/secondary-storage.d.ts +1 -1
- package/dist/bin/notify-ci.d.ts +1 -0
- package/dist/bin/notify-ci.js +24 -0
- package/dist/bin/notify-ci.js.map +1 -0
- package/dist/client-Dv1Ch7yY.js +20 -0
- package/dist/client-Dv1Ch7yY.js.map +1 -0
- package/dist/cloudflare/auth-secondary-storage.d.ts +2 -36
- package/dist/cloudflare/durable-object-client.js +1 -1
- package/dist/cloudflare/durable-object-errors.js +2 -47
- package/dist/cloudflare/kv-client.js +1 -1
- package/dist/cloudflare/kv-errors.js +2 -143
- package/dist/cloudflare/r2-client.js +1 -1
- package/dist/cloudflare/r2-errors.js +2 -89
- package/dist/durable-object-errors-DkMFXQnK.js +48 -0
- package/dist/durable-object-errors-DkMFXQnK.js.map +1 -0
- package/dist/kv-errors-BUTpFy0t.js +144 -0
- package/dist/kv-errors-BUTpFy0t.js.map +1 -0
- package/dist/metrics-CWv37yf9.js +30 -0
- package/dist/metrics-CWv37yf9.js.map +1 -0
- package/dist/metrics-cv9P5nvk.d.ts +19 -0
- package/dist/metrics-cv9P5nvk.d.ts.map +1 -0
- package/dist/payment-service-DUYd3Itt.d.ts +52 -0
- package/dist/payment-service-DUYd3Itt.d.ts.map +1 -0
- package/dist/payment-service.error-CqF7GkNp.js +30 -0
- package/dist/payment-service.error-CqF7GkNp.js.map +1 -0
- package/dist/posthog/metrics.d.ts +2 -18
- package/dist/posthog/metrics.js +2 -29
- package/dist/posthog/server.d.ts +1 -1
- package/dist/posthog/server.js +1 -1
- package/dist/r2-errors-rk7n7rvk.js +90 -0
- package/dist/r2-errors-rk7n7rvk.js.map +1 -0
- package/dist/request-4vJEWxyZ.js +21 -0
- package/dist/request-4vJEWxyZ.js.map +1 -0
- package/dist/slack-webhook-j1FCPeu8.js +270 -0
- package/dist/slack-webhook-j1FCPeu8.js.map +1 -0
- package/dist/slack.d.ts +102 -0
- package/dist/slack.d.ts.map +1 -0
- package/dist/slack.js +3 -0
- package/dist/stripe/client.js +2 -19
- package/dist/stripe/payment-service.d.ts +2 -51
- package/dist/stripe/payment-service.error.js +2 -29
- package/dist/stripe/payment-service.js +1 -1
- package/dist/stripe/request.js +2 -20
- package/dist/stripe-fixture-yFUknTJO.js +33 -0
- package/dist/stripe-fixture-yFUknTJO.js.map +1 -0
- package/dist/testing/payment-service.d.ts +1 -1
- package/dist/testing/payment-service.js +2 -2
- package/dist/testing/stripe.js +2 -32
- package/package.json +6 -1
- package/src/infrastructure/slack/ci-message.ts +276 -0
- package/src/infrastructure/slack/index.ts +10 -0
- package/src/infrastructure/slack/notify-ci.ts +22 -0
- package/src/infrastructure/slack/slack-webhook.ts +36 -0
- package/dist/cloudflare/auth-secondary-storage.d.ts.map +0 -1
- package/dist/cloudflare/durable-object-errors.js.map +0 -1
- package/dist/cloudflare/kv-errors.js.map +0 -1
- package/dist/cloudflare/r2-errors.js.map +0 -1
- package/dist/posthog/metrics.d.ts.map +0 -1
- package/dist/posthog/metrics.js.map +0 -1
- package/dist/stripe/client.js.map +0 -1
- package/dist/stripe/payment-service.d.ts.map +0 -1
- package/dist/stripe/payment-service.error.js.map +0 -1
- package/dist/stripe/request.js.map +0 -1
- package/dist/testing/stripe.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kv-errors-BUTpFy0t.js","names":[],"sources":["../src/infrastructure/cloudflare/kv-errors.ts"],"sourcesContent":["import {\n AppError,\n isCloudflareOverloadedError,\n isCloudflareRetryableError,\n isTransientTransportError,\n type FailureClassification,\n} from '@/core'\n\n/** KV operations recognized in binding error messages. */\nexport type KVOperation = 'get' | 'put' | 'delete' | 'list'\n\n/** KV failure kinds retained from Typist's binding tests. */\nexport type KVErrorKind =\n | 'invalid_key'\n | 'key_too_large'\n | 'invalid_expiration_ttl'\n | 'invalid_expiration'\n | 'invalid_cache_ttl'\n | 'invalid_list_limit'\n | 'value_too_large'\n | 'client_error'\n | 'rate_limited'\n | 'server_error'\n\n/** Evidence extracted from a KV error and its causes. */\nexport interface KVErrorMeta {\n kind: KVErrorKind\n message: string\n status?: number | undefined\n operation?: Uppercase<KVOperation> | undefined\n}\n\n/** Extract Typist's verified KV message formats, including nested causes. */\nexport function extractKVMeta(cause: unknown): KVErrorMeta | undefined {\n const message = getDeepErrorMessage(cause)\n if (!message) return undefined\n const statusMatch = message.match(/\\bKV\\s+(GET|PUT|DELETE|LIST)\\s+failed:\\s+(\\d{3})\\b/)\n // SAFETY: The regular expression permits only uppercase KV operation names in this capture.\n const operation = statusMatch?.[1] as Uppercase<KVOperation> | undefined\n const status = statusMatch ? Number(statusMatch[2]) : undefined\n\n if (/Key name cannot be empty/i.test(message)) {\n return { kind: 'invalid_key', message, status, operation }\n }\n if (/exceeds key length limit/i.test(message)) {\n return { kind: 'key_too_large', message, status, operation }\n }\n if (/Invalid expiration_ttl/i.test(message)) {\n return { kind: 'invalid_expiration_ttl', message, status, operation }\n }\n if (/Invalid expiration of/i.test(message)) {\n return { kind: 'invalid_expiration', message, status, operation }\n }\n if (/Invalid cache_ttl/i.test(message)) {\n return { kind: 'invalid_cache_ttl', message, status, operation }\n }\n if (/Invalid key_count_limit/i.test(message)) {\n return { kind: 'invalid_list_limit', message, status, operation }\n }\n if (/value.*(too large|exceeds)|exceeds.*value/i.test(message)) {\n return { kind: 'value_too_large', message, status, operation }\n }\n if (status === 429) return { kind: 'rate_limited', message, status, operation }\n if (status !== undefined && status >= 500) {\n return { kind: 'server_error', message, status, operation }\n }\n if (status !== undefined && status >= 400) {\n return { kind: 'client_error', message, status, operation }\n }\n return undefined\n}\n\n/** Identify recognized KV failures that another attempt cannot resolve. */\nexport function isTerminalKVError(cause: unknown): boolean {\n const meta = extractKVMeta(cause)\n if (!meta) return false\n if (meta.kind === 'rate_limited' || meta.kind === 'server_error') return false\n if (meta.status === 408) return false\n return true\n}\n\n/** Identify a PUT rate limit that requires at least one second before retrying. */\nexport function isKVPutRateLimitError(cause: unknown): boolean {\n const meta = extractKVMeta(cause)\n return meta?.operation === 'PUT' && meta.kind === 'rate_limited'\n}\n\n/** Identify PUT rate limits, server failures, and request timeouts. */\nexport function isTransientKVError(cause: unknown): boolean {\n const meta = extractKVMeta(cause)\n if (!meta) return false\n return isKVPutRateLimitError(cause) || meta.kind === 'server_error' || meta.status === 408\n}\n\n/** Apply Typist's KV, overload, platform, and transport checks in their original order. */\nexport function isRetryableKVError(cause: unknown): boolean {\n if (isTerminalKVError(cause)) return false\n if (isTransientKVError(cause)) return true\n if (isCloudflareOverloadedError(cause)) return false\n return isCloudflareRetryableError(cause) || isTransientTransportError(cause)\n}\n\n/** Return the existing retry decision and minimum delay in milliseconds. */\nexport function decideKVRetry(\n cause: unknown,\n): { retry: false } | { retry: true; retryAfterMs: number } {\n if (!isRetryableKVError(cause)) return { retry: false }\n return { retry: true, retryAfterMs: isKVPutRateLimitError(cause) ? 1_000 : 0 }\n}\n\n/** A final KV failure with Typist's diagnostics and Better Ship's error classification. */\nexport class KVError extends AppError {\n readonly _tag = 'KVError'\n readonly classification: FailureClassification\n retryable: boolean\n readonly operation: KVOperation\n readonly key?: string | undefined\n override readonly cause: unknown\n readonly kv?: KVErrorMeta | undefined\n readonly context: {\n operation: KVOperation\n key?: string | undefined\n kv?: KVErrorMeta | undefined\n }\n\n constructor(args: { operation: KVOperation; key?: string | undefined; cause: unknown }) {\n const kv = extractKVMeta(args.cause)\n const causeMessage = getDeepErrorMessage(args.cause) || String(args.cause)\n const keyContext = args.key === undefined ? '' : ` for key \"${args.key}\"`\n super(`KV ${args.operation} failed${keyContext}: ${causeMessage}`, { cause: args.cause })\n this.operation = args.operation\n this.key = args.key\n this.cause = args.cause\n this.kv = kv\n this.context = { operation: args.operation, key: args.key, kv }\n this.retryable = isRetryableKVError(args.cause)\n this.classification = isTerminalKVError(args.cause)\n ? 'terminal'\n : this.retryable\n ? 'transient'\n : 'unknown'\n }\n}\n\n// oxlint-disable anti-slop/no-runtime-typeof -- Preserve Typist's primitive messages and nested causes at the raw binding boundary.\nfunction getDeepErrorMessage(cause: unknown): string {\n if (typeof cause === 'string') return cause\n if (cause === null || cause === undefined) return ''\n if (typeof cause === 'number' || typeof cause === 'boolean' || typeof cause === 'bigint') {\n return String(cause)\n }\n if (typeof cause !== 'object') return ''\n const message = 'message' in cause && typeof cause.message === 'string' ? cause.message : ''\n const causeMessage = 'cause' in cause ? getDeepErrorMessage(cause.cause) : ''\n return `${message} ${causeMessage}`.trim()\n}\n// oxlint-enable anti-slop/no-runtime-typeof\n"],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//#region src/infrastructure/posthog/metrics.ts
|
|
2
|
+
/** PostHog Metrics with Worker lifetime management, ported from Typist's metrics-service.ts. */
|
|
3
|
+
var PostHogMetricsService = class {
|
|
4
|
+
client;
|
|
5
|
+
waitUntil;
|
|
6
|
+
/** Use the analytics client's metrics; the runtime owns each flush promise. */
|
|
7
|
+
constructor(client, waitUntil) {
|
|
8
|
+
this.client = client;
|
|
9
|
+
this.waitUntil = waitUntil;
|
|
10
|
+
}
|
|
11
|
+
/** Record a total and schedule its flush. */
|
|
12
|
+
count(name, value = 1, options) {
|
|
13
|
+
this.client.metrics.count(name, value, options);
|
|
14
|
+
this.waitUntil(this.client.metrics.flush());
|
|
15
|
+
}
|
|
16
|
+
/** Record a current value and schedule its flush. */
|
|
17
|
+
gauge(name, value, options) {
|
|
18
|
+
this.client.metrics.gauge(name, value, options);
|
|
19
|
+
this.waitUntil(this.client.metrics.flush());
|
|
20
|
+
}
|
|
21
|
+
/** Record an observation and schedule its flush. */
|
|
22
|
+
histogram(name, value, options) {
|
|
23
|
+
this.client.metrics.histogram(name, value, options);
|
|
24
|
+
this.waitUntil(this.client.metrics.flush());
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
export { PostHogMetricsService as t };
|
|
30
|
+
//# sourceMappingURL=metrics-CWv37yf9.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics-CWv37yf9.js","names":[],"sources":["../src/infrastructure/posthog/metrics.ts"],"sourcesContent":["import type { PostHog } from 'posthog-node'\n\nimport type { MetricOptions, MetricsService } from '@/application'\n\n/** PostHog Metrics with Worker lifetime management, ported from Typist's metrics-service.ts. */\nexport class PostHogMetricsService implements MetricsService {\n /** Use the analytics client's metrics; the runtime owns each flush promise. */\n constructor(\n private readonly client: Pick<PostHog, 'metrics'>,\n private readonly waitUntil: (promise: Promise<unknown>) => void,\n ) {}\n\n /** Record a total and schedule its flush. */\n count(name: string, value = 1, options?: MetricOptions): void {\n this.client.metrics.count(name, value, options)\n this.waitUntil(this.client.metrics.flush())\n }\n\n /** Record a current value and schedule its flush. */\n gauge(name: string, value: number, options?: MetricOptions): void {\n this.client.metrics.gauge(name, value, options)\n this.waitUntil(this.client.metrics.flush())\n }\n\n /** Record an observation and schedule its flush. */\n histogram(name: string, value: number, options?: MetricOptions): void {\n this.client.metrics.histogram(name, value, options)\n this.waitUntil(this.client.metrics.flush())\n }\n}\n"],"mappings":";;AAKA,IAAa,wBAAb,MAA6D;CAGxC;CACA;;CAFnB,YACE,AAAiB,QACjB,AAAiB,WACjB;EAFiB;EACA;CAChB;;CAGH,MAAM,MAAc,QAAQ,GAAG,SAA+B;EAC5D,KAAK,OAAO,QAAQ,MAAM,MAAM,OAAO,OAAO;EAC9C,KAAK,UAAU,KAAK,OAAO,QAAQ,MAAM,CAAC;CAC5C;;CAGA,MAAM,MAAc,OAAe,SAA+B;EAChE,KAAK,OAAO,QAAQ,MAAM,MAAM,OAAO,OAAO;EAC9C,KAAK,UAAU,KAAK,OAAO,QAAQ,MAAM,CAAC;CAC5C;;CAGA,UAAU,MAAc,OAAe,SAA+B;EACpE,KAAK,OAAO,QAAQ,UAAU,MAAM,OAAO,OAAO;EAClD,KAAK,UAAU,KAAK,OAAO,QAAQ,MAAM,CAAC;CAC5C;AACF"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { X as MetricOptions, Z as MetricsService } from "./index-lS_g1olK.js";
|
|
2
|
+
import { PostHog } from "posthog-node";
|
|
3
|
+
//#region src/infrastructure/posthog/metrics.d.ts
|
|
4
|
+
/** PostHog Metrics with Worker lifetime management, ported from Typist's metrics-service.ts. */
|
|
5
|
+
declare class PostHogMetricsService implements MetricsService {
|
|
6
|
+
private readonly client;
|
|
7
|
+
private readonly waitUntil;
|
|
8
|
+
/** Use the analytics client's metrics; the runtime owns each flush promise. */
|
|
9
|
+
constructor(client: Pick<PostHog, 'metrics'>, waitUntil: (promise: Promise<unknown>) => void);
|
|
10
|
+
/** Record a total and schedule its flush. */
|
|
11
|
+
count(name: string, value?: number, options?: MetricOptions): void;
|
|
12
|
+
/** Record a current value and schedule its flush. */
|
|
13
|
+
gauge(name: string, value: number, options?: MetricOptions): void;
|
|
14
|
+
/** Record an observation and schedule its flush. */
|
|
15
|
+
histogram(name: string, value: number, options?: MetricOptions): void;
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
export { PostHogMetricsService as t };
|
|
19
|
+
//# sourceMappingURL=metrics-cv9P5nvk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics-cv9P5nvk.d.ts","names":[],"sources":["../src/infrastructure/posthog/metrics.ts"],"mappings":";;;;cAKa,iCAAiC;mBAGzB;mBACA;;EAFnB,YACmB,QAAQ,KAAK,qBACb,YAAY,SAAS;;EAIxC,MAAM,cAAc,gBAAW,UAAU;;EAMzC,MAAM,cAAc,eAAe,UAAU;;EAM7C,UAAU,cAAc,eAAe,UAAU"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import Stripe from "stripe";
|
|
2
|
+
//#region src/infrastructure/stripe/payment-service.d.ts
|
|
3
|
+
/** Supply the same key and parameters when repeating one durable payment attempt. */
|
|
4
|
+
type PaymentRequestOptions = Stripe.RequestOptions & Required<Pick<Stripe.RequestOptions, 'idempotencyKey'>>;
|
|
5
|
+
/** Native Checkout parameters; the service selects payment or setup mode. */
|
|
6
|
+
type CheckoutInput = Omit<Stripe.Checkout.SessionCreateParams, 'mode'>;
|
|
7
|
+
/** A saved customer method; the service confirms the payment off-session. */
|
|
8
|
+
type SavedMethodCharge = Omit<Stripe.PaymentIntentCreateParams, 'confirm' | 'off_session'> & Required<Pick<Stripe.PaymentIntentCreateParams, 'customer' | 'payment_method'>>;
|
|
9
|
+
/** Native customer response, including Stripe request metadata. */
|
|
10
|
+
type CustomerResult = Awaited<ReturnType<Stripe['customers']['create']>>;
|
|
11
|
+
/** Native Checkout response. Its URL can be null; this does not confirm payment. */
|
|
12
|
+
type CheckoutResult = Awaited<ReturnType<Stripe['checkout']['sessions']['create']>>;
|
|
13
|
+
/** Native PaymentIntent response. Applications decide what each status permits. */
|
|
14
|
+
type PaymentResult = Awaited<ReturnType<Stripe['paymentIntents']['create']>>;
|
|
15
|
+
/**
|
|
16
|
+
* Stripe payment operations, with native types and stable keys for mutations.
|
|
17
|
+
* Rejects with PaymentServiceError for Stripe failures; non-Stripe failures propagate unchanged.
|
|
18
|
+
*/
|
|
19
|
+
interface IPaymentService {
|
|
20
|
+
/** Create a customer; the application owns persistence and reuse of the customer link. */
|
|
21
|
+
createCustomer(input: Stripe.CustomerCreateParams, options: PaymentRequestOptions): Promise<CustomerResult>;
|
|
22
|
+
/** Create one-time payment checkout; application fulfillment requires verified payment confirmation. */
|
|
23
|
+
createCheckout(input: CheckoutInput, options: PaymentRequestOptions): Promise<CheckoutResult>;
|
|
24
|
+
/** Create checkout in setup mode. The application records consent and verifies the saved method's owner. */
|
|
25
|
+
setupPaymentMethod(input: CheckoutInput, options: PaymentRequestOptions): Promise<CheckoutResult>;
|
|
26
|
+
/** Confirm a saved method off-session. PaymentServiceError retains card errors in cause. */
|
|
27
|
+
chargeSavedMethod(input: SavedMethodCharge, options: PaymentRequestOptions): Promise<PaymentResult>;
|
|
28
|
+
/** Retrieve an existing payment without starting another payment attempt. */
|
|
29
|
+
getPayment(paymentId: string, params?: Stripe.PaymentIntentRetrieveParams, options?: Stripe.RequestOptions): Promise<PaymentResult>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Ported from Typist's StripeClient request boundary; the native SDK now owns retries.
|
|
33
|
+
* The injected client owns retry and timeout defaults. Native request options can override them.
|
|
34
|
+
*/
|
|
35
|
+
declare class StripePaymentService implements IPaymentService {
|
|
36
|
+
private readonly stripe;
|
|
37
|
+
/** Inject a native SDK instance; its transport, credentials, and API version remain caller-owned. */
|
|
38
|
+
constructor(stripe: Stripe);
|
|
39
|
+
/** Create a customer with a stable application key and preserve the native response. */
|
|
40
|
+
createCustomer(input: Stripe.CustomerCreateParams, options: PaymentRequestOptions): Promise<CustomerResult>;
|
|
41
|
+
/** Force payment mode after caller parameters; return the native Checkout Session. */
|
|
42
|
+
createCheckout(input: CheckoutInput, options: PaymentRequestOptions): Promise<CheckoutResult>;
|
|
43
|
+
/** Force setup mode after caller parameters; this operation does not charge the customer. */
|
|
44
|
+
setupPaymentMethod(input: CheckoutInput, options: PaymentRequestOptions): Promise<CheckoutResult>;
|
|
45
|
+
/** Force confirmation and off-session use; retain native card failures in PaymentServiceError.cause. */
|
|
46
|
+
chargeSavedMethod(input: SavedMethodCharge, options: PaymentRequestOptions): Promise<PaymentResult>;
|
|
47
|
+
/** Preserve every native status and response field when reconciling an existing payment. */
|
|
48
|
+
getPayment(paymentId: string, params?: Stripe.PaymentIntentRetrieveParams, options?: Stripe.RequestOptions): Promise<PaymentResult>;
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
export { PaymentRequestOptions as a, StripePaymentService as c, IPaymentService as i, CheckoutResult as n, PaymentResult as o, CustomerResult as r, SavedMethodCharge as s, CheckoutInput as t };
|
|
52
|
+
//# sourceMappingURL=payment-service-DUYd3Itt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payment-service-DUYd3Itt.d.ts","names":[],"sources":["../src/infrastructure/stripe/payment-service.ts"],"mappings":";;;KAKY,wBAAwB,OAAO,iBACzC,SAAS,KAAK,OAAO;;KAGX,gBAAgB,KAAK,OAAO,SAAS;;KAGrC,oBAAoB,KAAK,OAAO,wDAC1C,SAAS,KAAK,OAAO;;KAGX,iBAAiB,QAAQ,WAAW;;KAGpC,iBAAiB,QAAQ,WAAW;;KAGpC,gBAAgB,QAAQ,WAAW;;;;;UAM9B;;EAEf,eACE,OAAO,OAAO,sBACd,SAAS,wBACR,QAAQ;;EAEX,eAAe,OAAO,eAAe,SAAS,wBAAwB,QAAQ;;EAE9E,mBAAmB,OAAO,eAAe,SAAS,wBAAwB,QAAQ;;EAElF,kBACE,OAAO,mBACP,SAAS,wBACR,QAAQ;;EAEX,WACE,mBACA,SAAS,OAAO,6BAChB,UAAU,OAAO,iBAChB,QAAQ;;;;;;cAOA,gCAAgC;mBAEd;;EAA7B,YAA6B,QAAQ;;EAGrC,eACE,OAAO,OAAO,sBACd,SAAS,wBACR,QAAQ;;EAKX,eAAe,OAAO,eAAe,SAAS,wBAAwB,QAAQ;;EAO9E,mBACE,OAAO,eACP,SAAS,wBACR,QAAQ;;EAOX,kBACE,OAAO,mBACP,SAAS,wBACR,QAAQ;;EAOX,WACE,mBACA,SAAS,OAAO,6BAChB,UAAU,OAAO,iBAChB,QAAQ"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { nt as AppError } from "./core-BiN6S_cm.js";
|
|
2
|
+
import Stripe from "stripe";
|
|
3
|
+
|
|
4
|
+
//#region src/infrastructure/stripe/payment-service.error.ts
|
|
5
|
+
/** A final Stripe request failure. The caller logs once; native details remain in cause. */
|
|
6
|
+
var PaymentServiceError = class extends AppError {
|
|
7
|
+
operation;
|
|
8
|
+
_tag = "PaymentServiceError";
|
|
9
|
+
classification;
|
|
10
|
+
/** Classify the final SDK error without scheduling another payment attempt. */
|
|
11
|
+
constructor(operation, cause) {
|
|
12
|
+
super(`Stripe request failed: ${operation}`, { cause });
|
|
13
|
+
this.operation = operation;
|
|
14
|
+
this.classification = classifyStripeError(cause);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
function classifyStripeError(error) {
|
|
18
|
+
if (error.headers?.["stripe-should-retry"] === "false") return "terminal";
|
|
19
|
+
const status = error.statusCode;
|
|
20
|
+
if (status !== void 0 && status >= 400 && status < 500 && status !== 409 && status !== 429) return "terminal";
|
|
21
|
+
if (error instanceof Stripe.errors.StripeAuthenticationError || error instanceof Stripe.errors.StripePermissionError || error instanceof Stripe.errors.StripeInvalidRequestError || error instanceof Stripe.errors.StripeIdempotencyError || error instanceof Stripe.errors.StripeCardError) return "terminal";
|
|
22
|
+
if (error.headers?.["stripe-should-retry"] === "true") return "transient";
|
|
23
|
+
if (status === 409 || status === 429 || status !== void 0 && status >= 500) return "transient";
|
|
24
|
+
if (error instanceof Stripe.errors.StripeConnectionError || error instanceof Stripe.errors.StripeAPIError || error instanceof Stripe.errors.StripeRateLimitError) return "transient";
|
|
25
|
+
return "unknown";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
export { PaymentServiceError as t };
|
|
30
|
+
//# sourceMappingURL=payment-service.error-CqF7GkNp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payment-service.error-CqF7GkNp.js","names":[],"sources":["../src/infrastructure/stripe/payment-service.error.ts"],"sourcesContent":["import Stripe from 'stripe'\n\nimport { AppError, type FailureClassification } from '@/core'\n\n/** A final Stripe request failure. The caller logs once; native details remain in cause. */\nexport class PaymentServiceError extends AppError {\n readonly _tag = 'PaymentServiceError'\n readonly classification: FailureClassification\n\n /** Classify the final SDK error without scheduling another payment attempt. */\n constructor(\n readonly operation: string,\n cause: Stripe.errors.StripeError,\n ) {\n super(`Stripe request failed: ${operation}`, { cause })\n this.classification = classifyStripeError(cause)\n }\n}\n\nfunction classifyStripeError(error: Stripe.errors.StripeError): FailureClassification {\n if (error.headers?.['stripe-should-retry'] === 'false') return 'terminal'\n const status = error.statusCode\n if (status !== undefined && status >= 400 && status < 500 && status !== 409 && status !== 429)\n return 'terminal'\n if (\n error instanceof Stripe.errors.StripeAuthenticationError ||\n error instanceof Stripe.errors.StripePermissionError ||\n error instanceof Stripe.errors.StripeInvalidRequestError ||\n error instanceof Stripe.errors.StripeIdempotencyError ||\n error instanceof Stripe.errors.StripeCardError\n )\n return 'terminal'\n if (error.headers?.['stripe-should-retry'] === 'true') return 'transient'\n if (status === 409 || status === 429 || (status !== undefined && status >= 500))\n return 'transient'\n if (\n error instanceof Stripe.errors.StripeConnectionError ||\n error instanceof Stripe.errors.StripeAPIError ||\n error instanceof Stripe.errors.StripeRateLimitError\n )\n return 'transient'\n return 'unknown'\n}\n"],"mappings":";;;;;AAKA,IAAa,sBAAb,cAAyC,SAAS;CAMrC;CALX,AAAS,OAAO;CAChB,AAAS;;CAGT,YACE,AAAS,WACT,OACA;EACA,MAAM,0BAA0B,aAAa,EAAE,MAAM,CAAC;EAH7C;EAIT,KAAK,iBAAiB,oBAAoB,KAAK;CACjD;AACF;AAEA,SAAS,oBAAoB,OAAyD;CACpF,IAAI,MAAM,UAAU,2BAA2B,SAAS,OAAO;CAC/D,MAAM,SAAS,MAAM;CACrB,IAAI,WAAW,UAAa,UAAU,OAAO,SAAS,OAAO,WAAW,OAAO,WAAW,KACxF,OAAO;CACT,IACE,iBAAiB,OAAO,OAAO,6BAC/B,iBAAiB,OAAO,OAAO,yBAC/B,iBAAiB,OAAO,OAAO,6BAC/B,iBAAiB,OAAO,OAAO,0BAC/B,iBAAiB,OAAO,OAAO,iBAE/B,OAAO;CACT,IAAI,MAAM,UAAU,2BAA2B,QAAQ,OAAO;CAC9D,IAAI,WAAW,OAAO,WAAW,OAAQ,WAAW,UAAa,UAAU,KACzE,OAAO;CACT,IACE,iBAAiB,OAAO,OAAO,yBAC/B,iBAAiB,OAAO,OAAO,kBAC/B,iBAAiB,OAAO,OAAO,sBAE/B,OAAO;CACT,OAAO;AACT"}
|
|
@@ -1,18 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
//#region src/infrastructure/posthog/metrics.d.ts
|
|
4
|
-
/** PostHog Metrics with Worker lifetime management, ported from Typist's metrics-service.ts. */
|
|
5
|
-
export declare class PostHogMetricsService implements MetricsService {
|
|
6
|
-
private readonly client;
|
|
7
|
-
private readonly waitUntil;
|
|
8
|
-
/** Use the analytics client's metrics; the runtime owns each flush promise. */
|
|
9
|
-
constructor(client: Pick<PostHog, 'metrics'>, waitUntil: (promise: Promise<unknown>) => void);
|
|
10
|
-
/** Record a total and schedule its flush. */
|
|
11
|
-
count(name: string, value?: number, options?: MetricOptions): void;
|
|
12
|
-
/** Record a current value and schedule its flush. */
|
|
13
|
-
gauge(name: string, value: number, options?: MetricOptions): void;
|
|
14
|
-
/** Record an observation and schedule its flush. */
|
|
15
|
-
histogram(name: string, value: number, options?: MetricOptions): void;
|
|
16
|
-
}
|
|
17
|
-
//#endregion
|
|
18
|
-
//# sourceMappingURL=metrics.d.ts.map
|
|
1
|
+
import { t as PostHogMetricsService } from "../metrics-cv9P5nvk.js";
|
|
2
|
+
export { PostHogMetricsService };
|
package/dist/posthog/metrics.js
CHANGED
|
@@ -1,30 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
/** PostHog Metrics with Worker lifetime management, ported from Typist's metrics-service.ts. */
|
|
3
|
-
var PostHogMetricsService = class {
|
|
4
|
-
client;
|
|
5
|
-
waitUntil;
|
|
6
|
-
/** Use the analytics client's metrics; the runtime owns each flush promise. */
|
|
7
|
-
constructor(client, waitUntil) {
|
|
8
|
-
this.client = client;
|
|
9
|
-
this.waitUntil = waitUntil;
|
|
10
|
-
}
|
|
11
|
-
/** Record a total and schedule its flush. */
|
|
12
|
-
count(name, value = 1, options) {
|
|
13
|
-
this.client.metrics.count(name, value, options);
|
|
14
|
-
this.waitUntil(this.client.metrics.flush());
|
|
15
|
-
}
|
|
16
|
-
/** Record a current value and schedule its flush. */
|
|
17
|
-
gauge(name, value, options) {
|
|
18
|
-
this.client.metrics.gauge(name, value, options);
|
|
19
|
-
this.waitUntil(this.client.metrics.flush());
|
|
20
|
-
}
|
|
21
|
-
/** Record an observation and schedule its flush. */
|
|
22
|
-
histogram(name, value, options) {
|
|
23
|
-
this.client.metrics.histogram(name, value, options);
|
|
24
|
-
this.waitUntil(this.client.metrics.flush());
|
|
25
|
-
}
|
|
26
|
-
};
|
|
1
|
+
import { t as PostHogMetricsService } from "../metrics-CWv37yf9.js";
|
|
27
2
|
|
|
28
|
-
|
|
29
|
-
export { PostHogMetricsService };
|
|
30
|
-
//# sourceMappingURL=metrics.js.map
|
|
3
|
+
export { PostHogMetricsService };
|
package/dist/posthog/server.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Gt as AnalyticsEventIdentity, Kt as AnalyticsEventInput, qt as AnalyticsProperties } from "../index-DUFS0SJ_.js";
|
|
2
2
|
import { $ as AnalyticsService } from "../index-lS_g1olK.js";
|
|
3
|
-
import { PostHogMetricsService } from "
|
|
3
|
+
import { t as PostHogMetricsService } from "../metrics-cv9P5nvk.js";
|
|
4
4
|
import { PostHog, PostHogOptions } from "posthog-node";
|
|
5
5
|
//#region src/infrastructure/posthog/server.d.ts
|
|
6
6
|
/** Deployment values and SDK options; the runtime owns delivery promises. */
|
package/dist/posthog/server.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { tt as MissingConfigurationError } from "../core-BiN6S_cm.js";
|
|
2
|
-
import { PostHogMetricsService } from "
|
|
2
|
+
import { t as PostHogMetricsService } from "../metrics-CWv37yf9.js";
|
|
3
3
|
import { PostHog } from "posthog-node";
|
|
4
4
|
|
|
5
5
|
//#region src/infrastructure/posthog/server.ts
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { nt as AppError, u as classifyBoundaryError, v as isAbortError } from "./core-BiN6S_cm.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
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 { R2JsonError as n, R2Error as t };
|
|
90
|
+
//# sourceMappingURL=r2-errors-rk7n7rvk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"r2-errors-rk7n7rvk.js","names":[],"sources":["../src/infrastructure/cloudflare/r2-errors.ts"],"sourcesContent":["import { z } from 'zod'\n\nimport { AppError, classifyBoundaryError, isAbortError, type FailureClassification } from '@/core'\n\n/** The storage operation that failed. */\nexport type R2Operation =\n | 'head'\n | 'get'\n | 'getJson'\n | 'put'\n | 'delete'\n | 'list'\n | 'createMultipartUpload'\n | 'uploadPart'\n | 'complete'\n | 'abort'\n\n/** Provider diagnostics from the outer failure, without request bodies or credentials. */\nexport type R2Failure = {\n readonly code: number\n readonly action?: string | undefined\n}\n\nconst R2FailureSchema = z.object({\n code: z.number().int().positive(),\n action: z.string().optional(),\n})\n\n// R2 codes describe failures; the caller owns retry decisions and repeat safety.\nconst classifications: ReadonlyMap<number, FailureClassification> = new Map([\n [10001, 'transient'],\n [10043, 'transient'],\n [10054, 'transient'],\n [10058, 'transient'],\n [10013, 'transient'],\n [10002, 'terminal'],\n [10003, 'terminal'],\n [10018, 'terminal'],\n [10035, 'terminal'],\n [10042, 'terminal'],\n [10005, 'terminal'],\n [10006, 'terminal'],\n [10008, 'terminal'],\n [10009, 'terminal'],\n [10073, 'terminal'],\n [10007, 'terminal'],\n [10020, 'terminal'],\n [100100, 'terminal'],\n [10012, 'terminal'],\n [10069, 'terminal'],\n [10033, 'terminal'],\n [10014, 'terminal'],\n [10037, 'terminal'],\n [10039, 'terminal'],\n [10031, 'terminal'],\n [10011, 'terminal'],\n [10024, 'terminal'],\n [10025, 'terminal'],\n [10048, 'terminal'],\n])\n\nfunction extractR2Failure(cause: unknown): R2Failure | undefined {\n if (!(cause instanceof Error)) return undefined\n const parsed = R2FailureSchema.safeParse(cause)\n if (parsed.success) return parsed.data\n // Cloudflare documents the numeric suffix for bindings that omit structured fields.\n const match = /\\((\\d{5,6})\\)\\s*$/.exec(cause.message)\n return match?.[1] === undefined ? undefined : { code: Number(match[1]) }\n}\n\nfunction classifyR2Code(cause: unknown): FailureClassification | undefined {\n const failure = extractR2Failure(cause)\n return failure === undefined ? undefined : (classifications.get(failure.code) ?? 'unknown')\n}\n\nfunction classifyR2Error(cause: unknown): FailureClassification {\n if (isAbortError(cause)) return 'terminal'\n if (cause instanceof AppError) return cause.classification\n return classifyBoundaryError({ cause, classifyBoundary: classifyR2Code, includeCloudflare: true })\n}\n\n/** A raw binding failure after one attempt. Classification does not establish whether a mutation committed. */\nexport class R2Error extends AppError {\n readonly _tag = 'R2Error'\n readonly classification: FailureClassification\n readonly operation: R2Operation\n readonly r2?: R2Failure | undefined\n override readonly cause: unknown\n\n constructor(input: { operation: R2Operation; cause: unknown }) {\n super(`R2 ${input.operation} failed`, { cause: input.cause })\n this.operation = input.operation\n this.cause = input.cause\n this.r2 = extractR2Failure(input.cause)\n this.classification = classifyR2Error(input.cause)\n }\n}\n\n/** JSON serialization or syntax failed. Binding and body-read failures use R2Error. */\nexport class R2JsonError extends AppError {\n readonly _tag = 'R2JsonError'\n readonly classification = 'terminal'\n readonly operation: 'encode' | 'decode'\n override readonly cause: unknown\n\n constructor(input: { operation: 'encode' | 'decode'; cause: unknown }) {\n super(`R2 JSON ${input.operation} failed`, { cause: input.cause })\n this.operation = input.operation\n this.cause = input.cause\n }\n}\n"],"mappings":";;;;AAuBA,MAAM,kBAAkB,EAAE,OAAO;CAC/B,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS;CAChC,QAAQ,EAAE,OAAO,CAAC,CAAC,SAAS;AAC9B,CAAC;AAGD,MAAM,kCAA8D,IAAI,IAAI;CAC1E,CAAC,OAAO,WAAW;CACnB,CAAC,OAAO,WAAW;CACnB,CAAC,OAAO,WAAW;CACnB,CAAC,OAAO,WAAW;CACnB,CAAC,OAAO,WAAW;CACnB,CAAC,OAAO,UAAU;CAClB,CAAC,OAAO,UAAU;CAClB,CAAC,OAAO,UAAU;CAClB,CAAC,OAAO,UAAU;CAClB,CAAC,OAAO,UAAU;CAClB,CAAC,OAAO,UAAU;CAClB,CAAC,OAAO,UAAU;CAClB,CAAC,OAAO,UAAU;CAClB,CAAC,OAAO,UAAU;CAClB,CAAC,OAAO,UAAU;CAClB,CAAC,OAAO,UAAU;CAClB,CAAC,OAAO,UAAU;CAClB,CAAC,QAAQ,UAAU;CACnB,CAAC,OAAO,UAAU;CAClB,CAAC,OAAO,UAAU;CAClB,CAAC,OAAO,UAAU;CAClB,CAAC,OAAO,UAAU;CAClB,CAAC,OAAO,UAAU;CAClB,CAAC,OAAO,UAAU;CAClB,CAAC,OAAO,UAAU;CAClB,CAAC,OAAO,UAAU;CAClB,CAAC,OAAO,UAAU;CAClB,CAAC,OAAO,UAAU;CAClB,CAAC,OAAO,UAAU;AACpB,CAAC;AAED,SAAS,iBAAiB,OAAuC;CAC/D,IAAI,EAAE,iBAAiB,QAAQ,OAAO;CACtC,MAAM,SAAS,gBAAgB,UAAU,KAAK;CAC9C,IAAI,OAAO,SAAS,OAAO,OAAO;CAElC,MAAM,QAAQ,oBAAoB,KAAK,MAAM,OAAO;CACpD,OAAO,QAAQ,OAAO,SAAY,SAAY,EAAE,MAAM,OAAO,MAAM,EAAE,EAAE;AACzE;AAEA,SAAS,eAAe,OAAmD;CACzE,MAAM,UAAU,iBAAiB,KAAK;CACtC,OAAO,YAAY,SAAY,SAAa,gBAAgB,IAAI,QAAQ,IAAI,KAAK;AACnF;AAEA,SAAS,gBAAgB,OAAuC;CAC9D,IAAI,aAAa,KAAK,GAAG,OAAO;CAChC,IAAI,iBAAiB,UAAU,OAAO,MAAM;CAC5C,OAAO,sBAAsB;EAAE;EAAO,kBAAkB;EAAgB,mBAAmB;CAAK,CAAC;AACnG;;AAGA,IAAa,UAAb,cAA6B,SAAS;CACpC,AAAS,OAAO;CAChB,AAAS;CACT,AAAS;CACT,AAAS;CACT,AAAkB;CAElB,YAAY,OAAmD;EAC7D,MAAM,MAAM,MAAM,UAAU,UAAU,EAAE,OAAO,MAAM,MAAM,CAAC;EAC5D,KAAK,YAAY,MAAM;EACvB,KAAK,QAAQ,MAAM;EACnB,KAAK,KAAK,iBAAiB,MAAM,KAAK;EACtC,KAAK,iBAAiB,gBAAgB,MAAM,KAAK;CACnD;AACF;;AAGA,IAAa,cAAb,cAAiC,SAAS;CACxC,AAAS,OAAO;CAChB,AAAS,iBAAiB;CAC1B,AAAS;CACT,AAAkB;CAElB,YAAY,OAA2D;EACrE,MAAM,WAAW,MAAM,UAAU,UAAU,EAAE,OAAO,MAAM,MAAM,CAAC;EACjE,KAAK,YAAY,MAAM;EACvB,KAAK,QAAQ,MAAM;CACrB;AACF"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { t as PaymentServiceError } from "./payment-service.error-CqF7GkNp.js";
|
|
2
|
+
import Stripe from "stripe";
|
|
3
|
+
|
|
4
|
+
//#region src/infrastructure/stripe/request.ts
|
|
5
|
+
/**
|
|
6
|
+
* Invoke one SDK operation; the SDK owns retries configured on its client or request.
|
|
7
|
+
* Convert every Stripe error to PaymentServiceError without logging; preserve the original error in cause.
|
|
8
|
+
* Non-Stripe failures propagate unchanged.
|
|
9
|
+
*/
|
|
10
|
+
async function stripeRequest(operation, work) {
|
|
11
|
+
try {
|
|
12
|
+
return await work();
|
|
13
|
+
} catch (cause) {
|
|
14
|
+
if (!(cause instanceof Stripe.errors.StripeError)) throw cause;
|
|
15
|
+
throw new PaymentServiceError(operation, cause);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
//#endregion
|
|
20
|
+
export { stripeRequest as t };
|
|
21
|
+
//# sourceMappingURL=request-4vJEWxyZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-4vJEWxyZ.js","names":[],"sources":["../src/infrastructure/stripe/request.ts"],"sourcesContent":["import Stripe from 'stripe'\n\nimport { PaymentServiceError } from './payment-service.error.ts'\n\n/**\n * Invoke one SDK operation; the SDK owns retries configured on its client or request.\n * Convert every Stripe error to PaymentServiceError without logging; preserve the original error in cause.\n * Non-Stripe failures propagate unchanged.\n */\nexport async function stripeRequest<T>(operation: string, work: () => Promise<T>): Promise<T> {\n try {\n return await work()\n } catch (cause) {\n if (!(cause instanceof Stripe.errors.StripeError)) throw cause\n throw new PaymentServiceError(operation, cause)\n }\n}\n"],"mappings":";;;;;;;;;AASA,eAAsB,cAAiB,WAAmB,MAAoC;CAC5F,IAAI;EACF,OAAO,MAAM,KAAK;CACpB,SAAS,OAAO;EACd,IAAI,EAAE,iBAAiB,OAAO,OAAO,cAAc,MAAM;EACzD,MAAM,IAAI,oBAAoB,WAAW,KAAK;CAChD;AACF"}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
//#region src/infrastructure/slack/ci-message.ts
|
|
4
|
+
/** The CI environment could not be parsed. `issues` names each invalid variable. */
|
|
5
|
+
var CiRunParseError = class extends Error {
|
|
6
|
+
issues;
|
|
7
|
+
cause;
|
|
8
|
+
_tag = "CiRunParseError";
|
|
9
|
+
constructor(issues, cause) {
|
|
10
|
+
super(`Invalid CI environment: ${issues.join(", ")}`);
|
|
11
|
+
this.issues = issues;
|
|
12
|
+
this.cause = cause;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
const OutcomeSchema = z.enum([
|
|
16
|
+
"success",
|
|
17
|
+
"failure",
|
|
18
|
+
"cancelled",
|
|
19
|
+
"skipped"
|
|
20
|
+
]);
|
|
21
|
+
const jsonRecord = (value) => z.string().transform((text, context) => {
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(text);
|
|
24
|
+
} catch {
|
|
25
|
+
context.addIssue({
|
|
26
|
+
code: "custom",
|
|
27
|
+
message: "Invalid JSON"
|
|
28
|
+
});
|
|
29
|
+
return z.NEVER;
|
|
30
|
+
}
|
|
31
|
+
}).pipe(z.record(z.string(), value));
|
|
32
|
+
const CiEnvSchema = z.object({
|
|
33
|
+
REPO: z.string().regex(/^[\w.-]+\/[\w.-]+$/),
|
|
34
|
+
COMMIT_SHA: z.string().regex(/^[0-9a-f]{40}$/),
|
|
35
|
+
BRANCH: z.string().min(1),
|
|
36
|
+
RUN_URL: z.httpUrl(),
|
|
37
|
+
JOBS_JSON: jsonRecord(z.object({ result: OutcomeSchema })).optional(),
|
|
38
|
+
STEPS_JSON: jsonRecord(z.object({ outcome: OutcomeSchema })).optional(),
|
|
39
|
+
PR_NUMBER: z.coerce.number().int().positive().optional(),
|
|
40
|
+
PR_URL: z.httpUrl().optional(),
|
|
41
|
+
PR_TITLE: z.string().min(1).optional(),
|
|
42
|
+
ACTION: z.enum([
|
|
43
|
+
"check",
|
|
44
|
+
"deploy",
|
|
45
|
+
"publish"
|
|
46
|
+
]).default("check"),
|
|
47
|
+
CLOUDFLARE_ACCOUNT_ID: z.string().min(1).optional(),
|
|
48
|
+
CLOUDFLARE_WORKER: z.string().min(1).optional(),
|
|
49
|
+
SITE_URL: z.httpUrl().optional(),
|
|
50
|
+
DEPLOY_VERSION_ID: z.string().min(1).optional(),
|
|
51
|
+
NPM_PACKAGE: z.string().min(1).optional(),
|
|
52
|
+
NPM_VERSION: z.string().min(1).optional()
|
|
53
|
+
});
|
|
54
|
+
/**
|
|
55
|
+
* Parse the variables that a workflow passes to the notifier. GitHub expressions render a
|
|
56
|
+
* missing value as an empty string, so an empty variable counts as unset.
|
|
57
|
+
* @throws {CiRunParseError} when a variable is missing or invalid.
|
|
58
|
+
*/
|
|
59
|
+
function parseCiRun(env) {
|
|
60
|
+
const present = Object.fromEntries(Object.entries(env).filter(([, value]) => value !== ""));
|
|
61
|
+
const parsed = CiEnvSchema.safeParse(present);
|
|
62
|
+
if (!parsed.success) {
|
|
63
|
+
const issues = parsed.error.issues.map((issue) => issue.path.join("."));
|
|
64
|
+
throw new CiRunParseError(issues, parsed.error);
|
|
65
|
+
}
|
|
66
|
+
return toCiRun(parsed.data);
|
|
67
|
+
}
|
|
68
|
+
function toCiRun(env) {
|
|
69
|
+
const ref = env.PR_NUMBER !== void 0 && env.PR_URL !== void 0 && env.PR_TITLE !== void 0 ? {
|
|
70
|
+
kind: "pullRequest",
|
|
71
|
+
number: env.PR_NUMBER,
|
|
72
|
+
url: env.PR_URL,
|
|
73
|
+
title: env.PR_TITLE
|
|
74
|
+
} : {
|
|
75
|
+
kind: "branch",
|
|
76
|
+
name: env.BRANCH
|
|
77
|
+
};
|
|
78
|
+
return {
|
|
79
|
+
repository: env.REPO,
|
|
80
|
+
commitSha: env.COMMIT_SHA,
|
|
81
|
+
ref,
|
|
82
|
+
runUrl: env.RUN_URL,
|
|
83
|
+
jobs: Object.entries(env.JOBS_JSON ?? {}).map(([id, job]) => ({
|
|
84
|
+
name: displayName(id),
|
|
85
|
+
outcome: job.result
|
|
86
|
+
})),
|
|
87
|
+
steps: Object.entries(env.STEPS_JSON ?? {}).map(([id, step]) => ({
|
|
88
|
+
name: displayName(id),
|
|
89
|
+
outcome: step.outcome
|
|
90
|
+
})),
|
|
91
|
+
target: toTarget(env)
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function toTarget(env) {
|
|
95
|
+
switch (env.ACTION) {
|
|
96
|
+
case "check": return { kind: "check" };
|
|
97
|
+
case "deploy": {
|
|
98
|
+
const { CLOUDFLARE_ACCOUNT_ID: accountId, CLOUDFLARE_WORKER: worker, SITE_URL: siteUrl } = env;
|
|
99
|
+
if (accountId === void 0 || worker === void 0 || siteUrl === void 0) throw new CiRunParseError([
|
|
100
|
+
"CLOUDFLARE_ACCOUNT_ID",
|
|
101
|
+
"CLOUDFLARE_WORKER",
|
|
102
|
+
"SITE_URL"
|
|
103
|
+
], void 0);
|
|
104
|
+
return {
|
|
105
|
+
kind: "deploy",
|
|
106
|
+
accountId,
|
|
107
|
+
worker,
|
|
108
|
+
siteUrl,
|
|
109
|
+
versionId: env.DEPLOY_VERSION_ID
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
case "publish":
|
|
113
|
+
if (env.NPM_PACKAGE === void 0) throw new CiRunParseError(["NPM_PACKAGE"], void 0);
|
|
114
|
+
return {
|
|
115
|
+
kind: "publish",
|
|
116
|
+
packageName: env.NPM_PACKAGE,
|
|
117
|
+
version: env.NPM_VERSION
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function displayName(id) {
|
|
122
|
+
const words = id.replaceAll(/[-_]/g, " ");
|
|
123
|
+
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
124
|
+
}
|
|
125
|
+
const logos = {
|
|
126
|
+
depot: "https://avatars.githubusercontent.com/u/68166601?s=64",
|
|
127
|
+
github: "https://avatars.githubusercontent.com/u/9919?s=64",
|
|
128
|
+
cloudflare: "https://avatars.githubusercontent.com/u/314135?s=64",
|
|
129
|
+
npm: "https://avatars.githubusercontent.com/u/6078720?s=64"
|
|
130
|
+
};
|
|
131
|
+
/** Build the four-line CI message: result, change, links, and identity. */
|
|
132
|
+
function buildCiMessage(run, commit) {
|
|
133
|
+
const headline = headlineFor(run);
|
|
134
|
+
const change = run.ref.kind === "pullRequest" ? run.ref.title : commit.subject;
|
|
135
|
+
return {
|
|
136
|
+
text: `${headline} — ${change}`,
|
|
137
|
+
blocks: [
|
|
138
|
+
{
|
|
139
|
+
type: "section",
|
|
140
|
+
text: {
|
|
141
|
+
type: "mrkdwn",
|
|
142
|
+
text: `*${headline}*\n${escape(change)}`
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
type: "context",
|
|
147
|
+
elements: linkElements(run)
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
type: "context",
|
|
151
|
+
elements: [{
|
|
152
|
+
type: "mrkdwn",
|
|
153
|
+
text: identityLine(run, commit)
|
|
154
|
+
}]
|
|
155
|
+
}
|
|
156
|
+
]
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function headlineFor(run) {
|
|
160
|
+
const failedJobs = namesWith(run.jobs, "failure");
|
|
161
|
+
if (failedJobs) return `:x: CI failed: ${failedJobs}`;
|
|
162
|
+
const cancelledJobs = namesWith(run.jobs, "cancelled");
|
|
163
|
+
if (cancelledJobs) return `:white_circle: CI cancelled: ${cancelledJobs}`;
|
|
164
|
+
const failedSteps = namesWith(run.steps, "failure");
|
|
165
|
+
const { target } = run;
|
|
166
|
+
switch (target.kind) {
|
|
167
|
+
case "check": return failedSteps ? `:x: CI failed: ${failedSteps}` : ":white_check_mark: CI passed";
|
|
168
|
+
case "deploy":
|
|
169
|
+
if (failedSteps) return `:x: Deploy failed: ${failedSteps}`;
|
|
170
|
+
return target.versionId === void 0 ? ":white_check_mark: CI passed" : ":white_check_mark: Deployed to production";
|
|
171
|
+
case "publish":
|
|
172
|
+
if (failedSteps) return `:x: Publish failed: ${failedSteps}`;
|
|
173
|
+
return target.version === void 0 ? ":white_check_mark: CI passed, nothing to publish" : `:white_check_mark: Published ${target.packageName} ${target.version}`;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function namesWith(results, outcome) {
|
|
177
|
+
return results.filter((result) => result.outcome === outcome).map((result) => result.name).join(", ");
|
|
178
|
+
}
|
|
179
|
+
function linkElements(run) {
|
|
180
|
+
const runHost = new URL(run.runUrl).hostname;
|
|
181
|
+
const elements = [{
|
|
182
|
+
type: "image",
|
|
183
|
+
image_url: runHost.endsWith("github.com") ? logos.github : logos.depot,
|
|
184
|
+
alt_text: runHost
|
|
185
|
+
}, {
|
|
186
|
+
type: "mrkdwn",
|
|
187
|
+
text: `<${run.runUrl}|CI run>`
|
|
188
|
+
}];
|
|
189
|
+
const { target } = run;
|
|
190
|
+
if (target.kind === "deploy" && target.versionId !== void 0) {
|
|
191
|
+
const dashboard = `https://dash.cloudflare.com/${target.accountId}/workers/services/view/${target.worker}/production/deployments`;
|
|
192
|
+
const site = new URL(target.siteUrl);
|
|
193
|
+
elements.push({
|
|
194
|
+
type: "mrkdwn",
|
|
195
|
+
text: "·"
|
|
196
|
+
}, {
|
|
197
|
+
type: "image",
|
|
198
|
+
image_url: logos.cloudflare,
|
|
199
|
+
alt_text: "Cloudflare"
|
|
200
|
+
}, {
|
|
201
|
+
type: "mrkdwn",
|
|
202
|
+
text: `<${dashboard}|Version \`${target.versionId.slice(0, 8)}\`>`
|
|
203
|
+
}, {
|
|
204
|
+
type: "mrkdwn",
|
|
205
|
+
text: `· :globe_with_meridians: <${site.origin}|${site.host}>`
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
if (target.kind === "publish" && target.version !== void 0) {
|
|
209
|
+
const npm = `https://www.npmjs.com/package/${target.packageName}/v/${target.version}`;
|
|
210
|
+
elements.push({
|
|
211
|
+
type: "mrkdwn",
|
|
212
|
+
text: "·"
|
|
213
|
+
}, {
|
|
214
|
+
type: "image",
|
|
215
|
+
image_url: logos.npm,
|
|
216
|
+
alt_text: "npm"
|
|
217
|
+
}, {
|
|
218
|
+
type: "mrkdwn",
|
|
219
|
+
text: `<${npm}|${target.packageName}@${target.version}>`
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
return elements;
|
|
223
|
+
}
|
|
224
|
+
function identityLine(run, commit) {
|
|
225
|
+
return [
|
|
226
|
+
run.repository.split("/").at(-1) ?? run.repository,
|
|
227
|
+
run.ref.kind === "pullRequest" ? `<${run.ref.url}|PR #${run.ref.number}>` : run.ref.name,
|
|
228
|
+
`<${`https://github.com/${run.repository}/commit/${run.commitSha}`}|\`${run.commitSha.slice(0, 7)}\`>`,
|
|
229
|
+
escape(commit.author)
|
|
230
|
+
].join(" · ");
|
|
231
|
+
}
|
|
232
|
+
function escape(text) {
|
|
233
|
+
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
//#endregion
|
|
237
|
+
//#region src/infrastructure/slack/slack-webhook.ts
|
|
238
|
+
/** Slack refused the message or could not be reached. */
|
|
239
|
+
var SlackWebhookError = class extends Error {
|
|
240
|
+
status;
|
|
241
|
+
cause;
|
|
242
|
+
_tag = "SlackWebhookError";
|
|
243
|
+
constructor(status, cause) {
|
|
244
|
+
super(status === void 0 ? "Slack webhook unreachable" : `Slack webhook returned ${status}`);
|
|
245
|
+
this.status = status;
|
|
246
|
+
this.cause = cause;
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
/**
|
|
250
|
+
* Post one message to a Slack incoming webhook.
|
|
251
|
+
* @throws {SlackWebhookError} when the request fails or Slack answers with an error status.
|
|
252
|
+
*/
|
|
253
|
+
async function postSlackMessage(webhookUrl, message, fetcher = fetch) {
|
|
254
|
+
let response;
|
|
255
|
+
try {
|
|
256
|
+
response = await fetcher(webhookUrl, {
|
|
257
|
+
method: "POST",
|
|
258
|
+
headers: { "Content-Type": "application/json" },
|
|
259
|
+
body: JSON.stringify(message),
|
|
260
|
+
signal: AbortSignal.timeout(3e4)
|
|
261
|
+
});
|
|
262
|
+
} catch (cause) {
|
|
263
|
+
throw new SlackWebhookError(void 0, cause);
|
|
264
|
+
}
|
|
265
|
+
if (!response.ok) throw new SlackWebhookError(response.status, await response.text());
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
//#endregion
|
|
269
|
+
export { parseCiRun as a, buildCiMessage as i, postSlackMessage as n, CiRunParseError as r, SlackWebhookError as t };
|
|
270
|
+
//# sourceMappingURL=slack-webhook-j1FCPeu8.js.map
|