typedapi-client-helpers 0.3.5 → 0.4.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/README.md +68 -28
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/utils/formatApiError.js +215 -0
- package/dist/cjs/utils/formatApiError.js.map +1 -0
- package/dist/cjs/utils/handleApiResponse.js +19 -28
- package/dist/cjs/utils/handleApiResponse.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/utils/formatApiError.js +212 -0
- package/dist/esm/utils/formatApiError.js.map +1 -0
- package/dist/esm/utils/handleApiResponse.js +19 -28
- package/dist/esm/utils/handleApiResponse.js.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/interfaces/ApiResult.d.ts +5 -1
- package/dist/types/interfaces/ApiResult.d.ts.map +1 -1
- package/dist/types/utils/formatApiError.d.ts +7 -0
- package/dist/types/utils/formatApiError.d.ts.map +1 -0
- package/dist/types/utils/handleApiResponse.d.ts.map +1 -1
- package/package.json +1 -1
- package/scripts/generate-api-minimal.mjs +50 -9
- package/scripts/openapi-generator/index.mjs +101 -15
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A small Fetch runtime and OpenAPI 3 TypeScript generator designed to work with `TypedApi.Swagger` on ASP.NET Core.
|
|
4
4
|
|
|
5
|
-
## Version 0.
|
|
5
|
+
## Version 0.4.0 highlights
|
|
6
6
|
|
|
7
7
|
* Supports `[TypedApiFilterForm]` endpoints through the `x-typedapi-filter-form` OpenAPI extension.
|
|
8
8
|
* Generates grouped filter-form arguments containing normal query values and `FilterFormValues`.
|
|
@@ -14,6 +14,8 @@ A small Fetch runtime and OpenAPI 3 TypeScript generator designed to work with `
|
|
|
14
14
|
* Consumes readable generic schema IDs such as `ApiPaginationResponseOfProjectModel`.
|
|
15
15
|
* Generates method-specific unions from typed `ProblemDetails` error responses.
|
|
16
16
|
* Validates TypedApi OpenAPI contract version 2.
|
|
17
|
+
* Converts HTTP and transport failures into a readable `error: string` for toasts, while preserving the original typed body in `rawError`.
|
|
18
|
+
* Formats ASP.NET Core validation errors, `ProblemDetails`, custom JSON/text errors, and unknown error objects without displaying `[object Object]`.
|
|
17
19
|
|
|
18
20
|
## Filter-form endpoints
|
|
19
21
|
|
|
@@ -165,9 +167,9 @@ export type EmailNotificationModel = NotificationModelBase & {
|
|
|
165
167
|
};
|
|
166
168
|
```
|
|
167
169
|
|
|
168
|
-
## Typed
|
|
170
|
+
## Typed error contracts
|
|
169
171
|
|
|
170
|
-
When
|
|
172
|
+
When an OpenAPI operation documents an error body, the generated method keeps its error type (or union) for `rawError`. `error` is always a human-readable `string`, suitable for a toast:
|
|
171
173
|
|
|
172
174
|
```ts
|
|
173
175
|
ApiResult<
|
|
@@ -176,10 +178,12 @@ ApiResult<
|
|
|
176
178
|
>
|
|
177
179
|
```
|
|
178
180
|
|
|
181
|
+
On failure, access the original documented payload through `result.rawError`; see [Error handling](#error-handling) for examples.
|
|
182
|
+
|
|
179
183
|
## Install
|
|
180
184
|
|
|
181
185
|
```bash
|
|
182
|
-
npm install typedapi-client-helpers@0.
|
|
186
|
+
npm install typedapi-client-helpers@0.4.0
|
|
183
187
|
```
|
|
184
188
|
|
|
185
189
|
The generator expects an OpenAPI 3.x document. Swagger 2.0 documents are rejected with a clear error.
|
|
@@ -470,40 +474,71 @@ It does not add:
|
|
|
470
474
|
* `sortBy`
|
|
471
475
|
* `sortDirection`
|
|
472
476
|
|
|
473
|
-
##
|
|
474
|
-
|
|
475
|
-
When the OpenAPI document describes error response bodies, generated methods expose their generated type or union.
|
|
477
|
+
## Error handling
|
|
476
478
|
|
|
477
|
-
|
|
479
|
+
Version 0.4.0 separates **display text** from **structured diagnostics** on every failed request:
|
|
478
480
|
|
|
479
481
|
```ts
|
|
480
|
-
const result = await cancelOrder({
|
|
481
|
-
id,
|
|
482
|
-
reason,
|
|
483
|
-
});
|
|
482
|
+
const result = await cancelOrder({ id, reason });
|
|
484
483
|
|
|
485
484
|
if (!result.ok) {
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
console.error(result.error);
|
|
493
|
-
}
|
|
485
|
+
// Always text: safe to show in a toast, alert, or form message.
|
|
486
|
+
console.error(result.error);
|
|
487
|
+
|
|
488
|
+
// Original error body / transport failure, preserving types and details.
|
|
489
|
+
console.debug(result.rawError);
|
|
490
|
+
console.debug(result.status);
|
|
494
491
|
}
|
|
495
492
|
```
|
|
496
493
|
|
|
497
|
-
|
|
494
|
+
Use `result.error` (not `result.rawError`) for a toast, e.g. `toast.error(result.error)` if your app uses a toast library. This also applies in `onError` callbacks. Existing success results and `onSuccess` callbacks are unchanged.
|
|
498
495
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
496
|
+
Typical failure result:
|
|
497
|
+
|
|
498
|
+
```ts
|
|
499
|
+
{
|
|
500
|
+
ok: false,
|
|
501
|
+
status: 400,
|
|
502
|
+
response: undefined,
|
|
503
|
+
error: "Setting value validation failed:\n• SettingValues[4].IntValue: 'Card transaction count notification threshold' must be between 1 and 100000 (inclusive).",
|
|
504
|
+
rawError: {
|
|
505
|
+
title: "Setting value validation failed",
|
|
506
|
+
status: 400,
|
|
507
|
+
errors: {
|
|
508
|
+
"SettingValues[4].IntValue": [
|
|
509
|
+
"'Card transaction count notification threshold' must be between 1 and 100000 (inclusive).",
|
|
510
|
+
],
|
|
511
|
+
},
|
|
512
|
+
},
|
|
513
|
+
}
|
|
514
|
+
```
|
|
503
515
|
|
|
504
|
-
|
|
516
|
+
The error message includes **every validation message**, retaining its original field path; the example above is shortened to one field. Messages are deduplicated. Other supported shapes include:
|
|
505
517
|
|
|
506
|
-
|
|
518
|
+
* ASP.NET Core `ValidationProblemDetails` and `HttpValidationProblemDetails`, `ModelState`, and validation arrays.
|
|
519
|
+
* RFC 7807 `ProblemDetails` (`title` and `detail`).
|
|
520
|
+
* JSON with `message`, `error`, `error_description`, or a nested `data` wrapper.
|
|
521
|
+
* Text responses and JSON-encoded strings.
|
|
522
|
+
* Undocumented/unknown JSON objects (formatted JSON rather than `[object Object]`).
|
|
523
|
+
* Empty HTTP error bodies (status-specific messages), binary bodies, network/abort/timeout errors, and malformed JSON.
|
|
524
|
+
|
|
525
|
+
For an HTTP response **without a documented OpenAPI error schema**, generated methods use `ApiHttpError` as the *typed raw error* instead of `unknown`. `result.rawError` can contain `{ kind: "http", status, body }`. Other client-generated raw errors have `kind: "network"`, `"aborted"`, or `"parse"`.
|
|
526
|
+
|
|
527
|
+
You can also format a payload outside the API client:
|
|
528
|
+
|
|
529
|
+
```ts
|
|
530
|
+
import { formatApiError } from "typedapi-client-helpers";
|
|
531
|
+
|
|
532
|
+
const message = formatApiError(
|
|
533
|
+
{ title: "Validation failed", errors: { Email: ["Email is required"] } },
|
|
534
|
+
400,
|
|
535
|
+
);
|
|
536
|
+
// "Validation failed:\n• Email: Email is required"
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
`handleApiResponse` supports a caller-provided `fallbackErrorMessage`, used when no usable error text or HTTP status is available. Unknown nonempty responses retain their content instead of being replaced by that fallback.
|
|
540
|
+
|
|
541
|
+
Malformed JSON yields a readable parsing error and preserves its raw response body in `rawError`. Exceptions thrown by consumer `onSuccess` or `onError` callbacks still propagate normally. See [`src/utils/README.md`](src/utils/README.md) and [`src/interfaces/README.md`](src/interfaces/README.md) for the full result shape.
|
|
507
542
|
|
|
508
543
|
## Runtime configuration
|
|
509
544
|
|
|
@@ -538,13 +573,14 @@ The runtime also exports:
|
|
|
538
573
|
* `toRequestHeaders`
|
|
539
574
|
* `toCookieHeader`
|
|
540
575
|
* `handleApiResponse`
|
|
576
|
+
* `formatApiError`
|
|
541
577
|
* `buildFilterQuery`
|
|
542
578
|
* `buildQuery`
|
|
543
579
|
* `toFormData`
|
|
544
580
|
|
|
545
581
|
## Backend pairing
|
|
546
582
|
|
|
547
|
-
Use `TypedApi.Swagger` 0.3.
|
|
583
|
+
Use `TypedApi.Swagger` 0.3.3 (or a compatible newer release) on the backend for:
|
|
548
584
|
|
|
549
585
|
* `[TypedApiFilterForm]`
|
|
550
586
|
* Generic reconstruction
|
|
@@ -556,3 +592,7 @@ Use `TypedApi.Swagger` 0.3.2 or newer on the backend for:
|
|
|
556
592
|
The backend package emits `x-typedapi-filter-form` for opted-in filter endpoints.
|
|
557
593
|
|
|
558
594
|
It also emits `x-typedapi.contractVersion = 2` for contract compatibility. The generator validates the contract version before replacing generated files.
|
|
595
|
+
|
|
596
|
+
## Tests
|
|
597
|
+
|
|
598
|
+
For the npm package alone, run `npm run verify` from `npm/typed-api-client` to type-check, build, and run **every** `test/*.test.mjs` file. From the repository root, `npm test` runs the npm suite, test client lint/build, .NET tests, and real HTTP integration tests. See [`../../TESTING.md`](../../TESTING.md) for prerequisites and details.
|
package/dist/cjs/index.js
CHANGED
|
@@ -22,6 +22,7 @@ __exportStar(require("./types/ApiMethodTypes"), exports);
|
|
|
22
22
|
__exportStar(require("./types/ApiCallOptions"), exports);
|
|
23
23
|
__exportStar(require("./types/ApiMethodOptions"), exports);
|
|
24
24
|
__exportStar(require("./utils/buildQuery"), exports);
|
|
25
|
+
__exportStar(require("./utils/formatApiError"), exports);
|
|
25
26
|
__exportStar(require("./utils/handleApiResponse"), exports);
|
|
26
27
|
__exportStar(require("./utils/SortTypeFromSortDirection"), exports);
|
|
27
28
|
__exportStar(require("./utils/toFormData"), exports);
|
package/dist/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+CAA6B;AAE7B,yDAAuC;AACvC,sDAAoC;AACpC,wDAAsC;AAEtC,yDAAuC;AACvC,yDAAuC;AACvC,2DAAyC;AAEzC,qDAAmC;AACnC,4DAA0C;AAC1C,oEAAkD;AAClD,qDAAmC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+CAA6B;AAE7B,yDAAuC;AACvC,sDAAoC;AACpC,wDAAsC;AAEtC,yDAAuC;AACvC,yDAAuC;AACvC,2DAAyC;AAEzC,qDAAmC;AACnC,yDAAuC;AACvC,4DAA0C;AAC1C,oEAAkD;AAClD,qDAAmC"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatApiError = formatApiError;
|
|
4
|
+
/** Format API errors for notifications without discarding the original payload. */
|
|
5
|
+
const defaultErrorMessage = "An unknown error occurred.";
|
|
6
|
+
const statusMessages = {
|
|
7
|
+
400: "Bad request",
|
|
8
|
+
401: "Authentication required",
|
|
9
|
+
403: "Access denied",
|
|
10
|
+
404: "Resource not found",
|
|
11
|
+
405: "Method not allowed",
|
|
12
|
+
408: "Request timed out",
|
|
13
|
+
409: "Conflict",
|
|
14
|
+
413: "Request too large",
|
|
15
|
+
415: "Unsupported media type",
|
|
16
|
+
422: "Validation failed",
|
|
17
|
+
429: "Too many requests",
|
|
18
|
+
500: "Internal server error",
|
|
19
|
+
502: "Bad gateway",
|
|
20
|
+
503: "Service unavailable",
|
|
21
|
+
504: "Gateway timeout",
|
|
22
|
+
};
|
|
23
|
+
function isRecord(value) {
|
|
24
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
25
|
+
}
|
|
26
|
+
function property(record, key) {
|
|
27
|
+
if (Object.prototype.hasOwnProperty.call(record, key))
|
|
28
|
+
return record[key];
|
|
29
|
+
const matchingKey = Object.keys(record).find((candidate) => candidate.toLowerCase() === key.toLowerCase());
|
|
30
|
+
return matchingKey === undefined ? undefined : record[matchingKey];
|
|
31
|
+
}
|
|
32
|
+
function nonEmptyString(value) {
|
|
33
|
+
if (typeof value !== "string")
|
|
34
|
+
return undefined;
|
|
35
|
+
const trimmed = value.trim();
|
|
36
|
+
return trimmed || undefined;
|
|
37
|
+
}
|
|
38
|
+
function readableJson(value) {
|
|
39
|
+
const seen = new WeakSet();
|
|
40
|
+
try {
|
|
41
|
+
const json = JSON.stringify(value, (_key, entry) => {
|
|
42
|
+
if (typeof entry === "bigint")
|
|
43
|
+
return entry.toString();
|
|
44
|
+
if (entry instanceof Error)
|
|
45
|
+
return { name: entry.name, message: entry.message };
|
|
46
|
+
if (entry && typeof entry === "object") {
|
|
47
|
+
if (seen.has(entry))
|
|
48
|
+
return "[Circular]";
|
|
49
|
+
seen.add(entry);
|
|
50
|
+
}
|
|
51
|
+
return entry;
|
|
52
|
+
}, 2);
|
|
53
|
+
if (json && json !== "{}" && json !== "[]")
|
|
54
|
+
return json;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Non-serializable input; use a status-based fallback below.
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
function parseJsonLikeString(text) {
|
|
62
|
+
if (!text.startsWith("{") && !text.startsWith("["))
|
|
63
|
+
return text;
|
|
64
|
+
try {
|
|
65
|
+
return JSON.parse(text);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return text;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** ASP.NET ValidationProblemDetails, ModelState and common errors arrays. */
|
|
72
|
+
function validationMessages(value) {
|
|
73
|
+
const messages = [];
|
|
74
|
+
const seen = new WeakSet();
|
|
75
|
+
let visited = 0;
|
|
76
|
+
function visit(current, field) {
|
|
77
|
+
if (++visited > 500 || current === null || current === undefined)
|
|
78
|
+
return;
|
|
79
|
+
if (typeof current === "string" || typeof current === "number" || typeof current === "boolean") {
|
|
80
|
+
const message = String(current).trim();
|
|
81
|
+
if (message)
|
|
82
|
+
messages.push(field ? `${field}: ${message}` : message);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (typeof current !== "object" || current instanceof Blob || seen.has(current))
|
|
86
|
+
return;
|
|
87
|
+
seen.add(current);
|
|
88
|
+
if (Array.isArray(current)) {
|
|
89
|
+
for (const item of current)
|
|
90
|
+
visit(item, field);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const record = current;
|
|
94
|
+
const message = nonEmptyString(property(record, "message"))
|
|
95
|
+
?? nonEmptyString(property(record, "errorMessage"))
|
|
96
|
+
?? nonEmptyString(property(record, "description"));
|
|
97
|
+
if (message) {
|
|
98
|
+
const specificField = nonEmptyString(property(record, "field"))
|
|
99
|
+
?? nonEmptyString(property(record, "property"))
|
|
100
|
+
?? nonEmptyString(property(record, "propertyName"))
|
|
101
|
+
?? field;
|
|
102
|
+
messages.push(specificField ? `${specificField}: ${message}` : message);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
for (const [key, child] of Object.entries(record)) {
|
|
106
|
+
// Preserve the validation property path rather than collapsing all messages into "[object Object]".
|
|
107
|
+
visit(child, field ? `${field}.${key}` : key);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
visit(value);
|
|
111
|
+
return [...new Set(messages)];
|
|
112
|
+
}
|
|
113
|
+
function fallback(status, fallbackMessage) {
|
|
114
|
+
const statusText = status ? statusMessages[status] : undefined;
|
|
115
|
+
if (statusText)
|
|
116
|
+
return `${statusText} (HTTP ${status}).`;
|
|
117
|
+
if (status)
|
|
118
|
+
return `Request failed (HTTP ${status}).`;
|
|
119
|
+
return nonEmptyString(fallbackMessage) ?? defaultErrorMessage;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Produce a readable string for .NET ProblemDetails, validation failures, plain-text
|
|
123
|
+
* errors, transport errors and unknown response shapes. Unknown payloads are returned
|
|
124
|
+
* as formatted JSON rather than being converted to "[object Object]".
|
|
125
|
+
*/
|
|
126
|
+
function formatApiError(error, status, fallbackMessage) {
|
|
127
|
+
const seen = new WeakSet();
|
|
128
|
+
function format(value, depth, httpStatus) {
|
|
129
|
+
if (depth > 10 || value === null || value === undefined)
|
|
130
|
+
return undefined;
|
|
131
|
+
if (typeof value === "string") {
|
|
132
|
+
const trimmed = value.trim();
|
|
133
|
+
if (!trimmed)
|
|
134
|
+
return undefined;
|
|
135
|
+
const parsed = parseJsonLikeString(trimmed);
|
|
136
|
+
return parsed === trimmed ? trimmed : format(parsed, depth + 1, httpStatus) ?? trimmed;
|
|
137
|
+
}
|
|
138
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
139
|
+
return String(value);
|
|
140
|
+
}
|
|
141
|
+
if (value instanceof Error) {
|
|
142
|
+
return nonEmptyString(value.message)
|
|
143
|
+
? `${value.name && value.name !== "Error" ? `${value.name}: ` : ""}${value.message}`
|
|
144
|
+
: value.name;
|
|
145
|
+
}
|
|
146
|
+
if (value instanceof Blob)
|
|
147
|
+
return `Server returned a ${value.type || "binary"} error response (${value.size} bytes).`;
|
|
148
|
+
if (typeof value !== "object" || seen.has(value))
|
|
149
|
+
return undefined;
|
|
150
|
+
seen.add(value);
|
|
151
|
+
if (Array.isArray(value)) {
|
|
152
|
+
const entries = value.map((item) => format(item, depth + 1, httpStatus)).filter((item) => !!item);
|
|
153
|
+
if (entries.length)
|
|
154
|
+
return entries.map((entry) => `• ${entry}`).join("\n");
|
|
155
|
+
return readableJson(value);
|
|
156
|
+
}
|
|
157
|
+
const record = value;
|
|
158
|
+
const kind = nonEmptyString(property(record, "kind"));
|
|
159
|
+
if (kind === "http") {
|
|
160
|
+
const wrappedStatus = property(record, "status");
|
|
161
|
+
const code = typeof wrappedStatus === "number" ? wrappedStatus : httpStatus;
|
|
162
|
+
return format(property(record, "body"), depth + 1, code) ?? fallback(code, fallbackMessage);
|
|
163
|
+
}
|
|
164
|
+
if (kind === "parse") {
|
|
165
|
+
const rawBody = nonEmptyString(property(record, "rawBody"));
|
|
166
|
+
const code = property(record, "status");
|
|
167
|
+
return `Invalid server response${typeof code === "number" ? ` (HTTP ${code})` : ""}: expected valid JSON${rawBody ? `. Received: ${rawBody}` : "."}`;
|
|
168
|
+
}
|
|
169
|
+
if (kind === "aborted")
|
|
170
|
+
return "Request was cancelled or timed out.";
|
|
171
|
+
if (kind === "network") {
|
|
172
|
+
const cause = format(property(record, "cause"), depth + 1, httpStatus);
|
|
173
|
+
return cause ? `Network error: ${cause}` : "Network error: Could not reach the server.";
|
|
174
|
+
}
|
|
175
|
+
const nestedData = property(record, "data");
|
|
176
|
+
const hasOwnMessage = property(record, "message") !== undefined
|
|
177
|
+
|| property(record, "detail") !== undefined
|
|
178
|
+
|| property(record, "title") !== undefined
|
|
179
|
+
|| property(record, "errors") !== undefined;
|
|
180
|
+
if (!hasOwnMessage && nestedData !== undefined) {
|
|
181
|
+
const result = format(nestedData, depth + 1, httpStatus);
|
|
182
|
+
if (result)
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
const title = nonEmptyString(property(record, "title"));
|
|
186
|
+
const detail = nonEmptyString(property(record, "detail"));
|
|
187
|
+
const message = nonEmptyString(property(record, "message"))
|
|
188
|
+
?? nonEmptyString(property(record, "error_description"))
|
|
189
|
+
?? nonEmptyString(property(record, "description"));
|
|
190
|
+
const errors = property(record, "errors") ?? property(record, "modelState");
|
|
191
|
+
const validation = errors === undefined ? [] : validationMessages(errors);
|
|
192
|
+
if (validation.length) {
|
|
193
|
+
const heading = title ?? message ?? detail ?? "Validation failed";
|
|
194
|
+
return `${heading}:\n${validation.map((entry) => `• ${entry}`).join("\n")}`;
|
|
195
|
+
}
|
|
196
|
+
if (detail && title && detail.toLowerCase() !== title.toLowerCase())
|
|
197
|
+
return `${title}: ${detail}`;
|
|
198
|
+
if (detail)
|
|
199
|
+
return detail;
|
|
200
|
+
if (message)
|
|
201
|
+
return message;
|
|
202
|
+
if (title)
|
|
203
|
+
return title;
|
|
204
|
+
const nestedError = property(record, "error");
|
|
205
|
+
if (nestedError !== undefined) {
|
|
206
|
+
const formatted = format(nestedError, depth + 1, httpStatus);
|
|
207
|
+
if (formatted)
|
|
208
|
+
return formatted;
|
|
209
|
+
}
|
|
210
|
+
// Do not discard undocumented error objects. Display their original fields.
|
|
211
|
+
return readableJson(value);
|
|
212
|
+
}
|
|
213
|
+
return format(error, 0, status) ?? fallback(status, fallbackMessage);
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=formatApiError.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatApiError.js","sourceRoot":"","sources":["../../../src/utils/formatApiError.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createApiHttpError = createApiHttpError;
|
|
4
4
|
exports.handleApiResponse = handleApiResponse;
|
|
5
|
-
const
|
|
5
|
+
const formatApiError_1 = require("./formatApiError");
|
|
6
6
|
class ResponseParseError extends Error {
|
|
7
7
|
constructor(status, rawBody, parseCause) {
|
|
8
8
|
super(`Could not parse JSON response with status ${status}.`);
|
|
@@ -49,7 +49,8 @@ function createApiHttpError(status, body) {
|
|
|
49
49
|
body: body,
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
|
-
|
|
52
|
+
/** Preserve the transport or parsing failure for callers that need details. */
|
|
53
|
+
function clientError(error) {
|
|
53
54
|
if (error instanceof ResponseParseError) {
|
|
54
55
|
return {
|
|
55
56
|
kind: "parse",
|
|
@@ -64,7 +65,17 @@ function clientError(error, fallbackErrorMessage) {
|
|
|
64
65
|
if (name === "AbortError" || name === "TimeoutError") {
|
|
65
66
|
return { kind: "aborted", cause: error };
|
|
66
67
|
}
|
|
67
|
-
return
|
|
68
|
+
return { kind: "network", cause: error };
|
|
69
|
+
}
|
|
70
|
+
/** Return an error as readable text but keep the original typed payload in `rawError`. */
|
|
71
|
+
function failure(status, rawError, fallbackErrorMessage) {
|
|
72
|
+
return {
|
|
73
|
+
ok: false,
|
|
74
|
+
status,
|
|
75
|
+
response: undefined,
|
|
76
|
+
error: (0, formatApiError_1.formatApiError)(rawError, status || undefined, fallbackErrorMessage),
|
|
77
|
+
rawError,
|
|
78
|
+
};
|
|
68
79
|
}
|
|
69
80
|
async function handleApiResponse(call, options) {
|
|
70
81
|
let result;
|
|
@@ -82,12 +93,7 @@ async function handleApiResponse(call, options) {
|
|
|
82
93
|
};
|
|
83
94
|
}
|
|
84
95
|
catch (error) {
|
|
85
|
-
result =
|
|
86
|
-
ok: false,
|
|
87
|
-
status: response.status,
|
|
88
|
-
response: undefined,
|
|
89
|
-
error: clientError(error, options?.fallbackErrorMessage),
|
|
90
|
-
};
|
|
96
|
+
result = failure(response.status, clientError(error), options?.fallbackErrorMessage);
|
|
91
97
|
}
|
|
92
98
|
}
|
|
93
99
|
catch (error) {
|
|
@@ -98,31 +104,16 @@ async function handleApiResponse(call, options) {
|
|
|
98
104
|
? options.transformError(rawErrorData, error)
|
|
99
105
|
: rawErrorData;
|
|
100
106
|
const errorData = transformedError === undefined
|
|
101
|
-
?
|
|
107
|
+
? createApiHttpError(error.status, undefined)
|
|
102
108
|
: transformedError;
|
|
103
|
-
result =
|
|
104
|
-
ok: false,
|
|
105
|
-
status: error.status,
|
|
106
|
-
response: undefined,
|
|
107
|
-
error: errorData,
|
|
108
|
-
};
|
|
109
|
+
result = failure(error.status, errorData, options?.fallbackErrorMessage);
|
|
109
110
|
}
|
|
110
111
|
catch (parseError) {
|
|
111
|
-
result =
|
|
112
|
-
ok: false,
|
|
113
|
-
status: error.status,
|
|
114
|
-
response: undefined,
|
|
115
|
-
error: clientError(parseError, options?.fallbackErrorMessage),
|
|
116
|
-
};
|
|
112
|
+
result = failure(error.status, clientError(parseError), options?.fallbackErrorMessage);
|
|
117
113
|
}
|
|
118
114
|
}
|
|
119
115
|
else {
|
|
120
|
-
result =
|
|
121
|
-
ok: false,
|
|
122
|
-
status: 0,
|
|
123
|
-
response: undefined,
|
|
124
|
-
error: clientError(error, options?.fallbackErrorMessage),
|
|
125
|
-
};
|
|
116
|
+
result = failure(0, clientError(error), options?.fallbackErrorMessage);
|
|
126
117
|
}
|
|
127
118
|
}
|
|
128
119
|
// Consumer callbacks intentionally execute outside the request-catching block.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handleApiResponse.js","sourceRoot":"","sources":["../../../src/utils/handleApiResponse.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"handleApiResponse.js","sourceRoot":"","sources":["../../../src/utils/handleApiResponse.ts"],"names":[],"mappings":"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"}
|
package/dist/esm/index.js
CHANGED
|
@@ -6,6 +6,7 @@ export * from "./types/ApiMethodTypes.js";
|
|
|
6
6
|
export * from "./types/ApiCallOptions.js";
|
|
7
7
|
export * from "./types/ApiMethodOptions.js";
|
|
8
8
|
export * from "./utils/buildQuery.js";
|
|
9
|
+
export * from "./utils/formatApiError.js";
|
|
9
10
|
export * from "./utils/handleApiResponse.js";
|
|
10
11
|
export * from "./utils/SortTypeFromSortDirection.js";
|
|
11
12
|
export * from "./utils/toFormData.js";
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAE7B,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AAEtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AAEzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mCAAmC,CAAC;AAClD,cAAc,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAE7B,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AAEtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AAEzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mCAAmC,CAAC;AAClD,cAAc,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/** Format API errors for notifications without discarding the original payload. */
|
|
2
|
+
const defaultErrorMessage = "An unknown error occurred.";
|
|
3
|
+
const statusMessages = {
|
|
4
|
+
400: "Bad request",
|
|
5
|
+
401: "Authentication required",
|
|
6
|
+
403: "Access denied",
|
|
7
|
+
404: "Resource not found",
|
|
8
|
+
405: "Method not allowed",
|
|
9
|
+
408: "Request timed out",
|
|
10
|
+
409: "Conflict",
|
|
11
|
+
413: "Request too large",
|
|
12
|
+
415: "Unsupported media type",
|
|
13
|
+
422: "Validation failed",
|
|
14
|
+
429: "Too many requests",
|
|
15
|
+
500: "Internal server error",
|
|
16
|
+
502: "Bad gateway",
|
|
17
|
+
503: "Service unavailable",
|
|
18
|
+
504: "Gateway timeout",
|
|
19
|
+
};
|
|
20
|
+
function isRecord(value) {
|
|
21
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22
|
+
}
|
|
23
|
+
function property(record, key) {
|
|
24
|
+
if (Object.prototype.hasOwnProperty.call(record, key))
|
|
25
|
+
return record[key];
|
|
26
|
+
const matchingKey = Object.keys(record).find((candidate) => candidate.toLowerCase() === key.toLowerCase());
|
|
27
|
+
return matchingKey === undefined ? undefined : record[matchingKey];
|
|
28
|
+
}
|
|
29
|
+
function nonEmptyString(value) {
|
|
30
|
+
if (typeof value !== "string")
|
|
31
|
+
return undefined;
|
|
32
|
+
const trimmed = value.trim();
|
|
33
|
+
return trimmed || undefined;
|
|
34
|
+
}
|
|
35
|
+
function readableJson(value) {
|
|
36
|
+
const seen = new WeakSet();
|
|
37
|
+
try {
|
|
38
|
+
const json = JSON.stringify(value, (_key, entry) => {
|
|
39
|
+
if (typeof entry === "bigint")
|
|
40
|
+
return entry.toString();
|
|
41
|
+
if (entry instanceof Error)
|
|
42
|
+
return { name: entry.name, message: entry.message };
|
|
43
|
+
if (entry && typeof entry === "object") {
|
|
44
|
+
if (seen.has(entry))
|
|
45
|
+
return "[Circular]";
|
|
46
|
+
seen.add(entry);
|
|
47
|
+
}
|
|
48
|
+
return entry;
|
|
49
|
+
}, 2);
|
|
50
|
+
if (json && json !== "{}" && json !== "[]")
|
|
51
|
+
return json;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// Non-serializable input; use a status-based fallback below.
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
function parseJsonLikeString(text) {
|
|
59
|
+
if (!text.startsWith("{") && !text.startsWith("["))
|
|
60
|
+
return text;
|
|
61
|
+
try {
|
|
62
|
+
return JSON.parse(text);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return text;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/** ASP.NET ValidationProblemDetails, ModelState and common errors arrays. */
|
|
69
|
+
function validationMessages(value) {
|
|
70
|
+
const messages = [];
|
|
71
|
+
const seen = new WeakSet();
|
|
72
|
+
let visited = 0;
|
|
73
|
+
function visit(current, field) {
|
|
74
|
+
if (++visited > 500 || current === null || current === undefined)
|
|
75
|
+
return;
|
|
76
|
+
if (typeof current === "string" || typeof current === "number" || typeof current === "boolean") {
|
|
77
|
+
const message = String(current).trim();
|
|
78
|
+
if (message)
|
|
79
|
+
messages.push(field ? `${field}: ${message}` : message);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (typeof current !== "object" || current instanceof Blob || seen.has(current))
|
|
83
|
+
return;
|
|
84
|
+
seen.add(current);
|
|
85
|
+
if (Array.isArray(current)) {
|
|
86
|
+
for (const item of current)
|
|
87
|
+
visit(item, field);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const record = current;
|
|
91
|
+
const message = nonEmptyString(property(record, "message"))
|
|
92
|
+
?? nonEmptyString(property(record, "errorMessage"))
|
|
93
|
+
?? nonEmptyString(property(record, "description"));
|
|
94
|
+
if (message) {
|
|
95
|
+
const specificField = nonEmptyString(property(record, "field"))
|
|
96
|
+
?? nonEmptyString(property(record, "property"))
|
|
97
|
+
?? nonEmptyString(property(record, "propertyName"))
|
|
98
|
+
?? field;
|
|
99
|
+
messages.push(specificField ? `${specificField}: ${message}` : message);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
for (const [key, child] of Object.entries(record)) {
|
|
103
|
+
// Preserve the validation property path rather than collapsing all messages into "[object Object]".
|
|
104
|
+
visit(child, field ? `${field}.${key}` : key);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
visit(value);
|
|
108
|
+
return [...new Set(messages)];
|
|
109
|
+
}
|
|
110
|
+
function fallback(status, fallbackMessage) {
|
|
111
|
+
const statusText = status ? statusMessages[status] : undefined;
|
|
112
|
+
if (statusText)
|
|
113
|
+
return `${statusText} (HTTP ${status}).`;
|
|
114
|
+
if (status)
|
|
115
|
+
return `Request failed (HTTP ${status}).`;
|
|
116
|
+
return nonEmptyString(fallbackMessage) ?? defaultErrorMessage;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Produce a readable string for .NET ProblemDetails, validation failures, plain-text
|
|
120
|
+
* errors, transport errors and unknown response shapes. Unknown payloads are returned
|
|
121
|
+
* as formatted JSON rather than being converted to "[object Object]".
|
|
122
|
+
*/
|
|
123
|
+
export function formatApiError(error, status, fallbackMessage) {
|
|
124
|
+
const seen = new WeakSet();
|
|
125
|
+
function format(value, depth, httpStatus) {
|
|
126
|
+
if (depth > 10 || value === null || value === undefined)
|
|
127
|
+
return undefined;
|
|
128
|
+
if (typeof value === "string") {
|
|
129
|
+
const trimmed = value.trim();
|
|
130
|
+
if (!trimmed)
|
|
131
|
+
return undefined;
|
|
132
|
+
const parsed = parseJsonLikeString(trimmed);
|
|
133
|
+
return parsed === trimmed ? trimmed : format(parsed, depth + 1, httpStatus) ?? trimmed;
|
|
134
|
+
}
|
|
135
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
136
|
+
return String(value);
|
|
137
|
+
}
|
|
138
|
+
if (value instanceof Error) {
|
|
139
|
+
return nonEmptyString(value.message)
|
|
140
|
+
? `${value.name && value.name !== "Error" ? `${value.name}: ` : ""}${value.message}`
|
|
141
|
+
: value.name;
|
|
142
|
+
}
|
|
143
|
+
if (value instanceof Blob)
|
|
144
|
+
return `Server returned a ${value.type || "binary"} error response (${value.size} bytes).`;
|
|
145
|
+
if (typeof value !== "object" || seen.has(value))
|
|
146
|
+
return undefined;
|
|
147
|
+
seen.add(value);
|
|
148
|
+
if (Array.isArray(value)) {
|
|
149
|
+
const entries = value.map((item) => format(item, depth + 1, httpStatus)).filter((item) => !!item);
|
|
150
|
+
if (entries.length)
|
|
151
|
+
return entries.map((entry) => `• ${entry}`).join("\n");
|
|
152
|
+
return readableJson(value);
|
|
153
|
+
}
|
|
154
|
+
const record = value;
|
|
155
|
+
const kind = nonEmptyString(property(record, "kind"));
|
|
156
|
+
if (kind === "http") {
|
|
157
|
+
const wrappedStatus = property(record, "status");
|
|
158
|
+
const code = typeof wrappedStatus === "number" ? wrappedStatus : httpStatus;
|
|
159
|
+
return format(property(record, "body"), depth + 1, code) ?? fallback(code, fallbackMessage);
|
|
160
|
+
}
|
|
161
|
+
if (kind === "parse") {
|
|
162
|
+
const rawBody = nonEmptyString(property(record, "rawBody"));
|
|
163
|
+
const code = property(record, "status");
|
|
164
|
+
return `Invalid server response${typeof code === "number" ? ` (HTTP ${code})` : ""}: expected valid JSON${rawBody ? `. Received: ${rawBody}` : "."}`;
|
|
165
|
+
}
|
|
166
|
+
if (kind === "aborted")
|
|
167
|
+
return "Request was cancelled or timed out.";
|
|
168
|
+
if (kind === "network") {
|
|
169
|
+
const cause = format(property(record, "cause"), depth + 1, httpStatus);
|
|
170
|
+
return cause ? `Network error: ${cause}` : "Network error: Could not reach the server.";
|
|
171
|
+
}
|
|
172
|
+
const nestedData = property(record, "data");
|
|
173
|
+
const hasOwnMessage = property(record, "message") !== undefined
|
|
174
|
+
|| property(record, "detail") !== undefined
|
|
175
|
+
|| property(record, "title") !== undefined
|
|
176
|
+
|| property(record, "errors") !== undefined;
|
|
177
|
+
if (!hasOwnMessage && nestedData !== undefined) {
|
|
178
|
+
const result = format(nestedData, depth + 1, httpStatus);
|
|
179
|
+
if (result)
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
const title = nonEmptyString(property(record, "title"));
|
|
183
|
+
const detail = nonEmptyString(property(record, "detail"));
|
|
184
|
+
const message = nonEmptyString(property(record, "message"))
|
|
185
|
+
?? nonEmptyString(property(record, "error_description"))
|
|
186
|
+
?? nonEmptyString(property(record, "description"));
|
|
187
|
+
const errors = property(record, "errors") ?? property(record, "modelState");
|
|
188
|
+
const validation = errors === undefined ? [] : validationMessages(errors);
|
|
189
|
+
if (validation.length) {
|
|
190
|
+
const heading = title ?? message ?? detail ?? "Validation failed";
|
|
191
|
+
return `${heading}:\n${validation.map((entry) => `• ${entry}`).join("\n")}`;
|
|
192
|
+
}
|
|
193
|
+
if (detail && title && detail.toLowerCase() !== title.toLowerCase())
|
|
194
|
+
return `${title}: ${detail}`;
|
|
195
|
+
if (detail)
|
|
196
|
+
return detail;
|
|
197
|
+
if (message)
|
|
198
|
+
return message;
|
|
199
|
+
if (title)
|
|
200
|
+
return title;
|
|
201
|
+
const nestedError = property(record, "error");
|
|
202
|
+
if (nestedError !== undefined) {
|
|
203
|
+
const formatted = format(nestedError, depth + 1, httpStatus);
|
|
204
|
+
if (formatted)
|
|
205
|
+
return formatted;
|
|
206
|
+
}
|
|
207
|
+
// Do not discard undocumented error objects. Display their original fields.
|
|
208
|
+
return readableJson(value);
|
|
209
|
+
}
|
|
210
|
+
return format(error, 0, status) ?? fallback(status, fallbackMessage);
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=formatApiError.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatApiError.js","sourceRoot":"","sources":["../../../src/utils/formatApiError.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import { formatApiError } from "./formatApiError.js";
|
|
2
2
|
class ResponseParseError extends Error {
|
|
3
3
|
constructor(status, rawBody, parseCause) {
|
|
4
4
|
super(`Could not parse JSON response with status ${status}.`);
|
|
@@ -45,7 +45,8 @@ export function createApiHttpError(status, body) {
|
|
|
45
45
|
body: body,
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
|
-
|
|
48
|
+
/** Preserve the transport or parsing failure for callers that need details. */
|
|
49
|
+
function clientError(error) {
|
|
49
50
|
if (error instanceof ResponseParseError) {
|
|
50
51
|
return {
|
|
51
52
|
kind: "parse",
|
|
@@ -60,7 +61,17 @@ function clientError(error, fallbackErrorMessage) {
|
|
|
60
61
|
if (name === "AbortError" || name === "TimeoutError") {
|
|
61
62
|
return { kind: "aborted", cause: error };
|
|
62
63
|
}
|
|
63
|
-
return
|
|
64
|
+
return { kind: "network", cause: error };
|
|
65
|
+
}
|
|
66
|
+
/** Return an error as readable text but keep the original typed payload in `rawError`. */
|
|
67
|
+
function failure(status, rawError, fallbackErrorMessage) {
|
|
68
|
+
return {
|
|
69
|
+
ok: false,
|
|
70
|
+
status,
|
|
71
|
+
response: undefined,
|
|
72
|
+
error: formatApiError(rawError, status || undefined, fallbackErrorMessage),
|
|
73
|
+
rawError,
|
|
74
|
+
};
|
|
64
75
|
}
|
|
65
76
|
export async function handleApiResponse(call, options) {
|
|
66
77
|
let result;
|
|
@@ -78,12 +89,7 @@ export async function handleApiResponse(call, options) {
|
|
|
78
89
|
};
|
|
79
90
|
}
|
|
80
91
|
catch (error) {
|
|
81
|
-
result =
|
|
82
|
-
ok: false,
|
|
83
|
-
status: response.status,
|
|
84
|
-
response: undefined,
|
|
85
|
-
error: clientError(error, options?.fallbackErrorMessage),
|
|
86
|
-
};
|
|
92
|
+
result = failure(response.status, clientError(error), options?.fallbackErrorMessage);
|
|
87
93
|
}
|
|
88
94
|
}
|
|
89
95
|
catch (error) {
|
|
@@ -94,31 +100,16 @@ export async function handleApiResponse(call, options) {
|
|
|
94
100
|
? options.transformError(rawErrorData, error)
|
|
95
101
|
: rawErrorData;
|
|
96
102
|
const errorData = transformedError === undefined
|
|
97
|
-
?
|
|
103
|
+
? createApiHttpError(error.status, undefined)
|
|
98
104
|
: transformedError;
|
|
99
|
-
result =
|
|
100
|
-
ok: false,
|
|
101
|
-
status: error.status,
|
|
102
|
-
response: undefined,
|
|
103
|
-
error: errorData,
|
|
104
|
-
};
|
|
105
|
+
result = failure(error.status, errorData, options?.fallbackErrorMessage);
|
|
105
106
|
}
|
|
106
107
|
catch (parseError) {
|
|
107
|
-
result =
|
|
108
|
-
ok: false,
|
|
109
|
-
status: error.status,
|
|
110
|
-
response: undefined,
|
|
111
|
-
error: clientError(parseError, options?.fallbackErrorMessage),
|
|
112
|
-
};
|
|
108
|
+
result = failure(error.status, clientError(parseError), options?.fallbackErrorMessage);
|
|
113
109
|
}
|
|
114
110
|
}
|
|
115
111
|
else {
|
|
116
|
-
result =
|
|
117
|
-
ok: false,
|
|
118
|
-
status: 0,
|
|
119
|
-
response: undefined,
|
|
120
|
-
error: clientError(error, options?.fallbackErrorMessage),
|
|
121
|
-
};
|
|
112
|
+
result = failure(0, clientError(error), options?.fallbackErrorMessage);
|
|
122
113
|
}
|
|
123
114
|
}
|
|
124
115
|
// Consumer callbacks intentionally execute outside the request-catching block.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handleApiResponse.js","sourceRoot":"","sources":["../../../src/utils/handleApiResponse.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"handleApiResponse.js","sourceRoot":"","sources":["../../../src/utils/handleApiResponse.ts"],"names":[],"mappings":"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"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export * from "./types/ApiMethodTypes";
|
|
|
6
6
|
export * from "./types/ApiCallOptions";
|
|
7
7
|
export * from "./types/ApiMethodOptions";
|
|
8
8
|
export * from "./utils/buildQuery";
|
|
9
|
+
export * from "./utils/formatApiError";
|
|
9
10
|
export * from "./utils/handleApiResponse";
|
|
10
11
|
export * from "./utils/SortTypeFromSortDirection";
|
|
11
12
|
export * from "./utils/toFormData";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAE7B,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AAEtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AAEzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mCAAmC,CAAC;AAClD,cAAc,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAE7B,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AAEtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AAEzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mCAAmC,CAAC;AAClD,cAAc,oBAAoB,CAAC"}
|
|
@@ -26,11 +26,15 @@ export type ApiResult<TResponse, TError = unknown> = {
|
|
|
26
26
|
status: number;
|
|
27
27
|
response: TResponse;
|
|
28
28
|
error?: never;
|
|
29
|
+
rawError?: never;
|
|
29
30
|
} | {
|
|
30
31
|
ok: false;
|
|
31
32
|
status: number;
|
|
32
33
|
response?: undefined;
|
|
33
|
-
error
|
|
34
|
+
/** Human-readable error text, safe for toast notifications and UI messages. */
|
|
35
|
+
error: string;
|
|
36
|
+
/** Original typed error response or client error, without information loss. */
|
|
37
|
+
rawError: TError | ApiHttpError | ApiClientError | string;
|
|
34
38
|
};
|
|
35
39
|
export type ApiSuccessResult<TResponse, TError = unknown> = Extract<ApiResult<TResponse, TError>, {
|
|
36
40
|
ok: true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApiResult.d.ts","sourceRoot":"","sources":["../../../src/interfaces/ApiResult.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GACxB,MAAM,GACN,MAAM,GACN,OAAO,GACP,IAAI,GACJ,IAAI,GACJ,SAAS,GACT,gBAAgB,EAAE,GAClB;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAAA;CAAE,CAAC;AAExC,wEAAwE;AACxE,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,gBAAgB,CAAC;CACxB,CAAC;AAEF,0FAA0F;AAC1F,MAAM,MAAM,cAAc,GACtB;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;CAChB,GACD;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;CAChB,GACD;IACE,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEN,yEAAyE;AACzE,MAAM,MAAM,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,IAC7C;IACE,EAAE,EAAE,IAAI,CAAC;IACT,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,SAAS,CAAC;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"ApiResult.d.ts","sourceRoot":"","sources":["../../../src/interfaces/ApiResult.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GACxB,MAAM,GACN,MAAM,GACN,OAAO,GACP,IAAI,GACJ,IAAI,GACJ,SAAS,GACT,gBAAgB,EAAE,GAClB;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAAA;CAAE,CAAC;AAExC,wEAAwE;AACxE,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,gBAAgB,CAAC;CACxB,CAAC;AAEF,0FAA0F;AAC1F,MAAM,MAAM,cAAc,GACtB;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;CAChB,GACD;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;CAChB,GACD;IACE,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEN,yEAAyE;AACzE,MAAM,MAAM,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,IAC7C;IACE,EAAE,EAAE,IAAI,CAAC;IACT,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,SAAS,CAAC;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,QAAQ,CAAC,EAAE,KAAK,CAAC;CAClB,GACD;IACE,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,+EAA+E;IAC/E,KAAK,EAAE,MAAM,CAAC;IACd,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,cAAc,GAAG,MAAM,CAAC;CAC3D,CAAC;AAEN,MAAM,MAAM,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,IAAI,OAAO,CACjE,SAAS,CAAC,SAAS,EAAE,MAAM,CAAC,EAC5B;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,CACb,CAAC;AAEF,MAAM,MAAM,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,IAAI,OAAO,CAC/D,SAAS,CAAC,SAAS,EAAE,MAAM,CAAC,EAC5B;IAAE,EAAE,EAAE,KAAK,CAAA;CAAE,CACd,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Produce a readable string for .NET ProblemDetails, validation failures, plain-text
|
|
3
|
+
* errors, transport errors and unknown response shapes. Unknown payloads are returned
|
|
4
|
+
* as formatted JSON rather than being converted to "[object Object]".
|
|
5
|
+
*/
|
|
6
|
+
export declare function formatApiError(error: unknown, status?: number, fallbackMessage?: string): string;
|
|
7
|
+
//# sourceMappingURL=formatApiError.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatApiError.d.ts","sourceRoot":"","sources":["../../../src/utils/formatApiError.ts"],"names":[],"mappings":"AAmHA;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAqFhG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handleApiResponse.d.ts","sourceRoot":"","sources":["../../../src/utils/handleApiResponse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,YAAY,EAAoB,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACzG,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"handleApiResponse.d.ts","sourceRoot":"","sources":["../../../src/utils/handleApiResponse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,YAAY,EAAoB,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACzG,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAGlD,MAAM,MAAM,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,IAAI;IAClE,SAAS,CAAC,EAAE,iBAAiB,CAAC,SAAS,CAAC,CAAC;IACzC,OAAO,CAAC,EAAE,eAAe,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAC7C,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,SAAS,CAAC;IAClD,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,KAAK,MAAM,CAAC;IAChE,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B,CAAC;AAkDF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,YAAY,CAM9E;AAuCD,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,EACjE,IAAI,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,EACpD,OAAO,CAAC,EAAE,wBAAwB,CAAC,SAAS,EAAE,MAAM,CAAC,GACpD,OAAO,CAAC,SAAS,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CA0CvC"}
|
package/package.json
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
cleanupGenerationArtifacts,
|
|
7
|
+
generateApi,
|
|
8
|
+
} from "./openapi-generator/index.mjs";
|
|
6
9
|
|
|
7
10
|
const __filename = fileURLToPath(import.meta.url);
|
|
8
11
|
const __dirname = path.dirname(__filename);
|
|
@@ -93,6 +96,28 @@ const apiRoot = path.resolve(
|
|
|
93
96
|
),
|
|
94
97
|
);
|
|
95
98
|
|
|
99
|
+
let terminationSignalHandled = false;
|
|
100
|
+
|
|
101
|
+
function handleTerminationSignal(signal) {
|
|
102
|
+
if (terminationSignalHandled) return;
|
|
103
|
+
|
|
104
|
+
terminationSignalHandled = true;
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
cleanupGenerationArtifacts(apiRoot);
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.error(
|
|
110
|
+
`Failed to clean TypedApi temporary files after ${signal}:`,
|
|
111
|
+
error,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
process.exit(signal === "SIGINT" ? 130 : 143);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
process.once("SIGINT", () => handleTerminationSignal("SIGINT"));
|
|
119
|
+
process.once("SIGTERM", () => handleTerminationSignal("SIGTERM"));
|
|
120
|
+
|
|
96
121
|
const methodsDir = path.join(apiRoot, "methods");
|
|
97
122
|
|
|
98
123
|
const defaultFunctionsPathSetting = getStringSetting(
|
|
@@ -222,7 +247,8 @@ function getMethodNameStyle(...values) {
|
|
|
222
247
|
const normalized = value.replace(/[\s_-]/g, "").toLowerCase();
|
|
223
248
|
|
|
224
249
|
if (["operationid", "operation"].includes(normalized)) return "operationId";
|
|
225
|
-
if (["action", "actionname", "controllermethod"].includes(normalized))
|
|
250
|
+
if (["action", "actionname", "controllermethod"].includes(normalized))
|
|
251
|
+
return "action";
|
|
226
252
|
|
|
227
253
|
throw new Error(
|
|
228
254
|
`Invalid typedApiMethodNameStyle ${JSON.stringify(value)}. Use "operationId" or "action".`,
|
|
@@ -284,7 +310,12 @@ function copySwaggerBackupFromFile(filePath) {
|
|
|
284
310
|
async function downloadSwaggerBackupFromUrl(url) {
|
|
285
311
|
const controller = new AbortController();
|
|
286
312
|
const timeout = setTimeout(
|
|
287
|
-
() =>
|
|
313
|
+
() =>
|
|
314
|
+
controller.abort(
|
|
315
|
+
new Error(
|
|
316
|
+
`Swagger download timed out after ${swaggerDownloadTimeoutMs}ms.`,
|
|
317
|
+
),
|
|
318
|
+
),
|
|
288
319
|
swaggerDownloadTimeoutMs,
|
|
289
320
|
);
|
|
290
321
|
let response;
|
|
@@ -323,7 +354,9 @@ function useBackupSwaggerInput(reason) {
|
|
|
323
354
|
async function resolveSwaggerInput() {
|
|
324
355
|
if (offlineMode) {
|
|
325
356
|
if (!fileExists(swaggerBackupPath)) {
|
|
326
|
-
throw new Error(
|
|
357
|
+
throw new Error(
|
|
358
|
+
`Offline mode requires an existing Swagger backup: ${swaggerBackupPath}`,
|
|
359
|
+
);
|
|
327
360
|
}
|
|
328
361
|
return { input: swaggerBackupPath, label: swaggerBackupPath };
|
|
329
362
|
}
|
|
@@ -342,7 +375,9 @@ async function resolveSwaggerInput() {
|
|
|
342
375
|
|
|
343
376
|
const backupInput = strictMode
|
|
344
377
|
? undefined
|
|
345
|
-
: useBackupSwaggerInput(
|
|
378
|
+
: useBackupSwaggerInput(
|
|
379
|
+
`Swagger file was not found: ${swaggerFilePath}.`,
|
|
380
|
+
);
|
|
346
381
|
|
|
347
382
|
if (backupInput) return backupInput;
|
|
348
383
|
|
|
@@ -452,7 +487,6 @@ export const ${defaultErrorMessageName} = "An unknown error occurred.";
|
|
|
452
487
|
return resolvedFilePath;
|
|
453
488
|
}
|
|
454
489
|
|
|
455
|
-
|
|
456
490
|
function addDefaultErrorMessageIfMissing(filePath) {
|
|
457
491
|
const source = fs.readFileSync(filePath, "utf8");
|
|
458
492
|
if (fileExportsName(source, defaultErrorMessageName)) return;
|
|
@@ -527,10 +561,13 @@ function resolveDefaultHandlers() {
|
|
|
527
561
|
? defaultFunctionsFilePath
|
|
528
562
|
: `${defaultFunctionsFilePath}.ts`;
|
|
529
563
|
if (checkMode && !fs.existsSync(candidatePath)) {
|
|
530
|
-
if (verboseMode)
|
|
564
|
+
if (verboseMode)
|
|
565
|
+
console.log("Skipping missing default handler file in check mode.");
|
|
531
566
|
return undefined;
|
|
532
567
|
}
|
|
533
|
-
const defaultFunctionsFile = createDefaultFunctionsFileIfMissing(
|
|
568
|
+
const defaultFunctionsFile = createDefaultFunctionsFileIfMissing(
|
|
569
|
+
defaultFunctionsFilePath,
|
|
570
|
+
);
|
|
534
571
|
const source = fs.readFileSync(defaultFunctionsFile, "utf8");
|
|
535
572
|
const hasSuccessHandler = fileExportsName(source, defaultSuccessHandlerName);
|
|
536
573
|
const hasErrorHandler = fileExportsName(source, defaultErrorHandlerName);
|
|
@@ -562,6 +599,8 @@ function resolveDefaultHandlers() {
|
|
|
562
599
|
}
|
|
563
600
|
|
|
564
601
|
async function main() {
|
|
602
|
+
cleanupGenerationArtifacts(apiRoot);
|
|
603
|
+
|
|
565
604
|
const swaggerInput = await resolveSwaggerInput();
|
|
566
605
|
|
|
567
606
|
console.log(`Generating API from: ${swaggerInput.label}`);
|
|
@@ -588,7 +627,9 @@ async function main() {
|
|
|
588
627
|
);
|
|
589
628
|
}
|
|
590
629
|
|
|
591
|
-
console.log(
|
|
630
|
+
console.log(
|
|
631
|
+
checkMode ? "Generated API is up to date." : "API generation completed.",
|
|
632
|
+
);
|
|
592
633
|
}
|
|
593
634
|
|
|
594
635
|
main().catch((error) => {
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
collectOperations,
|
|
6
|
+
validateOpenApiDocument,
|
|
7
|
+
} from "./openapi-utils.mjs";
|
|
5
8
|
import { generateDataContracts } from "./schema.mjs";
|
|
6
9
|
import { generateHttpClient } from "./http-client-template.mjs";
|
|
7
10
|
import {
|
|
@@ -54,13 +57,18 @@ export async function generateApi(options) {
|
|
|
54
57
|
|
|
55
58
|
const controllers = new Map();
|
|
56
59
|
for (const operationInfo of operations) {
|
|
57
|
-
const controllerName = controllerNameForOperation(
|
|
60
|
+
const controllerName = controllerNameForOperation(
|
|
61
|
+
operationInfo,
|
|
62
|
+
normalizedOptions,
|
|
63
|
+
);
|
|
58
64
|
const controllerOperations = controllers.get(controllerName) ?? [];
|
|
59
65
|
controllerOperations.push(operationInfo);
|
|
60
66
|
controllers.set(controllerName, controllerOperations);
|
|
61
67
|
}
|
|
62
68
|
|
|
63
|
-
const controllerNames = [...controllers.keys()].sort((a, b) =>
|
|
69
|
+
const controllerNames = [...controllers.keys()].sort((a, b) =>
|
|
70
|
+
a.localeCompare(b),
|
|
71
|
+
);
|
|
64
72
|
for (const controllerName of controllerNames) {
|
|
65
73
|
files.set(
|
|
66
74
|
`methods/${methodFileNameForController(controllerName)}`,
|
|
@@ -73,7 +81,6 @@ export async function generateApi(options) {
|
|
|
73
81
|
);
|
|
74
82
|
}
|
|
75
83
|
|
|
76
|
-
|
|
77
84
|
const changes = compareOutput(output, files);
|
|
78
85
|
if (options.check) {
|
|
79
86
|
if (changes.length > 0) {
|
|
@@ -81,7 +88,10 @@ export async function generateApi(options) {
|
|
|
81
88
|
`Generated API is out of date:\n${changes.map((item) => `- ${item}`).join("\n")}`,
|
|
82
89
|
);
|
|
83
90
|
}
|
|
84
|
-
return {
|
|
91
|
+
return {
|
|
92
|
+
files: [...files.keys()].map((file) => path.join(output, file)),
|
|
93
|
+
changed: false,
|
|
94
|
+
};
|
|
85
95
|
}
|
|
86
96
|
|
|
87
97
|
const writeStrategy = writeOutputTransactionally(
|
|
@@ -105,7 +115,8 @@ function compareOutput(output, files) {
|
|
|
105
115
|
changes.push(`${relativePath} is missing`);
|
|
106
116
|
continue;
|
|
107
117
|
}
|
|
108
|
-
if (fs.readFileSync(target, "utf8") !== expected)
|
|
118
|
+
if (fs.readFileSync(target, "utf8") !== expected)
|
|
119
|
+
changes.push(`${relativePath} differs`);
|
|
109
120
|
}
|
|
110
121
|
|
|
111
122
|
for (const relativePath of listManagedFiles(output)) {
|
|
@@ -123,7 +134,9 @@ function listManagedFiles(output) {
|
|
|
123
134
|
result.push(topLevel);
|
|
124
135
|
continue;
|
|
125
136
|
}
|
|
126
|
-
walk(target, (file) =>
|
|
137
|
+
walk(target, (file) =>
|
|
138
|
+
result.push(path.relative(output, file).replaceAll(path.sep, "/")),
|
|
139
|
+
);
|
|
127
140
|
}
|
|
128
141
|
return result;
|
|
129
142
|
}
|
|
@@ -142,6 +155,61 @@ function removeManagedOutput(root) {
|
|
|
142
155
|
}
|
|
143
156
|
}
|
|
144
157
|
|
|
158
|
+
export function cleanupGenerationArtifacts(output) {
|
|
159
|
+
const resolvedOutput = path.resolve(output);
|
|
160
|
+
const parent = path.dirname(resolvedOutput);
|
|
161
|
+
|
|
162
|
+
if (!fs.existsSync(parent)) return;
|
|
163
|
+
|
|
164
|
+
const outputName = path.basename(resolvedOutput);
|
|
165
|
+
const stagingPrefix = `.${outputName}.typedapi-staging-`;
|
|
166
|
+
const backupPrefix = `.${outputName}.typedapi-backup-`;
|
|
167
|
+
const managedBackupPrefix = `.${outputName}.typedapi-managed-backup-`;
|
|
168
|
+
const backups = [];
|
|
169
|
+
|
|
170
|
+
for (const entry of fs.readdirSync(parent, { withFileTypes: true })) {
|
|
171
|
+
if (!entry.isDirectory()) continue;
|
|
172
|
+
|
|
173
|
+
const entryPath = path.join(parent, entry.name);
|
|
174
|
+
|
|
175
|
+
if (
|
|
176
|
+
entry.name.startsWith(stagingPrefix) ||
|
|
177
|
+
entry.name.startsWith(managedBackupPrefix)
|
|
178
|
+
) {
|
|
179
|
+
removePathWithRetry(entryPath);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (entry.name.startsWith(backupPrefix)) {
|
|
184
|
+
backups.push(entryPath);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (backups.length === 0) return;
|
|
189
|
+
|
|
190
|
+
if (!fs.existsSync(resolvedOutput)) {
|
|
191
|
+
backups.sort(
|
|
192
|
+
(left, right) => fs.statSync(right).mtimeMs - fs.statSync(left).mtimeMs,
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
const [backupToRestore, ...staleBackups] = backups;
|
|
196
|
+
|
|
197
|
+
retryFileSystemOperation(() =>
|
|
198
|
+
fs.renameSync(backupToRestore, resolvedOutput),
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
for (const backup of staleBackups) {
|
|
202
|
+
removePathWithRetry(backup);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
for (const backup of backups) {
|
|
209
|
+
removePathWithRetry(backup);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
145
213
|
const RETRYABLE_FS_ERROR_CODES = new Set([
|
|
146
214
|
"EACCES",
|
|
147
215
|
"EBUSY",
|
|
@@ -151,14 +219,25 @@ const RETRYABLE_FS_ERROR_CODES = new Set([
|
|
|
151
219
|
"EPERM",
|
|
152
220
|
]);
|
|
153
221
|
|
|
154
|
-
function writeOutputTransactionally(
|
|
222
|
+
function writeOutputTransactionally(
|
|
223
|
+
output,
|
|
224
|
+
files,
|
|
225
|
+
cleanOutput,
|
|
226
|
+
forceInPlaceOutput = false,
|
|
227
|
+
) {
|
|
155
228
|
const parent = path.dirname(output);
|
|
156
229
|
fs.mkdirSync(parent, { recursive: true });
|
|
157
|
-
const staging = fs.mkdtempSync(
|
|
158
|
-
|
|
230
|
+
const staging = fs.mkdtempSync(
|
|
231
|
+
path.join(parent, `.${path.basename(output)}.typedapi-staging-`),
|
|
232
|
+
);
|
|
233
|
+
const backup = path.join(
|
|
234
|
+
parent,
|
|
235
|
+
`.${path.basename(output)}.typedapi-backup-${process.pid}-${Date.now()}`,
|
|
236
|
+
);
|
|
159
237
|
|
|
160
238
|
try {
|
|
161
|
-
if (fs.existsSync(output))
|
|
239
|
+
if (fs.existsSync(output))
|
|
240
|
+
fs.cpSync(output, staging, { recursive: true, force: true });
|
|
162
241
|
if (cleanOutput) removeManagedOutput(staging);
|
|
163
242
|
|
|
164
243
|
for (const [relativePath, content] of files) {
|
|
@@ -212,7 +291,8 @@ function writeOutputTransactionally(output, files, cleanOutput, forceInPlaceOutp
|
|
|
212
291
|
return "directory-swap";
|
|
213
292
|
} finally {
|
|
214
293
|
removePathWithRetry(staging);
|
|
215
|
-
if (fs.existsSync(backup) && fs.existsSync(output))
|
|
294
|
+
if (fs.existsSync(backup) && fs.existsSync(output))
|
|
295
|
+
removePathWithRetry(backup);
|
|
216
296
|
}
|
|
217
297
|
}
|
|
218
298
|
|
|
@@ -272,7 +352,8 @@ function syncPath(source, target) {
|
|
|
272
352
|
syncPath(path.join(source, entry), path.join(target, entry));
|
|
273
353
|
}
|
|
274
354
|
for (const entry of fs.readdirSync(target)) {
|
|
275
|
-
if (!sourceEntries.has(entry))
|
|
355
|
+
if (!sourceEntries.has(entry))
|
|
356
|
+
removePathWithRetry(path.join(target, entry));
|
|
276
357
|
}
|
|
277
358
|
return;
|
|
278
359
|
}
|
|
@@ -312,7 +393,8 @@ function retryFileSystemOperation(operation, attempts = 12) {
|
|
|
312
393
|
return operation();
|
|
313
394
|
} catch (error) {
|
|
314
395
|
lastError = error;
|
|
315
|
-
if (!isRetryableFileSystemError(error) || attempt === attempts - 1)
|
|
396
|
+
if (!isRetryableFileSystemError(error) || attempt === attempts - 1)
|
|
397
|
+
throw error;
|
|
316
398
|
sleepSynchronously(Math.min(25 * 2 ** attempt, 250));
|
|
317
399
|
}
|
|
318
400
|
}
|
|
@@ -320,7 +402,11 @@ function retryFileSystemOperation(operation, attempts = 12) {
|
|
|
320
402
|
}
|
|
321
403
|
|
|
322
404
|
function isRetryableFileSystemError(error) {
|
|
323
|
-
return Boolean(
|
|
405
|
+
return Boolean(
|
|
406
|
+
error &&
|
|
407
|
+
typeof error === "object" &&
|
|
408
|
+
RETRYABLE_FS_ERROR_CODES.has(error.code),
|
|
409
|
+
);
|
|
324
410
|
}
|
|
325
411
|
|
|
326
412
|
function sleepSynchronously(milliseconds) {
|