@distilled.cloud/core 1.0.0-rc.11 → 1.0.0-rc.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/lib/api.d.ts +4 -4
  2. package/lib/api.d.ts.map +1 -1
  3. package/lib/api.js +1 -1
  4. package/lib/api.js.map +1 -1
  5. package/lib/category.js +1 -1
  6. package/lib/category.js.map +1 -1
  7. package/lib/codegen/boolean-string-enums.d.ts +8 -8
  8. package/lib/codegen/boolean-string-enums.d.ts.map +1 -1
  9. package/lib/codegen/boolean-string-enums.js +84 -27
  10. package/lib/codegen/boolean-string-enums.js.map +1 -1
  11. package/lib/codegen/boolean-string-enums.test.js +130 -0
  12. package/lib/codegen/boolean-string-enums.test.js.map +1 -1
  13. package/lib/codegen/cli.js +2 -2
  14. package/lib/codegen/cli.js.map +1 -1
  15. package/lib/codegen/generator.d.ts +20 -3
  16. package/lib/codegen/generator.d.ts.map +1 -1
  17. package/lib/codegen/generator.js +36 -9
  18. package/lib/codegen/generator.js.map +1 -1
  19. package/lib/codegen/generator.test.d.ts +2 -0
  20. package/lib/codegen/generator.test.d.ts.map +1 -0
  21. package/lib/codegen/generator.test.js +126 -0
  22. package/lib/codegen/generator.test.js.map +1 -0
  23. package/lib/codegen/graphql-client.d.ts +7 -1
  24. package/lib/codegen/graphql-client.d.ts.map +1 -1
  25. package/lib/codegen/graphql-client.js +262 -125
  26. package/lib/codegen/graphql-client.js.map +1 -1
  27. package/lib/codegen/graphql-client.test.js +104 -236
  28. package/lib/codegen/graphql-client.test.js.map +1 -1
  29. package/lib/codegen/openapi-binary.test.d.ts +2 -0
  30. package/lib/codegen/openapi-binary.test.d.ts.map +1 -0
  31. package/lib/codegen/openapi-binary.test.js +67 -0
  32. package/lib/codegen/openapi-binary.test.js.map +1 -0
  33. package/lib/codegen/openapi.d.ts +7 -1
  34. package/lib/codegen/openapi.d.ts.map +1 -1
  35. package/lib/codegen/openapi.js +15 -4
  36. package/lib/codegen/openapi.js.map +1 -1
  37. package/lib/codegen/patches.d.ts +16 -1
  38. package/lib/codegen/patches.d.ts.map +1 -1
  39. package/lib/codegen/patches.js +70 -4
  40. package/lib/codegen/patches.js.map +1 -1
  41. package/lib/codegen/patches.test.js +48 -2
  42. package/lib/codegen/patches.test.js.map +1 -1
  43. package/lib/codegen/untagged-unions.test.d.ts +2 -0
  44. package/lib/codegen/untagged-unions.test.d.ts.map +1 -0
  45. package/lib/codegen/untagged-unions.test.js +80 -0
  46. package/lib/codegen/untagged-unions.test.js.map +1 -0
  47. package/lib/errors.d.ts +2 -1
  48. package/lib/errors.d.ts.map +1 -1
  49. package/lib/errors.js +5 -2
  50. package/lib/errors.js.map +1 -1
  51. package/lib/fixtures/xml/upstream-validator.json +397 -0
  52. package/lib/graphql.d.ts +296 -205
  53. package/lib/graphql.d.ts.map +1 -1
  54. package/lib/graphql.js +968 -631
  55. package/lib/graphql.js.map +1 -1
  56. package/lib/graphql.test.js +134 -763
  57. package/lib/graphql.test.js.map +1 -1
  58. package/lib/graphql.types.d.ts +10 -0
  59. package/lib/graphql.types.d.ts.map +1 -1
  60. package/lib/graphql.types.js +4 -44
  61. package/lib/graphql.types.js.map +1 -1
  62. package/lib/index.d.ts +27 -0
  63. package/lib/index.d.ts.map +1 -0
  64. package/lib/index.js +27 -0
  65. package/lib/index.js.map +1 -0
  66. package/lib/pagination.d.ts +2 -1
  67. package/lib/pagination.d.ts.map +1 -1
  68. package/lib/pagination.js +24 -16
  69. package/lib/pagination.js.map +1 -1
  70. package/lib/pagination.test.d.ts +2 -0
  71. package/lib/pagination.test.d.ts.map +1 -0
  72. package/lib/pagination.test.js +134 -0
  73. package/lib/pagination.test.js.map +1 -0
  74. package/lib/protocol-http.d.ts +12 -17
  75. package/lib/protocol-http.d.ts.map +1 -1
  76. package/lib/protocol-http.js +100 -45
  77. package/lib/protocol-http.js.map +1 -1
  78. package/lib/protocol-http.test.js +130 -0
  79. package/lib/protocol-http.test.js.map +1 -1
  80. package/lib/protocol-rest.d.ts +18 -1
  81. package/lib/protocol-rest.d.ts.map +1 -1
  82. package/lib/protocol-rest.js +24 -4
  83. package/lib/protocol-rest.js.map +1 -1
  84. package/lib/query.d.ts +29 -0
  85. package/lib/query.d.ts.map +1 -0
  86. package/lib/query.js +27 -0
  87. package/lib/query.js.map +1 -0
  88. package/lib/response-validation.d.ts +67 -0
  89. package/lib/response-validation.d.ts.map +1 -0
  90. package/lib/response-validation.js +76 -0
  91. package/lib/response-validation.js.map +1 -0
  92. package/lib/response-validation.test.d.ts +2 -0
  93. package/lib/response-validation.test.d.ts.map +1 -0
  94. package/lib/response-validation.test.js +67 -0
  95. package/lib/response-validation.test.js.map +1 -0
  96. package/lib/testing.d.ts +39 -0
  97. package/lib/testing.d.ts.map +1 -0
  98. package/lib/testing.js +41 -0
  99. package/lib/testing.js.map +1 -0
  100. package/lib/trait.d.ts +29 -9
  101. package/lib/trait.d.ts.map +1 -1
  102. package/lib/trait.js +8 -0
  103. package/lib/trait.js.map +1 -1
  104. package/lib/xml.d.ts +44 -0
  105. package/lib/xml.d.ts.map +1 -0
  106. package/lib/xml.js +315 -0
  107. package/lib/xml.js.map +1 -0
  108. package/lib/xml.test.d.ts +2 -0
  109. package/lib/xml.test.d.ts.map +1 -0
  110. package/lib/xml.test.js +211 -0
  111. package/lib/xml.test.js.map +1 -0
  112. package/package.json +4 -4
  113. package/src/api.ts +4 -4
  114. package/src/category.ts +1 -1
  115. package/src/codegen/boolean-string-enums.test.ts +147 -0
  116. package/src/codegen/boolean-string-enums.ts +84 -27
  117. package/src/codegen/cli.ts +2 -2
  118. package/src/codegen/generator.test.ts +151 -0
  119. package/src/codegen/generator.ts +70 -12
  120. package/src/codegen/graphql-client.test.ts +116 -283
  121. package/src/codegen/graphql-client.ts +326 -169
  122. package/src/codegen/openapi-binary.test.ts +98 -0
  123. package/src/codegen/openapi.ts +26 -4
  124. package/src/codegen/patches.test.ts +66 -1
  125. package/src/codegen/patches.ts +84 -4
  126. package/src/codegen/untagged-unions.test.ts +89 -0
  127. package/src/errors.ts +5 -2
  128. package/src/fixtures/xml/upstream-validator.json +397 -0
  129. package/src/graphql.test.ts +174 -948
  130. package/src/graphql.ts +1592 -1167
  131. package/src/graphql.types.ts +92 -163
  132. package/src/index.ts +26 -0
  133. package/src/pagination.test.ts +191 -0
  134. package/src/pagination.ts +35 -25
  135. package/src/protocol-http.test.ts +163 -0
  136. package/src/protocol-http.ts +122 -46
  137. package/src/protocol-rest.ts +62 -13
  138. package/src/query.ts +39 -0
  139. package/src/response-validation.test.ts +95 -0
  140. package/src/response-validation.ts +116 -0
  141. package/src/testing.ts +78 -0
  142. package/src/trait.ts +39 -8
  143. package/src/xml.test.ts +269 -0
  144. package/src/xml.ts +339 -0
  145. package/lib/graphql.fixture.d.ts +0 -249
  146. package/lib/graphql.fixture.d.ts.map +0 -1
  147. package/lib/graphql.fixture.js +0 -240
  148. package/lib/graphql.fixture.js.map +0 -1
  149. package/src/graphql.fixture.ts +0 -371
@@ -23,7 +23,7 @@
23
23
  * across operations; anonymous nested objects synthesize names from the
24
24
  * parent + member path
25
25
  * • responses: 200 → 201 → 204 precedence (`successStatuses` overrides),
26
- * `application/json` only; object
26
+ * JSON or opt-in binary `application/octet-stream`; object
27
27
  * results become `<Op>Response` structures (a sole `$ref` reuses the named
28
28
  * shape); bare array/scalar results wrap in a structure whose single
29
29
  * member carries `com.distilled.openapi#rawResponse` (the SdkSpec maps it
@@ -161,6 +161,12 @@ export interface OpenApiConvertOptions {
161
161
  * ask for them.
162
162
  */
163
163
  readonly headerParams?: boolean;
164
+ /**
165
+ * Convert `format: binary` strings to blobs and include binary
166
+ * `application/octet-stream` responses. Opt in only when the provider's
167
+ * generator and protocol support binary inputs and outputs. Default false.
168
+ */
169
+ readonly binaryTypes?: boolean;
164
170
  /**
165
171
  * Response statuses to read the operation's output shape from, most
166
172
  * preferred first. Default `["200", "201", "204"]`. Extend it for an API
@@ -226,6 +232,7 @@ export interface SmithyModel {
226
232
  const PRELUDE = {
227
233
  Unit: "smithy.api#Unit",
228
234
  String: "smithy.api#String",
235
+ Blob: "smithy.api#Blob",
229
236
  Boolean: "smithy.api#Boolean",
230
237
  Double: "smithy.api#Double",
231
238
  Integer: "smithy.api#Integer",
@@ -308,6 +315,7 @@ interface Ctx {
308
315
  */
309
316
  readonly dirSensitiveRefs: Map<Dir, ReadonlySet<string>>;
310
317
  readonly sensitivePatterns: readonly RegExp[];
318
+ readonly binaryTypes: boolean;
311
319
  }
312
320
 
313
321
  interface Converted {
@@ -913,7 +921,12 @@ const convertSchema = (
913
921
  // --- scalars --------------------------------------------------------------
914
922
  switch (t) {
915
923
  case "string":
916
- return inline(PRELUDE.String, nullable);
924
+ return inline(
925
+ ctx.binaryTypes && def.format === "binary"
926
+ ? PRELUDE.Blob
927
+ : PRELUDE.String,
928
+ nullable,
929
+ );
917
930
  case "boolean":
918
931
  return inline(PRELUDE.Boolean, nullable);
919
932
  case "integer":
@@ -1104,7 +1117,7 @@ const opDoc = (op: any): string | undefined => {
1104
1117
  // Responses
1105
1118
  // ============================================================================
1106
1119
 
1107
- /** First declared status in `order` wins; response-level `$ref` resolved; JSON only. */
1120
+ /** First declared status wins; resolve response refs and preserve JSON or binary bodies. */
1108
1121
  const successSchema = (
1109
1122
  ctx: Ctx,
1110
1123
  responses: any,
@@ -1118,7 +1131,15 @@ const successSchema = (
1118
1131
  if (ctx.version === "2.0") {
1119
1132
  return { schema: resp.schema };
1120
1133
  }
1121
- return { schema: resp.content?.["application/json"]?.schema };
1134
+ const json = resp.content?.["application/json"]?.schema;
1135
+ const binary = resp.content?.["application/octet-stream"]?.schema;
1136
+ return {
1137
+ schema:
1138
+ json ??
1139
+ (ctx.binaryTypes && deref(ctx, binary)?.format === "binary"
1140
+ ? binary
1141
+ : undefined),
1142
+ };
1122
1143
  }
1123
1144
  return { schema: undefined };
1124
1145
  };
@@ -1269,6 +1290,7 @@ export const convertOpenApiToSmithy = (
1269
1290
  refs: new Map(),
1270
1291
  dirSensitiveRefs: new Map(),
1271
1292
  sensitivePatterns: options.sensitivePatterns ?? SENSITIVE_FIELD_PATTERNS,
1293
+ binaryTypes: options.binaryTypes ?? false,
1272
1294
  };
1273
1295
  const statusToErrorClass =
1274
1296
  options.statusToErrorClass ?? DEFAULT_STATUS_TO_ERROR_CLASS;
@@ -1,11 +1,14 @@
1
- import { describe, expect, test } from "bun:test";
1
+ import { afterEach, describe, expect, test } from "bun:test";
2
2
  import { mkdtempSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  import {
6
6
  FINALIZED_KEY,
7
+ SKIP_PATCHES_ENV,
8
+ applyRfc6902Files,
7
9
  danglingTargets,
8
10
  finalizeConvert,
11
+ listRfc6902PatchFiles,
9
12
  syncServiceOperations,
10
13
  } from "./patches.ts";
11
14
 
@@ -128,3 +131,65 @@ describe("syncServiceOperations", () => {
128
131
  ]);
129
132
  });
130
133
  });
134
+
135
+ describe(SKIP_PATCHES_ENV, () => {
136
+ afterEach(() => {
137
+ delete process.env[SKIP_PATCHES_ENV];
138
+ });
139
+
140
+ const patchDir = () => {
141
+ const dir = mkdtempSync(join(tmpdir(), "skip-"));
142
+ mkdirSync(join(dir, "svc"));
143
+ writeFileSync(
144
+ join(dir, "svc", "a.json"),
145
+ JSON.stringify({
146
+ patches: [
147
+ { op: "add", path: "/x", value: 1 },
148
+ { op: "add", path: "/y", value: 2 },
149
+ ],
150
+ }),
151
+ );
152
+ writeFileSync(
153
+ join(dir, "svc", "b.json"),
154
+ JSON.stringify({ patches: [{ op: "add", path: "/z", value: 3 }] }),
155
+ );
156
+ return join(dir, "svc");
157
+ };
158
+
159
+ test("unset applies everything", async () => {
160
+ const target: Record<string, unknown> = {};
161
+ const files = await listRfc6902PatchFiles(patchDir());
162
+ expect(files.map((f) => f.split("/").pop())).toEqual(["a.json", "b.json"]);
163
+ expect((await applyRfc6902Files(target, files)).applied).toBe(3);
164
+ expect(target).toEqual({ x: 1, y: 2, z: 3 });
165
+ });
166
+
167
+ test("all lists no files", async () => {
168
+ process.env[SKIP_PATCHES_ENV] = "all";
169
+ expect(await listRfc6902PatchFiles(patchDir())).toEqual([]);
170
+ });
171
+
172
+ test("a basename or a path suffix drops that file", async () => {
173
+ const dir = patchDir();
174
+ process.env[SKIP_PATCHES_ENV] = "a.json";
175
+ expect(
176
+ (await listRfc6902PatchFiles(dir)).map((f) => f.split("/").pop()),
177
+ ).toEqual(["b.json"]);
178
+ process.env[SKIP_PATCHES_ENV] = "svc/b.json";
179
+ expect(
180
+ (await listRfc6902PatchFiles(dir)).map((f) => f.split("/").pop()),
181
+ ).toEqual(["a.json"]);
182
+ process.env[SKIP_PATCHES_ENV] = "other/b.json";
183
+ expect((await listRfc6902PatchFiles(dir)).length).toBe(2);
184
+ });
185
+
186
+ test("<file>:<index> drops one op and keeps the rest of the file", async () => {
187
+ const dir = patchDir();
188
+ const files = await listRfc6902PatchFiles(dir);
189
+ process.env[SKIP_PATCHES_ENV] = "a.json:1";
190
+ const target: Record<string, unknown> = {};
191
+ const result = await applyRfc6902Files(target, files);
192
+ expect(result.applied).toBe(2);
193
+ expect(target).toEqual({ x: 1, z: 3 });
194
+ });
195
+ });
@@ -40,12 +40,89 @@ const exists = async (p: string): Promise<boolean> => {
40
40
  }
41
41
  };
42
42
 
43
+ /**
44
+ * Leave patches out of one convert run (dev-time only; the seam
45
+ * `scripts/patches.ts audit` uses to find patches the spec no longer
46
+ * needs). Comma-separated entries:
47
+ *
48
+ * all skip every patch file
49
+ * <file> skip a file — its basename, or a path suffix
50
+ * (`svc/a.json`) when basenames repeat
51
+ * <file>:<index> skip one op of a file, by its index in `patches`
52
+ *
53
+ * Like `DISTILLED_SPECS_LOCAL`, it lives in one command's environment: a
54
+ * model written under it is announced on stderr and must not be committed.
55
+ */
56
+ export const SKIP_PATCHES_ENV = "DISTILLED_SKIP_PATCHES";
57
+
58
+ interface SkipList {
59
+ readonly all: boolean;
60
+ readonly files: readonly string[];
61
+ readonly ops: ReadonlyMap<string, ReadonlySet<number>>;
62
+ }
63
+
64
+ const parseSkipList = (): SkipList | undefined => {
65
+ const raw = (process.env[SKIP_PATCHES_ENV] ?? "").trim();
66
+ if (raw === "") return undefined;
67
+ const files: string[] = [];
68
+ const ops = new Map<string, Set<number>>();
69
+ for (const entry of raw.split(",")) {
70
+ const item = entry.trim();
71
+ if (item === "") continue;
72
+ if (item === "all") return { all: true, files: [], ops: new Map() };
73
+ const at = item.lastIndexOf(":");
74
+ const index = at === -1 ? NaN : Number(item.slice(at + 1));
75
+ if (Number.isInteger(index) && index >= 0) {
76
+ const file = item.slice(0, at);
77
+ ops.set(file, (ops.get(file) ?? new Set()).add(index));
78
+ } else {
79
+ files.push(item);
80
+ }
81
+ }
82
+ return { all: false, files, ops };
83
+ };
84
+
85
+ const skipMatches = (file: string, entry: string): boolean =>
86
+ path.basename(file) === entry || file.endsWith(path.sep + entry);
87
+
88
+ let skipAnnounced = false;
89
+
90
+ const skipList = (): SkipList | undefined => {
91
+ const list = parseSkipList();
92
+ if (list && !skipAnnounced) {
93
+ skipAnnounced = true;
94
+ // stderr for the same reason as the DISTILLED_SPECS_LOCAL notice.
95
+ console.error(
96
+ `⚠ ${SKIP_PATCHES_ENV}=${process.env[SKIP_PATCHES_ENV]} — this model is missing patches; do not commit it.`,
97
+ );
98
+ }
99
+ return list;
100
+ };
101
+
102
+ const skipsFile = (list: SkipList | undefined, file: string): boolean =>
103
+ list !== undefined &&
104
+ (list.all || list.files.some((entry) => skipMatches(file, entry)));
105
+
106
+ const skipsOp = (
107
+ list: SkipList | undefined,
108
+ file: string,
109
+ index: number,
110
+ ): boolean => {
111
+ if (list === undefined) return false;
112
+ for (const [entry, indices] of list.ops) {
113
+ if (indices.has(index) && skipMatches(file, entry)) return true;
114
+ }
115
+ return false;
116
+ };
117
+
43
118
  /**
44
119
  * RFC-6902 files in `dir`: every `*.json`, `*.manual.json` last (those
45
- * usually target post-rename shape names). Missing dir → `[]`.
120
+ * usually target post-rename shape names). Missing dir → `[]`. Files named
121
+ * by {@link SKIP_PATCHES_ENV} are left out.
46
122
  */
47
123
  export const listRfc6902PatchFiles = async (dir: string): Promise<string[]> => {
48
124
  if (!(await exists(dir))) return [];
125
+ const skip = skipList();
49
126
  return (await fs.readdir(dir))
50
127
  .filter((f) => f.endsWith(".json"))
51
128
  .sort(
@@ -53,7 +130,8 @@ export const listRfc6902PatchFiles = async (dir: string): Promise<string[]> => {
53
130
  Number(a.endsWith(".manual.json")) -
54
131
  Number(b.endsWith(".manual.json")) || a.localeCompare(b),
55
132
  )
56
- .map((f) => path.join(dir, f));
133
+ .map((f) => path.join(dir, f))
134
+ .filter((f) => !skipsFile(skip, f));
57
135
  };
58
136
 
59
137
  export const applyRfc6902Files = async (
@@ -73,12 +151,14 @@ export const applyRfc6902Files = async (
73
151
  stale: 0,
74
152
  errors: [],
75
153
  };
154
+ const skip = skipList();
76
155
  for (const file of files) {
156
+ if (skipsFile(skip, file)) continue;
77
157
  const parsed = JSON.parse(await fs.readFile(file, "utf8")) as PatchFile;
78
158
  const label = opts.label?.(file) ?? path.basename(file);
79
159
  result.files++;
80
- for (const patchOp of parsed.patches ?? []) {
81
- if (!include(patchOp)) continue;
160
+ for (const [index, patchOp] of (parsed.patches ?? []).entries()) {
161
+ if (!include(patchOp) || skipsOp(skip, file, index)) continue;
82
162
  try {
83
163
  applyOperation(target, patchOp);
84
164
  result.applied++;
@@ -0,0 +1,89 @@
1
+ import { expect, test } from "bun:test";
2
+ import { generateService } from "./generator.ts";
3
+
4
+ const model = {
5
+ smithy: "2.0",
6
+ shapes: {
7
+ "com.example#Service": {
8
+ type: "service",
9
+ version: "1",
10
+ operations: [{ target: "com.example#GetValue" }],
11
+ },
12
+ "com.example#GetValue": {
13
+ type: "operation",
14
+ input: { target: "com.example#Input" },
15
+ output: { target: "com.example#Output" },
16
+ traits: {
17
+ "smithy.api#http": { method: "GET", uri: "/value", code: 200 },
18
+ },
19
+ },
20
+ "com.example#Input": { type: "structure", members: {} },
21
+ "com.example#Output": {
22
+ type: "structure",
23
+ members: { value: { target: "com.example#Choice" } },
24
+ },
25
+ "com.example#Choice": {
26
+ type: "union",
27
+ members: {
28
+ boolean: { target: "smithy.api#Boolean" },
29
+ paths: { target: "com.example#Paths" },
30
+ },
31
+ },
32
+ "com.example#Paths": {
33
+ type: "list",
34
+ member: { target: "smithy.api#String" },
35
+ },
36
+ },
37
+ };
38
+
39
+ const operationDecl = {
40
+ contextType: "ExampleOpContext",
41
+ commonErrorType: "ExampleOpError",
42
+ commonErrorClasses: [],
43
+ protocol: "ExampleProtocol",
44
+ };
45
+
46
+ test("untagged unions validate primitive and collection alternatives", () => {
47
+ const { code } = generateService(model, {
48
+ unionStyle: "untagged",
49
+ operationDecl,
50
+ });
51
+ expect(code).toContain("export type Choice = boolean | Paths;");
52
+ expect(code).toContain("S.Union([S.Boolean, Paths])");
53
+ expect(code).not.toContain("S.Unknown as any as S.Schema<Choice>");
54
+ });
55
+
56
+ test("untagged unions honor the configured schema type", () => {
57
+ for (const schemaType of ["Schema", "Codec"] as const) {
58
+ const { code } = generateService(model, {
59
+ unionStyle: "untagged",
60
+ schemaType,
61
+ operationDecl,
62
+ });
63
+ expect(code).toContain(
64
+ `S.Union([S.Boolean, Paths]) as any as S.${schemaType}<Choice>`,
65
+ );
66
+ expect(code).toContain(
67
+ `S.Array(S.String) as any as S.${schemaType}<Paths>`,
68
+ );
69
+ }
70
+ });
71
+
72
+ test("omitting a retry policy emits neither a retry import nor an operation setting", () => {
73
+ const { code } = generateService(model, {
74
+ unionStyle: "untagged",
75
+ operationDecl,
76
+ });
77
+ expect(code).not.toContain("../retry.ts");
78
+ expect(code).not.toContain("retry:");
79
+ expect(code).not.toContain('import { } from "../errors.ts"');
80
+ });
81
+
82
+ test("an explicit retry policy retains the existing import and operation setting", () => {
83
+ const { code } = generateService(model, {
84
+ unionStyle: "untagged",
85
+ operationDecl: { ...operationDecl, retry: "Retry.Retry" },
86
+ });
87
+ expect(code).toContain('import * as Retry from "../retry.ts"');
88
+ expect(code).toContain("retry: Retry.Retry");
89
+ });
package/src/errors.ts CHANGED
@@ -133,7 +133,7 @@ export class ServiceUnavailable extends Schema.TaggedError<ServiceUnavailable>()
133
133
  ).pipe(Category.withServerError, Category.withRetryable()) {}
134
134
 
135
135
  /**
136
- * GatewayTimeout - Gateway timeout (504).
136
+ * GatewayTimeout - HTTP request or gateway timeout (408, 504).
137
137
  */
138
138
  export class GatewayTimeout extends Schema.TaggedError<GatewayTimeout>()(
139
139
  "GatewayTimeout",
@@ -163,6 +163,7 @@ export const HTTP_STATUS_MAP = {
163
163
  401: Unauthorized,
164
164
  403: Forbidden,
165
165
  404: NotFound,
166
+ 408: GatewayTimeout,
166
167
  409: Conflict,
167
168
  422: UnprocessableEntity,
168
169
  423: Locked,
@@ -189,7 +190,9 @@ export const DEFAULT_ERROR_STATUSES = new Set([401, 429, 500, 502, 503, 504]);
189
190
  * classes (BadRequest/Unauthorized/etc.) would silently retain it as a
190
191
  * stale field on the instance and pollute serialized output.
191
192
  */
192
- export const RETRYABLE_HTTP_STATUSES = new Set([423, 429, 500, 502, 503, 504]);
193
+ export const RETRYABLE_HTTP_STATUSES = new Set([
194
+ 408, 423, 429, 500, 502, 503, 504,
195
+ ]);
193
196
 
194
197
  /**
195
198
  * All common API error classes.