@invonetwork/web-sdk 0.3.0 → 0.4.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
@@ -1,7 +1,26 @@
1
1
  # Changelog
2
2
 
3
3
  All notable changes to `@invonetwork/web-sdk` are documented here. This project follows
4
- [Semantic Versioning](https://semver.org/).
4
+ [Semantic Versioning](https://semver.org/). Releases are managed with
5
+ [changesets](https://github.com/changesets/changesets).
6
+
7
+ ## [0.4.0] — 2026-06-30
8
+
9
+ Additive release — more server reads, edge-ready webhooks, cancellation, and tooling.
10
+
11
+ - **`getInboundPending({ playerEmail | playerPhone })`** — live, unclaimed inbound
12
+ sends/transfers for a player (the source of truth behind the "you have X to collect"
13
+ badge; pairs with `transfer.claim_pending`).
14
+ - **`verifyWebhookAsync`** — Web Crypto variant of `verifyWebhook` that runs on
15
+ Cloudflare Workers / Deno / Vercel+Netlify Edge / Bun / browsers; and
16
+ **`createWebhookHandler`** — a zero-dep Fetch-API `(Request) => Promise<Response>`
17
+ webhook route handler (Next.js App Router, Workers, Deno, Hono, Bun).
18
+ - **`iterateItemPurchaseHistory`** — async iterator that pages through a player's
19
+ full item-purchase history.
20
+ - **Per-call `AbortSignal`** — every method accepts an optional `{ signal }`; an
21
+ aborted call throws `InvoError` code `ABORTED` and is never retried.
22
+ - **Tooling**: ESLint (+ lint in CI), changesets release automation, `SECURITY.md`,
23
+ and `CODEOWNERS`.
5
24
 
6
25
  ## [0.3.0] — 2026-06-30
7
26
 
@@ -80,6 +99,7 @@ Initial scaffold.
80
99
  - Contracts extracted + auditor-verified against the INVO backend.
81
100
 
82
101
  ### Hardening (independent red-team pass)
102
+
83
103
  - **Guardian/minor `202` path no longer mismapped to `verificationMethod:"sms"`** —
84
104
  `initiateSend`/`initiateTransfer` now return `verificationMethod: undefined` and a
85
105
  `guardianApproval` block on the guardian path, so callers don't route into the
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  First-party TypeScript SDK for integrating **INVO** into partner **web** platforms (storefronts, web games, dashboards). It wraps INVO's web money flows behind a typed, versioned API — the web analog of the Unity/Unreal plugins.
4
4
 
5
- > **Status:** `v0.3.0`, published on npm. The backend it wraps is **live** on sandbox + production, so you can build and test against sandbox today.
5
+ > **Status:** `v0.4.0`, published on npm. The backend it wraps is **live** on sandbox + production, so you can build and test against sandbox today.
6
6
  > Canonical partner reference: **https://docs.invo.network/docs/currency-purchase** and **https://docs.invo.network/docs/game-developer-integration**.
7
7
 
8
8
  ## What it does
@@ -232,7 +232,7 @@ const item = await server.purchaseItem({
232
232
  - Client-side validation (missing fields, quantity outside `1..1000`, bad price, total ≠ unit×qty) throws `INVALID_INPUT` **before** any network call.
233
233
  - Fee split: **90% developer / 10% INVO** by default (per-partner override). Not guardian-gated.
234
234
 
235
- **Companion reads:** `server.getItemPurchaseHistory({ playerEmail, limit?, offset? })` and `server.getItemOrderDetails({ orderId | transactionId | clientRequestId })` (pass **exactly one** id — use `clientRequestId` for recovery: "did this purchase complete?").
235
+ **Companion reads:** `server.getItemPurchaseHistory({ playerEmail, limit?, offset? })` and `server.getItemOrderDetails({ orderId | transactionId | clientRequestId })` (pass **exactly one** id — use `clientRequestId` for recovery: "did this purchase complete?"). To walk the full history, `for await (const row of server.iterateItemPurchaseHistory({ playerEmail }))` pages automatically.
236
236
 
237
237
  ---
238
238
 
@@ -294,6 +294,14 @@ try {
294
294
  - **`err.isReceiverNotEnrolled`** on `confirmReceipt*` is the explicit signal to switch to claim-code entry.
295
295
  - Transfer self-claim additionally requires `SDK_TRANSFER_CONFIRM_RECEIPT_ENABLED`; if it's off, surface the claim-code path.
296
296
 
297
+ **"You have X to collect"** — to render a collect badge, list a player's live, unclaimed inbound sends/transfers (the source of truth behind the `transfer.claim_pending` webhook):
298
+
299
+ ```ts
300
+ const { inboundPending } = await server.getInboundPending({ playerEmail: "q@example.com" });
301
+ // each row: { transactionId, flow, amount, netAmount, sourceGame, toPhone, toIdentityId, claimCodeExpiresAt }
302
+ // match toPhone to the logged-in player (toIdentityId is null when the phone maps to >1 of your players)
303
+ ```
304
+
297
305
  ---
298
306
 
299
307
  ## Passkeys (enroll, approve, link)
@@ -349,6 +357,22 @@ function handler(req, res) {
349
357
 
350
358
  `verifyWebhook` does constant-time HMAC-SHA256 over `${t}.${rawBody}`, enforces a 5-minute replay window, and accepts an **array of secrets** during rotation (`verifyWebhook(body, sig, [oldSecret, newSecret])`). It returns a typed `InvoWebhookEvent` (discriminate on `event_type`) and throws `InvoError` (`WEBHOOK_SIGNATURE_INVALID` / `WEBHOOK_TIMESTAMP_EXPIRED` / `WEBHOOK_MALFORMED` / `WEBHOOK_SECRET_MISSING`) on any failure. **De-dupe yourself on `X-Invo-Idempotency-Key`** — the SDK verifies, it doesn't track delivery.
351
359
 
360
+ **Edge / serverless** (Cloudflare Workers, Deno, Vercel/Netlify Edge, Bun): `verifyWebhook` uses `node:crypto`, so use **`verifyWebhookAsync`** (Web Crypto) — same args/result, just `await` it — or the ready-made Fetch-API handler:
361
+
362
+ ```ts
363
+ import { createWebhookHandler } from "@invonetwork/web-sdk/server";
364
+
365
+ // Next.js App Router — app/invo/webhooks/route.ts
366
+ export const POST = createWebhookHandler({
367
+ secret: process.env.INVO_WEBHOOK_SECRET!,
368
+ onEvent: async (event, { idempotencyKey }) => {
369
+ // de-dupe on idempotencyKey, then grant value. Throw to return 500 (Invo retries).
370
+ },
371
+ });
372
+ ```
373
+
374
+ `createWebhookHandler` returns `(request: Request) => Promise<Response>` and runs in Next.js, Workers, Deno, Hono, and Bun. Bad signature → `400`; a throwing `onEvent` → `500`.
375
+
352
376
  ### Event types
353
377
 
354
378
  | Event | Fires for | Use it to |
@@ -381,6 +405,7 @@ new InvoServer({
381
405
  > Note: hook payloads include the request `url`, which for some calls embeds a player email (e.g. balance-by-email). Tokens and the game secret are sent as headers and are **never** passed to hooks — but redact the `url` if you log hook payloads.
382
406
 
383
407
  - **Request ids.** `InvoError.requestId` carries the backend request id (from `x-invo-request-id` / `x-request-id`) — quote it in support tickets.
408
+ - **Cancellation.** Every method takes an optional `{ signal }` (an `AbortSignal`) as its last argument — `server.getPlayerBalance({ playerEmail }, { signal })`. Aborting throws `InvoError` with `.code === "ABORTED"`, and an aborted call is never retried.
384
409
 
385
410
  ---
386
411
 
@@ -434,7 +459,13 @@ try {
434
459
  | `getItemPurchaseHistory({ playerEmail, limit?, offset? })` | `{ history, pagination, raw }` |
435
460
  | `getItemOrderDetails({ orderId? \| transactionId? \| clientRequestId? })` | `{ order, financialSummary, statusTimeline, raw }` |
436
461
  | `getPlayerBalance({ playerEmail? \| playerId? })` | `{ player, balances, summary, raw }` |
462
+ | `getInboundPending({ playerEmail? \| playerPhone? })` | `{ inboundPending, raw }` — live unclaimed inbound sends/transfers |
463
+ | `iterateItemPurchaseHistory({ playerEmail, pageSize? })` | async iterator over all history rows |
437
464
  | `verifyWebhook(rawBody, signatureHeader, secret \| secrets, opts?)` | typed `InvoWebhookEvent` (throws on bad signature) |
465
+ | `verifyWebhookAsync(...)` | same as `verifyWebhook`, Web Crypto (edge/Workers/Deno/Bun) |
466
+ | `createWebhookHandler({ secret, onEvent })` | `(Request) => Promise<Response>` webhook route handler |
467
+
468
+ Every method also accepts an optional final `{ signal }` (`AbortSignal`) for cancellation.
438
469
 
439
470
  ### `InvoClient` (`@invonetwork/web-sdk`)
440
471
 
@@ -96,11 +96,11 @@ var _Http = class _Http {
96
96
  * (e.g. single-use WebAuthn assertions) are NEVER auto-retried.
97
97
  */
98
98
  async post(path, body, auth, opts) {
99
- return this.request("POST", path, body, auth, opts?.idempotent ?? false);
99
+ return this.request("POST", path, body, auth, opts?.idempotent ?? false, opts?.signal);
100
100
  }
101
101
  // GET is always idempotent → safe to retry.
102
- async get(path, auth) {
103
- return this.request("GET", path, void 0, auth, true);
102
+ async get(path, auth, opts) {
103
+ return this.request("GET", path, void 0, auth, true, opts?.signal);
104
104
  }
105
105
  authHeaders(auth) {
106
106
  switch (auth.kind) {
@@ -112,7 +112,7 @@ var _Http = class _Http {
112
112
  return {};
113
113
  }
114
114
  }
115
- async request(method, path, body, auth, idempotent) {
115
+ async request(method, path, body, auth, idempotent, signal) {
116
116
  const url = `${this.baseUrl}${path}`;
117
117
  const headers = {
118
118
  Accept: "application/json",
@@ -122,10 +122,13 @@ var _Http = class _Http {
122
122
  if (body !== void 0) headers["Content-Type"] = "application/json";
123
123
  const payload = body !== void 0 ? JSON.stringify(body) : void 0;
124
124
  for (let attempt = 0; ; attempt++) {
125
+ if (signal?.aborted) throw abortError(path);
125
126
  const start = Date.now();
126
127
  this.fire("onRequest", { method, url, attempt });
127
128
  const controller = new AbortController();
128
129
  const timer = setTimeout(() => controller.abort(), this.timeoutMs);
130
+ const onAbort = () => controller.abort();
131
+ signal?.addEventListener("abort", onAbort, { once: true });
129
132
  let res;
130
133
  let networkError;
131
134
  try {
@@ -138,12 +141,14 @@ var _Http = class _Http {
138
141
  });
139
142
  } finally {
140
143
  clearTimeout(timer);
144
+ signal?.removeEventListener("abort", onAbort);
141
145
  }
142
146
  if (networkError) {
147
+ if (signal?.aborted) throw abortError(path);
143
148
  const willRetry = idempotent && attempt < this.maxRetries;
144
149
  this.fire("onError", { method, url, attempt, error: networkError, willRetry });
145
150
  if (willRetry) {
146
- await sleep(this.backoff(attempt));
151
+ await sleep(this.backoff(attempt), signal);
147
152
  continue;
148
153
  }
149
154
  throw networkError;
@@ -179,7 +184,7 @@ var _Http = class _Http {
179
184
  }
180
185
  this.fire("onError", { method, url, attempt, error: err, willRetry: wait !== void 0 });
181
186
  if (wait !== void 0) {
182
- await sleep(wait);
187
+ await sleep(wait, signal);
183
188
  continue;
184
189
  }
185
190
  throw err;
@@ -205,8 +210,21 @@ var _Http = class _Http {
205
210
  /** HTTP statuses worth retrying — rate limit + transient gateway/server errors. */
206
211
  _Http.RETRIABLE_STATUS = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
207
212
  var Http = _Http;
208
- function sleep(ms) {
209
- return new Promise((resolve) => setTimeout(resolve, ms));
213
+ function sleep(ms, signal) {
214
+ return new Promise((resolve) => {
215
+ if (signal?.aborted) return resolve();
216
+ const timer = setTimeout(done, ms);
217
+ const onAbort = () => done();
218
+ function done() {
219
+ clearTimeout(timer);
220
+ signal?.removeEventListener("abort", onAbort);
221
+ resolve();
222
+ }
223
+ signal?.addEventListener("abort", onAbort, { once: true });
224
+ });
225
+ }
226
+ function abortError(path) {
227
+ return new InvoError({ message: `Request to ${path} was aborted`, code: "ABORTED", status: 0 });
210
228
  }
211
229
  function pickRequestId(headers) {
212
230
  if (!headers || typeof headers.get !== "function") return void 0;
@@ -227,5 +245,5 @@ function retryAfterMs(parsed, headers) {
227
245
  }
228
246
 
229
247
  export { Http, InvoError, assertSecureBaseUrl };
230
- //# sourceMappingURL=chunk-DV3WZGMH.js.map
231
- //# sourceMappingURL=chunk-DV3WZGMH.js.map
248
+ //# sourceMappingURL=chunk-EEWOAUXO.js.map
249
+ //# sourceMappingURL=chunk-EEWOAUXO.js.map
package/dist/index.cjs CHANGED
@@ -98,11 +98,11 @@ var _Http = class _Http {
98
98
  * (e.g. single-use WebAuthn assertions) are NEVER auto-retried.
99
99
  */
100
100
  async post(path, body, auth, opts) {
101
- return this.request("POST", path, body, auth, opts?.idempotent ?? false);
101
+ return this.request("POST", path, body, auth, opts?.idempotent ?? false, opts?.signal);
102
102
  }
103
103
  // GET is always idempotent → safe to retry.
104
- async get(path, auth) {
105
- return this.request("GET", path, void 0, auth, true);
104
+ async get(path, auth, opts) {
105
+ return this.request("GET", path, void 0, auth, true, opts?.signal);
106
106
  }
107
107
  authHeaders(auth) {
108
108
  switch (auth.kind) {
@@ -114,7 +114,7 @@ var _Http = class _Http {
114
114
  return {};
115
115
  }
116
116
  }
117
- async request(method, path, body, auth, idempotent) {
117
+ async request(method, path, body, auth, idempotent, signal) {
118
118
  const url = `${this.baseUrl}${path}`;
119
119
  const headers = {
120
120
  Accept: "application/json",
@@ -124,10 +124,13 @@ var _Http = class _Http {
124
124
  if (body !== void 0) headers["Content-Type"] = "application/json";
125
125
  const payload = body !== void 0 ? JSON.stringify(body) : void 0;
126
126
  for (let attempt = 0; ; attempt++) {
127
+ if (signal?.aborted) throw abortError(path);
127
128
  const start = Date.now();
128
129
  this.fire("onRequest", { method, url, attempt });
129
130
  const controller = new AbortController();
130
131
  const timer = setTimeout(() => controller.abort(), this.timeoutMs);
132
+ const onAbort = () => controller.abort();
133
+ signal?.addEventListener("abort", onAbort, { once: true });
131
134
  let res;
132
135
  let networkError;
133
136
  try {
@@ -140,12 +143,14 @@ var _Http = class _Http {
140
143
  });
141
144
  } finally {
142
145
  clearTimeout(timer);
146
+ signal?.removeEventListener("abort", onAbort);
143
147
  }
144
148
  if (networkError) {
149
+ if (signal?.aborted) throw abortError(path);
145
150
  const willRetry = idempotent && attempt < this.maxRetries;
146
151
  this.fire("onError", { method, url, attempt, error: networkError, willRetry });
147
152
  if (willRetry) {
148
- await sleep(this.backoff(attempt));
153
+ await sleep(this.backoff(attempt), signal);
149
154
  continue;
150
155
  }
151
156
  throw networkError;
@@ -181,7 +186,7 @@ var _Http = class _Http {
181
186
  }
182
187
  this.fire("onError", { method, url, attempt, error: err, willRetry: wait !== void 0 });
183
188
  if (wait !== void 0) {
184
- await sleep(wait);
189
+ await sleep(wait, signal);
185
190
  continue;
186
191
  }
187
192
  throw err;
@@ -207,8 +212,21 @@ var _Http = class _Http {
207
212
  /** HTTP statuses worth retrying — rate limit + transient gateway/server errors. */
208
213
  _Http.RETRIABLE_STATUS = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
209
214
  var Http = _Http;
210
- function sleep(ms) {
211
- return new Promise((resolve) => setTimeout(resolve, ms));
215
+ function sleep(ms, signal) {
216
+ return new Promise((resolve) => {
217
+ if (signal?.aborted) return resolve();
218
+ const timer = setTimeout(done, ms);
219
+ const onAbort = () => done();
220
+ function done() {
221
+ clearTimeout(timer);
222
+ signal?.removeEventListener("abort", onAbort);
223
+ resolve();
224
+ }
225
+ signal?.addEventListener("abort", onAbort, { once: true });
226
+ });
227
+ }
228
+ function abortError(path) {
229
+ return new InvoError({ message: `Request to ${path} was aborted`, code: "ABORTED", status: 0 });
212
230
  }
213
231
  function pickRequestId(headers) {
214
232
  if (!headers || typeof headers.get !== "function") return void 0;
@@ -333,38 +351,43 @@ var InvoClient = class {
333
351
  }
334
352
  }
335
353
  /** Enroll a passkey for the token's identity (register/begin -> create() -> register/complete). */
336
- async enrollPasskey() {
354
+ async enrollPasskey(opts) {
337
355
  this.assertWebAuthn();
356
+ const signal = opts?.signal;
338
357
  return this.withTokenRetry(async () => {
339
358
  const options = await this.post(
340
- "/api/sdk/webauthn/register/begin"
359
+ "/api/sdk/webauthn/register/begin",
360
+ void 0,
361
+ signal
341
362
  );
342
363
  const cred = await navigator.credentials.create({
343
- publicKey: toCreationOptions(options)
364
+ publicKey: toCreationOptions(options),
365
+ signal
344
366
  });
345
367
  if (!cred) throw new Error("Passkey creation was cancelled or returned no credential.");
346
368
  const raw = await this.post(
347
369
  "/api/sdk/webauthn/register/complete",
348
- { credential: registrationToJSON(cred) }
370
+ { credential: registrationToJSON(cred) },
371
+ signal
349
372
  );
350
373
  return { status: String(raw["status"] ?? ""), device: raw["device"] ?? null, raw };
351
374
  });
352
375
  }
353
376
  /** Approve a SEND with the player's passkey. */
354
- async approveSend(transactionId) {
355
- return this.approve("send", transactionId);
377
+ async approveSend(transactionId, opts) {
378
+ return this.approve("send", transactionId, opts);
356
379
  }
357
380
  /** Approve a TRANSFER with the player's passkey (returns the sender's claim code). */
358
- async approveTransfer(transactionId) {
359
- return this.approve("transfers", transactionId);
381
+ async approveTransfer(transactionId, opts) {
382
+ return this.approve("transfers", transactionId, opts);
360
383
  }
361
384
  /** Recipient self-claims a SEND with their passkey. */
362
- async confirmReceiptSend(transactionId) {
363
- return this.confirmReceipt("send", transactionId);
385
+ async confirmReceiptSend(transactionId, opts) {
386
+ return this.confirmReceipt("send", transactionId, opts);
364
387
  }
365
388
  /** Recipient self-claims a TRANSFER with their passkey. */
366
- async confirmReceiptTransfer(transactionId) {
367
- return this.confirmReceipt("transfers", transactionId);
389
+ async confirmReceiptTransfer(transactionId, opts) {
390
+ return this.confirmReceipt("transfers", transactionId, opts);
368
391
  }
369
392
  /**
370
393
  * Interchangeable methods (§4.6): prove an *already-enrolled* method (e.g. the
@@ -374,15 +397,19 @@ var InvoClient = class {
374
397
  *
375
398
  * begin -> navigator.credentials.get() -> complete with { link_id, webauthn_assertion }.
376
399
  */
377
- async linkDevice(linkId) {
400
+ async linkDevice(linkId, opts) {
378
401
  if (!linkId) throw new Error("linkDevice requires a `linkId`.");
402
+ const signal = opts?.signal;
379
403
  return this.withTokenRetry(async () => {
380
- const assertion = await this.runAssertion("/api/sdk/device/link/webauthn/begin", {
381
- link_id: linkId
382
- });
404
+ const assertion = await this.runAssertion(
405
+ "/api/sdk/device/link/webauthn/begin",
406
+ { link_id: linkId },
407
+ signal
408
+ );
383
409
  const raw = await this.post(
384
410
  "/api/sdk/device/link/webauthn/complete",
385
- { link_id: linkId, webauthn_assertion: assertion }
411
+ { link_id: linkId, webauthn_assertion: assertion },
412
+ signal
386
413
  );
387
414
  return { status: String(raw["status"] ?? ""), raw };
388
415
  });
@@ -390,8 +417,8 @@ var InvoClient = class {
390
417
  // --- internals ---
391
418
  /** POST with the current player token. Token-expiry retry is handled one level
392
419
  * up by withTokenRetry (which re-runs the whole ceremony, not a single call). */
393
- async post(path, body) {
394
- return this.http.post(path, body, this.auth);
420
+ async post(path, body, signal) {
421
+ return this.http.post(path, body, this.auth, { signal });
395
422
  }
396
423
  /**
397
424
  * Run a whole flow, retrying it ONCE if any call fails with SDK_TOKEN_EXPIRED
@@ -422,22 +449,29 @@ var InvoClient = class {
422
449
  this.auth = { kind: "bearer", token: fresh };
423
450
  return true;
424
451
  }
425
- async runAssertion(beginPath, beginBody) {
452
+ async runAssertion(beginPath, beginBody, signal) {
426
453
  this.assertWebAuthn();
427
- const options = await this.post(beginPath, beginBody);
454
+ const options = await this.post(beginPath, beginBody, signal);
428
455
  const cred = await navigator.credentials.get({
429
- publicKey: toRequestOptions(options)
456
+ publicKey: toRequestOptions(options),
457
+ signal
430
458
  });
431
459
  if (!cred) throw new Error("Passkey assertion was cancelled or returned no credential.");
432
460
  return assertionToJSON(cred);
433
461
  }
434
- async approve(flow, transactionId) {
462
+ async approve(flow, transactionId, opts) {
435
463
  const id = encodeURIComponent(transactionId);
464
+ const signal = opts?.signal;
436
465
  return this.withTokenRetry(async () => {
437
- const assertion = await this.runAssertion(`/api/sdk/${flow}/${id}/approve/webauthn/begin`);
466
+ const assertion = await this.runAssertion(
467
+ `/api/sdk/${flow}/${id}/approve/webauthn/begin`,
468
+ void 0,
469
+ signal
470
+ );
438
471
  const raw = await this.post(
439
472
  `/api/sdk/${flow}/${id}/approve`,
440
- { webauthn_assertion: assertion }
473
+ { webauthn_assertion: assertion },
474
+ signal
441
475
  );
442
476
  return {
443
477
  status: String(raw["status"] ?? ""),
@@ -449,15 +483,19 @@ var InvoClient = class {
449
483
  };
450
484
  });
451
485
  }
452
- async confirmReceipt(flow, transactionId) {
486
+ async confirmReceipt(flow, transactionId, opts) {
453
487
  const id = encodeURIComponent(transactionId);
488
+ const signal = opts?.signal;
454
489
  return this.withTokenRetry(async () => {
455
490
  const assertion = await this.runAssertion(
456
- `/api/sdk/${flow}/${id}/confirm-receipt/webauthn/begin`
491
+ `/api/sdk/${flow}/${id}/confirm-receipt/webauthn/begin`,
492
+ void 0,
493
+ signal
457
494
  );
458
495
  const raw = await this.post(
459
496
  `/api/sdk/${flow}/${id}/confirm-receipt`,
460
- { webauthn_assertion: assertion }
497
+ { webauthn_assertion: assertion },
498
+ signal
461
499
  );
462
500
  return { status: String(raw["status"] ?? ""), raw };
463
501
  });
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as ClientConfig, A as ApproveResult, a as ConfirmReceiptResult, L as LinkDeviceResult } from './types-CBkoUymV.cjs';
2
- export { I as InvoError, b as InvoErrorInfo, c as InvoHooks, d as InvoRequestInfo, e as InvoResponseInfo, R as Rail, V as VerificationMethod } from './types-CBkoUymV.cjs';
1
+ import { C as ClientConfig, a as CallOptions, A as ApproveResult, b as ConfirmReceiptResult, L as LinkDeviceResult } from './types-CBMLNwbe.cjs';
2
+ export { I as InvoError, c as InvoErrorInfo, d as InvoHooks, e as InvoRequestInfo, f as InvoResponseInfo, R as Rail, V as VerificationMethod } from './types-CBMLNwbe.cjs';
3
3
 
4
4
  declare class InvoClient {
5
5
  private readonly http;
@@ -10,19 +10,19 @@ declare class InvoClient {
10
10
  constructor(config: ClientConfig);
11
11
  private assertWebAuthn;
12
12
  /** Enroll a passkey for the token's identity (register/begin -> create() -> register/complete). */
13
- enrollPasskey(): Promise<{
13
+ enrollPasskey(opts?: CallOptions): Promise<{
14
14
  status: string;
15
15
  device: unknown;
16
16
  raw: Record<string, unknown>;
17
17
  }>;
18
18
  /** Approve a SEND with the player's passkey. */
19
- approveSend(transactionId: string): Promise<ApproveResult>;
19
+ approveSend(transactionId: string, opts?: CallOptions): Promise<ApproveResult>;
20
20
  /** Approve a TRANSFER with the player's passkey (returns the sender's claim code). */
21
- approveTransfer(transactionId: string): Promise<ApproveResult>;
21
+ approveTransfer(transactionId: string, opts?: CallOptions): Promise<ApproveResult>;
22
22
  /** Recipient self-claims a SEND with their passkey. */
23
- confirmReceiptSend(transactionId: string): Promise<ConfirmReceiptResult>;
23
+ confirmReceiptSend(transactionId: string, opts?: CallOptions): Promise<ConfirmReceiptResult>;
24
24
  /** Recipient self-claims a TRANSFER with their passkey. */
25
- confirmReceiptTransfer(transactionId: string): Promise<ConfirmReceiptResult>;
25
+ confirmReceiptTransfer(transactionId: string, opts?: CallOptions): Promise<ConfirmReceiptResult>;
26
26
  /**
27
27
  * Interchangeable methods (§4.6): prove an *already-enrolled* method (e.g. the
28
28
  * INVO app device key) to authorize adding a new partner passkey. The returned
@@ -31,7 +31,7 @@ declare class InvoClient {
31
31
  *
32
32
  * begin -> navigator.credentials.get() -> complete with { link_id, webauthn_assertion }.
33
33
  */
34
- linkDevice(linkId: string): Promise<LinkDeviceResult>;
34
+ linkDevice(linkId: string, opts?: CallOptions): Promise<LinkDeviceResult>;
35
35
  /** POST with the current player token. Token-expiry retry is handled one level
36
36
  * up by withTokenRetry (which re-runs the whole ceremony, not a single call). */
37
37
  private post;
@@ -49,4 +49,4 @@ declare class InvoClient {
49
49
  private confirmReceipt;
50
50
  }
51
51
 
52
- export { ApproveResult, ClientConfig, ConfirmReceiptResult, InvoClient, LinkDeviceResult };
52
+ export { ApproveResult, CallOptions, ClientConfig, ConfirmReceiptResult, InvoClient, LinkDeviceResult };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as ClientConfig, A as ApproveResult, a as ConfirmReceiptResult, L as LinkDeviceResult } from './types-CBkoUymV.js';
2
- export { I as InvoError, b as InvoErrorInfo, c as InvoHooks, d as InvoRequestInfo, e as InvoResponseInfo, R as Rail, V as VerificationMethod } from './types-CBkoUymV.js';
1
+ import { C as ClientConfig, a as CallOptions, A as ApproveResult, b as ConfirmReceiptResult, L as LinkDeviceResult } from './types-CBMLNwbe.js';
2
+ export { I as InvoError, c as InvoErrorInfo, d as InvoHooks, e as InvoRequestInfo, f as InvoResponseInfo, R as Rail, V as VerificationMethod } from './types-CBMLNwbe.js';
3
3
 
4
4
  declare class InvoClient {
5
5
  private readonly http;
@@ -10,19 +10,19 @@ declare class InvoClient {
10
10
  constructor(config: ClientConfig);
11
11
  private assertWebAuthn;
12
12
  /** Enroll a passkey for the token's identity (register/begin -> create() -> register/complete). */
13
- enrollPasskey(): Promise<{
13
+ enrollPasskey(opts?: CallOptions): Promise<{
14
14
  status: string;
15
15
  device: unknown;
16
16
  raw: Record<string, unknown>;
17
17
  }>;
18
18
  /** Approve a SEND with the player's passkey. */
19
- approveSend(transactionId: string): Promise<ApproveResult>;
19
+ approveSend(transactionId: string, opts?: CallOptions): Promise<ApproveResult>;
20
20
  /** Approve a TRANSFER with the player's passkey (returns the sender's claim code). */
21
- approveTransfer(transactionId: string): Promise<ApproveResult>;
21
+ approveTransfer(transactionId: string, opts?: CallOptions): Promise<ApproveResult>;
22
22
  /** Recipient self-claims a SEND with their passkey. */
23
- confirmReceiptSend(transactionId: string): Promise<ConfirmReceiptResult>;
23
+ confirmReceiptSend(transactionId: string, opts?: CallOptions): Promise<ConfirmReceiptResult>;
24
24
  /** Recipient self-claims a TRANSFER with their passkey. */
25
- confirmReceiptTransfer(transactionId: string): Promise<ConfirmReceiptResult>;
25
+ confirmReceiptTransfer(transactionId: string, opts?: CallOptions): Promise<ConfirmReceiptResult>;
26
26
  /**
27
27
  * Interchangeable methods (§4.6): prove an *already-enrolled* method (e.g. the
28
28
  * INVO app device key) to authorize adding a new partner passkey. The returned
@@ -31,7 +31,7 @@ declare class InvoClient {
31
31
  *
32
32
  * begin -> navigator.credentials.get() -> complete with { link_id, webauthn_assertion }.
33
33
  */
34
- linkDevice(linkId: string): Promise<LinkDeviceResult>;
34
+ linkDevice(linkId: string, opts?: CallOptions): Promise<LinkDeviceResult>;
35
35
  /** POST with the current player token. Token-expiry retry is handled one level
36
36
  * up by withTokenRetry (which re-runs the whole ceremony, not a single call). */
37
37
  private post;
@@ -49,4 +49,4 @@ declare class InvoClient {
49
49
  private confirmReceipt;
50
50
  }
51
51
 
52
- export { ApproveResult, ClientConfig, ConfirmReceiptResult, InvoClient, LinkDeviceResult };
52
+ export { ApproveResult, CallOptions, ClientConfig, ConfirmReceiptResult, InvoClient, LinkDeviceResult };