@shapediver/sdk.geometry-api-sdk-v2 1.8.2 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/ShapeDiverErrors.d.ts.map +1 -1
  2. package/dist/ShapeDiverErrors.js +1 -1
  3. package/dist/ShapeDiverErrors.js.map +1 -1
  4. package/dist/ShapeDiverSdk.d.ts.map +1 -1
  5. package/dist/ShapeDiverSdk.js +2 -2
  6. package/dist/ShapeDiverSdk.js.map +1 -1
  7. package/dist/index.d.ts +2 -2
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/resources/ShapeDiverAnalyticsApi.d.ts.map +1 -1
  11. package/dist/resources/ShapeDiverAnalyticsApi.js +3 -1
  12. package/dist/resources/ShapeDiverAnalyticsApi.js.map +1 -1
  13. package/dist/resources/ShapeDiverArSceneApi.d.ts.map +1 -1
  14. package/dist/resources/ShapeDiverArSceneApi.js.map +1 -1
  15. package/dist/resources/ShapeDiverAssetApi.d.ts.map +1 -1
  16. package/dist/resources/ShapeDiverAssetApi.js.map +1 -1
  17. package/dist/resources/ShapeDiverExportApi.d.ts.map +1 -1
  18. package/dist/resources/ShapeDiverExportApi.js.map +1 -1
  19. package/dist/resources/ShapeDiverFileApi.d.ts.map +1 -1
  20. package/dist/resources/ShapeDiverFileApi.js +10 -7
  21. package/dist/resources/ShapeDiverFileApi.js.map +1 -1
  22. package/dist/resources/ShapeDiverGltfApi.d.ts.map +1 -1
  23. package/dist/resources/ShapeDiverGltfApi.js +6 -3
  24. package/dist/resources/ShapeDiverGltfApi.js.map +1 -1
  25. package/dist/resources/ShapeDiverModelApi.d.ts.map +1 -1
  26. package/dist/resources/ShapeDiverModelApi.js +52 -15
  27. package/dist/resources/ShapeDiverModelApi.js.map +1 -1
  28. package/dist/resources/ShapeDiverOutputApi.d.ts.map +1 -1
  29. package/dist/resources/ShapeDiverOutputApi.js +12 -4
  30. package/dist/resources/ShapeDiverOutputApi.js.map +1 -1
  31. package/dist/resources/ShapeDiverSdtfApi.d.ts.map +1 -1
  32. package/dist/resources/ShapeDiverSdtfApi.js +9 -3
  33. package/dist/resources/ShapeDiverSdtfApi.js.map +1 -1
  34. package/dist/resources/ShapeDiverSessionApi.d.ts.map +1 -1
  35. package/dist/resources/ShapeDiverSessionApi.js +3 -1
  36. package/dist/resources/ShapeDiverSessionApi.js.map +1 -1
  37. package/dist/resources/ShapeDiverSystemApi.d.ts.map +1 -1
  38. package/dist/resources/ShapeDiverSystemApi.js +18 -6
  39. package/dist/resources/ShapeDiverSystemApi.js.map +1 -1
  40. package/dist/resources/ShapeDiverTextureApi.d.ts.map +1 -1
  41. package/dist/resources/ShapeDiverTextureApi.js +3 -1
  42. package/dist/resources/ShapeDiverTextureApi.js.map +1 -1
  43. package/dist/resources/ShapeDiverUtilsApi.d.ts +3 -5
  44. package/dist/resources/ShapeDiverUtilsApi.d.ts.map +1 -1
  45. package/dist/resources/ShapeDiverUtilsApi.js +39 -23
  46. package/dist/resources/ShapeDiverUtilsApi.js.map +1 -1
  47. package/dist/utils/utils.d.ts +1 -1
  48. package/dist/utils/utils.d.ts.map +1 -1
  49. package/dist/utils/utils.js +17 -18
  50. package/dist/utils/utils.js.map +1 -1
  51. package/package.json +11 -8
  52. package/src/ShapeDiverErrors.ts +34 -0
  53. package/src/ShapeDiverSdk.ts +114 -0
  54. package/src/index.ts +17 -0
  55. package/src/resources/ShapeDiverAnalyticsApi.ts +34 -0
  56. package/src/resources/ShapeDiverArSceneApi.ts +75 -0
  57. package/src/resources/ShapeDiverAssetApi.ts +213 -0
  58. package/src/resources/ShapeDiverExportApi.ts +100 -0
  59. package/src/resources/ShapeDiverFileApi.ts +151 -0
  60. package/src/resources/ShapeDiverGltfApi.ts +45 -0
  61. package/src/resources/ShapeDiverModelApi.ts +357 -0
  62. package/src/resources/ShapeDiverOutputApi.ts +100 -0
  63. package/src/resources/ShapeDiverSdtfApi.ts +95 -0
  64. package/src/resources/ShapeDiverSessionApi.ts +113 -0
  65. package/src/resources/ShapeDiverSystemApi.ts +116 -0
  66. package/src/resources/ShapeDiverTextureApi.ts +28 -0
  67. package/src/resources/ShapeDiverUtilsApi.ts +314 -0
  68. package/src/utils/utils.ts +97 -0
@@ -0,0 +1,116 @@
1
+ import {
2
+ ShapeDiverRequestAuthorizationGroup,
3
+ ShapeDiverRequestLogMessage,
4
+ ShapeDiverResponseDto,
5
+ } from "@shapediver/api.geometry-api-dto-v2";
6
+ import {
7
+ BaseResourceApi,
8
+ ShapeDiverSdkApi,
9
+ } from "@shapediver/sdk.geometry-api-sdk-core";
10
+ import { sendRequest } from "../utils/utils";
11
+
12
+ export class ShapeDiverSystemApi extends BaseResourceApi {
13
+ constructor(api: ShapeDiverSdkApi) {
14
+ super(api);
15
+ }
16
+
17
+ /**
18
+ * Log the specified message.
19
+ *
20
+ * @param sessionId
21
+ * @param body
22
+ */
23
+ async log(
24
+ sessionId: string,
25
+ body: ShapeDiverRequestLogMessage,
26
+ ): Promise<ShapeDiverResponseDto> {
27
+ return await sendRequest(
28
+ async () =>
29
+ (
30
+ await this.api.post<ShapeDiverResponseDto>(
31
+ this.buildSessionUri(sessionId) + "/log/message",
32
+ body,
33
+ )
34
+ )[1],
35
+ );
36
+ }
37
+
38
+ /**
39
+ * Remove a model with all it's components.
40
+ *
41
+ * **WARNING: This operation cannot be undone!**
42
+ *
43
+ * @param modelId
44
+ */
45
+ async pruneModel(modelId: string): Promise<ShapeDiverResponseDto> {
46
+ return await sendRequest(
47
+ async () =>
48
+ (
49
+ await this.api.delete<ShapeDiverResponseDto>(
50
+ `${this.buildSystemUri()}/model/${modelId}`,
51
+ )
52
+ )[1],
53
+ );
54
+ }
55
+
56
+ /**
57
+ * Decrypt the given ticket.
58
+ *
59
+ * @param ticket
60
+ */
61
+ async decryptTicket(ticket: string): Promise<ShapeDiverResponseDto> {
62
+ return await sendRequest(
63
+ async () =>
64
+ (
65
+ await this.api.get<ShapeDiverResponseDto>(this.buildTicketUri(ticket))
66
+ )[1],
67
+ );
68
+ }
69
+
70
+ /**
71
+ * Create a new authentication group.
72
+ *
73
+ * @param body
74
+ */
75
+ async authGroup(
76
+ body: ShapeDiverRequestAuthorizationGroup,
77
+ ): Promise<ShapeDiverResponseDto> {
78
+ return await sendRequest(
79
+ async () =>
80
+ (
81
+ await this.api.post<ShapeDiverResponseDto>(
82
+ this.buildAuthGroupUri(),
83
+ body,
84
+ )
85
+ )[1],
86
+ );
87
+ }
88
+
89
+ /**
90
+ * Returns system information about the Geometry Minions.
91
+ */
92
+ async getMinionInfo(): Promise<ShapeDiverResponseDto> {
93
+ return sendRequest(
94
+ async () =>
95
+ (
96
+ await this.api.get<ShapeDiverResponseDto>(
97
+ `${this.buildSystemUri()}/minions/info`,
98
+ )
99
+ )[1],
100
+ );
101
+ }
102
+
103
+ /**
104
+ * Returns system information about the Geometry Workers.
105
+ */
106
+ async getWorkerInfo(): Promise<ShapeDiverResponseDto> {
107
+ return sendRequest(
108
+ async () =>
109
+ (
110
+ await this.api.get<ShapeDiverResponseDto>(
111
+ `${this.buildSystemUri()}/workers/info`,
112
+ )
113
+ )[1],
114
+ );
115
+ }
116
+ }
@@ -0,0 +1,28 @@
1
+ import { ShapeDiverResponseDto } from "@shapediver/api.geometry-api-dto-v2";
2
+ import {
3
+ BaseResourceApi,
4
+ ShapeDiverSdkApi,
5
+ } from "@shapediver/sdk.geometry-api-sdk-core";
6
+ import { sendRequest } from "../utils/utils";
7
+
8
+ export class ShapeDiverTextureApi extends BaseResourceApi {
9
+ constructor(api: ShapeDiverSdkApi) {
10
+ super(api);
11
+ }
12
+
13
+ /**
14
+ * List all textures of the specified model.
15
+ *
16
+ * @param sessionId
17
+ */
18
+ async listModelTextures(sessionId: string): Promise<ShapeDiverResponseDto> {
19
+ return await sendRequest(
20
+ async () =>
21
+ (
22
+ await this.api.get<ShapeDiverResponseDto>(
23
+ this.buildSessionUri(sessionId) + "/texture/list",
24
+ )
25
+ )[1],
26
+ );
27
+ }
28
+ }
@@ -0,0 +1,314 @@
1
+ import {
2
+ ShapeDiverRequestCache,
3
+ ShapeDiverRequestCustomization,
4
+ ShapeDiverRequestExport,
5
+ ShapeDiverResponseDto,
6
+ ShapeDiverResponseAssetUploadHeaders,
7
+ ShapeDiverResponseExport,
8
+ ShapeDiverResponseOutput,
9
+ } from "@shapediver/api.geometry-api-dto-v2";
10
+ import {
11
+ BaseResourceApi,
12
+ ShapeDiverError,
13
+ ShapeDiverSdkApi,
14
+ ShapeDiverSdkApiResponseType,
15
+ } from "@shapediver/sdk.geometry-api-sdk-core";
16
+ import { ShapeDiverSdk } from "../ShapeDiverSdk";
17
+ import { sendRequest, sleep } from "../utils/utils";
18
+
19
+ const contentDisposition = require("content-disposition");
20
+
21
+ // TypeScript wrapper for a response type that depend on an input type
22
+ type ShapeDiverSdkUtilsDownloadType<T extends ShapeDiverSdkApiResponseType> =
23
+ T extends ShapeDiverSdkApiResponseType.TEXT
24
+ ? string
25
+ : T extends ShapeDiverSdkApiResponseType.JSON
26
+ ? Record<string, any>
27
+ : T extends ShapeDiverSdkApiResponseType.DATA
28
+ ? ArrayBuffer
29
+ : never;
30
+
31
+ export class ShapeDiverUtilsApi extends BaseResourceApi {
32
+ constructor(api: ShapeDiverSdkApi) {
33
+ super(api);
34
+ }
35
+
36
+ /**
37
+ * Upload the given file to the specified URL.
38
+ *
39
+ * @param url - The target URL of the upload request.
40
+ * @param data - The data that should be uploaded.
41
+ * @param contentType - Indicate the original media type of the resource.
42
+ * @param filename - The name of the file to be uploaded. When a filename has been specified in
43
+ * the request-upload call, then the same filename has to be specified for the upload as well.
44
+ */
45
+ async upload(
46
+ url: string,
47
+ data: ArrayBuffer | Record<string, any> | string,
48
+ contentType: string,
49
+ filename?: string,
50
+ ): Promise<any> {
51
+ return await sendRequest(async () =>
52
+ this.api.put<any>(url, data, {
53
+ contentType: contentType,
54
+ contentDisposition: filename ? contentDisposition(filename) : undefined,
55
+ responseType: ShapeDiverSdkApiResponseType.JSON,
56
+ disableAuthorization: true,
57
+ disableCustomHeaders: true,
58
+ }),
59
+ );
60
+ }
61
+
62
+ /**
63
+ * Upload the given asset to the specified ShapeDiver URL.
64
+ *
65
+ * @param url - The ShapeDiver URL of the upload request.
66
+ * @param data - The data that should be uploaded.
67
+ * @param headers - The headers object that was returned from the request-upload call.
68
+ */
69
+ async uploadAsset(
70
+ url: string,
71
+ data: ArrayBuffer | Record<string, any> | string,
72
+ headers: ShapeDiverResponseAssetUploadHeaders,
73
+ ): Promise<any> {
74
+ return await sendRequest(async () =>
75
+ this.api.put<any>(url, data, {
76
+ contentType: headers.contentType,
77
+ contentDisposition: headers.contentDisposition,
78
+ responseType: ShapeDiverSdkApiResponseType.JSON,
79
+ disableAuthorization: true,
80
+ disableCustomHeaders: true,
81
+ }),
82
+ );
83
+ }
84
+
85
+ /**
86
+ * Download from the given URL.
87
+ *
88
+ * @param url - The target URL of the download request.
89
+ * @param responseType - Indicates the type of data that the server should respond with if possible.
90
+ * @returns Array of size 2: [0] = response headers, [1] = response data
91
+ */
92
+ async download<T extends ShapeDiverSdkApiResponseType>(
93
+ url: string,
94
+ responseType: T,
95
+ ): Promise<[Record<string, any>, ShapeDiverSdkUtilsDownloadType<T>]> {
96
+ return await sendRequest(async () =>
97
+ this.api.get<any>(url, {
98
+ contentType: "application/json",
99
+ responseType: responseType,
100
+ disableAuthorization: true,
101
+ disableCustomHeaders: true,
102
+ }),
103
+ );
104
+ }
105
+
106
+ /**
107
+ * Submit a customization request and wait for the result to be finished.
108
+ *
109
+ * @param sdk
110
+ * @param sessionId
111
+ * @param body
112
+ * @param maxWaitMsec - Maximum duration to wait for result (in milliseconds), pass value < 0 to disable limit.
113
+ * @throws {@link ShapeDiverError} in case a maximum duration has been specified and is exceeded.
114
+ * @returns
115
+ */
116
+ async submitAndWaitForCustomization(
117
+ sdk: ShapeDiverSdk,
118
+ sessionId: string,
119
+ body: ShapeDiverRequestCustomization,
120
+ maxWaitMsec = -1,
121
+ ): Promise<ShapeDiverResponseDto> {
122
+ const startMsec = Date.now();
123
+ const dto = await sendRequest(async () =>
124
+ sdk.output.customize(sessionId, body),
125
+ );
126
+ const waitMsec = Date.now() - startMsec;
127
+
128
+ // Reduce the total max waiting time by the amount the customization-request took
129
+ maxWaitMsec =
130
+ maxWaitMsec < 0 ? maxWaitMsec : Math.max(0, maxWaitMsec - waitMsec);
131
+
132
+ return ShapeDiverUtilsApi.waitForCustomizationResult(
133
+ sdk,
134
+ sessionId,
135
+ dto,
136
+ maxWaitMsec,
137
+ );
138
+ }
139
+
140
+ /**
141
+ * Submit an export request and wait for the result to be finished.
142
+ *
143
+ * @param sdk
144
+ * @param sessionId
145
+ * @param body
146
+ * @param maxWaitMsec - Maximum duration to wait for result (in milliseconds), pass value < 0 to disable limit.
147
+ * @throws {@link ShapeDiverError} in case a maximum duration has been specified and is exceeded.
148
+ * @returns
149
+ */
150
+ async submitAndWaitForExport(
151
+ sdk: ShapeDiverSdk,
152
+ sessionId: string,
153
+ body: ShapeDiverRequestExport,
154
+ maxWaitMsec = -1,
155
+ ): Promise<ShapeDiverResponseDto> {
156
+ const startMsec = Date.now();
157
+ const dto = await sendRequest(async () =>
158
+ sdk.export.compute(sessionId, body),
159
+ );
160
+ const waitMsec = Date.now() - startMsec;
161
+
162
+ // Reduce the total max waiting time by the amount the compute-request took
163
+ maxWaitMsec =
164
+ maxWaitMsec < 0 ? maxWaitMsec : Math.max(0, maxWaitMsec - waitMsec);
165
+
166
+ return ShapeDiverUtilsApi.waitForExportResult(
167
+ sdk,
168
+ sessionId,
169
+ body,
170
+ dto,
171
+ maxWaitMsec,
172
+ );
173
+ }
174
+
175
+ /**
176
+ * Given a DTO resulting from a customization request, wait for the results to be finished.
177
+ *
178
+ * @param sdk
179
+ * @param sessionId
180
+ * @param dto
181
+ * @param maxWaitMsec - Maximum duration to wait for result (in milliseconds), pass value < 0 to disable limit.
182
+ * @returns
183
+ */
184
+ private static async waitForCustomizationResult(
185
+ sdk: ShapeDiverSdk,
186
+ sessionId: string,
187
+ dto: ShapeDiverResponseDto,
188
+ maxWaitMsec = -1,
189
+ ): Promise<ShapeDiverResponseDto> {
190
+ if (!dto.outputs) return dto;
191
+
192
+ // Build new cache request
193
+ const outputVersions: ShapeDiverRequestCache = {};
194
+ Object.keys(dto.outputs).forEach(
195
+ (id) =>
196
+ (outputVersions[id] = (
197
+ dto.outputs![id] as ShapeDiverResponseOutput
198
+ ).version),
199
+ );
200
+
201
+ let delay = ShapeDiverUtilsApi.getMaxOutputDelay(dto);
202
+ const startMsec = Date.now();
203
+
204
+ while (delay > 0) {
205
+ // Check whether maxWaitMsec has been reached
206
+ if (maxWaitMsec >= 0) {
207
+ const waitMsec = Date.now() - startMsec;
208
+ if (waitMsec >= maxWaitMsec) {
209
+ throw new ShapeDiverError(
210
+ `Maximum wait time of ${maxWaitMsec} ms reached`,
211
+ );
212
+ }
213
+ if (waitMsec + delay > maxWaitMsec) {
214
+ delay = maxWaitMsec - waitMsec;
215
+ }
216
+ }
217
+
218
+ await sleep(delay);
219
+
220
+ // Send cache request
221
+ dto = await sendRequest(async () =>
222
+ sdk.output.getCache(sessionId, outputVersions),
223
+ );
224
+ delay = ShapeDiverUtilsApi.getMaxOutputDelay(dto);
225
+ }
226
+
227
+ return dto;
228
+ }
229
+
230
+ /**
231
+ * Given a DTO resulting from an export request, wait for the result to be finished.
232
+ *
233
+ * @param sdk
234
+ * @param sessionId
235
+ * @param dto
236
+ * @param exportIds
237
+ * @param outputIds
238
+ * @param maxWaitMsec - Maximum duration to wait for result (in milliseconds), pass value < 0 to disable limit.
239
+ * @returns
240
+ */
241
+ private static async waitForExportResult(
242
+ sdk: ShapeDiverSdk,
243
+ sessionId: string,
244
+ body: ShapeDiverRequestExport,
245
+ dto: ShapeDiverResponseDto,
246
+ maxWaitMsec = -1,
247
+ ): Promise<ShapeDiverResponseDto> {
248
+ let delay = ShapeDiverUtilsApi.getMaxExportDelay(dto, body);
249
+ const startMsec = Date.now();
250
+
251
+ while (delay > 0) {
252
+ // Check whether maxWaitMsec has been reached
253
+ if (maxWaitMsec >= 0) {
254
+ const waitMsec = Date.now() - startMsec;
255
+ if (waitMsec >= maxWaitMsec) {
256
+ throw new ShapeDiverError(
257
+ `Maximum wait time of ${maxWaitMsec} ms reached`,
258
+ );
259
+ }
260
+ if (waitMsec + delay > maxWaitMsec) {
261
+ delay = maxWaitMsec - waitMsec;
262
+ }
263
+ }
264
+
265
+ await sleep(delay);
266
+
267
+ // Send cache request
268
+ dto = await sendRequest(async () => sdk.export.getCache(sessionId, body));
269
+ delay = ShapeDiverUtilsApi.getMaxExportDelay(dto, body);
270
+ }
271
+
272
+ return dto;
273
+ }
274
+
275
+ /**
276
+ * Get the maximum delay that was reported for output versions.
277
+ *
278
+ * @returns maximum delay, -1 in case no delay was reported
279
+ */
280
+ private static getMaxOutputDelay(dto: ShapeDiverResponseDto): number {
281
+ return Math.max(
282
+ ...Object.values(dto.outputs!)
283
+ .map((output) => output as ShapeDiverResponseOutput)
284
+ .map((output) => output.delay ?? -1),
285
+ );
286
+ }
287
+
288
+ /**
289
+ * Get the maximum delay that was reported for the exports. If outputs have been reported as
290
+ * well, their delay time is included too.
291
+ *
292
+ * @returns delay, -1 in case no delay was reported
293
+ */
294
+ private static getMaxExportDelay(
295
+ dto: ShapeDiverResponseDto,
296
+ body: ShapeDiverRequestExport,
297
+ ): number {
298
+ const exports = Array.isArray(body.exports)
299
+ ? body.exports
300
+ : [body.exports.id];
301
+ const outputs = body.outputs ?? [];
302
+
303
+ return Math.max(
304
+ ...Object.values(dto.exports ?? {})
305
+ .filter((e) => exports.includes(e.id))
306
+ .map((e) => e as ShapeDiverResponseExport)
307
+ .map((e) => e.delay ?? -1),
308
+ ...Object.values(dto.outputs ?? {})
309
+ .filter((o) => outputs.includes(o.id))
310
+ .map((o) => o as ShapeDiverResponseOutput)
311
+ .map((o) => o.delay ?? -1),
312
+ );
313
+ }
314
+ }
@@ -0,0 +1,97 @@
1
+ import {
2
+ ShapeDiverError,
3
+ ShapeDiverErrorType,
4
+ ShapeDiverRequestError as ShapeDiverRequestErrorCore,
5
+ ShapeDiverResponseError as ShapeDiverResponseErrorCore,
6
+ } from "@shapediver/sdk.geometry-api-sdk-core";
7
+ import { ShapeDiverResponseError } from "../ShapeDiverErrors";
8
+
9
+ /** Type guard for all error types of the Geometry Backend SDK package. */
10
+ export function isGBError(
11
+ e: any,
12
+ ): e is ShapeDiverError & ShapeDiverRequestErrorCore & ShapeDiverResponseError {
13
+ return (
14
+ e instanceof Error &&
15
+ "errorType" in e &&
16
+ Object.values(ShapeDiverErrorType).includes(e.errorType as any)
17
+ );
18
+ }
19
+
20
+ /** Type guard for a Geometry Backend SDK generic error. */
21
+ export function isGBGenericError(e: any): e is ShapeDiverError {
22
+ return (
23
+ e instanceof Error &&
24
+ "errorType" in e &&
25
+ e.errorType === ShapeDiverErrorType.Generic
26
+ );
27
+ }
28
+
29
+ /** Type guard for a Geometry Backend SDK request error. */
30
+ export function isGBRequestError(e: any): e is ShapeDiverRequestErrorCore {
31
+ return (
32
+ e instanceof Error &&
33
+ "errorType" in e &&
34
+ e.errorType === ShapeDiverErrorType.Request
35
+ );
36
+ }
37
+
38
+ /** Type guard for a Geometry Backend SDK response error. */
39
+ export function isGBResponseError(e: any): e is ShapeDiverResponseError {
40
+ return (
41
+ e instanceof Error &&
42
+ "errorType" in e &&
43
+ e.errorType === ShapeDiverErrorType.Response
44
+ );
45
+ }
46
+
47
+ /**
48
+ * Sends the given request, handles retries for the HTTP status 429, and maps
49
+ * the response error object to its typed representation.
50
+ */
51
+ export async function sendRequest<T>(call: () => Promise<T>): Promise<T> {
52
+ const retryLimit = 5;
53
+ let retryCounter = 0;
54
+
55
+ while (retryCounter < retryLimit) {
56
+ try {
57
+ return await call();
58
+ } catch (e) {
59
+ retryCounter++;
60
+
61
+ if (e instanceof ShapeDiverResponseErrorCore) {
62
+ /* Check for special response statuses */
63
+ if (e.status === 429) {
64
+ // 429 Too Many Requests - Extract waiting time from Retry-After header if existing.
65
+ const seconds = e.headers["retry-after"] ?? 60; // default is 1 minute
66
+ await sleep(Number(seconds) * 1000);
67
+ continue;
68
+ } else if (e.status === 502) {
69
+ await sleep(1000); // 1 second
70
+ continue;
71
+ }
72
+
73
+ // Error mapping
74
+ throw new ShapeDiverResponseError(e);
75
+ } else {
76
+ // Not a response error -> something went wrong
77
+ throw e;
78
+ }
79
+ }
80
+ }
81
+
82
+ throw new ShapeDiverError("Could not send request: Retry-limit reached");
83
+ }
84
+
85
+ /** Delays the response for the given number of milliseconds */
86
+ export function sleep(ms: number): Promise<void> {
87
+ return new Promise((resolve) => setTimeout(resolve, ms));
88
+ }
89
+
90
+ /** Encode the given string to base64 */
91
+ export function encodeBase64(str: string): string {
92
+ try {
93
+ return btoa(str);
94
+ } catch (err) {
95
+ return Buffer.from(str).toString("base64");
96
+ }
97
+ }