@gibwork/sdk 0.0.5 → 0.1.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/CHANGELOG.md CHANGED
@@ -4,6 +4,13 @@ All notable changes to this package will be documented here.
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ - Add cross-creator task discovery with `tasks.listAvailable`.
8
+ - Add paid submission creation, prepare/submit methods, and intent status reads.
9
+ - Preserve idempotency and payment recovery context without automatic retries.
10
+ - Validate submission inputs and intent responses, and expose `Retry-After`.
11
+ - Keep timeouts, cancellation, and ambiguous-submit handling active through response body reads.
12
+ - Add thirteen API signing vectors, participation/recovery tests, and a persisted stage example.
13
+
7
14
  - Add the typed Gibwork client with complete External API route coverage.
8
15
  - Add high-level task creation, refund, and submission approval workflows.
9
16
  - Add injected-wallet and Node private-key signer support.
package/README.md CHANGED
@@ -120,16 +120,100 @@ All request methods accept an optional final `{ signal }` argument for
120
120
  cancellation. Set the default request timeout with `timeoutMs` when creating
121
121
  the client.
122
122
 
123
+ ## Discover tasks and submit work
124
+
125
+ ```ts
126
+ const available = await gibwork.tasks.listAvailable({ page: 1, limit: 15 });
127
+
128
+ // Persist the key and exact input before sending. Supply your own UUIDv4.
129
+ const input = {
130
+ content: '<p>Completed work: https://example.com/work</p>',
131
+ idempotencyKey: savedIdempotencyKey,
132
+ };
133
+ const intent = await gibwork.submissions.create(taskId, input);
134
+
135
+ if (intent.status === 'submitted') {
136
+ const current = await gibwork.submissions.getIntent(taskId, intent.intentId);
137
+ }
138
+ ```
139
+
140
+ Discovery is free and wallet-authenticated across creators; `tasks.list` lists
141
+ created tasks. Discovery accepts only `page` and `limit` (defaults 1/15, maximum
142
+ limit 100). Results show eligibility requirements, not a guarantee that your
143
+ wallet can submit. `asset.amount` and `minSubmissionAmount` are strings in base
144
+ units; the asset amount is the original reward pool, not remaining funds.
145
+
146
+ Submitting work requires an active platform wallet, eligibility, and currently
147
+ **0.15 USDC**. Network costs are sponsored. Read the returned `fee` for amounts.
148
+ Optional `referral` and `mediaIds` are supported; media must already be uploaded
149
+ and owned by the caller. The SDK preserves submission content/referral exactly
150
+ when signing, including whitespace and HTML.
151
+
152
+ For separate phases use:
153
+
154
+ - `submissions.prepareCreate(taskId, input)`
155
+ - `signPreparedTransaction(prepared.serializedTransaction, signer)`
156
+ - `submissions.submitCreate(taskId, intentId, { paymentAttemptId, signedTransaction })`
157
+ - `submissions.getIntent(taskId, intentId)`
158
+
159
+ Prepare can return an existing intent without a transaction. `submissions.create`
160
+ signs only a pending intent with a transaction and returns the state after one
161
+ submit; it does not poll or retry automatically.
162
+
163
+ | State | Meaning and recovery |
164
+ | -------------------- | -------------------------------------------------------------------------------------- |
165
+ | `pending` | Not submitted. Retry prepare with the original input/key to obtain the transaction. |
166
+ | `submitted` | Unresolved. Read the same intent later. |
167
+ | `fulfilled` | Fee confirmed and submission created. |
168
+ | `failed` / `expired` | Resolve the attempt before explicitly choosing a new key. |
169
+ | `requires_review` | Fee paid; operator attention required. Retain the intent ID/hash and do not pay again. |
170
+
171
+ Only `fulfilled` confirms creation. `taskSubmissionId` is allocated in advance;
172
+ its presence and HTTP 200 alone do not prove success. Intent status does not
173
+ report subsequent creator review/payout, and existing creator submission reads
174
+ have not been extended to participants.
175
+
176
+ After an interrupted prepare, reuse the exact input/key with the same wallet
177
+ and environment. After an uncertain submit, use `getIntent` with the original
178
+ wallet, including after wallet replacement. Each call generates fresh auth.
179
+ The same key still returns the original intent after failure/expiry. A 409 error
180
+ may contain an existing intent ID in its structured body.
181
+
182
+ The upstream wallet limits are discovery 30/minute, prepare 2/minute, submit
183
+ 5/minute, and status 60/minute. Respect `GibworkApiError.retryAfter` (seconds or an
184
+ HTTP date) before another request; it is optional. The
185
+ [stage example](examples/create-submission.mjs) persists recovery data before
186
+ payment and uses explicit status reads on later invocations:
187
+
188
+ ```bash
189
+ npm run build
190
+ node --env-file=.env examples/create-submission.mjs work.json recovery.json
191
+ ```
192
+
193
+ `work.json` contains `{ "taskId": "UUID", "input": { "content": "...",
194
+ "idempotencyKey": "UUIDv4" } }`. This example may charge the stage participation
195
+ fee. Once it records an intent, rerunning only reads status. No media upload or
196
+ operator resolution endpoint is provided by this SDK.
197
+
123
198
  ## Errors
124
199
 
125
200
  API failures throw `GibworkApiError` with `status` and the parsed response
126
- `body`. Network failures and timeouts use typed SDK errors.
201
+ `body`, plus optional `requestId` and raw `retryAfter`. Network failures and timeouts use typed SDK errors.
127
202
 
128
203
  Transaction submits are never retried automatically. If the network or server
129
204
  fails while submitting, the SDK throws `GibworkAmbiguousSubmitError` with safe
130
205
  task, submission, and intent identifiers. Do not resubmit the same intent
131
206
  blindly.
132
207
 
208
+ `GibworkValidationError` reports invalid discovery/submission input before a
209
+ request. `GibworkProtocolError` reports an unusable intent response. The
210
+ `submissions.create` convenience method wraps non-ambiguous failures in
211
+ `GibworkSubmissionOperationError`: `context` retains the phase, original
212
+ idempotency key, wallet/environment/API location, and intent identifiers when
213
+ known; `cause` retains the original typed error. Ambiguous payments remain
214
+ `GibworkAmbiguousSubmitError` with enriched context. A wallet rejection or
215
+ cancellation before dispatch is not classified as a broadcast payment.
216
+
133
217
  ## Development
134
218
 
135
219
  ```bash
@@ -14,19 +14,21 @@ var GibworkConfigurationError = class extends GibworkError {
14
14
  name = "GibworkConfigurationError";
15
15
  };
16
16
  var GibworkApiError = class extends GibworkError {
17
- constructor(status, body, method, path, requestId) {
17
+ constructor(status, body, method, path, requestId, retryAfter) {
18
18
  super(`Gibwork API request failed with HTTP ${status}`);
19
19
  this.status = status;
20
20
  this.body = body;
21
21
  this.method = method;
22
22
  this.path = path;
23
23
  this.requestId = requestId;
24
+ this.retryAfter = retryAfter;
24
25
  }
25
26
  status;
26
27
  body;
27
28
  method;
28
29
  path;
29
30
  requestId;
31
+ retryAfter;
30
32
  name = "GibworkApiError";
31
33
  };
32
34
  var GibworkNetworkError = class extends GibworkError {
@@ -71,6 +73,51 @@ var GibworkAmbiguousSubmitError = class extends GibworkNetworkError {
71
73
  context;
72
74
  name = "GibworkAmbiguousSubmitError";
73
75
  };
76
+ var GibworkValidationError = class extends GibworkError {
77
+ name = "GibworkValidationError";
78
+ };
79
+ var GibworkProtocolError = class extends GibworkError {
80
+ name = "GibworkProtocolError";
81
+ };
82
+ var GibworkSubmissionOperationError = class extends GibworkError {
83
+ constructor(context, options) {
84
+ super(
85
+ "Submission creation interrupted; recover using the original input/key or intent",
86
+ options
87
+ );
88
+ this.context = context;
89
+ }
90
+ context;
91
+ name = "GibworkSubmissionOperationError";
92
+ };
93
+
94
+ // src/auth/validation.ts
95
+ var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
96
+ var UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
97
+ function isRecord(value) {
98
+ return typeof value === "object" && value !== null && !Array.isArray(value);
99
+ }
100
+ function assertObjectKeys(value, keys) {
101
+ if (!isRecord(value) || Object.keys(value).some((key) => !keys.includes(key))) {
102
+ throw new GibworkValidationError(
103
+ `Expected an object containing only: ${keys.join(", ")}`
104
+ );
105
+ }
106
+ }
107
+ function isUuid(value, v4 = false) {
108
+ return typeof value === "string" && (v4 ? UUID_V4 : UUID).test(value);
109
+ }
110
+ function assertUuid(value, name, v4 = false) {
111
+ if (!isUuid(value, v4))
112
+ throw new GibworkValidationError(`${name} must be a UUID${v4 ? "v4" : ""}`);
113
+ }
114
+ function assertText(value, name, min, max) {
115
+ if (typeof value !== "string" || Array.from(value).length < min || Array.from(value).length > max) {
116
+ throw new GibworkValidationError(
117
+ `${name} must contain ${min}\u2013${max} characters`
118
+ );
119
+ }
120
+ }
74
121
  var HTML_SANITIZE_OPTIONS = {
75
122
  allowedTags: sanitizeHtml.defaults.allowedTags.concat(["img"]),
76
123
  allowedAttributes: {
@@ -225,6 +272,42 @@ function trimOptional(value) {
225
272
  const trimmed = value.trim();
226
273
  return trimmed === "" ? void 0 : trimmed;
227
274
  }
275
+ function normalizeAvailableTasksQuery(query = {}) {
276
+ assertObjectKeys(query, ["page", "limit"]);
277
+ const page = query.page === void 0 ? 1 : query.page;
278
+ const limit = query.limit === void 0 ? 15 : query.limit;
279
+ if (!Number.isSafeInteger(page) || page < 1 || !Number.isSafeInteger(limit) || limit < 1 || limit > 100 || !Number.isSafeInteger((page - 1) * limit)) {
280
+ throw new GibworkValidationError(
281
+ "page must be a positive safe integer, limit must be 1\u2013100, and the offset must be safe"
282
+ );
283
+ }
284
+ return { page, limit };
285
+ }
286
+ function normalizeCreateSubmission(input, walletAddress) {
287
+ assertObjectKeys(input, [
288
+ "content",
289
+ "referral",
290
+ "mediaIds",
291
+ "idempotencyKey"
292
+ ]);
293
+ assertText(input.content, "content", 1, 5e4);
294
+ if (input.referral != null) assertText(input.referral, "referral", 0, 200);
295
+ assertUuid(input.idempotencyKey, "idempotencyKey", true);
296
+ const mediaIds = input.mediaIds ?? [];
297
+ if (!Array.isArray(mediaIds) || mediaIds.length > 10 || new Set(mediaIds).size !== mediaIds.length) {
298
+ throw new GibworkValidationError(
299
+ "mediaIds must contain at most ten distinct UUIDv4 values"
300
+ );
301
+ }
302
+ for (const id of mediaIds) assertUuid(id, "mediaIds", true);
303
+ return {
304
+ walletAddress,
305
+ content: input.content,
306
+ referral: input.referral ?? null,
307
+ mediaIds: [...mediaIds],
308
+ idempotencyKey: input.idempotencyKey
309
+ };
310
+ }
228
311
  async function createWalletAuthHeaders(signer, descriptor, dependencies = {}) {
229
312
  const values = {
230
313
  walletAddress: signer.publicKey.toBase58(),
@@ -261,6 +344,30 @@ function buildWalletAuthMessage(descriptor, values) {
261
344
  `${descriptor.hash[0]}:${descriptor.hash[1]}`
262
345
  ].join("\n");
263
346
  }
347
+
348
+ // src/resources/submissions/submission-intent.ts
349
+ function submissionIntentPath(taskId, intentId) {
350
+ assertUuid(taskId, "taskId");
351
+ if (intentId !== void 0) assertUuid(intentId, "intentId");
352
+ return `/v2/int/tasks/${taskId}/submission-intents${intentId === void 0 ? "" : `/${intentId}`}`;
353
+ }
354
+ var statuses = [
355
+ "pending",
356
+ "submitted",
357
+ "fulfilled",
358
+ "failed",
359
+ "expired",
360
+ "requires_review"
361
+ ];
362
+ var nonempty = (value) => typeof value === "string" && value.length > 0;
363
+ var decimal = (value) => typeof value === "string" && /^\d+(\.\d+)?$/.test(value);
364
+ var natural = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
365
+ var sameId = (value, expected) => value.toLowerCase() === expected.toLowerCase();
366
+ function validateSubmissionIntent(value, taskId, intentId, paymentAttemptId) {
367
+ if (!isRecord(value) || !isUuid(value.intentId) || !isUuid(value.taskId) || !isUuid(value.taskSubmissionId) || !isUuid(value.paymentAttemptId, true) || !sameId(value.taskId, taskId) || intentId !== void 0 && !sameId(value.intentId, intentId) || paymentAttemptId !== void 0 && !sameId(value.paymentAttemptId, paymentAttemptId) || typeof value.status !== "string" || !statuses.includes(value.status) || !(value.txHash === null || nonempty(value.txHash)) || !nonempty(value.expiresAt) || !Number.isFinite(Date.parse(value.expiresAt)) || !natural(value.lastValidBlockHeight) || value.reviewReason !== void 0 && typeof value.reviewReason !== "string" || value.serializedTransaction !== void 0 && !nonempty(value.serializedTransaction) || !isRecord(value.fee) || !decimal(value.fee.amount) || !decimal(value.fee.totalDebit) || typeof value.fee.amountBaseUnits !== "string" || !/^\d+$/.test(value.fee.amountBaseUnits) || value.fee.symbol !== "USDC" || !natural(value.fee.decimals) || !nonempty(value.fee.mintAddress) || !nonempty(value.fee.destinationAddress)) {
368
+ throw new GibworkProtocolError("Invalid submission intent response");
369
+ }
370
+ }
264
371
  async function signPreparedTransaction(serializedTransaction, signer) {
265
372
  if (typeof serializedTransaction !== "string" || !serializedTransaction) {
266
373
  throw new GibworkConfigurationError(
@@ -350,6 +457,142 @@ var SubmissionsResource = class {
350
457
  transport;
351
458
  signer;
352
459
  comments;
460
+ /** Creates work through a participation fee; only fulfilled confirms creation. */
461
+ async create(taskId, input, options) {
462
+ const path = submissionIntentPath(taskId);
463
+ const { walletAddress, ...snapshot } = normalizeCreateSubmission(
464
+ input,
465
+ this.walletAddress
466
+ );
467
+ let context = {
468
+ taskId,
469
+ idempotencyKey: snapshot.idempotencyKey,
470
+ walletAddress,
471
+ ...this.transport.recoveryLocation,
472
+ phase: "prepare"
473
+ };
474
+ try {
475
+ assertNotAborted(options, path);
476
+ const prepared = await this.prepareCreate(taskId, snapshot, options);
477
+ context = {
478
+ ...context,
479
+ intentId: prepared.intentId,
480
+ paymentAttemptId: prepared.paymentAttemptId,
481
+ submissionId: prepared.taskSubmissionId,
482
+ phase: "sign"
483
+ };
484
+ if (prepared.status !== "pending") return prepared;
485
+ if (!prepared.serializedTransaction)
486
+ throw new GibworkProtocolError(
487
+ "Pending prepare response is missing its transaction"
488
+ );
489
+ assertNotAborted(options, path);
490
+ const signedTransaction = await signPreparedTransaction(
491
+ prepared.serializedTransaction,
492
+ this.signer
493
+ );
494
+ assertNotAborted(options, path);
495
+ context = { ...context, phase: "submit" };
496
+ return await this.submitCreate(
497
+ taskId,
498
+ prepared.intentId,
499
+ { paymentAttemptId: prepared.paymentAttemptId, signedTransaction },
500
+ options
501
+ );
502
+ } catch (cause) {
503
+ if (cause instanceof GibworkAmbiguousSubmitError) {
504
+ throw new GibworkAmbiguousSubmitError(
505
+ cause.method,
506
+ cause.path,
507
+ {
508
+ ...cause.context,
509
+ ...context,
510
+ operation: "create-submission",
511
+ intentId: cause.context.intentId
512
+ },
513
+ { cause }
514
+ );
515
+ }
516
+ throw new GibworkSubmissionOperationError(context, { cause });
517
+ }
518
+ }
519
+ /** Reuse the exact input and idempotency key after an interrupted prepare. */
520
+ async prepareCreate(taskId, input, options) {
521
+ const path = submissionIntentPath(taskId);
522
+ const body = normalizeCreateSubmission(input, this.walletAddress);
523
+ assertNotAborted(options, path);
524
+ const headers = await createWalletAuthHeaders(this.signer, {
525
+ operation: "gibwork:create-task-submission-intent",
526
+ method: "POST",
527
+ path,
528
+ resourceFields: [["taskId", taskId]],
529
+ hash: ["payloadHash", sha256Json(body)]
530
+ });
531
+ return this.transport.request({
532
+ method: "POST",
533
+ path,
534
+ headers,
535
+ body,
536
+ ...options ? { options } : {},
537
+ validateResponse: (value) => validateSubmissionIntent(value, taskId)
538
+ });
539
+ }
540
+ /** Never automatically retries. Recover an uncertain payment with getIntent. */
541
+ async submitCreate(taskId, intentId, input, options) {
542
+ const path = `${submissionIntentPath(taskId, intentId)}/submit`;
543
+ assertObjectKeys(input, ["paymentAttemptId", "signedTransaction"]);
544
+ assertUuid(input.paymentAttemptId, "paymentAttemptId", true);
545
+ assertText(input.signedTransaction, "signedTransaction", 1, 1e4);
546
+ const body = {
547
+ paymentAttemptId: input.paymentAttemptId,
548
+ signedTransaction: input.signedTransaction
549
+ };
550
+ return this.transport.request({
551
+ method: "POST",
552
+ path,
553
+ body,
554
+ ...options ? { options } : {},
555
+ ambiguousSubmit: {
556
+ operation: "create-submission",
557
+ taskId,
558
+ intentId,
559
+ paymentAttemptId: body.paymentAttemptId,
560
+ walletAddress: this.walletAddress,
561
+ ...this.transport.recoveryLocation
562
+ },
563
+ validateResponse: (value) => validateSubmissionIntent(
564
+ value,
565
+ taskId,
566
+ intentId,
567
+ body.paymentAttemptId
568
+ )
569
+ });
570
+ }
571
+ /** Reads/reconciles creation using the original wallet; does not report review/payout. */
572
+ async getIntent(taskId, intentId, options) {
573
+ const path = submissionIntentPath(taskId, intentId);
574
+ if (options?.signal?.aborted)
575
+ throw new GibworkRequestAbortedError("GET", path, {
576
+ cause: options.signal.reason
577
+ });
578
+ const headers = await createWalletAuthHeaders(this.signer, {
579
+ operation: "gibwork:view-task-submission-intent",
580
+ method: "GET",
581
+ path,
582
+ resourceFields: [
583
+ ["taskId", taskId],
584
+ ["intentId", intentId]
585
+ ],
586
+ hash: ["queryHash", sha256Json({})]
587
+ });
588
+ return this.transport.request({
589
+ method: "GET",
590
+ path,
591
+ headers,
592
+ ...options ? { options } : {},
593
+ validateResponse: (value) => validateSubmissionIntent(value, taskId, intentId)
594
+ });
595
+ }
353
596
  async list(taskId, query = {}, options) {
354
597
  const normalized = normalizeSubmissionPagination(query);
355
598
  const path = `/v2/int/tasks/${encodeURIComponent(taskId)}/submissions`;
@@ -369,6 +612,25 @@ var SubmissionsResource = class {
369
612
  ...options ? { options } : {}
370
613
  });
371
614
  }
615
+ async get(taskId, submissionId, options) {
616
+ const path = `/v2/int/tasks/${encodeURIComponent(taskId)}/submissions/${encodeURIComponent(submissionId)}`;
617
+ const headers = await createWalletAuthHeaders(this.signer, {
618
+ operation: "gibwork:view-task-submission",
619
+ method: "GET",
620
+ path,
621
+ resourceFields: [
622
+ ["taskId", taskId],
623
+ ["taskSubmissionId", submissionId]
624
+ ],
625
+ hash: ["queryHash", sha256Json({})]
626
+ });
627
+ return this.transport.request({
628
+ method: "GET",
629
+ path,
630
+ headers,
631
+ ...options ? { options } : {}
632
+ });
633
+ }
372
634
  async approve(taskId, submissionId, input, options) {
373
635
  const prepared = await this.prepareApproval(
374
636
  taskId,
@@ -520,6 +782,23 @@ var TasksResource = class {
520
782
  ...options ? { options } : {}
521
783
  });
522
784
  }
785
+ /** Discover tasks across creators. Eligibility is checked when preparing work. */
786
+ async listAvailable(query = {}, options) {
787
+ const normalized = normalizeAvailableTasksQuery(query);
788
+ const path = "/v2/int/tasks/available";
789
+ const headers = await this.auth({
790
+ operation: "gibwork:view-available-tasks",
791
+ method: "GET",
792
+ path,
793
+ hash: ["queryHash", sha256Json(normalized)]
794
+ });
795
+ return this.transport.request({
796
+ method: "GET",
797
+ path: `${path}?${toQueryString(normalized)}`,
798
+ headers,
799
+ ...options ? { options } : {}
800
+ });
801
+ }
523
802
  async update(taskId, input, options) {
524
803
  const path = `/v2/int/tasks/${encodeURIComponent(taskId)}`;
525
804
  const body = normalizeUpdateTask(input, this.walletAddress);
@@ -665,6 +944,16 @@ var HttpTransport = class {
665
944
  );
666
945
  }
667
946
  }
947
+ /** Recovery location without URL credentials. Retains reverse-proxy path prefixes. */
948
+ get recoveryLocation() {
949
+ const url = new URL(this.baseUrl);
950
+ url.username = "";
951
+ url.password = "";
952
+ return {
953
+ apiUrl: url.toString().replace(/\/+$/, ""),
954
+ environment: this.production ? "prod" : "stage"
955
+ };
956
+ }
668
957
  async request(request) {
669
958
  const controller = new AbortController();
670
959
  const callerSignal = request.options?.signal;
@@ -674,12 +963,6 @@ var HttpTransport = class {
674
963
  cause: callerSignal.reason
675
964
  });
676
965
  }
677
- const abortFromCaller = () => controller.abort(callerSignal?.reason);
678
- callerSignal?.addEventListener("abort", abortFromCaller, { once: true });
679
- const timeout = setTimeout(() => {
680
- timedOut = true;
681
- controller.abort();
682
- }, this.timeoutMs);
683
966
  const headers = {
684
967
  accept: "application/json",
685
968
  ...request.headers,
@@ -694,13 +977,40 @@ var HttpTransport = class {
694
977
  headers["content-type"] = "application/json";
695
978
  init.body = JSON.stringify(request.body);
696
979
  }
697
- let response;
980
+ const abortFromCaller = () => controller.abort(callerSignal?.reason);
981
+ callerSignal?.addEventListener("abort", abortFromCaller, { once: true });
982
+ let rejectOnAbort = () => {
983
+ };
984
+ const aborted = new Promise((_, reject) => {
985
+ rejectOnAbort = () => reject(controller.signal.reason);
986
+ controller.signal.addEventListener("abort", rejectOnAbort, {
987
+ once: true
988
+ });
989
+ });
990
+ const timeout = setTimeout(() => {
991
+ timedOut = true;
992
+ controller.abort();
993
+ }, this.timeoutMs);
698
994
  try {
699
- response = await this.fetchImplementation(
700
- `${this.baseUrl}${request.path}`,
701
- init
702
- );
995
+ const response = await Promise.race([
996
+ this.fetchImplementation(`${this.baseUrl}${request.path}`, init),
997
+ aborted
998
+ ]);
999
+ const body = await Promise.race([parseResponseBody(response), aborted]);
1000
+ if (!response.ok) {
1001
+ throw new GibworkApiError(
1002
+ response.status,
1003
+ body,
1004
+ request.method,
1005
+ request.path,
1006
+ response.headers.get("x-request-id") ?? void 0,
1007
+ response.headers.get("retry-after") ?? void 0
1008
+ );
1009
+ }
1010
+ request.validateResponse?.(body);
1011
+ return body;
703
1012
  } catch (cause) {
1013
+ if (cause instanceof GibworkApiError && cause.status < 500) throw cause;
704
1014
  if (request.ambiguousSubmit) {
705
1015
  throw new GibworkAmbiguousSubmitError(
706
1016
  request.method,
@@ -709,6 +1019,8 @@ var HttpTransport = class {
709
1019
  { cause }
710
1020
  );
711
1021
  }
1022
+ if (cause instanceof GibworkApiError || cause instanceof GibworkProtocolError)
1023
+ throw cause;
712
1024
  if (timedOut) {
713
1025
  throw new GibworkTimeoutError(
714
1026
  request.method,
@@ -723,7 +1035,7 @@ var HttpTransport = class {
723
1035
  });
724
1036
  }
725
1037
  throw new GibworkNetworkError(
726
- "Could not reach the Gibwork API",
1038
+ "Could not read the Gibwork API response",
727
1039
  request.method,
728
1040
  request.path,
729
1041
  { cause }
@@ -731,28 +1043,8 @@ var HttpTransport = class {
731
1043
  } finally {
732
1044
  clearTimeout(timeout);
733
1045
  callerSignal?.removeEventListener("abort", abortFromCaller);
1046
+ controller.signal.removeEventListener("abort", rejectOnAbort);
734
1047
  }
735
- const body = await parseResponseBody(response);
736
- if (!response.ok) {
737
- const requestId = response.headers.get("x-request-id") ?? void 0;
738
- const apiError = new GibworkApiError(
739
- response.status,
740
- body,
741
- request.method,
742
- request.path,
743
- requestId
744
- );
745
- if (request.ambiguousSubmit && response.status >= 500) {
746
- throw new GibworkAmbiguousSubmitError(
747
- request.method,
748
- request.path,
749
- request.ambiguousSubmit,
750
- { cause: apiError }
751
- );
752
- }
753
- throw apiError;
754
- }
755
- return body;
756
1048
  }
757
1049
  };
758
1050
  function normalizeBaseUrl(value) {
@@ -811,6 +1103,6 @@ function assertSigner(signer) {
811
1103
  }
812
1104
  }
813
1105
 
814
- export { DEFAULT_GIBWORK_API_URL, GibworkAmbiguousSubmitError, GibworkApiError, GibworkClient, GibworkConfigurationError, GibworkError, GibworkNetworkError, GibworkRequestAbortedError, GibworkTimeoutError, signPreparedTransaction };
815
- //# sourceMappingURL=chunk-BNAVP5XZ.js.map
816
- //# sourceMappingURL=chunk-BNAVP5XZ.js.map
1106
+ export { DEFAULT_GIBWORK_API_URL, GibworkAmbiguousSubmitError, GibworkApiError, GibworkClient, GibworkConfigurationError, GibworkError, GibworkNetworkError, GibworkProtocolError, GibworkRequestAbortedError, GibworkSubmissionOperationError, GibworkTimeoutError, GibworkValidationError, signPreparedTransaction };
1107
+ //# sourceMappingURL=chunk-HJ5D5SOE.js.map
1108
+ //# sourceMappingURL=chunk-HJ5D5SOE.js.map