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

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