@gscdump/sdk 7.8.3 → 7.8.4

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/dist/package.mjs CHANGED
@@ -1,2 +1,2 @@
1
- var version = "7.8.3";
1
+ var version = "7.8.4";
2
2
  export { version };
@@ -49,6 +49,8 @@ export interface CreateGscdumpV1ClientOptions {
49
49
  fetch?: typeof fetch;
50
50
  headers?: GscdumpV1HeadersResolver;
51
51
  retry?: GscdumpV1RetryOptions;
52
+ /** Share concurrent identical queries within this client. Defaults to true. */
53
+ dedupe?: boolean;
52
54
  }
53
55
  export interface GscdumpV1ExecuteOptions {
54
56
  signal?: AbortSignal;
package/dist/v1/http.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import { canonicalJson } from "../canonical-json.mjs";
1
2
  var GscdumpV1Error = class extends Error {
2
3
  tag = "GscdumpV1Error";
3
4
  code;
@@ -249,6 +250,7 @@ function createGscdumpV1Client(options) {
249
250
  const retryOptions = resolveRetryOptions(options.retry);
250
251
  const apiRoot = options.apiRoot ?? DEFAULT_API_ROOT;
251
252
  const fetchImpl = options.fetch ?? globalThis.fetch;
253
+ const inflight = /* @__PURE__ */ new Map();
252
254
  if (typeof fetchImpl !== "function") throw new TypeError("createGscdumpV1Client requires a fetch implementation in this runtime.");
253
255
  const execute = executeUntyped;
254
256
  async function executeUntyped(operationId, input, executeOptions = {}) {
@@ -278,90 +280,114 @@ function createGscdumpV1Client(options) {
278
280
  throw requestValidationError(operation.id, "query", cause);
279
281
  }
280
282
  const canRetry = operation.semantics.retry === "idempotent";
281
- for (let attempt = 1; attempt <= retryOptions.maxAttempts; attempt++) {
282
- if (executeOptions.signal?.aborted) throw abortedRequestError(operation, executeOptions);
283
- const headers = await awaitWithAbort(buildRequestHeaders(options, operation, prepared, executeOptions), operation, executeOptions);
284
- if (executeOptions.signal?.aborted) throw abortedRequestError(operation, executeOptions);
285
- let response;
286
- try {
287
- response = await fetchImpl(url, {
288
- method: operation.method,
289
- headers,
290
- body: prepared.body === void 0 ? void 0 : JSON.stringify(prepared.body),
291
- signal: executeOptions.signal
283
+ if (executeOptions.signal?.aborted) throw abortedRequestError(operation, executeOptions);
284
+ const firstHeaders = await awaitWithAbort(buildRequestHeaders(options, operation, prepared, executeOptions), operation, executeOptions);
285
+ if (executeOptions.signal?.aborted) throw abortedRequestError(operation, executeOptions);
286
+ const dedupeKey = options.dedupe !== false && !executeOptions.signal && operation.semantics.kind === "query" ? canonicalJson([
287
+ operation.id,
288
+ url,
289
+ prepared.body,
290
+ [...firstHeaders.entries()].sort(([a], [b]) => a.localeCompare(b))
291
+ ]) : null;
292
+ if (dedupeKey) {
293
+ const pending = inflight.get(dedupeKey);
294
+ if (pending) return pending;
295
+ }
296
+ const pending = performRequest();
297
+ if (dedupeKey) {
298
+ inflight.set(dedupeKey, pending);
299
+ const remove = () => {
300
+ if (inflight.get(dedupeKey) === pending) inflight.delete(dedupeKey);
301
+ };
302
+ pending.then(remove, remove);
303
+ }
304
+ return pending;
305
+ async function performRequest() {
306
+ for (let attempt = 1; attempt <= retryOptions.maxAttempts; attempt++) {
307
+ if (executeOptions.signal?.aborted) throw abortedRequestError(operation, executeOptions);
308
+ const headers = attempt === 1 || dedupeKey !== null ? firstHeaders : await awaitWithAbort(buildRequestHeaders(options, operation, prepared, executeOptions), operation, executeOptions);
309
+ if (executeOptions.signal?.aborted) throw abortedRequestError(operation, executeOptions);
310
+ let response;
311
+ try {
312
+ response = await fetchImpl(url, {
313
+ method: operation.method,
314
+ headers,
315
+ body: prepared.body === void 0 ? void 0 : JSON.stringify(prepared.body),
316
+ signal: executeOptions.signal
317
+ });
318
+ } catch (cause) {
319
+ if (isAbortError(cause, executeOptions.signal)) throw abortedRequestError(operation, executeOptions, cause);
320
+ if (canRetry && attempt < retryOptions.maxAttempts) {
321
+ await waitForRetry(retryDelay(attempt, null, retryOptions), operation, executeOptions);
322
+ continue;
323
+ }
324
+ throw new GscdumpV1Error({
325
+ code: "network_error",
326
+ message: `${operation.id}: network request failed`,
327
+ requestId: executeOptions.requestId,
328
+ retryable: canRetry,
329
+ details: { attempts: attempt },
330
+ cause
331
+ });
332
+ }
333
+ const requestId = requestIdFrom(response) ?? executeOptions.requestId;
334
+ let payload;
335
+ try {
336
+ payload = await readJson(response);
337
+ } catch (cause) {
338
+ if (isAbortError(cause, executeOptions.signal)) throw abortedRequestError(operation, executeOptions, cause);
339
+ throw responseValidationError(operation.id, response.status, requestId, cause);
340
+ }
341
+ if (executeOptions.signal?.aborted) throw abortedRequestError(operation, executeOptions);
342
+ const responseContract = operation.responses[response.status];
343
+ if (responseContract) try {
344
+ return responseContract.client.parse(payload);
345
+ } catch (cause) {
346
+ throw responseValidationError(operation.id, response.status, requestId, cause);
347
+ }
348
+ if (response.ok) throw new GscdumpV1Error({
349
+ code: "protocol_error",
350
+ message: `${operation.id}: undeclared success status ${response.status}`,
351
+ status: response.status,
352
+ requestId,
353
+ retryable: false,
354
+ details: { status: response.status }
292
355
  });
293
- } catch (cause) {
294
- if (isAbortError(cause, executeOptions.signal)) throw abortedRequestError(operation, executeOptions, cause);
295
- if (canRetry && attempt < retryOptions.maxAttempts) {
296
- await waitForRetry(retryDelay(attempt, null, retryOptions), operation, executeOptions);
297
- continue;
356
+ let envelope;
357
+ try {
358
+ envelope = protocol.schemas.errorEnvelope.client.parse(payload);
359
+ } catch (cause) {
360
+ throw responseValidationError(operation.id, response.status, requestId, cause);
298
361
  }
299
- throw new GscdumpV1Error({
300
- code: "network_error",
301
- message: `${operation.id}: network request failed`,
302
- requestId: executeOptions.requestId,
303
- retryable: canRetry,
304
- details: { attempts: attempt },
305
- cause
362
+ if (!operation.errors.includes(envelope.error.code)) throw new GscdumpV1Error({
363
+ code: "protocol_error",
364
+ message: `${operation.id}: undeclared error code ${envelope.error.code}`,
365
+ status: response.status,
366
+ requestId: envelope.error.requestId,
367
+ retryable: false,
368
+ details: { code: envelope.error.code }
306
369
  });
370
+ const apiError = new GscdumpV1Error({
371
+ code: envelope.error.code,
372
+ message: envelope.error.message,
373
+ status: response.status,
374
+ requestId: envelope.error.requestId,
375
+ retryable: envelope.error.retryable,
376
+ details: envelope.error.details ?? {}
377
+ });
378
+ if (canRetry && apiError.retryable && attempt < retryOptions.maxAttempts) {
379
+ await waitForRetry(retryDelay(attempt, response.headers.get("retry-after"), retryOptions), operation, executeOptions);
380
+ continue;
381
+ }
382
+ throw apiError;
307
383
  }
308
- const requestId = requestIdFrom(response) ?? executeOptions.requestId;
309
- let payload;
310
- try {
311
- payload = await readJson(response);
312
- } catch (cause) {
313
- if (isAbortError(cause, executeOptions.signal)) throw abortedRequestError(operation, executeOptions, cause);
314
- throw responseValidationError(operation.id, response.status, requestId, cause);
315
- }
316
- if (executeOptions.signal?.aborted) throw abortedRequestError(operation, executeOptions);
317
- const responseContract = operation.responses[response.status];
318
- if (responseContract) try {
319
- return responseContract.client.parse(payload);
320
- } catch (cause) {
321
- throw responseValidationError(operation.id, response.status, requestId, cause);
322
- }
323
- if (response.ok) throw new GscdumpV1Error({
324
- code: "protocol_error",
325
- message: `${operation.id}: undeclared success status ${response.status}`,
326
- status: response.status,
327
- requestId,
328
- retryable: false,
329
- details: { status: response.status }
330
- });
331
- let envelope;
332
- try {
333
- envelope = protocol.schemas.errorEnvelope.client.parse(payload);
334
- } catch (cause) {
335
- throw responseValidationError(operation.id, response.status, requestId, cause);
336
- }
337
- if (!operation.errors.includes(envelope.error.code)) throw new GscdumpV1Error({
338
- code: "protocol_error",
339
- message: `${operation.id}: undeclared error code ${envelope.error.code}`,
340
- status: response.status,
341
- requestId: envelope.error.requestId,
384
+ throw new GscdumpV1Error({
385
+ code: "network_error",
386
+ message: `${operationId}: retry budget exhausted`,
342
387
  retryable: false,
343
- details: { code: envelope.error.code }
344
- });
345
- const apiError = new GscdumpV1Error({
346
- code: envelope.error.code,
347
- message: envelope.error.message,
348
- status: response.status,
349
- requestId: envelope.error.requestId,
350
- retryable: envelope.error.retryable,
351
- details: envelope.error.details ?? {}
388
+ details: {}
352
389
  });
353
- if (canRetry && apiError.retryable && attempt < retryOptions.maxAttempts) {
354
- await waitForRetry(retryDelay(attempt, response.headers.get("retry-after"), retryOptions), operation, executeOptions);
355
- continue;
356
- }
357
- throw apiError;
358
390
  }
359
- throw new GscdumpV1Error({
360
- code: "network_error",
361
- message: `${operationId}: retry budget exhausted`,
362
- retryable: false,
363
- details: {}
364
- });
365
391
  }
366
392
  return {
367
393
  getRegisteredHostIdentity: (input, executeOptions) => execute("partner.sites.registered_host.identity.get", input, executeOptions),
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gscdump/sdk",
3
3
  "type": "module",
4
- "version": "7.8.3",
4
+ "version": "7.8.4",
5
5
  "description": "Consumer SDK for hosted gscdump.com integrations.",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
@@ -144,8 +144,8 @@
144
144
  "node": ">=22.13.0"
145
145
  },
146
146
  "dependencies": {
147
- "@gscdump/contracts": "^7.8.3",
148
- "gscdump": "^7.8.3",
147
+ "@gscdump/contracts": "^7.8.4",
148
+ "gscdump": "^7.8.4",
149
149
  "ofetch": "^1.5.1",
150
150
  "zod": "^4.6.5"
151
151
  },