@polyester/sdk 0.23.1 → 0.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/dist/account-signer/types.d.ts +1 -1
- package/dist/account-signer/types.js.map +1 -1
- package/dist/core-client.d.ts.map +1 -1
- package/dist/core-client.js +18 -45
- package/dist/core-client.js.map +1 -1
- package/dist/gen/orders/v1/orders_read_pb.d.ts +136 -13
- package/dist/gen/orders/v1/orders_read_pb.d.ts.map +1 -1
- package/dist/gen/orders/v1/orders_read_pb.js +22 -16
- package/dist/gen/orders/v1/orders_read_pb.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/realtime/client.js +45 -26
- package/dist/realtime/client.js.map +1 -1
- package/dist/services/auth/account-signer-auth.js +1 -1
- package/dist/services/auth/account-signer-auth.js.map +1 -1
- package/dist/services/candles/candles.schemas.d.ts +10 -10
- package/dist/services/heatmap/heatmap.schemas.d.ts +13 -13
- package/dist/services/market-overview/market-overview.js +5 -4
- package/dist/services/market-overview/market-overview.js.map +1 -1
- package/dist/services/market-overview/market-overview.schemas.d.ts +3 -3
- package/dist/services/orderbook/orderbook.schemas.d.ts +1 -1
- package/dist/services/orders/index.d.ts +4 -3
- package/dist/services/orders/order-lineage.schemas.d.ts +11 -0
- package/dist/services/orders/order-lineage.schemas.d.ts.map +1 -0
- package/dist/services/orders/order-lineage.schemas.js +13 -0
- package/dist/services/orders/order-lineage.schemas.js.map +1 -0
- package/dist/services/orders/orders-batch.schemas.d.ts +14 -14
- package/dist/services/orders/orders-batch.schemas.js +1 -1
- package/dist/services/orders/orders-input.schemas.d.ts +24 -1
- package/dist/services/orders/orders-input.schemas.d.ts.map +1 -1
- package/dist/services/orders/orders-input.schemas.js +5 -2
- package/dist/services/orders/orders-input.schemas.js.map +1 -1
- package/dist/services/orders/orders-modify.schemas.js +1 -1
- package/dist/services/orders/orders-output.schemas.d.ts +63 -20
- package/dist/services/orders/orders-output.schemas.d.ts.map +1 -1
- package/dist/services/orders/orders-output.schemas.js +7 -1
- package/dist/services/orders/orders-output.schemas.js.map +1 -1
- package/dist/services/orders/orders.d.ts +6 -2
- package/dist/services/orders/orders.d.ts.map +1 -1
- package/dist/services/orders/orders.js +6 -2
- package/dist/services/orders/orders.js.map +1 -1
- package/dist/services/orders/orders.schemas.d.ts +4 -3
- package/dist/services/orders/orders.types.d.ts +4 -3
- package/dist/services/subaccounts/subaccounts.schemas.d.ts +5 -5
- package/dist/services/trades/trades.d.ts +4 -0
- package/dist/services/trades/trades.d.ts.map +1 -1
- package/dist/services/trades/trades.js +4 -0
- package/dist/services/trades/trades.js.map +1 -1
- package/dist/services/trades/trades.schemas.d.ts +248 -4
- package/dist/services/trades/trades.schemas.d.ts.map +1 -1
- package/dist/services/trades/trades.schemas.js +52 -12
- package/dist/services/trades/trades.schemas.js.map +1 -1
- package/dist/services/triggers/trigger-input.schemas.d.ts +3 -3
- package/dist/services/triggers/triggers-output.schemas.d.ts +23 -23
- package/dist/shared/connect-error-mapping.d.ts.map +1 -1
- package/dist/shared/connect-error-mapping.js +15 -5
- package/dist/shared/connect-error-mapping.js.map +1 -1
- package/dist/shared/transports.d.ts +5 -1
- package/dist/shared/transports.d.ts.map +1 -1
- package/dist/shared/transports.js.map +1 -1
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +1 -0
- package/dist/utils/errors.js.map +1 -1
- package/package.json +1 -1
|
@@ -31,62 +31,62 @@ declare const CreateTriggerResultSchema: v.SchemaWithPipe<readonly [v.ObjectSche
|
|
|
31
31
|
type CreateTriggerResult = v.InferOutput<typeof CreateTriggerResultSchema>;
|
|
32
32
|
declare const CancelTriggerResultSchema: v.SchemaWithPipe<readonly [v.ObjectSchema<{
|
|
33
33
|
readonly triggerId: v.SchemaWithPipe<readonly [v.BigintSchema<undefined>, v.TransformAction<bigint, string>]>;
|
|
34
|
-
readonly status: v.SchemaWithPipe<readonly [v.EnumSchema<typeof TriggerStatus, undefined>, v.TransformAction<TriggerStatus, "unspecified" | "
|
|
34
|
+
readonly status: v.SchemaWithPipe<readonly [v.EnumSchema<typeof TriggerStatus, undefined>, v.TransformAction<TriggerStatus, "unspecified" | "failed" | "cancelled" | "completed" | "created" | "armed" | "running" | "paused">]>;
|
|
35
35
|
readonly tsNs: v.BigintSchema<undefined>;
|
|
36
36
|
}, undefined>, v.TransformAction<{
|
|
37
37
|
triggerId: string;
|
|
38
|
-
status: "unspecified" | "
|
|
38
|
+
status: "unspecified" | "failed" | "cancelled" | "completed" | "created" | "armed" | "running" | "paused";
|
|
39
39
|
tsNs: bigint;
|
|
40
40
|
}, {
|
|
41
41
|
ts: number;
|
|
42
42
|
tsNs: string;
|
|
43
43
|
triggerId: string;
|
|
44
|
-
status: "unspecified" | "
|
|
44
|
+
status: "unspecified" | "failed" | "cancelled" | "completed" | "created" | "armed" | "running" | "paused";
|
|
45
45
|
}>]>;
|
|
46
46
|
type CancelTriggerResult = v.InferOutput<typeof CancelTriggerResultSchema>;
|
|
47
47
|
declare const ModifyTriggerResultSchema: v.SchemaWithPipe<readonly [v.ObjectSchema<{
|
|
48
48
|
readonly triggerId: v.SchemaWithPipe<readonly [v.BigintSchema<undefined>, v.TransformAction<bigint, string>]>;
|
|
49
|
-
readonly status: v.SchemaWithPipe<readonly [v.EnumSchema<typeof TriggerStatus, undefined>, v.TransformAction<TriggerStatus, "unspecified" | "
|
|
49
|
+
readonly status: v.SchemaWithPipe<readonly [v.EnumSchema<typeof TriggerStatus, undefined>, v.TransformAction<TriggerStatus, "unspecified" | "failed" | "cancelled" | "completed" | "created" | "armed" | "running" | "paused">]>;
|
|
50
50
|
readonly tsNs: v.BigintSchema<undefined>;
|
|
51
51
|
}, undefined>, v.TransformAction<{
|
|
52
52
|
triggerId: string;
|
|
53
|
-
status: "unspecified" | "
|
|
53
|
+
status: "unspecified" | "failed" | "cancelled" | "completed" | "created" | "armed" | "running" | "paused";
|
|
54
54
|
tsNs: bigint;
|
|
55
55
|
}, {
|
|
56
56
|
ts: number;
|
|
57
57
|
tsNs: string;
|
|
58
58
|
triggerId: string;
|
|
59
|
-
status: "unspecified" | "
|
|
59
|
+
status: "unspecified" | "failed" | "cancelled" | "completed" | "created" | "armed" | "running" | "paused";
|
|
60
60
|
}>]>;
|
|
61
61
|
type ModifyTriggerResult = v.InferOutput<typeof ModifyTriggerResultSchema>;
|
|
62
62
|
declare const PauseTriggerResultSchema: v.SchemaWithPipe<readonly [v.ObjectSchema<{
|
|
63
63
|
readonly triggerId: v.SchemaWithPipe<readonly [v.BigintSchema<undefined>, v.TransformAction<bigint, string>]>;
|
|
64
|
-
readonly status: v.SchemaWithPipe<readonly [v.EnumSchema<typeof TriggerStatus, undefined>, v.TransformAction<TriggerStatus, "unspecified" | "
|
|
64
|
+
readonly status: v.SchemaWithPipe<readonly [v.EnumSchema<typeof TriggerStatus, undefined>, v.TransformAction<TriggerStatus, "unspecified" | "failed" | "cancelled" | "completed" | "created" | "armed" | "running" | "paused">]>;
|
|
65
65
|
readonly tsNs: v.BigintSchema<undefined>;
|
|
66
66
|
}, undefined>, v.TransformAction<{
|
|
67
67
|
triggerId: string;
|
|
68
|
-
status: "unspecified" | "
|
|
68
|
+
status: "unspecified" | "failed" | "cancelled" | "completed" | "created" | "armed" | "running" | "paused";
|
|
69
69
|
tsNs: bigint;
|
|
70
70
|
}, {
|
|
71
71
|
ts: number;
|
|
72
72
|
tsNs: string;
|
|
73
73
|
triggerId: string;
|
|
74
|
-
status: "unspecified" | "
|
|
74
|
+
status: "unspecified" | "failed" | "cancelled" | "completed" | "created" | "armed" | "running" | "paused";
|
|
75
75
|
}>]>;
|
|
76
76
|
type PauseTriggerResult = v.InferOutput<typeof PauseTriggerResultSchema>;
|
|
77
77
|
declare const ResumeTriggerResultSchema: v.SchemaWithPipe<readonly [v.ObjectSchema<{
|
|
78
78
|
readonly triggerId: v.SchemaWithPipe<readonly [v.BigintSchema<undefined>, v.TransformAction<bigint, string>]>;
|
|
79
|
-
readonly status: v.SchemaWithPipe<readonly [v.EnumSchema<typeof TriggerStatus, undefined>, v.TransformAction<TriggerStatus, "unspecified" | "
|
|
79
|
+
readonly status: v.SchemaWithPipe<readonly [v.EnumSchema<typeof TriggerStatus, undefined>, v.TransformAction<TriggerStatus, "unspecified" | "failed" | "cancelled" | "completed" | "created" | "armed" | "running" | "paused">]>;
|
|
80
80
|
readonly tsNs: v.BigintSchema<undefined>;
|
|
81
81
|
}, undefined>, v.TransformAction<{
|
|
82
82
|
triggerId: string;
|
|
83
|
-
status: "unspecified" | "
|
|
83
|
+
status: "unspecified" | "failed" | "cancelled" | "completed" | "created" | "armed" | "running" | "paused";
|
|
84
84
|
tsNs: bigint;
|
|
85
85
|
}, {
|
|
86
86
|
ts: number;
|
|
87
87
|
tsNs: string;
|
|
88
88
|
triggerId: string;
|
|
89
|
-
status: "unspecified" | "
|
|
89
|
+
status: "unspecified" | "failed" | "cancelled" | "completed" | "created" | "armed" | "running" | "paused";
|
|
90
90
|
}>]>;
|
|
91
91
|
type ResumeTriggerResult = v.InferOutput<typeof ResumeTriggerResultSchema>;
|
|
92
92
|
type StopDetailsOutput = {
|
|
@@ -553,12 +553,12 @@ declare function createTriggerSchema(scales: SdkScales): v.SchemaWithPipe<readon
|
|
|
553
553
|
};
|
|
554
554
|
}, {
|
|
555
555
|
runtimeDetails: TriggerDetailsOutput;
|
|
556
|
-
cancelReason: "unspecified" | "
|
|
556
|
+
cancelReason: "unspecified" | "oco" | "user_request" | "parent_canceled_no_fill" | "missing_reason_code" | "internal_error";
|
|
557
557
|
failureReason: undefined;
|
|
558
558
|
triggerId: string;
|
|
559
559
|
subaccountId: string;
|
|
560
560
|
symbolId: number;
|
|
561
|
-
status: "unspecified" | "
|
|
561
|
+
status: "unspecified" | "failed" | "cancelled" | "completed" | "created" | "armed" | "running" | "paused";
|
|
562
562
|
parentOrderId: string | undefined;
|
|
563
563
|
qty: string;
|
|
564
564
|
feeAsset: "unspecified" | "quote" | "base";
|
|
@@ -714,7 +714,7 @@ declare function createTriggerSchema(scales: SdkScales): v.SchemaWithPipe<readon
|
|
|
714
714
|
triggerId: string;
|
|
715
715
|
subaccountId: string;
|
|
716
716
|
symbolId: number;
|
|
717
|
-
status: "unspecified" | "
|
|
717
|
+
status: "unspecified" | "failed" | "cancelled" | "completed" | "created" | "armed" | "running" | "paused";
|
|
718
718
|
parentOrderId: string | undefined;
|
|
719
719
|
qty: string;
|
|
720
720
|
feeAsset: "unspecified" | "quote" | "base";
|
|
@@ -870,7 +870,7 @@ declare function createTriggerSchema(scales: SdkScales): v.SchemaWithPipe<readon
|
|
|
870
870
|
triggerId: string;
|
|
871
871
|
subaccountId: string;
|
|
872
872
|
symbolId: number;
|
|
873
|
-
status: "unspecified" | "
|
|
873
|
+
status: "unspecified" | "failed" | "cancelled" | "completed" | "created" | "armed" | "running" | "paused";
|
|
874
874
|
parentOrderId: string | undefined;
|
|
875
875
|
qty: string;
|
|
876
876
|
feeAsset: "unspecified" | "quote" | "base";
|
|
@@ -1063,13 +1063,13 @@ declare function createTriggerEventSchema(scales: SdkScales): v.SchemaWithPipe<r
|
|
|
1063
1063
|
value?: undefined;
|
|
1064
1064
|
};
|
|
1065
1065
|
}, {
|
|
1066
|
-
cancelReason: "unspecified" | "
|
|
1066
|
+
cancelReason: "unspecified" | "oco" | "user_request" | "parent_canceled_no_fill" | "missing_reason_code" | "internal_error";
|
|
1067
1067
|
failureReason: undefined;
|
|
1068
1068
|
triggerId: string;
|
|
1069
1069
|
subaccountId: string;
|
|
1070
1070
|
symbolId: number;
|
|
1071
|
-
triggerType: "unspecified" | "
|
|
1072
|
-
eventType: "unspecified" | "
|
|
1071
|
+
triggerType: "unspecified" | "stop_loss" | "take_profit" | "trailing_stop" | "twap" | "ladder";
|
|
1072
|
+
eventType: "unspecified" | "failed" | "updated" | "canceled" | "fired";
|
|
1073
1073
|
ts: number;
|
|
1074
1074
|
childSeq: number;
|
|
1075
1075
|
childOrderId: string | undefined;
|
|
@@ -1080,8 +1080,8 @@ declare function createTriggerEventSchema(scales: SdkScales): v.SchemaWithPipe<r
|
|
|
1080
1080
|
triggerId: string;
|
|
1081
1081
|
subaccountId: string;
|
|
1082
1082
|
symbolId: number;
|
|
1083
|
-
triggerType: "unspecified" | "
|
|
1084
|
-
eventType: "unspecified" | "
|
|
1083
|
+
triggerType: "unspecified" | "stop_loss" | "take_profit" | "trailing_stop" | "twap" | "ladder";
|
|
1084
|
+
eventType: "unspecified" | "failed" | "updated" | "canceled" | "fired";
|
|
1085
1085
|
ts: number;
|
|
1086
1086
|
childSeq: number;
|
|
1087
1087
|
childOrderId: string | undefined;
|
|
@@ -1092,8 +1092,8 @@ declare function createTriggerEventSchema(scales: SdkScales): v.SchemaWithPipe<r
|
|
|
1092
1092
|
triggerId: string;
|
|
1093
1093
|
subaccountId: string;
|
|
1094
1094
|
symbolId: number;
|
|
1095
|
-
triggerType: "unspecified" | "
|
|
1096
|
-
eventType: "unspecified" | "
|
|
1095
|
+
triggerType: "unspecified" | "stop_loss" | "take_profit" | "trailing_stop" | "twap" | "ladder";
|
|
1096
|
+
eventType: "unspecified" | "failed" | "updated" | "canceled" | "fired";
|
|
1097
1097
|
ts: number;
|
|
1098
1098
|
childSeq: number;
|
|
1099
1099
|
childOrderId: string | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connect-error-mapping.d.ts","names":[],"sources":["../../src/shared/connect-error-mapping.ts"],"mappings":";;;;;;;iBA+HgB,6BAA6B,IAAI,eAAe;;;;;;iBAsJhD,iBAAiB;;;;;;
|
|
1
|
+
{"version":3,"file":"connect-error-mapping.d.ts","names":[],"sources":["../../src/shared/connect-error-mapping.ts"],"mappings":";;;;;;;iBA+HgB,6BAA6B,IAAI,eAAe;;;;;;iBAsJhD,iBAAiB;;;;;;iBA0CjB,iCAAiC"}
|
|
@@ -146,11 +146,19 @@ function toPolyesterError(err) {
|
|
|
146
146
|
}
|
|
147
147
|
return err;
|
|
148
148
|
}
|
|
149
|
-
|
|
149
|
+
function callerAbortError(signal) {
|
|
150
|
+
const reason = signal.reason;
|
|
151
|
+
return reason instanceof DOMException && reason.name === "AbortError" ? reason : new DOMException("Request canceled.", "AbortError");
|
|
152
|
+
}
|
|
153
|
+
function toTransportError(err, signal) {
|
|
154
|
+
if (signal?.aborted && err instanceof ConnectError && err.code === Code.Canceled) return callerAbortError(signal);
|
|
155
|
+
return toPolyesterError(err);
|
|
156
|
+
}
|
|
157
|
+
async function* mapStreamErrors(source, signal) {
|
|
150
158
|
try {
|
|
151
159
|
yield* source;
|
|
152
160
|
} catch (err) {
|
|
153
|
-
throw
|
|
161
|
+
throw toTransportError(err, signal);
|
|
154
162
|
}
|
|
155
163
|
}
|
|
156
164
|
/**
|
|
@@ -179,21 +187,23 @@ function createErrorMappingInterceptor() {
|
|
|
179
187
|
function createErrorMappingTransport(transport) {
|
|
180
188
|
return {
|
|
181
189
|
async unary(method, signal, timeoutMs, header, input, contextValues) {
|
|
190
|
+
if (signal?.aborted) throw callerAbortError(signal);
|
|
182
191
|
try {
|
|
183
192
|
return await transport.unary(method, signal, timeoutMs, header, input, contextValues);
|
|
184
193
|
} catch (error) {
|
|
185
|
-
throw
|
|
194
|
+
throw toTransportError(error, signal);
|
|
186
195
|
}
|
|
187
196
|
},
|
|
188
197
|
async stream(method, signal, timeoutMs, header, input, contextValues) {
|
|
198
|
+
if (signal?.aborted) throw callerAbortError(signal);
|
|
189
199
|
try {
|
|
190
200
|
const response = await transport.stream(method, signal, timeoutMs, header, input, contextValues);
|
|
191
201
|
return {
|
|
192
202
|
...response,
|
|
193
|
-
message: mapStreamErrors(response.message)
|
|
203
|
+
message: mapStreamErrors(response.message, signal)
|
|
194
204
|
};
|
|
195
205
|
} catch (error) {
|
|
196
|
-
throw
|
|
206
|
+
throw toTransportError(error, signal);
|
|
197
207
|
}
|
|
198
208
|
}
|
|
199
209
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connect-error-mapping.js","names":[],"sources":["../../src/shared/connect-error-mapping.ts"],"sourcesContent":["import { Code, ConnectError, type Interceptor, type Transport } from \"@connectrpc/connect\";\nimport { parseConnectErrorDetail, type PolyesterErrorDetail } from \"./error-detail.js\";\nimport {\n AlreadyExistsError,\n AuthenticationError,\n InternalServerError,\n isAbortError,\n MfaEnrollmentRequiredError,\n MfaLastFactorRequiredError,\n MfaVerificationError,\n type MfaVerificationFailureReason,\n normalizeErrorMessage,\n PermissionError,\n PolicyInUseError,\n PolicyLockedError,\n PolicyScopeMismatchError,\n PolyesterError,\n type PolyesterErrorOptions,\n PreconditionFailedError,\n RateLimitError,\n ResourceNotFoundError,\n RevisionConflictError,\n ServiceUnavailableError,\n SessionElevationRequiredError,\n StaleQuoteError,\n StepUpRequiredError,\n TimeoutError,\n TransientError,\n ValidationError,\n} from \"./errors.js\";\nimport type { RateLimitDetail } from \"./rate-limit.schemas.js\";\n\nfunction hasTransientTransferError(detail: PolyesterErrorDetail | undefined): boolean {\n if (detail?.service === \"withdraw\") {\n return [\n \"SERVICE_UNAVAILABLE\",\n \"SOURCE_SMART_ACCOUNT_UNAVAILABLE\",\n \"CAPITAL_VIEW_UNAVAILABLE\",\n \"CHAIN_METADATA_UNAVAILABLE\",\n \"FEE_UNAVAILABLE\",\n \"SUPPLY_UNAVAILABLE\",\n \"STEP_UP_UNAVAILABLE\",\n \"DESTINATION_VALIDATION_UNAVAILABLE\",\n \"ACCOUNT_SHARD_UNAVAILABLE\",\n ].includes(detail.code);\n }\n if (detail?.service === \"internal_transfer\") {\n return [\n \"SERVICE_UNAVAILABLE\",\n \"SMART_ACCOUNT_UNAVAILABLE\",\n \"STEP_UP_UNAVAILABLE\",\n \"CAPITAL_VIEW_UNAVAILABLE\",\n \"ACCOUNT_SHARD_UNAVAILABLE\",\n ].includes(detail.code);\n }\n return false;\n}\n\nfunction safeRetryAfterMs(rateLimit: RateLimitDetail | undefined): number | undefined {\n if (rateLimit?.retryAfterMs === undefined) return undefined;\n const value = BigInt(rateLimit.retryAfterMs);\n return value <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(value) : undefined;\n}\n\nconst HTTP_WEEKDAY = \"(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\";\nconst HTTP_WEEKDAY_LONG = \"(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)\";\nconst HTTP_MONTH = \"(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\";\nconst HTTP_DATE_RE = new RegExp(\n `^(?:${HTTP_WEEKDAY}, \\\\d{2} ${HTTP_MONTH} \\\\d{4} \\\\d{2}:\\\\d{2}:\\\\d{2} GMT|${HTTP_WEEKDAY_LONG}, \\\\d{2}-${HTTP_MONTH}-\\\\d{2} \\\\d{2}:\\\\d{2}:\\\\d{2} GMT|${HTTP_WEEKDAY} ${HTTP_MONTH} {1,2}\\\\d{1,2} \\\\d{2}:\\\\d{2}:\\\\d{2} \\\\d{4})$`,\n \"u\",\n);\n\n/**\n * Returns the error message without the leading \"[code] \" prefix that\n * `ConnectError` prepends, e.g. \"[invalid_argument] Insufficient funds.\"\n * becomes \"Insufficient funds.\".\n */\nexport function getNormalizedConnectMessage(err: unknown): string {\n const ce = ConnectError.from(err);\n const raw = ce.message ?? \"\";\n return normalizeErrorMessage(raw);\n}\n\nexport type MfaErrorKind = \"session-elevation\" | \"enrollment\" | \"step-up\";\n\n/**\n * Classifies an error as one of the MFA flows the backend can demand.\n * Classification is based only on stable structured auth error details.\n */\nexport function detectMfaErrorKind(err: unknown): MfaErrorKind | null {\n const mapped = toPolyesterError(err);\n return mfaErrorKind(mapped instanceof PolyesterError ? mapped.detail : undefined);\n}\n\nfunction mfaErrorKind(detail: PolyesterErrorDetail | undefined): MfaErrorKind | null {\n if (detail?.service === \"auth\" && detail.code === \"AUTH_MFA_ELEVATION_REQUIRED\") {\n return \"session-elevation\";\n }\n if (detail?.service === \"auth\" && detail.code === \"AUTH_STEP_UP_REQUIRED\") return \"step-up\";\n if (detail?.service === \"auth\" && detail.code === \"AUTH_MFA_NOT_ENROLLED\") return \"enrollment\";\n return null;\n}\n\nfunction parseRetryAfterMs(ce: ConnectError): number | undefined {\n const value = ce.metadata.get(\"retry-after\");\n if (!value) return undefined;\n if (/^\\d+$/u.test(value)) {\n const seconds = BigInt(value);\n const maxSafeSeconds = BigInt(Math.floor(Number.MAX_SAFE_INTEGER / 1000));\n return seconds <= maxSafeSeconds ? Number(seconds) * 1000 : undefined;\n }\n if (!HTTP_DATE_RE.test(value)) return undefined;\n const date = Date.parse(value);\n if (!Number.isNaN(date)) return Math.max(0, date - Date.now());\n return undefined;\n}\n\nconst MFA_ERROR_CLASSES = {\n \"session-elevation\": SessionElevationRequiredError,\n enrollment: MfaEnrollmentRequiredError,\n \"step-up\": StepUpRequiredError,\n} as const;\n\n/**\n * Maps a `ConnectError` from the Polyester backend onto the typed\n * {@link PolyesterError} tree. The original error is preserved as `cause`.\n */\nexport function connectErrorToPolyesterError(ce: ConnectError): PolyesterError {\n const message = getNormalizedConnectMessage(ce);\n const detail = parseConnectErrorDetail(ce);\n const options: PolyesterErrorOptions = { cause: ce, detail };\n const withFallback = (fallback: string) => message || fallback;\n\n if (detail?.service === \"auth\" && detail.code === \"AUTH_RESOURCE_NOT_FOUND\") {\n return new ResourceNotFoundError(withFallback(\"Resource not found.\"), options);\n }\n\n if (detail?.service === \"auth\" && detail.code === \"AUTH_REVISION_CONFLICT\") {\n return new RevisionConflictError(\n withFallback(\"Resource changed since it was last read.\"),\n options,\n );\n }\n\n if (detail?.service === \"auth\" && detail.code === \"AUTH_POLICY_IN_USE\") {\n return new PolicyInUseError(withFallback(\"Policy is still in use.\"), options);\n }\n if (detail?.service === \"auth\" && detail.code === \"AUTH_POLICY_LOCKED\") {\n return new PolicyLockedError(withFallback(\"Policy is locked.\"), options);\n }\n if (detail?.service === \"auth\" && detail.code === \"AUTH_POLICY_SCOPE_MISMATCH\") {\n return new PolicyScopeMismatchError(\n withFallback(\"Policy does not belong to the target account scope.\"),\n options,\n );\n }\n if (detail?.service === \"auth\" && detail.code === \"AUTH_MFA_LAST_FACTOR_REQUIRED\") {\n return new MfaLastFactorRequiredError(\n withFallback(\"At least one active MFA factor must remain enrolled.\"),\n options,\n );\n }\n if (detail?.service === \"auth\" && detail.code === \"AUTH_INTERNAL_ERROR\") {\n return new InternalServerError(withFallback(\"Internal server error.\"), options);\n }\n\n const mfaVerificationReason = getMfaVerificationFailureReason(detail);\n if (mfaVerificationReason) {\n return new MfaVerificationError(\n withFallback(\"MFA verification failed.\"),\n mfaVerificationReason,\n options,\n );\n }\n\n const mfaKind = mfaErrorKind(detail);\n if (mfaKind) {\n return new MFA_ERROR_CLASSES[mfaKind](\n withFallback(\"Multi-factor authentication required.\"),\n options,\n );\n }\n\n if (detail?.service === \"orders\" && detail.code === \"STALE_QUOTE\") {\n return new StaleQuoteError(withFallback(\"The submitted market quote is stale.\"), options);\n }\n\n const rateLimit = detail?.service === \"orders\" ? detail.rateLimit : undefined;\n if (\n rateLimit ||\n ((detail?.service === \"orders\" ||\n detail?.service === \"withdraw\" ||\n detail?.service === \"internal_transfer\") &&\n detail.code === \"RATE_LIMIT_EXCEEDED\")\n ) {\n return new RateLimitError(withFallback(\"Rate limit exceeded.\"), {\n ...options,\n rateLimit,\n retryAfterMs: safeRetryAfterMs(rateLimit) ?? parseRetryAfterMs(ce),\n });\n }\n\n if (hasTransientTransferError(detail)) {\n return new ServiceUnavailableError(withFallback(\"Service unavailable.\"), options);\n }\n\n switch (ce.code) {\n case Code.InvalidArgument:\n case Code.OutOfRange:\n return new ValidationError(withFallback(\"Invalid request.\"), options);\n case Code.NotFound:\n return new ResourceNotFoundError(withFallback(\"Resource not found.\"), options);\n case Code.AlreadyExists:\n return new AlreadyExistsError(withFallback(\"Resource already exists.\"), options);\n case Code.PermissionDenied:\n return new PermissionError(withFallback(\"Permission denied.\"), options);\n case Code.Unauthenticated:\n return new AuthenticationError(withFallback(\"Authentication required.\"), options);\n case Code.FailedPrecondition:\n return new PreconditionFailedError(withFallback(\"Precondition failed.\"), options);\n case Code.ResourceExhausted:\n return new RateLimitError(withFallback(\"Rate limit exceeded.\"), {\n ...options,\n retryAfterMs: parseRetryAfterMs(ce),\n });\n case Code.DeadlineExceeded:\n return new TimeoutError(withFallback(\"Request timed out.\"), options);\n case Code.Unavailable:\n return new ServiceUnavailableError(withFallback(\"Service unavailable.\"), options);\n case Code.Aborted:\n return new TransientError(withFallback(\"Operation aborted by the backend.\"), options);\n default:\n // Gateways sometimes surface outages as Unknown with this stock phrase.\n if (/service temporarily unavailable/i.test(message)) {\n return new ServiceUnavailableError(withFallback(\"Service unavailable.\"), options);\n }\n return new InternalServerError(withFallback(\"Internal server error.\"), options);\n }\n}\n\nfunction getMfaVerificationFailureReason(\n detail: PolyesterErrorDetail | undefined,\n): MfaVerificationFailureReason | null {\n if (detail?.service === \"auth\" && detail.code === \"AUTH_MFA_CHALLENGE_INVALID\") {\n return \"challenge-invalid\";\n }\n if (detail?.service === \"auth\" && detail.code === \"AUTH_MFA_CHALLENGE_LOCKED\") {\n return \"challenge-locked\";\n }\n if (detail?.service === \"auth\" && detail.code === \"AUTH_MFA_OTP_INVALID\") return \"otp-invalid\";\n if (detail?.service === \"auth\" && detail.code === \"AUTH_MFA_RECOVERY_INVALID\") {\n return \"recovery-code-invalid\";\n }\n if (\n (detail?.service === \"auth\" && detail.code === \"AUTH_MFA_PASSKEY_CREDENTIAL_INVALID\") ||\n (detail?.service === \"auth\" && detail.code === \"AUTH_MFA_PASSKEY_VERIFY_FAILED\")\n ) {\n return \"passkey-invalid\";\n }\n\n return null;\n}\n\nfunction findPolyesterErrorInCauseChain(err: unknown): PolyesterError | null {\n let current: unknown = err;\n for (let depth = 0; depth < 5 && current instanceof Error; depth++) {\n current = current.cause;\n if (current instanceof PolyesterError) return current;\n }\n return null;\n}\n\n/**\n * Converts any RPC-layer failure into its typed SDK error. Abort errors and\n * caller-cancelled requests pass through unchanged, as do errors that are\n * already typed (e.g. a `NetworkError` from the SDK fetch wrapper).\n */\nexport function toPolyesterError(err: unknown): unknown {\n if (err instanceof PolyesterError) return err;\n if (isAbortError(err)) return err;\n if (err instanceof ConnectError) {\n if (err.code === Code.Canceled) return err;\n const wrapped = findPolyesterErrorInCauseChain(err);\n if (wrapped) return wrapped;\n return connectErrorToPolyesterError(err);\n }\n return err;\n}\n\nasync function* mapStreamErrors<T>(source: AsyncIterable<T>): AsyncIterable<T> {\n try {\n yield* source;\n } catch (err) {\n throw toPolyesterError(err);\n }\n}\n\n/**\n * Interceptor that converts every RPC failure (unary and streaming) into the\n * typed {@link PolyesterError} hierarchy. Installed outermost by\n * `createTransports`, so callers of SDK services always see typed errors.\n */\nexport function createErrorMappingInterceptor(): Interceptor {\n return (next) => async (req) => {\n try {\n const res = await next(req);\n if (res.stream) {\n return { ...res, message: mapStreamErrors(res.message) };\n }\n return res;\n } catch (err) {\n throw toPolyesterError(err);\n }\n };\n}\n\n/**\n * Wraps a Connect transport so errors are translated after Connect's call\n * runner has applied its own error normalization.\n */\nexport function createErrorMappingTransport(transport: Transport): Transport {\n return {\n async unary(method, signal, timeoutMs, header, input, contextValues) {\n try {\n return await transport.unary(\n method,\n signal,\n timeoutMs,\n header,\n input,\n contextValues,\n );\n } catch (error) {\n throw toPolyesterError(error);\n }\n },\n async stream(method, signal, timeoutMs, header, input, contextValues) {\n try {\n const response = await transport.stream(\n method,\n signal,\n timeoutMs,\n header,\n input,\n contextValues,\n );\n return { ...response, message: mapStreamErrors(response.message) };\n } catch (error) {\n throw toPolyesterError(error);\n }\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"connect-error-mapping.js","names":[],"sources":["../../src/shared/connect-error-mapping.ts"],"sourcesContent":["import { Code, ConnectError, type Interceptor, type Transport } from \"@connectrpc/connect\";\nimport { parseConnectErrorDetail, type PolyesterErrorDetail } from \"./error-detail.js\";\nimport {\n AlreadyExistsError,\n AuthenticationError,\n InternalServerError,\n isAbortError,\n MfaEnrollmentRequiredError,\n MfaLastFactorRequiredError,\n MfaVerificationError,\n type MfaVerificationFailureReason,\n normalizeErrorMessage,\n PermissionError,\n PolicyInUseError,\n PolicyLockedError,\n PolicyScopeMismatchError,\n PolyesterError,\n type PolyesterErrorOptions,\n PreconditionFailedError,\n RateLimitError,\n ResourceNotFoundError,\n RevisionConflictError,\n ServiceUnavailableError,\n SessionElevationRequiredError,\n StaleQuoteError,\n StepUpRequiredError,\n TimeoutError,\n TransientError,\n ValidationError,\n} from \"./errors.js\";\nimport type { RateLimitDetail } from \"./rate-limit.schemas.js\";\n\nfunction hasTransientTransferError(detail: PolyesterErrorDetail | undefined): boolean {\n if (detail?.service === \"withdraw\") {\n return [\n \"SERVICE_UNAVAILABLE\",\n \"SOURCE_SMART_ACCOUNT_UNAVAILABLE\",\n \"CAPITAL_VIEW_UNAVAILABLE\",\n \"CHAIN_METADATA_UNAVAILABLE\",\n \"FEE_UNAVAILABLE\",\n \"SUPPLY_UNAVAILABLE\",\n \"STEP_UP_UNAVAILABLE\",\n \"DESTINATION_VALIDATION_UNAVAILABLE\",\n \"ACCOUNT_SHARD_UNAVAILABLE\",\n ].includes(detail.code);\n }\n if (detail?.service === \"internal_transfer\") {\n return [\n \"SERVICE_UNAVAILABLE\",\n \"SMART_ACCOUNT_UNAVAILABLE\",\n \"STEP_UP_UNAVAILABLE\",\n \"CAPITAL_VIEW_UNAVAILABLE\",\n \"ACCOUNT_SHARD_UNAVAILABLE\",\n ].includes(detail.code);\n }\n return false;\n}\n\nfunction safeRetryAfterMs(rateLimit: RateLimitDetail | undefined): number | undefined {\n if (rateLimit?.retryAfterMs === undefined) return undefined;\n const value = BigInt(rateLimit.retryAfterMs);\n return value <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(value) : undefined;\n}\n\nconst HTTP_WEEKDAY = \"(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\";\nconst HTTP_WEEKDAY_LONG = \"(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)\";\nconst HTTP_MONTH = \"(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\";\nconst HTTP_DATE_RE = new RegExp(\n `^(?:${HTTP_WEEKDAY}, \\\\d{2} ${HTTP_MONTH} \\\\d{4} \\\\d{2}:\\\\d{2}:\\\\d{2} GMT|${HTTP_WEEKDAY_LONG}, \\\\d{2}-${HTTP_MONTH}-\\\\d{2} \\\\d{2}:\\\\d{2}:\\\\d{2} GMT|${HTTP_WEEKDAY} ${HTTP_MONTH} {1,2}\\\\d{1,2} \\\\d{2}:\\\\d{2}:\\\\d{2} \\\\d{4})$`,\n \"u\",\n);\n\n/**\n * Returns the error message without the leading \"[code] \" prefix that\n * `ConnectError` prepends, e.g. \"[invalid_argument] Insufficient funds.\"\n * becomes \"Insufficient funds.\".\n */\nexport function getNormalizedConnectMessage(err: unknown): string {\n const ce = ConnectError.from(err);\n const raw = ce.message ?? \"\";\n return normalizeErrorMessage(raw);\n}\n\nexport type MfaErrorKind = \"session-elevation\" | \"enrollment\" | \"step-up\";\n\n/**\n * Classifies an error as one of the MFA flows the backend can demand.\n * Classification is based only on stable structured auth error details.\n */\nexport function detectMfaErrorKind(err: unknown): MfaErrorKind | null {\n const mapped = toPolyesterError(err);\n return mfaErrorKind(mapped instanceof PolyesterError ? mapped.detail : undefined);\n}\n\nfunction mfaErrorKind(detail: PolyesterErrorDetail | undefined): MfaErrorKind | null {\n if (detail?.service === \"auth\" && detail.code === \"AUTH_MFA_ELEVATION_REQUIRED\") {\n return \"session-elevation\";\n }\n if (detail?.service === \"auth\" && detail.code === \"AUTH_STEP_UP_REQUIRED\") return \"step-up\";\n if (detail?.service === \"auth\" && detail.code === \"AUTH_MFA_NOT_ENROLLED\") return \"enrollment\";\n return null;\n}\n\nfunction parseRetryAfterMs(ce: ConnectError): number | undefined {\n const value = ce.metadata.get(\"retry-after\");\n if (!value) return undefined;\n if (/^\\d+$/u.test(value)) {\n const seconds = BigInt(value);\n const maxSafeSeconds = BigInt(Math.floor(Number.MAX_SAFE_INTEGER / 1000));\n return seconds <= maxSafeSeconds ? Number(seconds) * 1000 : undefined;\n }\n if (!HTTP_DATE_RE.test(value)) return undefined;\n const date = Date.parse(value);\n if (!Number.isNaN(date)) return Math.max(0, date - Date.now());\n return undefined;\n}\n\nconst MFA_ERROR_CLASSES = {\n \"session-elevation\": SessionElevationRequiredError,\n enrollment: MfaEnrollmentRequiredError,\n \"step-up\": StepUpRequiredError,\n} as const;\n\n/**\n * Maps a `ConnectError` from the Polyester backend onto the typed\n * {@link PolyesterError} tree. The original error is preserved as `cause`.\n */\nexport function connectErrorToPolyesterError(ce: ConnectError): PolyesterError {\n const message = getNormalizedConnectMessage(ce);\n const detail = parseConnectErrorDetail(ce);\n const options: PolyesterErrorOptions = { cause: ce, detail };\n const withFallback = (fallback: string) => message || fallback;\n\n if (detail?.service === \"auth\" && detail.code === \"AUTH_RESOURCE_NOT_FOUND\") {\n return new ResourceNotFoundError(withFallback(\"Resource not found.\"), options);\n }\n\n if (detail?.service === \"auth\" && detail.code === \"AUTH_REVISION_CONFLICT\") {\n return new RevisionConflictError(\n withFallback(\"Resource changed since it was last read.\"),\n options,\n );\n }\n\n if (detail?.service === \"auth\" && detail.code === \"AUTH_POLICY_IN_USE\") {\n return new PolicyInUseError(withFallback(\"Policy is still in use.\"), options);\n }\n if (detail?.service === \"auth\" && detail.code === \"AUTH_POLICY_LOCKED\") {\n return new PolicyLockedError(withFallback(\"Policy is locked.\"), options);\n }\n if (detail?.service === \"auth\" && detail.code === \"AUTH_POLICY_SCOPE_MISMATCH\") {\n return new PolicyScopeMismatchError(\n withFallback(\"Policy does not belong to the target account scope.\"),\n options,\n );\n }\n if (detail?.service === \"auth\" && detail.code === \"AUTH_MFA_LAST_FACTOR_REQUIRED\") {\n return new MfaLastFactorRequiredError(\n withFallback(\"At least one active MFA factor must remain enrolled.\"),\n options,\n );\n }\n if (detail?.service === \"auth\" && detail.code === \"AUTH_INTERNAL_ERROR\") {\n return new InternalServerError(withFallback(\"Internal server error.\"), options);\n }\n\n const mfaVerificationReason = getMfaVerificationFailureReason(detail);\n if (mfaVerificationReason) {\n return new MfaVerificationError(\n withFallback(\"MFA verification failed.\"),\n mfaVerificationReason,\n options,\n );\n }\n\n const mfaKind = mfaErrorKind(detail);\n if (mfaKind) {\n return new MFA_ERROR_CLASSES[mfaKind](\n withFallback(\"Multi-factor authentication required.\"),\n options,\n );\n }\n\n if (detail?.service === \"orders\" && detail.code === \"STALE_QUOTE\") {\n return new StaleQuoteError(withFallback(\"The submitted market quote is stale.\"), options);\n }\n\n const rateLimit = detail?.service === \"orders\" ? detail.rateLimit : undefined;\n if (\n rateLimit ||\n ((detail?.service === \"orders\" ||\n detail?.service === \"withdraw\" ||\n detail?.service === \"internal_transfer\") &&\n detail.code === \"RATE_LIMIT_EXCEEDED\")\n ) {\n return new RateLimitError(withFallback(\"Rate limit exceeded.\"), {\n ...options,\n rateLimit,\n retryAfterMs: safeRetryAfterMs(rateLimit) ?? parseRetryAfterMs(ce),\n });\n }\n\n if (hasTransientTransferError(detail)) {\n return new ServiceUnavailableError(withFallback(\"Service unavailable.\"), options);\n }\n\n switch (ce.code) {\n case Code.InvalidArgument:\n case Code.OutOfRange:\n return new ValidationError(withFallback(\"Invalid request.\"), options);\n case Code.NotFound:\n return new ResourceNotFoundError(withFallback(\"Resource not found.\"), options);\n case Code.AlreadyExists:\n return new AlreadyExistsError(withFallback(\"Resource already exists.\"), options);\n case Code.PermissionDenied:\n return new PermissionError(withFallback(\"Permission denied.\"), options);\n case Code.Unauthenticated:\n return new AuthenticationError(withFallback(\"Authentication required.\"), options);\n case Code.FailedPrecondition:\n return new PreconditionFailedError(withFallback(\"Precondition failed.\"), options);\n case Code.ResourceExhausted:\n return new RateLimitError(withFallback(\"Rate limit exceeded.\"), {\n ...options,\n retryAfterMs: parseRetryAfterMs(ce),\n });\n case Code.DeadlineExceeded:\n return new TimeoutError(withFallback(\"Request timed out.\"), options);\n case Code.Unavailable:\n return new ServiceUnavailableError(withFallback(\"Service unavailable.\"), options);\n case Code.Aborted:\n return new TransientError(withFallback(\"Operation aborted by the backend.\"), options);\n default:\n // Gateways sometimes surface outages as Unknown with this stock phrase.\n if (/service temporarily unavailable/i.test(message)) {\n return new ServiceUnavailableError(withFallback(\"Service unavailable.\"), options);\n }\n return new InternalServerError(withFallback(\"Internal server error.\"), options);\n }\n}\n\nfunction getMfaVerificationFailureReason(\n detail: PolyesterErrorDetail | undefined,\n): MfaVerificationFailureReason | null {\n if (detail?.service === \"auth\" && detail.code === \"AUTH_MFA_CHALLENGE_INVALID\") {\n return \"challenge-invalid\";\n }\n if (detail?.service === \"auth\" && detail.code === \"AUTH_MFA_CHALLENGE_LOCKED\") {\n return \"challenge-locked\";\n }\n if (detail?.service === \"auth\" && detail.code === \"AUTH_MFA_OTP_INVALID\") return \"otp-invalid\";\n if (detail?.service === \"auth\" && detail.code === \"AUTH_MFA_RECOVERY_INVALID\") {\n return \"recovery-code-invalid\";\n }\n if (\n (detail?.service === \"auth\" && detail.code === \"AUTH_MFA_PASSKEY_CREDENTIAL_INVALID\") ||\n (detail?.service === \"auth\" && detail.code === \"AUTH_MFA_PASSKEY_VERIFY_FAILED\")\n ) {\n return \"passkey-invalid\";\n }\n\n return null;\n}\n\nfunction findPolyesterErrorInCauseChain(err: unknown): PolyesterError | null {\n let current: unknown = err;\n for (let depth = 0; depth < 5 && current instanceof Error; depth++) {\n current = current.cause;\n if (current instanceof PolyesterError) return current;\n }\n return null;\n}\n\n/**\n * Converts any RPC-layer failure into its typed SDK error. Abort errors and\n * caller-cancelled requests pass through unchanged, as do errors that are\n * already typed (e.g. a `NetworkError` from the SDK fetch wrapper).\n */\nexport function toPolyesterError(err: unknown): unknown {\n if (err instanceof PolyesterError) return err;\n if (isAbortError(err)) return err;\n if (err instanceof ConnectError) {\n if (err.code === Code.Canceled) return err;\n const wrapped = findPolyesterErrorInCauseChain(err);\n if (wrapped) return wrapped;\n return connectErrorToPolyesterError(err);\n }\n return err;\n}\n\nfunction callerAbortError(signal: AbortSignal): DOMException {\n const reason: unknown = signal.reason;\n return reason instanceof DOMException && reason.name === \"AbortError\"\n ? reason\n : new DOMException(\"Request canceled.\", \"AbortError\");\n}\n\nfunction toTransportError(err: unknown, signal?: AbortSignal): unknown {\n if (signal?.aborted && err instanceof ConnectError && err.code === Code.Canceled) {\n return callerAbortError(signal);\n }\n return toPolyesterError(err);\n}\n\nasync function* mapStreamErrors<T>(\n source: AsyncIterable<T>,\n signal?: AbortSignal,\n): AsyncIterable<T> {\n try {\n yield* source;\n } catch (err) {\n throw toTransportError(err, signal);\n }\n}\n\n/**\n * Interceptor that converts every RPC failure (unary and streaming) into the\n * typed {@link PolyesterError} hierarchy. Installed outermost by\n * `createTransports`, so callers of SDK services always see typed errors.\n */\nexport function createErrorMappingInterceptor(): Interceptor {\n return (next) => async (req) => {\n try {\n const res = await next(req);\n if (res.stream) {\n return { ...res, message: mapStreamErrors(res.message) };\n }\n return res;\n } catch (err) {\n throw toPolyesterError(err);\n }\n };\n}\n\n/**\n * Wraps a Connect transport so errors are translated after Connect's call\n * runner has applied its own error normalization.\n */\nexport function createErrorMappingTransport(transport: Transport): Transport {\n return {\n async unary(method, signal, timeoutMs, header, input, contextValues) {\n if (signal?.aborted) throw callerAbortError(signal);\n try {\n return await transport.unary(\n method,\n signal,\n timeoutMs,\n header,\n input,\n contextValues,\n );\n } catch (error) {\n throw toTransportError(error, signal);\n }\n },\n async stream(method, signal, timeoutMs, header, input, contextValues) {\n if (signal?.aborted) throw callerAbortError(signal);\n try {\n const response = await transport.stream(\n method,\n signal,\n timeoutMs,\n header,\n input,\n contextValues,\n );\n return { ...response, message: mapStreamErrors(response.message, signal) };\n } catch (error) {\n throw toTransportError(error, signal);\n }\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -16,7 +16,11 @@ type AuthAndPublicApiTransports = Pick<Transports, "authApi" | "publicApi">;
|
|
|
16
16
|
*/
|
|
17
17
|
interface JwtAuthProvider {
|
|
18
18
|
kind: "jwt";
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* Typically wraps the configured browser auth token storage. The SDK reads
|
|
21
|
+
* the current value for every request and for every realtime auth check, so
|
|
22
|
+
* keep this cheap and return the freshest credential rather than caching.
|
|
23
|
+
*/
|
|
20
24
|
getToken: () => string | null | Promise<string | null>;
|
|
21
25
|
}
|
|
22
26
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transports.d.ts","names":[],"sources":["../../src/shared/transports.ts"],"mappings":";;;UA8BiB;EACb,SAAS;EACT,WAAW;;;KAIH,oBAAoB,KAAK;;KAGzB,sBAAsB,KAAK;;KAG3B,6BAA6B,KAAK;;;;UAK7B;EACb
|
|
1
|
+
{"version":3,"file":"transports.d.ts","names":[],"sources":["../../src/shared/transports.ts"],"mappings":";;;UA8BiB;EACb,SAAS;EACT,WAAW;;;KAIH,oBAAoB,KAAK;;KAGzB,sBAAsB,KAAK;;KAG3B,6BAA6B,KAAK;;;;UAK7B;EACb;;;;;;EAMA,gCAAgC;;;;;;UAOnB;EACb;EACA,gCAAgC;;EAEhC,oBAAoB,oBAAoB,QAAQ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transports.js","names":[],"sources":["../../src/shared/transports.ts"],"sourcesContent":["import { createConnectTransport } from \"@connectrpc/connect-web\";\nimport type { Transport, Interceptor } from \"@connectrpc/connect\";\nimport { toBinary, toJsonString } from \"@bufbuild/protobuf\";\nimport { signAsync } from \"@noble/ed25519\";\nimport { createErrorMappingTransport } from \"./connect-error-mapping.js\";\nimport {\n AuthenticationError,\n ConfigurationError,\n isAbortError,\n NetworkError,\n PolyesterError,\n} from \"./errors.js\";\n\nexport { isAbortError };\n\nexport type ConnectWireFormat = \"binary\" | \"json\";\n\nexport const POLYESTER_MOCK_HEADER = \"x-polyester-mock\";\n\nexport interface TransportConfig {\n apiUrl: string;\n interceptors?: Interceptor[];\n auth?: JwtAuthProvider | ApiKeyEd25519AuthProvider;\n /**\n * Connect wire format. Defaults to binary for production performance.\n * Use `json` for the API Playground visualization.\n */\n wireFormat?: ConnectWireFormat;\n}\n\nexport interface Transports {\n authApi: Transport;\n publicApi: Transport;\n}\n\n/** Connect transports available to services that call authenticated APIs. */\nexport type AuthApiTransports = Pick<Transports, \"authApi\">;\n\n/** Connect transports available to services that call public APIs. */\nexport type PublicApiTransports = Pick<Transports, \"publicApi\">;\n\n/** Connect transports available to services that call both public and authenticated APIs. */\nexport type AuthAndPublicApiTransports = Pick<Transports, \"authApi\" | \"publicApi\">;\n\n/**\n * Generic JWT auth for HTTP/Connect endpoints that follow the Bearer token contract.\n */\nexport interface JwtAuthProvider {\n kind: \"jwt\";\n /** Typically wraps the configured browser auth token storage. */\n getToken: () => string | null | Promise<string | null>;\n}\n\n/**\n * Generic Ed25519 API key auth for HTTP/Connect endpoints that follow the\n * X-API-KEY-ID / X-API-TIMESTAMP / X-API-SIGNATURE contract.\n */\nexport interface ApiKeyEd25519AuthProvider {\n kind: \"api-key-ed25519\";\n getKeyId: () => string | null | Promise<string | null>;\n /** Ed25519 secret key bytes (not hex) for signing. */\n getSecretKey: () => Uint8Array | null | Promise<Uint8Array | null>;\n}\n\nexport interface ApiKeyEd25519SigningRequest {\n url: string | URL;\n method: string;\n body?: Uint8Array;\n timestamp?: string;\n}\n\n/**\n * Returns the fetch implementation used by SDK transports.\n */\nexport function makeFetch(): typeof fetch {\n const wrappedFetch = Object.assign(\n async (input: Parameters<typeof fetch>[0], init?: Parameters<typeof fetch>[1]) => {\n // Normalize headers into a mutable Headers object\n const headers = new Headers(init?.headers);\n\n try {\n const res = await fetch(input, {\n ...init,\n headers,\n redirect: \"manual\",\n });\n\n return res;\n } catch (err) {\n if (isAbortError(err)) throw err;\n throw new NetworkError(\"Transport request failed\", { cause: err });\n }\n },\n fetch,\n );\n\n return wrappedFetch;\n}\n\n/**\n * Creates Connect transports for SDK service clients.\n */\nexport function createTransports(config: TransportConfig): Transports {\n const { apiUrl, interceptors = [], auth, wireFormat = \"binary\" } = config;\n const useBinaryFormat = wireFormat === \"binary\";\n const publicApi = createErrorMappingTransport(\n createConnectTransport({\n baseUrl: apiUrl,\n useBinaryFormat,\n interceptors,\n fetch: makeFetch(),\n }),\n );\n\n const authInterceptors = auth\n ? auth.kind === \"jwt\"\n ? [createAuthInterceptor(auth, { wireFormat }), ...interceptors]\n : [...interceptors, createAuthInterceptor(auth, { wireFormat })]\n : interceptors;\n const authApi = createErrorMappingTransport(\n createConnectTransport({\n baseUrl: apiUrl,\n useBinaryFormat,\n interceptors: authInterceptors,\n fetch: makeFetch(),\n }),\n );\n\n return { authApi, publicApi };\n}\n\n/**\n * Creates an interceptor that enables mock transport behavior.\n */\nexport function createMockInterceptor(getEnabled: () => boolean): Interceptor {\n return (next) => async (req) => {\n if (getEnabled()) req.header.set(POLYESTER_MOCK_HEADER, \"true\");\n return next(req);\n };\n}\n\nasync function sha256Hex(bytes: Uint8Array): Promise<string> {\n if (typeof crypto === \"undefined\" || !crypto.subtle) {\n throw new ConfigurationError(\"SHA-256 not available in this environment\");\n }\n const hash = await crypto.subtle.digest(\"SHA-256\", bytes.slice());\n return Array.from(new Uint8Array(hash), (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nlet lastTimestamp = 0;\nfunction nextTimestamp(): string {\n lastTimestamp = Math.max(lastTimestamp + 1, Date.now());\n return String(lastTimestamp);\n}\n\nfunction canonicalQueryString(params: URLSearchParams): string {\n const pairs: string[] = [];\n for (const [k, v] of params.entries()) {\n pairs.push(`${encodeURIComponent(k)}=${encodeURIComponent(v)}`);\n }\n pairs.sort();\n return pairs.join(\"&\");\n}\n\n/**\n * Creates API-key authentication headers for endpoints using the Polyester Ed25519 signing contract.\n */\nexport async function createApiKeyEd25519AuthHeaders(\n auth: ApiKeyEd25519AuthProvider,\n request: ApiKeyEd25519SigningRequest,\n): Promise<Record<string, string>> {\n let keyId: string | null;\n let secretKey: Uint8Array | null;\n try {\n [keyId, secretKey] = await Promise.all([auth.getKeyId(), auth.getSecretKey()]);\n } catch (cause) {\n if (cause instanceof PolyesterError) throw cause;\n throw new ConfigurationError(\"API key credential provider failed\", { cause });\n }\n if (!keyId || !secretKey) throw new ConfigurationError(\"Missing API key ID or secret key\");\n if (!(secretKey instanceof Uint8Array) || secretKey.byteLength !== 32) {\n throw new ConfigurationError(\"API key secret key must contain exactly 32 bytes\");\n }\n\n const urlObj = new URL(request.url);\n const timestamp = request.timestamp ?? nextTimestamp();\n const bodyHash = await sha256Hex(request.body ?? new Uint8Array(0));\n const canonicalQuery = canonicalQueryString(urlObj.searchParams);\n const canonical = `${timestamp}\\n${request.method}\\n${urlObj.pathname}\\n${canonicalQuery}\\n${bodyHash}`;\n const msgBytes = new TextEncoder().encode(canonical);\n const sig = await signAsync(msgBytes, secretKey);\n const signatureHex = Array.from(sig, (b: number) => b.toString(16).padStart(2, \"0\")).join(\"\");\n\n return {\n \"X-API-KEY-ID\": keyId,\n \"X-API-TIMESTAMP\": timestamp,\n \"X-API-SIGNATURE\": signatureHex,\n };\n}\n\n/**\n * Resolves a JWT credential and translates provider failures or invalid values\n * into stable SDK errors.\n */\nexport async function resolveJwtToken(auth: JwtAuthProvider): Promise<string | null> {\n let token: string | null;\n try {\n token = await auth.getToken();\n } catch (cause) {\n if (cause instanceof PolyesterError) throw cause;\n throw new AuthenticationError(\"JWT token provider failed\", { cause });\n }\n if (token !== null && typeof token !== \"string\") {\n throw new ConfigurationError(\"JWT token provider must return a string or null\");\n }\n return token;\n}\n\n/**\n * Creates an interceptor that attaches SDK authentication headers.\n */\nexport function createAuthInterceptor(\n auth: JwtAuthProvider | ApiKeyEd25519AuthProvider,\n options?: { wireFormat?: ConnectWireFormat },\n): Interceptor {\n return (next) => async (req) => {\n const wireFormat = options?.wireFormat ?? \"binary\";\n if (auth.kind === \"jwt\") {\n const token = await resolveJwtToken(auth);\n if (token) {\n req.header.set(\"Authorization\", `Bearer ${token}`);\n }\n } else if (auth.kind === \"api-key-ed25519\") {\n if (req.stream) {\n throw new ConfigurationError(\n \"API key authentication does not support streaming Connect RPCs\",\n );\n }\n // Unary request: serialize the message using the method's input schema.\n const inputSchema = req.method.input;\n const bodyBytes =\n wireFormat === \"json\"\n ? new TextEncoder().encode(toJsonString(inputSchema, req.message))\n : toBinary(inputSchema, req.message);\n\n const headers = await createApiKeyEd25519AuthHeaders(auth, {\n url: req.url,\n method: req.requestMethod,\n body: bodyBytes,\n });\n for (const [key, value] of Object.entries(headers)) {\n req.header.set(key, value);\n }\n }\n return next(req);\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"transports.js","names":[],"sources":["../../src/shared/transports.ts"],"sourcesContent":["import { createConnectTransport } from \"@connectrpc/connect-web\";\nimport type { Transport, Interceptor } from \"@connectrpc/connect\";\nimport { toBinary, toJsonString } from \"@bufbuild/protobuf\";\nimport { signAsync } from \"@noble/ed25519\";\nimport { createErrorMappingTransport } from \"./connect-error-mapping.js\";\nimport {\n AuthenticationError,\n ConfigurationError,\n isAbortError,\n NetworkError,\n PolyesterError,\n} from \"./errors.js\";\n\nexport { isAbortError };\n\nexport type ConnectWireFormat = \"binary\" | \"json\";\n\nexport const POLYESTER_MOCK_HEADER = \"x-polyester-mock\";\n\nexport interface TransportConfig {\n apiUrl: string;\n interceptors?: Interceptor[];\n auth?: JwtAuthProvider | ApiKeyEd25519AuthProvider;\n /**\n * Connect wire format. Defaults to binary for production performance.\n * Use `json` for the API Playground visualization.\n */\n wireFormat?: ConnectWireFormat;\n}\n\nexport interface Transports {\n authApi: Transport;\n publicApi: Transport;\n}\n\n/** Connect transports available to services that call authenticated APIs. */\nexport type AuthApiTransports = Pick<Transports, \"authApi\">;\n\n/** Connect transports available to services that call public APIs. */\nexport type PublicApiTransports = Pick<Transports, \"publicApi\">;\n\n/** Connect transports available to services that call both public and authenticated APIs. */\nexport type AuthAndPublicApiTransports = Pick<Transports, \"authApi\" | \"publicApi\">;\n\n/**\n * Generic JWT auth for HTTP/Connect endpoints that follow the Bearer token contract.\n */\nexport interface JwtAuthProvider {\n kind: \"jwt\";\n /**\n * Typically wraps the configured browser auth token storage. The SDK reads\n * the current value for every request and for every realtime auth check, so\n * keep this cheap and return the freshest credential rather than caching.\n */\n getToken: () => string | null | Promise<string | null>;\n}\n\n/**\n * Generic Ed25519 API key auth for HTTP/Connect endpoints that follow the\n * X-API-KEY-ID / X-API-TIMESTAMP / X-API-SIGNATURE contract.\n */\nexport interface ApiKeyEd25519AuthProvider {\n kind: \"api-key-ed25519\";\n getKeyId: () => string | null | Promise<string | null>;\n /** Ed25519 secret key bytes (not hex) for signing. */\n getSecretKey: () => Uint8Array | null | Promise<Uint8Array | null>;\n}\n\nexport interface ApiKeyEd25519SigningRequest {\n url: string | URL;\n method: string;\n body?: Uint8Array;\n timestamp?: string;\n}\n\n/**\n * Returns the fetch implementation used by SDK transports.\n */\nexport function makeFetch(): typeof fetch {\n const wrappedFetch = Object.assign(\n async (input: Parameters<typeof fetch>[0], init?: Parameters<typeof fetch>[1]) => {\n // Normalize headers into a mutable Headers object\n const headers = new Headers(init?.headers);\n\n try {\n const res = await fetch(input, {\n ...init,\n headers,\n redirect: \"manual\",\n });\n\n return res;\n } catch (err) {\n if (isAbortError(err)) throw err;\n throw new NetworkError(\"Transport request failed\", { cause: err });\n }\n },\n fetch,\n );\n\n return wrappedFetch;\n}\n\n/**\n * Creates Connect transports for SDK service clients.\n */\nexport function createTransports(config: TransportConfig): Transports {\n const { apiUrl, interceptors = [], auth, wireFormat = \"binary\" } = config;\n const useBinaryFormat = wireFormat === \"binary\";\n const publicApi = createErrorMappingTransport(\n createConnectTransport({\n baseUrl: apiUrl,\n useBinaryFormat,\n interceptors,\n fetch: makeFetch(),\n }),\n );\n\n const authInterceptors = auth\n ? auth.kind === \"jwt\"\n ? [createAuthInterceptor(auth, { wireFormat }), ...interceptors]\n : [...interceptors, createAuthInterceptor(auth, { wireFormat })]\n : interceptors;\n const authApi = createErrorMappingTransport(\n createConnectTransport({\n baseUrl: apiUrl,\n useBinaryFormat,\n interceptors: authInterceptors,\n fetch: makeFetch(),\n }),\n );\n\n return { authApi, publicApi };\n}\n\n/**\n * Creates an interceptor that enables mock transport behavior.\n */\nexport function createMockInterceptor(getEnabled: () => boolean): Interceptor {\n return (next) => async (req) => {\n if (getEnabled()) req.header.set(POLYESTER_MOCK_HEADER, \"true\");\n return next(req);\n };\n}\n\nasync function sha256Hex(bytes: Uint8Array): Promise<string> {\n if (typeof crypto === \"undefined\" || !crypto.subtle) {\n throw new ConfigurationError(\"SHA-256 not available in this environment\");\n }\n const hash = await crypto.subtle.digest(\"SHA-256\", bytes.slice());\n return Array.from(new Uint8Array(hash), (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nlet lastTimestamp = 0;\nfunction nextTimestamp(): string {\n lastTimestamp = Math.max(lastTimestamp + 1, Date.now());\n return String(lastTimestamp);\n}\n\nfunction canonicalQueryString(params: URLSearchParams): string {\n const pairs: string[] = [];\n for (const [k, v] of params.entries()) {\n pairs.push(`${encodeURIComponent(k)}=${encodeURIComponent(v)}`);\n }\n pairs.sort();\n return pairs.join(\"&\");\n}\n\n/**\n * Creates API-key authentication headers for endpoints using the Polyester Ed25519 signing contract.\n */\nexport async function createApiKeyEd25519AuthHeaders(\n auth: ApiKeyEd25519AuthProvider,\n request: ApiKeyEd25519SigningRequest,\n): Promise<Record<string, string>> {\n let keyId: string | null;\n let secretKey: Uint8Array | null;\n try {\n [keyId, secretKey] = await Promise.all([auth.getKeyId(), auth.getSecretKey()]);\n } catch (cause) {\n if (cause instanceof PolyesterError) throw cause;\n throw new ConfigurationError(\"API key credential provider failed\", { cause });\n }\n if (!keyId || !secretKey) throw new ConfigurationError(\"Missing API key ID or secret key\");\n if (!(secretKey instanceof Uint8Array) || secretKey.byteLength !== 32) {\n throw new ConfigurationError(\"API key secret key must contain exactly 32 bytes\");\n }\n\n const urlObj = new URL(request.url);\n const timestamp = request.timestamp ?? nextTimestamp();\n const bodyHash = await sha256Hex(request.body ?? new Uint8Array(0));\n const canonicalQuery = canonicalQueryString(urlObj.searchParams);\n const canonical = `${timestamp}\\n${request.method}\\n${urlObj.pathname}\\n${canonicalQuery}\\n${bodyHash}`;\n const msgBytes = new TextEncoder().encode(canonical);\n const sig = await signAsync(msgBytes, secretKey);\n const signatureHex = Array.from(sig, (b: number) => b.toString(16).padStart(2, \"0\")).join(\"\");\n\n return {\n \"X-API-KEY-ID\": keyId,\n \"X-API-TIMESTAMP\": timestamp,\n \"X-API-SIGNATURE\": signatureHex,\n };\n}\n\n/**\n * Resolves a JWT credential and translates provider failures or invalid values\n * into stable SDK errors.\n */\nexport async function resolveJwtToken(auth: JwtAuthProvider): Promise<string | null> {\n let token: string | null;\n try {\n token = await auth.getToken();\n } catch (cause) {\n if (cause instanceof PolyesterError) throw cause;\n throw new AuthenticationError(\"JWT token provider failed\", { cause });\n }\n if (token !== null && typeof token !== \"string\") {\n throw new ConfigurationError(\"JWT token provider must return a string or null\");\n }\n return token;\n}\n\n/**\n * Creates an interceptor that attaches SDK authentication headers.\n */\nexport function createAuthInterceptor(\n auth: JwtAuthProvider | ApiKeyEd25519AuthProvider,\n options?: { wireFormat?: ConnectWireFormat },\n): Interceptor {\n return (next) => async (req) => {\n const wireFormat = options?.wireFormat ?? \"binary\";\n if (auth.kind === \"jwt\") {\n const token = await resolveJwtToken(auth);\n if (token) {\n req.header.set(\"Authorization\", `Bearer ${token}`);\n }\n } else if (auth.kind === \"api-key-ed25519\") {\n if (req.stream) {\n throw new ConfigurationError(\n \"API key authentication does not support streaming Connect RPCs\",\n );\n }\n // Unary request: serialize the message using the method's input schema.\n const inputSchema = req.method.input;\n const bodyBytes =\n wireFormat === \"json\"\n ? new TextEncoder().encode(toJsonString(inputSchema, req.message))\n : toBinary(inputSchema, req.message);\n\n const headers = await createApiKeyEd25519AuthHeaders(auth, {\n url: req.url,\n method: req.requestMethod,\n body: bodyBytes,\n });\n for (const [key, value] of Object.entries(headers)) {\n req.header.set(key, value);\n }\n }\n return next(req);\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","names":[],"sources":["../../src/utils/errors.ts"],"mappings":";;;;;;iBAqBgB,wBAAwB;;iBAaxB,wBAAwB;;iBAsBxB,mBAAmB;;iBAKnB,oBAAoB;;iBAKpB,2BAA2B;;;;;;iBAS3B,iBAAiB;;;;;;;;;;;iBAuBjB,mBACZ,cACA;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","names":[],"sources":["../../src/utils/errors.ts"],"mappings":";;;;;;iBAqBgB,wBAAwB;;iBAaxB,wBAAwB;;iBAsBxB,mBAAmB;;iBAKnB,oBAAoB;;iBAKpB,2BAA2B;;;;;;iBAS3B,iBAAiB;;;;;;;;;;;iBAuBjB,mBACZ,cACA;iBA2CY,sBAAsB,gBAAgB"}
|
package/dist/utils/errors.js
CHANGED
|
@@ -71,6 +71,7 @@ function formatConnectError(err, fallback = "Request failed. Please try again.")
|
|
|
71
71
|
* returns the caller-owned fallback.
|
|
72
72
|
*/
|
|
73
73
|
function userFacingTransportMessage(error) {
|
|
74
|
+
if (isAbortError(error)) return "Request canceled.";
|
|
74
75
|
if (error instanceof RateLimitError) return "You've made too many requests. Wait a moment and try again.";
|
|
75
76
|
if (error instanceof TimeoutError) return "This is taking longer than expected. Try again.";
|
|
76
77
|
if (error instanceof ServiceUnavailableError) return "This service is temporarily unavailable. Try again in a few minutes.";
|
package/dist/utils/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","names":[],"sources":["../../src/utils/errors.ts"],"sourcesContent":["import { Code, ConnectError } from \"@connectrpc/connect\";\nimport { getNormalizedConnectMessage, toPolyesterError } from \"../shared/connect-error-mapping.js\";\nimport {\n isAbortError,\n NetworkError,\n PolicyInUseError,\n PolicyLockedError,\n PolicyScopeMismatchError,\n PolyesterError,\n RateLimitError,\n ResourceNotFoundError,\n RevisionConflictError,\n ServiceUnavailableError,\n TimeoutError,\n} from \"../shared/errors.js\";\n\n/**\n * Whether the backend signalled that the requested resource does not exist.\n * Prefer `err instanceof ResourceNotFoundError`; this predicate also covers\n * raw `ConnectError`s (NotFound or the auth resource-not-found detail).\n */\nexport function isResourceNotFoundError(err: unknown): boolean {\n // Connect can re-wrap interceptor errors as an Unknown ConnectError whose\n // cause is the typed SDK error. Normalize first to recover that cause.\n const mapped = toPolyesterError(err);\n return (\n mapped instanceof ResourceNotFoundError ||\n (mapped instanceof PolyesterError &&\n mapped.detail?.service === \"auth\" &&\n mapped.detail.code === \"AUTH_RESOURCE_NOT_FOUND\")\n );\n}\n\n/** Whether an optimistic-concurrency update used a stale resource revision. */\nexport function isRevisionConflictError(err: unknown): boolean {\n const seen = new Set<unknown>();\n let current = err;\n while (current !== undefined && current !== null && !seen.has(current)) {\n seen.add(current);\n if (toPolyesterError(current) instanceof RevisionConflictError) return true;\n const connectError = ConnectError.from(current);\n if (\n (connectError.code === Code.Unknown || connectError.code === Code.Aborted) &&\n /\\brevision conflict\\b/i.test(getNormalizedConnectMessage(connectError))\n ) {\n return true;\n }\n current =\n typeof current === \"object\" && \"cause\" in current\n ? (current as { cause?: unknown }).cause\n : undefined;\n }\n return false;\n}\n\n/** Whether a policy mutation failed because the policy is still attached to a resource. */\nexport function isPolicyInUseError(err: unknown): boolean {\n return toPolyesterError(err) instanceof PolicyInUseError;\n}\n\n/** Whether a policy mutation failed because the policy is locked. */\nexport function isPolicyLockedError(err: unknown): boolean {\n return toPolyesterError(err) instanceof PolicyLockedError;\n}\n\n/** Whether the policy and target account belong to different scopes. */\nexport function isPolicyScopeMismatchError(err: unknown): boolean {\n return toPolyesterError(err) instanceof PolicyScopeMismatchError;\n}\n\n/**\n * Whether an error is retryable (network/timeout) for idempotent operations.\n * Uses same requestId on retry; backend dedupes within 2min.\n * Prefer `err instanceof TransientError` or `err.retryable` on typed errors.\n */\nexport function isRetryableError(err: unknown): boolean {\n if (isAbortError(err)) return false;\n const mapped = toPolyesterError(err);\n if (mapped instanceof PolyesterError) return mapped.retryable;\n const ce = ConnectError.from(mapped);\n return (\n ce.code === Code.Unavailable ||\n ce.code === Code.DeadlineExceeded ||\n ce.code === Code.ResourceExhausted ||\n ce.code === Code.Aborted\n );\n}\n\n/**\n * Diagnostic formatter for SDK and ConnectRPC errors.\n * Typed SDK errors already carry normalized messages; for raw ConnectErrors\n * this strips leading \"[CODE]\" prefixes. Falls back to a friendly default.\n * The returned message may contain backend implementation details. Never render\n * it to a user; use {@link formatUserFacingError} at presentation boundaries.\n * @param err - The error to format.\n * @param fallback - The fallback message when no message is available.\n * @returns The formatted error message.\n */\nexport function formatConnectError(\n err: unknown,\n fallback = \"Request failed. Please try again.\",\n): string {\n if (err == null) return fallback;\n if (err instanceof PolyesterError) return err.message || fallback;\n const withoutCode = getNormalizedConnectMessage(err);\n return withoutCode || fallback;\n}\n\n/**\n * Formats an untrusted failure for direct display to a user.\n *\n * This function never returns an exception or backend message. A small allowlist\n * of typed transport conditions receives SDK-owned copy; every other failure\n * returns the caller-owned fallback.\n */\nfunction userFacingTransportMessage(error: unknown): string | undefined {\n if (error instanceof RateLimitError) {\n return \"You've made too many requests. Wait a moment and try again.\";\n }\n if (error instanceof TimeoutError) return \"This is taking longer than expected. Try again.\";\n if (error instanceof ServiceUnavailableError) {\n return \"This service is temporarily unavailable. Try again in a few minutes.\";\n }\n if (error instanceof NetworkError) {\n return \"We couldn't connect. Check your internet connection and try again.\";\n }\n\n if (error instanceof ConnectError) {\n if (error.code === Code.ResourceExhausted) {\n return \"You've made too many requests. Wait a moment and try again.\";\n }\n if (error.code === Code.DeadlineExceeded) {\n return \"This is taking longer than expected. Try again.\";\n }\n if (error.code === Code.Unavailable) {\n return \"This service is temporarily unavailable. Try again in a few minutes.\";\n }\n }\n\n return undefined;\n}\n\nexport function formatUserFacingError(error: unknown, fallback: string): string {\n // Walk the cause chain and normalize ConnectErrors so wrapped submit failures\n // (e.g. Error → ConnectError[unknown] \"Service temporarily unavailable.\")\n // still hit the transport allowlist.\n let current: unknown = error;\n for (let depth = 0; depth < 5 && current != null; depth++) {\n const mapped = toPolyesterError(current);\n const fromMapped = userFacingTransportMessage(mapped);\n if (fromMapped !== undefined) return fromMapped;\n const fromCurrent = userFacingTransportMessage(current);\n if (fromCurrent !== undefined) return fromCurrent;\n\n current =\n typeof current === \"object\" && current !== null && \"cause\" in current\n ? (current as { cause?: unknown }).cause\n : undefined;\n }\n\n return fallback;\n}\n"],"mappings":";;;;;;;;;AAqBA,SAAgB,wBAAwB,KAAuB;CAG3D,MAAM,SAAS,iBAAiB,GAAG;CACnC,OACI,kBAAkB,yBACjB,kBAAkB,kBACf,OAAO,QAAQ,YAAY,UAC3B,OAAO,OAAO,SAAS;AAEnC;;AAGA,SAAgB,wBAAwB,KAAuB;CAC3D,MAAM,uBAAO,IAAI,IAAa;CAC9B,IAAI,UAAU;CACd,OAAO,YAAY,KAAA,KAAa,YAAY,QAAQ,CAAC,KAAK,IAAI,OAAO,GAAG;EACpE,KAAK,IAAI,OAAO;EAChB,IAAI,iBAAiB,OAAO,aAAa,uBAAuB,OAAO;EACvE,MAAM,eAAe,aAAa,KAAK,OAAO;EAC9C,KACK,aAAa,SAAS,KAAK,WAAW,aAAa,SAAS,KAAK,YAClE,yBAAyB,KAAK,4BAA4B,YAAY,CAAC,GAEvE,OAAO;EAEX,UACI,OAAO,YAAY,YAAY,WAAW,UACnC,QAAgC,QACjC,KAAA;CACd;CACA,OAAO;AACX;;AAGA,SAAgB,mBAAmB,KAAuB;CACtD,OAAO,iBAAiB,GAAG,aAAa;AAC5C;;AAGA,SAAgB,oBAAoB,KAAuB;CACvD,OAAO,iBAAiB,GAAG,aAAa;AAC5C;;AAGA,SAAgB,2BAA2B,KAAuB;CAC9D,OAAO,iBAAiB,GAAG,aAAa;AAC5C;;;;;;AAOA,SAAgB,iBAAiB,KAAuB;CACpD,IAAI,aAAa,GAAG,GAAG,OAAO;CAC9B,MAAM,SAAS,iBAAiB,GAAG;CACnC,IAAI,kBAAkB,gBAAgB,OAAO,OAAO;CACpD,MAAM,KAAK,aAAa,KAAK,MAAM;CACnC,OACI,GAAG,SAAS,KAAK,eACjB,GAAG,SAAS,KAAK,oBACjB,GAAG,SAAS,KAAK,qBACjB,GAAG,SAAS,KAAK;AAEzB;;;;;;;;;;;AAYA,SAAgB,mBACZ,KACA,WAAW,qCACL;CACN,IAAI,OAAO,MAAM,OAAO;CACxB,IAAI,eAAe,gBAAgB,OAAO,IAAI,WAAW;CAEzD,OADoB,4BAA4B,GAC/B,KAAK;AAC1B;;;;;;;;AASA,SAAS,2BAA2B,OAAoC;CACpE,IAAI,iBAAiB,gBACjB,OAAO;CAEX,IAAI,iBAAiB,cAAc,OAAO;CAC1C,IAAI,iBAAiB,yBACjB,OAAO;CAEX,IAAI,iBAAiB,cACjB,OAAO;CAGX,IAAI,iBAAiB,cAAc;EAC/B,IAAI,MAAM,SAAS,KAAK,mBACpB,OAAO;EAEX,IAAI,MAAM,SAAS,KAAK,kBACpB,OAAO;EAEX,IAAI,MAAM,SAAS,KAAK,aACpB,OAAO;CAEf;AAGJ;AAEA,SAAgB,sBAAsB,OAAgB,UAA0B;CAI5E,IAAI,UAAmB;CACvB,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,WAAW,MAAM,SAAS;EAEvD,MAAM,aAAa,2BADJ,iBAAiB,OACmB,CAAC;EACpD,IAAI,eAAe,KAAA,GAAW,OAAO;EACrC,MAAM,cAAc,2BAA2B,OAAO;EACtD,IAAI,gBAAgB,KAAA,GAAW,OAAO;EAEtC,UACI,OAAO,YAAY,YAAY,YAAY,QAAQ,WAAW,UACvD,QAAgC,QACjC,KAAA;CACd;CAEA,OAAO;AACX"}
|
|
1
|
+
{"version":3,"file":"errors.js","names":[],"sources":["../../src/utils/errors.ts"],"sourcesContent":["import { Code, ConnectError } from \"@connectrpc/connect\";\nimport { getNormalizedConnectMessage, toPolyesterError } from \"../shared/connect-error-mapping.js\";\nimport {\n isAbortError,\n NetworkError,\n PolicyInUseError,\n PolicyLockedError,\n PolicyScopeMismatchError,\n PolyesterError,\n RateLimitError,\n ResourceNotFoundError,\n RevisionConflictError,\n ServiceUnavailableError,\n TimeoutError,\n} from \"../shared/errors.js\";\n\n/**\n * Whether the backend signalled that the requested resource does not exist.\n * Prefer `err instanceof ResourceNotFoundError`; this predicate also covers\n * raw `ConnectError`s (NotFound or the auth resource-not-found detail).\n */\nexport function isResourceNotFoundError(err: unknown): boolean {\n // Connect can re-wrap interceptor errors as an Unknown ConnectError whose\n // cause is the typed SDK error. Normalize first to recover that cause.\n const mapped = toPolyesterError(err);\n return (\n mapped instanceof ResourceNotFoundError ||\n (mapped instanceof PolyesterError &&\n mapped.detail?.service === \"auth\" &&\n mapped.detail.code === \"AUTH_RESOURCE_NOT_FOUND\")\n );\n}\n\n/** Whether an optimistic-concurrency update used a stale resource revision. */\nexport function isRevisionConflictError(err: unknown): boolean {\n const seen = new Set<unknown>();\n let current = err;\n while (current !== undefined && current !== null && !seen.has(current)) {\n seen.add(current);\n if (toPolyesterError(current) instanceof RevisionConflictError) return true;\n const connectError = ConnectError.from(current);\n if (\n (connectError.code === Code.Unknown || connectError.code === Code.Aborted) &&\n /\\brevision conflict\\b/i.test(getNormalizedConnectMessage(connectError))\n ) {\n return true;\n }\n current =\n typeof current === \"object\" && \"cause\" in current\n ? (current as { cause?: unknown }).cause\n : undefined;\n }\n return false;\n}\n\n/** Whether a policy mutation failed because the policy is still attached to a resource. */\nexport function isPolicyInUseError(err: unknown): boolean {\n return toPolyesterError(err) instanceof PolicyInUseError;\n}\n\n/** Whether a policy mutation failed because the policy is locked. */\nexport function isPolicyLockedError(err: unknown): boolean {\n return toPolyesterError(err) instanceof PolicyLockedError;\n}\n\n/** Whether the policy and target account belong to different scopes. */\nexport function isPolicyScopeMismatchError(err: unknown): boolean {\n return toPolyesterError(err) instanceof PolicyScopeMismatchError;\n}\n\n/**\n * Whether an error is retryable (network/timeout) for idempotent operations.\n * Uses same requestId on retry; backend dedupes within 2min.\n * Prefer `err instanceof TransientError` or `err.retryable` on typed errors.\n */\nexport function isRetryableError(err: unknown): boolean {\n if (isAbortError(err)) return false;\n const mapped = toPolyesterError(err);\n if (mapped instanceof PolyesterError) return mapped.retryable;\n const ce = ConnectError.from(mapped);\n return (\n ce.code === Code.Unavailable ||\n ce.code === Code.DeadlineExceeded ||\n ce.code === Code.ResourceExhausted ||\n ce.code === Code.Aborted\n );\n}\n\n/**\n * Diagnostic formatter for SDK and ConnectRPC errors.\n * Typed SDK errors already carry normalized messages; for raw ConnectErrors\n * this strips leading \"[CODE]\" prefixes. Falls back to a friendly default.\n * The returned message may contain backend implementation details. Never render\n * it to a user; use {@link formatUserFacingError} at presentation boundaries.\n * @param err - The error to format.\n * @param fallback - The fallback message when no message is available.\n * @returns The formatted error message.\n */\nexport function formatConnectError(\n err: unknown,\n fallback = \"Request failed. Please try again.\",\n): string {\n if (err == null) return fallback;\n if (err instanceof PolyesterError) return err.message || fallback;\n const withoutCode = getNormalizedConnectMessage(err);\n return withoutCode || fallback;\n}\n\n/**\n * Formats an untrusted failure for direct display to a user.\n *\n * This function never returns an exception or backend message. A small allowlist\n * of typed transport conditions receives SDK-owned copy; every other failure\n * returns the caller-owned fallback.\n */\nfunction userFacingTransportMessage(error: unknown): string | undefined {\n if (isAbortError(error)) return \"Request canceled.\";\n if (error instanceof RateLimitError) {\n return \"You've made too many requests. Wait a moment and try again.\";\n }\n if (error instanceof TimeoutError) return \"This is taking longer than expected. Try again.\";\n if (error instanceof ServiceUnavailableError) {\n return \"This service is temporarily unavailable. Try again in a few minutes.\";\n }\n if (error instanceof NetworkError) {\n return \"We couldn't connect. Check your internet connection and try again.\";\n }\n\n if (error instanceof ConnectError) {\n if (error.code === Code.ResourceExhausted) {\n return \"You've made too many requests. Wait a moment and try again.\";\n }\n if (error.code === Code.DeadlineExceeded) {\n return \"This is taking longer than expected. Try again.\";\n }\n if (error.code === Code.Unavailable) {\n return \"This service is temporarily unavailable. Try again in a few minutes.\";\n }\n }\n\n return undefined;\n}\n\nexport function formatUserFacingError(error: unknown, fallback: string): string {\n // Walk the cause chain and normalize ConnectErrors so wrapped submit failures\n // (e.g. Error → ConnectError[unknown] \"Service temporarily unavailable.\")\n // still hit the transport allowlist.\n let current: unknown = error;\n for (let depth = 0; depth < 5 && current != null; depth++) {\n const mapped = toPolyesterError(current);\n const fromMapped = userFacingTransportMessage(mapped);\n if (fromMapped !== undefined) return fromMapped;\n const fromCurrent = userFacingTransportMessage(current);\n if (fromCurrent !== undefined) return fromCurrent;\n\n current =\n typeof current === \"object\" && current !== null && \"cause\" in current\n ? (current as { cause?: unknown }).cause\n : undefined;\n }\n\n return fallback;\n}\n"],"mappings":";;;;;;;;;AAqBA,SAAgB,wBAAwB,KAAuB;CAG3D,MAAM,SAAS,iBAAiB,GAAG;CACnC,OACI,kBAAkB,yBACjB,kBAAkB,kBACf,OAAO,QAAQ,YAAY,UAC3B,OAAO,OAAO,SAAS;AAEnC;;AAGA,SAAgB,wBAAwB,KAAuB;CAC3D,MAAM,uBAAO,IAAI,IAAa;CAC9B,IAAI,UAAU;CACd,OAAO,YAAY,KAAA,KAAa,YAAY,QAAQ,CAAC,KAAK,IAAI,OAAO,GAAG;EACpE,KAAK,IAAI,OAAO;EAChB,IAAI,iBAAiB,OAAO,aAAa,uBAAuB,OAAO;EACvE,MAAM,eAAe,aAAa,KAAK,OAAO;EAC9C,KACK,aAAa,SAAS,KAAK,WAAW,aAAa,SAAS,KAAK,YAClE,yBAAyB,KAAK,4BAA4B,YAAY,CAAC,GAEvE,OAAO;EAEX,UACI,OAAO,YAAY,YAAY,WAAW,UACnC,QAAgC,QACjC,KAAA;CACd;CACA,OAAO;AACX;;AAGA,SAAgB,mBAAmB,KAAuB;CACtD,OAAO,iBAAiB,GAAG,aAAa;AAC5C;;AAGA,SAAgB,oBAAoB,KAAuB;CACvD,OAAO,iBAAiB,GAAG,aAAa;AAC5C;;AAGA,SAAgB,2BAA2B,KAAuB;CAC9D,OAAO,iBAAiB,GAAG,aAAa;AAC5C;;;;;;AAOA,SAAgB,iBAAiB,KAAuB;CACpD,IAAI,aAAa,GAAG,GAAG,OAAO;CAC9B,MAAM,SAAS,iBAAiB,GAAG;CACnC,IAAI,kBAAkB,gBAAgB,OAAO,OAAO;CACpD,MAAM,KAAK,aAAa,KAAK,MAAM;CACnC,OACI,GAAG,SAAS,KAAK,eACjB,GAAG,SAAS,KAAK,oBACjB,GAAG,SAAS,KAAK,qBACjB,GAAG,SAAS,KAAK;AAEzB;;;;;;;;;;;AAYA,SAAgB,mBACZ,KACA,WAAW,qCACL;CACN,IAAI,OAAO,MAAM,OAAO;CACxB,IAAI,eAAe,gBAAgB,OAAO,IAAI,WAAW;CAEzD,OADoB,4BAA4B,GAC/B,KAAK;AAC1B;;;;;;;;AASA,SAAS,2BAA2B,OAAoC;CACpE,IAAI,aAAa,KAAK,GAAG,OAAO;CAChC,IAAI,iBAAiB,gBACjB,OAAO;CAEX,IAAI,iBAAiB,cAAc,OAAO;CAC1C,IAAI,iBAAiB,yBACjB,OAAO;CAEX,IAAI,iBAAiB,cACjB,OAAO;CAGX,IAAI,iBAAiB,cAAc;EAC/B,IAAI,MAAM,SAAS,KAAK,mBACpB,OAAO;EAEX,IAAI,MAAM,SAAS,KAAK,kBACpB,OAAO;EAEX,IAAI,MAAM,SAAS,KAAK,aACpB,OAAO;CAEf;AAGJ;AAEA,SAAgB,sBAAsB,OAAgB,UAA0B;CAI5E,IAAI,UAAmB;CACvB,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,WAAW,MAAM,SAAS;EAEvD,MAAM,aAAa,2BADJ,iBAAiB,OACmB,CAAC;EACpD,IAAI,eAAe,KAAA,GAAW,OAAO;EACrC,MAAM,cAAc,2BAA2B,OAAO;EACtD,IAAI,gBAAgB,KAAA,GAAW,OAAO;EAEtC,UACI,OAAO,YAAY,YAAY,YAAY,QAAQ,WAAW,UACvD,QAAgC,QACjC,KAAA;CACd;CAEA,OAAO;AACX"}
|
package/package.json
CHANGED