@unicitylabs/sphere-sdk 0.15.0 → 0.16.0-dev.1

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 (50) hide show
  1. package/README.md +15 -5
  2. package/dist/connect/index.cjs +14 -23
  3. package/dist/connect/index.cjs.map +1 -1
  4. package/dist/connect/index.d.cts +4 -0
  5. package/dist/connect/index.d.ts +4 -0
  6. package/dist/connect/index.js +14 -23
  7. package/dist/connect/index.js.map +1 -1
  8. package/dist/core/index.cjs +726 -225
  9. package/dist/core/index.cjs.map +1 -1
  10. package/dist/core/index.d.cts +172 -21
  11. package/dist/core/index.d.ts +172 -21
  12. package/dist/core/index.js +726 -224
  13. package/dist/core/index.js.map +1 -1
  14. package/dist/impl/browser/connect/index.cjs +14 -23
  15. package/dist/impl/browser/connect/index.cjs.map +1 -1
  16. package/dist/impl/browser/connect/index.js +14 -23
  17. package/dist/impl/browser/connect/index.js.map +1 -1
  18. package/dist/impl/browser/index.cjs +331 -504
  19. package/dist/impl/browser/index.cjs.map +1 -1
  20. package/dist/impl/browser/index.js +331 -504
  21. package/dist/impl/browser/index.js.map +1 -1
  22. package/dist/impl/nodejs/connect/index.cjs +13 -22
  23. package/dist/impl/nodejs/connect/index.cjs.map +1 -1
  24. package/dist/impl/nodejs/connect/index.js +13 -22
  25. package/dist/impl/nodejs/connect/index.js.map +1 -1
  26. package/dist/impl/nodejs/index.cjs +298 -569
  27. package/dist/impl/nodejs/index.cjs.map +1 -1
  28. package/dist/impl/nodejs/index.d.cts +90 -13
  29. package/dist/impl/nodejs/index.d.ts +90 -13
  30. package/dist/impl/nodejs/index.js +298 -569
  31. package/dist/impl/nodejs/index.js.map +1 -1
  32. package/dist/impl/shared/wallet-api/index.d.cts +54 -2
  33. package/dist/impl/shared/wallet-api/index.d.ts +54 -2
  34. package/dist/index.cjs +728 -225
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.cts +227 -108
  37. package/dist/index.d.ts +227 -108
  38. package/dist/index.js +728 -224
  39. package/dist/index.js.map +1 -1
  40. package/dist/modules/payments-v2/index.cjs +11 -7
  41. package/dist/modules/payments-v2/index.cjs.map +1 -1
  42. package/dist/modules/payments-v2/index.d.cts +56 -3
  43. package/dist/modules/payments-v2/index.d.ts +56 -3
  44. package/dist/modules/payments-v2/index.js +11 -7
  45. package/dist/modules/payments-v2/index.js.map +1 -1
  46. package/dist/token-engine/index.cjs +46 -0
  47. package/dist/token-engine/index.cjs.map +1 -1
  48. package/dist/token-engine/index.js +46 -0
  49. package/dist/token-engine/index.js.map +1 -1
  50. package/package.json +1 -1
@@ -404,6 +404,25 @@ type OutcomeClass = 'keep-open' | 'conflict' | 'other';
404
404
  * All operations are async for platform flexibility
405
405
  */
406
406
  interface StorageProvider extends BaseProvider {
407
+ /**
408
+ * Stable identity of the BACKING STORE this provider addresses — not of this
409
+ * object, and not of the class (`id` is a class constant like `'file-storage'`,
410
+ * which is exactly the wrong granularity).
411
+ *
412
+ * Two providers that return the SAME value address the same data, so erasing
413
+ * through one erases through the other: `Sphere.clear({ storage })` tears down
414
+ * the live Spheres of every provider sharing this value, not merely those built
415
+ * on this object. Compose it from everything that selects the store (file path,
416
+ * database name, key prefix) behind a scheme prefix, so two kinds of store can
417
+ * never collide on one string.
418
+ *
419
+ * It must not change over the provider's lifetime — it is read again on teardown,
420
+ * and a value that moved would strand the entry it was registered under.
421
+ *
422
+ * Optional: omit it and liveness falls back to per-object identity, i.e. a
423
+ * second provider over the same data is treated as unrelated.
424
+ */
425
+ readonly backingStoreId?: string;
407
426
  /**
408
427
  * Set identity for scoped storage
409
428
  */
@@ -433,11 +452,44 @@ interface StorageProvider extends BaseProvider {
433
452
  */
434
453
  clear(prefix?: string): Promise<void>;
435
454
  /**
436
- * Save tracked addresses (only user state: index, hidden, timestamps)
455
+ * Save tracked addresses (only user state: index, hidden, timestamps).
456
+ *
457
+ * MUST MERGE, NEVER REPLACE (#766 item 5). `entries` is ONE writer's snapshot,
458
+ * not the whole truth: every Sphere sharing this storage keeps its own copy of
459
+ * the registry and persists all of it, so writing the argument verbatim is a
460
+ * lost update — A activates index 1, B (whose snapshot predates that) activates
461
+ * index 2, and B's write erases index 1 while A still reports it. This happens
462
+ * on a single network with a single provider; do NOT "fix" it by renaming or
463
+ * network-scoping the key.
464
+ *
465
+ * The contract, implemented by `storage/tracked-addresses.ts` — reuse those
466
+ * helpers rather than re-deriving this:
467
+ * - read the stored registry, union it with `entries` BY `index`;
468
+ * - on a conflicting index, the entry with the greater `updatedAt` supplies
469
+ * `hidden`, and `createdAt` keeps the earlier value;
470
+ * - serialize concurrent calls on the provider instance, so one call's read
471
+ * cannot interleave with another's write;
472
+ * - a failed write must not brick later writes, and must still reject to its
473
+ * own caller.
474
+ *
475
+ * An `index` must be a UINT32 — a BIP32 child number. `deriveKeyAtPath` parseInt()s
476
+ * that path segment, so `1.5` derives index 1's keys and the row aliases a real
477
+ * address. The ceiling matters too: `deriveChildKey` pads the child number to 8 hex
478
+ * digits, so anything above `0xffffffff` emits extra bytes and derives off-standard.
479
+ * An `entries` row that is not one must REJECT the whole call (`mergeTrackedAddresses`
480
+ * throws `VALIDATION_ERROR`); dropping it silently on a write reports a save that
481
+ * never happened. Already-stored rows are dropped on READ instead, so one bad row
482
+ * cannot brick every later write. Validate before opening the write transaction if
483
+ * your platform would otherwise replace the reason with a generic abort.
484
+ *
485
+ * A union is safe because there is no delete path: entries are only ever added,
486
+ * and wiping the wallet removes the key itself (`Sphere.clear()`). Adding a
487
+ * per-entry delete would require revisiting this contract.
437
488
  */
438
489
  saveTrackedAddresses(entries: TrackedAddressEntry[]): Promise<void>;
439
490
  /**
440
- * Load tracked addresses
491
+ * Load tracked addresses. Tolerant: unusable/corrupt storage reads as `[]`
492
+ * (see `parseTrackedAddresses` in `storage/tracked-addresses.ts`).
441
493
  */
442
494
  loadTrackedAddresses(): Promise<TrackedAddressEntry[]>;
443
495
  }
@@ -1093,7 +1145,8 @@ declare class PaymentsFacade implements PaymentsV2 {
1093
1145
  start(): Promise<void>;
1094
1146
  /** §7 same-address restart gate: resolves only after in-flight ops settle. */
1095
1147
  stop(): Promise<void>;
1096
- /** Swaps what FUTURE operations snapshot; in-flight ops finish on the old engine. */
1148
+ /** Swaps what FUTURE operations snapshot. Chain ops finish on the old engine, but it is
1149
+ * DISPOSED here, so one mid-`verify()` is cancelled — MODULE_DESTROYED, #770(4). */
1097
1150
  setEngine(next: ITokenEngine): void;
1098
1151
  assets(coinId?: string): Promise<Asset[]>;
1099
1152
  tokens(filter?: {
@@ -404,6 +404,25 @@ type OutcomeClass = 'keep-open' | 'conflict' | 'other';
404
404
  * All operations are async for platform flexibility
405
405
  */
406
406
  interface StorageProvider extends BaseProvider {
407
+ /**
408
+ * Stable identity of the BACKING STORE this provider addresses — not of this
409
+ * object, and not of the class (`id` is a class constant like `'file-storage'`,
410
+ * which is exactly the wrong granularity).
411
+ *
412
+ * Two providers that return the SAME value address the same data, so erasing
413
+ * through one erases through the other: `Sphere.clear({ storage })` tears down
414
+ * the live Spheres of every provider sharing this value, not merely those built
415
+ * on this object. Compose it from everything that selects the store (file path,
416
+ * database name, key prefix) behind a scheme prefix, so two kinds of store can
417
+ * never collide on one string.
418
+ *
419
+ * It must not change over the provider's lifetime — it is read again on teardown,
420
+ * and a value that moved would strand the entry it was registered under.
421
+ *
422
+ * Optional: omit it and liveness falls back to per-object identity, i.e. a
423
+ * second provider over the same data is treated as unrelated.
424
+ */
425
+ readonly backingStoreId?: string;
407
426
  /**
408
427
  * Set identity for scoped storage
409
428
  */
@@ -433,11 +452,44 @@ interface StorageProvider extends BaseProvider {
433
452
  */
434
453
  clear(prefix?: string): Promise<void>;
435
454
  /**
436
- * Save tracked addresses (only user state: index, hidden, timestamps)
455
+ * Save tracked addresses (only user state: index, hidden, timestamps).
456
+ *
457
+ * MUST MERGE, NEVER REPLACE (#766 item 5). `entries` is ONE writer's snapshot,
458
+ * not the whole truth: every Sphere sharing this storage keeps its own copy of
459
+ * the registry and persists all of it, so writing the argument verbatim is a
460
+ * lost update — A activates index 1, B (whose snapshot predates that) activates
461
+ * index 2, and B's write erases index 1 while A still reports it. This happens
462
+ * on a single network with a single provider; do NOT "fix" it by renaming or
463
+ * network-scoping the key.
464
+ *
465
+ * The contract, implemented by `storage/tracked-addresses.ts` — reuse those
466
+ * helpers rather than re-deriving this:
467
+ * - read the stored registry, union it with `entries` BY `index`;
468
+ * - on a conflicting index, the entry with the greater `updatedAt` supplies
469
+ * `hidden`, and `createdAt` keeps the earlier value;
470
+ * - serialize concurrent calls on the provider instance, so one call's read
471
+ * cannot interleave with another's write;
472
+ * - a failed write must not brick later writes, and must still reject to its
473
+ * own caller.
474
+ *
475
+ * An `index` must be a UINT32 — a BIP32 child number. `deriveKeyAtPath` parseInt()s
476
+ * that path segment, so `1.5` derives index 1's keys and the row aliases a real
477
+ * address. The ceiling matters too: `deriveChildKey` pads the child number to 8 hex
478
+ * digits, so anything above `0xffffffff` emits extra bytes and derives off-standard.
479
+ * An `entries` row that is not one must REJECT the whole call (`mergeTrackedAddresses`
480
+ * throws `VALIDATION_ERROR`); dropping it silently on a write reports a save that
481
+ * never happened. Already-stored rows are dropped on READ instead, so one bad row
482
+ * cannot brick every later write. Validate before opening the write transaction if
483
+ * your platform would otherwise replace the reason with a generic abort.
484
+ *
485
+ * A union is safe because there is no delete path: entries are only ever added,
486
+ * and wiping the wallet removes the key itself (`Sphere.clear()`). Adding a
487
+ * per-entry delete would require revisiting this contract.
437
488
  */
438
489
  saveTrackedAddresses(entries: TrackedAddressEntry[]): Promise<void>;
439
490
  /**
440
- * Load tracked addresses
491
+ * Load tracked addresses. Tolerant: unusable/corrupt storage reads as `[]`
492
+ * (see `parseTrackedAddresses` in `storage/tracked-addresses.ts`).
441
493
  */
442
494
  loadTrackedAddresses(): Promise<TrackedAddressEntry[]>;
443
495
  }
@@ -1093,7 +1145,8 @@ declare class PaymentsFacade implements PaymentsV2 {
1093
1145
  start(): Promise<void>;
1094
1146
  /** §7 same-address restart gate: resolves only after in-flight ops settle. */
1095
1147
  stop(): Promise<void>;
1096
- /** Swaps what FUTURE operations snapshot; in-flight ops finish on the old engine. */
1148
+ /** Swaps what FUTURE operations snapshot. Chain ops finish on the old engine, but it is
1149
+ * DISPOSED here, so one mid-`verify()` is cancelled — MODULE_DESTROYED, #770(4). */
1097
1150
  setEngine(next: ITokenEngine): void;
1098
1151
  assets(coinId?: string): Promise<Asset[]>;
1099
1152
  tokens(filter?: {
@@ -3167,12 +3167,13 @@ var Receive = class {
3167
3167
  return this.drainFlight.run(() => this.doDrain());
3168
3168
  }
3169
3169
  start(pollIntervalMs = POLL_INTERVAL_MS) {
3170
- this.unsubscribeWake ??= this.deps.delivery.onWake?.(() => {
3171
- void this.drainOnce();
3172
- }) ?? null;
3173
- this.pollTimer ??= setInterval(() => {
3174
- void this.drainOnce();
3175
- }, pollIntervalMs);
3170
+ this.unsubscribeWake ??= this.deps.delivery.onWake?.(() => this.spawnDrain()) ?? null;
3171
+ this.pollTimer ??= setInterval(() => this.spawnDrain(), pollIntervalMs);
3172
+ }
3173
+ spawnDrain() {
3174
+ const op = this.drainOnce();
3175
+ if (this.deps.track !== void 0) this.deps.track(op);
3176
+ else void op;
3176
3177
  }
3177
3178
  stop() {
3178
3179
  if (this.pollTimer !== null) clearInterval(this.pollTimer);
@@ -4275,6 +4276,8 @@ function buildReceive(deps, hooks, historyStore, heldStates, refreshView) {
4275
4276
  );
4276
4277
  },
4277
4278
  syncEpoch: deps.syncEpoch,
4279
+ // #770: the poll/wake drains Receive spawns itself must hold stop() too.
4280
+ track: hooks.track,
4278
4281
  ...deps.now !== void 0 ? { now: deps.now } : {}
4279
4282
  });
4280
4283
  }
@@ -4417,7 +4420,8 @@ var PaymentsFacade = class {
4417
4420
  await Promise.allSettled([...this.pendingOps]);
4418
4421
  }
4419
4422
  }
4420
- /** Swaps what FUTURE operations snapshot; in-flight ops finish on the old engine. */
4423
+ /** Swaps what FUTURE operations snapshot. Chain ops finish on the old engine, but it is
4424
+ * DISPOSED here, so one mid-`verify()` is cancelled — MODULE_DESTROYED, #770(4). */
4421
4425
  setEngine(next) {
4422
4426
  const previous = this.currentEngine ?? this.deps.engineRef();
4423
4427
  this.currentEngine = next;