@unicitylabs/sphere-sdk 0.14.11-dev.5 → 0.14.11-dev.7
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/connect/index.cjs +1 -1
- package/dist/connect/index.cjs.map +1 -1
- package/dist/connect/index.js +1 -1
- package/dist/connect/index.js.map +1 -1
- package/dist/core/index.cjs +180 -31
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.js +180 -31
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/connect/index.cjs +1 -1
- package/dist/impl/browser/connect/index.cjs.map +1 -1
- package/dist/impl/browser/connect/index.js +1 -1
- package/dist/impl/browser/connect/index.js.map +1 -1
- package/dist/impl/wallet-api-v2/index.cjs +72 -3
- package/dist/impl/wallet-api-v2/index.cjs.map +1 -1
- package/dist/impl/wallet-api-v2/index.d.cts +25 -1
- package/dist/impl/wallet-api-v2/index.d.ts +25 -1
- package/dist/impl/wallet-api-v2/index.js +72 -3
- package/dist/impl/wallet-api-v2/index.js.map +1 -1
- package/dist/index.cjs +180 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +180 -31
- package/dist/index.js.map +1 -1
- package/dist/modules/payments-v2/index.cjs +108 -28
- package/dist/modules/payments-v2/index.cjs.map +1 -1
- package/dist/modules/payments-v2/index.d.cts +15 -0
- package/dist/modules/payments-v2/index.d.ts +15 -0
- package/dist/modules/payments-v2/index.js +108 -28
- package/dist/modules/payments-v2/index.js.map +1 -1
- package/package.json +1 -1
package/dist/core/index.js
CHANGED
|
@@ -11217,6 +11217,26 @@ var SerialChain = class {
|
|
|
11217
11217
|
return run;
|
|
11218
11218
|
}
|
|
11219
11219
|
};
|
|
11220
|
+
function coalesced(run) {
|
|
11221
|
+
let inFlight = false;
|
|
11222
|
+
let queued = false;
|
|
11223
|
+
const start = () => {
|
|
11224
|
+
inFlight = true;
|
|
11225
|
+
void run().catch(() => void 0).finally(() => {
|
|
11226
|
+
inFlight = false;
|
|
11227
|
+
if (!queued) return;
|
|
11228
|
+
queued = false;
|
|
11229
|
+
start();
|
|
11230
|
+
});
|
|
11231
|
+
};
|
|
11232
|
+
return () => {
|
|
11233
|
+
if (inFlight) {
|
|
11234
|
+
queued = true;
|
|
11235
|
+
return;
|
|
11236
|
+
}
|
|
11237
|
+
start();
|
|
11238
|
+
};
|
|
11239
|
+
}
|
|
11220
11240
|
|
|
11221
11241
|
// modules/payments-v2/stores.ts
|
|
11222
11242
|
function createScopedKV(storage, network, chainPubkey) {
|
|
@@ -12664,6 +12684,16 @@ var InventoryView = class {
|
|
|
12664
12684
|
this.inFlight.add(tokenId);
|
|
12665
12685
|
this.deps.emit("inventory:updated");
|
|
12666
12686
|
}
|
|
12687
|
+
/** Reserve a whole plan as ONE change — per token emitted 54 events per send. */
|
|
12688
|
+
markInFlightMany(tokenIds) {
|
|
12689
|
+
let changed = false;
|
|
12690
|
+
for (const tokenId of tokenIds) {
|
|
12691
|
+
if (this.inFlight.has(tokenId)) continue;
|
|
12692
|
+
this.inFlight.add(tokenId);
|
|
12693
|
+
changed = true;
|
|
12694
|
+
}
|
|
12695
|
+
if (changed) this.deps.emit("inventory:updated");
|
|
12696
|
+
}
|
|
12667
12697
|
release(tokenId) {
|
|
12668
12698
|
if (this.inFlight.delete(tokenId)) this.deps.emit("inventory:updated");
|
|
12669
12699
|
}
|
|
@@ -12888,6 +12918,11 @@ var InventoryView = class {
|
|
|
12888
12918
|
}
|
|
12889
12919
|
};
|
|
12890
12920
|
|
|
12921
|
+
// modules/payments-v2/ports.ts
|
|
12922
|
+
function isRetryableAckError(err) {
|
|
12923
|
+
return err !== null && typeof err === "object" && err.retryable === true;
|
|
12924
|
+
}
|
|
12925
|
+
|
|
12891
12926
|
// modules/payments-v2/receive/Receive.ts
|
|
12892
12927
|
var ACK_BATCH_SIZE = 200;
|
|
12893
12928
|
var REFRESH_INTERVAL_MS = 2500;
|
|
@@ -12969,6 +13004,7 @@ var Receive = class {
|
|
|
12969
13004
|
}
|
|
12970
13005
|
} catch (err) {
|
|
12971
13006
|
logger.warn("PaymentsV2", "receive drain interrupted \u2014 unacked entries retry next drain:", err);
|
|
13007
|
+
if (isRetryableAckError(err)) return stored;
|
|
12972
13008
|
await flushAcks(deps, pending, pageEpoch).catch((flushErr) => {
|
|
12973
13009
|
logger.warn("PaymentsV2", "receive ack flush failed \u2014 cursor holds at the acked prefix:", flushErr);
|
|
12974
13010
|
});
|
|
@@ -13043,14 +13079,18 @@ async function announce(deps, entry, record) {
|
|
|
13043
13079
|
};
|
|
13044
13080
|
}
|
|
13045
13081
|
async function flushAcks(deps, pending, epochOf) {
|
|
13082
|
+
if (pending.length === 0) return;
|
|
13046
13083
|
let lastAcked = null;
|
|
13047
13084
|
try {
|
|
13048
|
-
|
|
13049
|
-
|
|
13050
|
-
|
|
13051
|
-
lastAcked =
|
|
13052
|
-
|
|
13085
|
+
const settled = await settleAcks(deps, pending);
|
|
13086
|
+
let i = 0;
|
|
13087
|
+
while (i < pending.length && settled.has(pending[i].deliveryId)) {
|
|
13088
|
+
lastAcked = pending[i].cursor;
|
|
13089
|
+
i += 1;
|
|
13053
13090
|
}
|
|
13091
|
+
const stuck = pending.filter((p) => !settled.has(p.deliveryId));
|
|
13092
|
+
pending.length = 0;
|
|
13093
|
+
pending.push(...stuck);
|
|
13054
13094
|
} finally {
|
|
13055
13095
|
if (lastAcked !== null) {
|
|
13056
13096
|
const record = { cursor: lastAcked, syncEpoch: epochOf() };
|
|
@@ -13058,14 +13098,65 @@ async function flushAcks(deps, pending, epochOf) {
|
|
|
13058
13098
|
}
|
|
13059
13099
|
}
|
|
13060
13100
|
}
|
|
13101
|
+
async function settleAcks(deps, pending) {
|
|
13102
|
+
const batch = deps.delivery.ackBatch?.bind(deps.delivery);
|
|
13103
|
+
if (batch === void 0) return settleOneByOne(deps, pending);
|
|
13104
|
+
let outcomes;
|
|
13105
|
+
try {
|
|
13106
|
+
outcomes = await batch(pending.map(toAckRequest));
|
|
13107
|
+
} catch (err) {
|
|
13108
|
+
if (isRetryableAckError(err)) throw err;
|
|
13109
|
+
logger.warn("PaymentsV2", "batched mailbox ack failed \u2014 settling one at a time:", err);
|
|
13110
|
+
return settleOneByOne(deps, pending);
|
|
13111
|
+
}
|
|
13112
|
+
const settled = new Set(outcomes.filter((o) => o.status === "settled").map((o) => o.deliveryId));
|
|
13113
|
+
const conflicts = outcomes.filter((o) => o.status === "conflict").map((o) => o.deliveryId);
|
|
13114
|
+
if (conflicts.length > 0) await resolveConflicts(deps, pending, conflicts, settled);
|
|
13115
|
+
return settled;
|
|
13116
|
+
}
|
|
13117
|
+
async function resolveConflicts(deps, pending, conflicts, settled) {
|
|
13118
|
+
for (const deliveryId of conflicts) {
|
|
13119
|
+
const entry = pending.find((p) => p.deliveryId === deliveryId);
|
|
13120
|
+
try {
|
|
13121
|
+
await rejectStaleClaim(deps, deliveryId, entry?.transferId);
|
|
13122
|
+
} catch (err) {
|
|
13123
|
+
logger.warn("PaymentsV2", `stale-reject failed for ${deliveryId} \u2014 cursor holds here:`, err);
|
|
13124
|
+
continue;
|
|
13125
|
+
}
|
|
13126
|
+
settled.add(deliveryId);
|
|
13127
|
+
}
|
|
13128
|
+
}
|
|
13129
|
+
async function rejectStaleClaim(deps, deliveryId, transferId) {
|
|
13130
|
+
logger.warn("PaymentsV2", `mailbox claim CONFLICT for ${deliveryId} \u2014 rejected('other') as stale`);
|
|
13131
|
+
await deps.delivery.ack(deliveryId, "rejected", "other");
|
|
13132
|
+
deps.attention(transferId ?? "", ATTENTION_CLAIM_CONFLICT, deliveryId);
|
|
13133
|
+
}
|
|
13134
|
+
async function settleOneByOne(deps, pending) {
|
|
13135
|
+
const settled = /* @__PURE__ */ new Set();
|
|
13136
|
+
for (const ack of pending) {
|
|
13137
|
+
try {
|
|
13138
|
+
await ackOne(deps, ack);
|
|
13139
|
+
} catch (err) {
|
|
13140
|
+
logger.warn("PaymentsV2", `mailbox ack failed for ${ack.deliveryId} \u2014 cursor holds here:`, err);
|
|
13141
|
+
break;
|
|
13142
|
+
}
|
|
13143
|
+
settled.add(ack.deliveryId);
|
|
13144
|
+
}
|
|
13145
|
+
return settled;
|
|
13146
|
+
}
|
|
13147
|
+
function toAckRequest(ack) {
|
|
13148
|
+
return {
|
|
13149
|
+
deliveryId: ack.deliveryId,
|
|
13150
|
+
disposition: ack.disposition,
|
|
13151
|
+
...ack.reason !== void 0 ? { reason: ack.reason } : {}
|
|
13152
|
+
};
|
|
13153
|
+
}
|
|
13061
13154
|
async function ackOne(deps, ack) {
|
|
13062
13155
|
try {
|
|
13063
13156
|
await deps.delivery.ack(ack.deliveryId, ack.disposition, ack.reason);
|
|
13064
13157
|
} catch (err) {
|
|
13065
13158
|
if (ack.disposition !== "claimed" || !isClaimConflict(err)) throw err;
|
|
13066
|
-
|
|
13067
|
-
await deps.delivery.ack(ack.deliveryId, "rejected", "other");
|
|
13068
|
-
deps.attention(ack.transferId ?? "", ATTENTION_CLAIM_CONFLICT, ack.deliveryId);
|
|
13159
|
+
await rejectStaleClaim(deps, ack.deliveryId, ack.transferId);
|
|
13069
13160
|
}
|
|
13070
13161
|
}
|
|
13071
13162
|
function claimAck(entry) {
|
|
@@ -13819,16 +13910,6 @@ function parseAmount(raw) {
|
|
|
13819
13910
|
function supportsDeterministicMint(engine) {
|
|
13820
13911
|
return engine.deterministicMint === true;
|
|
13821
13912
|
}
|
|
13822
|
-
function makeSingleFlightRefresh(refresh) {
|
|
13823
|
-
let inFlight = false;
|
|
13824
|
-
return () => {
|
|
13825
|
-
if (inFlight) return;
|
|
13826
|
-
inFlight = true;
|
|
13827
|
-
void refresh().catch(() => void 0).finally(() => {
|
|
13828
|
-
inFlight = false;
|
|
13829
|
-
});
|
|
13830
|
-
};
|
|
13831
|
-
}
|
|
13832
13913
|
function composeFacadeParts(deps, hooks) {
|
|
13833
13914
|
const ownPubkeyBytes = hexToBytes4(deps.ownPubkey);
|
|
13834
13915
|
const ledger = new ReservationLedger();
|
|
@@ -13858,9 +13939,11 @@ function composeFacadeParts(deps, hooks) {
|
|
|
13858
13939
|
const machineDeps = buildMachineDeps(deps, hooks, historyStore);
|
|
13859
13940
|
const machineStores = createMachineStores(deps.kv);
|
|
13860
13941
|
const heldStates = /* @__PURE__ */ new Map();
|
|
13942
|
+
const refreshView = coalesced(() => view.delta());
|
|
13861
13943
|
return {
|
|
13862
13944
|
ownPubkeyBytes,
|
|
13863
13945
|
view,
|
|
13946
|
+
refreshView,
|
|
13864
13947
|
ledger,
|
|
13865
13948
|
pins: buildPins(deps, hooks, { ledger, view, machineStores, machineDeps }),
|
|
13866
13949
|
queue,
|
|
@@ -13869,7 +13952,7 @@ function composeFacadeParts(deps, hooks) {
|
|
|
13869
13952
|
machineDeps,
|
|
13870
13953
|
machine: new TransferMachine(machineDeps),
|
|
13871
13954
|
heldStates,
|
|
13872
|
-
receiveLoop: buildReceive(deps, hooks, historyStore, heldStates,
|
|
13955
|
+
receiveLoop: buildReceive(deps, hooks, historyStore, heldStates, refreshView),
|
|
13873
13956
|
requests: buildRequests(deps, hooks),
|
|
13874
13957
|
restoreDeps: buildRestoreDeps(deps, machineDeps, machineStores, view)
|
|
13875
13958
|
};
|
|
@@ -13936,7 +14019,7 @@ function buildMachineDeps(deps, hooks, historyStore) {
|
|
|
13936
14019
|
}
|
|
13937
14020
|
};
|
|
13938
14021
|
}
|
|
13939
|
-
function buildReceive(deps, hooks, historyStore, heldStates,
|
|
14022
|
+
function buildReceive(deps, hooks, historyStore, heldStates, refreshView) {
|
|
13940
14023
|
return new Receive({
|
|
13941
14024
|
delivery: deps.deliveryPort,
|
|
13942
14025
|
engine: () => hooks.engine(),
|
|
@@ -13950,10 +14033,7 @@ function buildReceive(deps, hooks, historyStore, heldStates, view) {
|
|
|
13950
14033
|
registry: deps.registry,
|
|
13951
14034
|
recordReceived: (record) => recordReceived(historyStore, record),
|
|
13952
14035
|
emit: (event, transfer) => deps.emit(event, transfer),
|
|
13953
|
-
|
|
13954
|
-
// firing per throttle tick regardless of the last one builds a queue that
|
|
13955
|
-
// drains in a burst at the END — a slower version of the bug being fixed.
|
|
13956
|
-
refreshView: makeSingleFlightRefresh(() => view.delta()),
|
|
14036
|
+
refreshView,
|
|
13957
14037
|
attention: (transferId, code, detail) => {
|
|
13958
14038
|
deps.emit(
|
|
13959
14039
|
"transfer:attention",
|
|
@@ -14005,6 +14085,7 @@ var PaymentsFacade = class {
|
|
|
14005
14085
|
isActiveOp: (id) => this.activeMoneyOps.has(id)
|
|
14006
14086
|
});
|
|
14007
14087
|
this.view = parts.view;
|
|
14088
|
+
this.wakeRefresh = parts.refreshView;
|
|
14008
14089
|
this.ledger = parts.ledger;
|
|
14009
14090
|
this.queue = parts.queue;
|
|
14010
14091
|
this.machine = parts.machine;
|
|
@@ -14034,6 +14115,7 @@ var PaymentsFacade = class {
|
|
|
14034
14115
|
now: () => this.nowMs()
|
|
14035
14116
|
});
|
|
14036
14117
|
}
|
|
14118
|
+
wakeRefresh;
|
|
14037
14119
|
prewarmed = new PrewarmCache();
|
|
14038
14120
|
view;
|
|
14039
14121
|
ledger;
|
|
@@ -14072,9 +14154,7 @@ var PaymentsFacade = class {
|
|
|
14072
14154
|
this.deps.session.subscribeStream("mailbox", () => {
|
|
14073
14155
|
this.trackTail(this.receiveLoop.drainOnce());
|
|
14074
14156
|
}),
|
|
14075
|
-
this.deps.session.subscribeStream("inventory",
|
|
14076
|
-
this.trackTail(this.view.delta());
|
|
14077
|
-
}),
|
|
14157
|
+
this.deps.session.subscribeStream("inventory", this.wakeRefresh),
|
|
14078
14158
|
this.deps.session.subscribeStream("payment_requests", () => {
|
|
14079
14159
|
this.trackTail(this.requests.drainIncoming());
|
|
14080
14160
|
})
|
|
@@ -14338,7 +14418,7 @@ var PaymentsFacade = class {
|
|
|
14338
14418
|
...spend.plan.direct,
|
|
14339
14419
|
...spend.plan.split !== void 0 ? [spend.plan.split.tokenId] : []
|
|
14340
14420
|
];
|
|
14341
|
-
|
|
14421
|
+
this.view.markInFlightMany(sourceIds);
|
|
14342
14422
|
if (spend.plan.split !== void 0) {
|
|
14343
14423
|
this.queue.declareExpectedChange(transferId, coinId, spend.plan.split.remainderAmount);
|
|
14344
14424
|
}
|
|
@@ -14624,6 +14704,10 @@ var WalletApiHttpError = class extends Error {
|
|
|
14624
14704
|
if (retryAfter !== void 0) this.retryAfter = retryAfter;
|
|
14625
14705
|
}
|
|
14626
14706
|
};
|
|
14707
|
+
var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([0, 408, 425, 429, 500, 502, 503, 504]);
|
|
14708
|
+
function isRetryableStatus(err) {
|
|
14709
|
+
return err instanceof WalletApiHttpError && RETRYABLE_STATUSES.has(err.status);
|
|
14710
|
+
}
|
|
14627
14711
|
function isWalletApiHttpError(err, status) {
|
|
14628
14712
|
return err instanceof WalletApiHttpError && (status === void 0 || err.status === status);
|
|
14629
14713
|
}
|
|
@@ -15336,6 +15420,23 @@ function computeDeliveryId(tokenIdHex, stateHashHex) {
|
|
|
15336
15420
|
return bytesToHex3(sha256(joined));
|
|
15337
15421
|
}
|
|
15338
15422
|
var DELIVERY_SEEN_KEY = "delivery:seen";
|
|
15423
|
+
var ACK_CHUNK = 50;
|
|
15424
|
+
function chunked(items, size) {
|
|
15425
|
+
const out = [];
|
|
15426
|
+
for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));
|
|
15427
|
+
return out;
|
|
15428
|
+
}
|
|
15429
|
+
var RetryableAckFailure = class extends Error {
|
|
15430
|
+
retryable = true;
|
|
15431
|
+
constructor(cause) {
|
|
15432
|
+
super(`mailbox ack batch could not be attempted: ${String(cause)}`);
|
|
15433
|
+
this.name = "RetryableAckFailure";
|
|
15434
|
+
this.cause = cause;
|
|
15435
|
+
}
|
|
15436
|
+
};
|
|
15437
|
+
function asRetryable(err) {
|
|
15438
|
+
return isRetryableStatus(err) ? new RetryableAckFailure(err) : err;
|
|
15439
|
+
}
|
|
15339
15440
|
var WalletApiDeliveryPort = class {
|
|
15340
15441
|
custody;
|
|
15341
15442
|
client;
|
|
@@ -15501,7 +15602,53 @@ var WalletApiDeliveryPort = class {
|
|
|
15501
15602
|
} else {
|
|
15502
15603
|
await this.client.reject([deliveryId], reason);
|
|
15503
15604
|
}
|
|
15504
|
-
await this.
|
|
15605
|
+
await this.addSeenAll([deliveryId]);
|
|
15606
|
+
}
|
|
15607
|
+
/**
|
|
15608
|
+
* §5.7/#757 batched settle. Claims go out grouped, rejects grouped by reason
|
|
15609
|
+
* (the endpoint takes one reason per call), and the seen-set is written ONCE
|
|
15610
|
+
* after every HTTP call has succeeded — never on a throw, so an aborted batch
|
|
15611
|
+
* re-lists rather than being silently forgotten.
|
|
15612
|
+
*/
|
|
15613
|
+
async ackBatch(acks) {
|
|
15614
|
+
const settled = [];
|
|
15615
|
+
const outcomes = [];
|
|
15616
|
+
try {
|
|
15617
|
+
await this.settleClaims(acks, settled, outcomes);
|
|
15618
|
+
await this.settleRejects(acks, settled);
|
|
15619
|
+
} catch (err) {
|
|
15620
|
+
throw asRetryable(err);
|
|
15621
|
+
}
|
|
15622
|
+
await this.addSeenAll(settled);
|
|
15623
|
+
return [...outcomes, ...settled.map((deliveryId) => ({ deliveryId, status: "settled" }))];
|
|
15624
|
+
}
|
|
15625
|
+
async settleClaims(acks, settled, outcomes) {
|
|
15626
|
+
const ids = acks.filter((a) => a.disposition === "claimed").map((a) => a.deliveryId);
|
|
15627
|
+
for (const chunk of chunked(ids, ACK_CHUNK)) {
|
|
15628
|
+
const result = await this.client.claim(chunk, this.custody === "inventory");
|
|
15629
|
+
settled.push(...result.claimed);
|
|
15630
|
+
settled.push(...result.alreadyClaimed.map((e) => e.entryId));
|
|
15631
|
+
for (const f of result.failed) {
|
|
15632
|
+
if (f.code === "CONFLICT") outcomes.push({ deliveryId: f.entryId, status: "conflict" });
|
|
15633
|
+
else throw new MailboxClaimFailedError(f.entryId, f.code);
|
|
15634
|
+
}
|
|
15635
|
+
}
|
|
15636
|
+
}
|
|
15637
|
+
async settleRejects(acks, settled) {
|
|
15638
|
+
const byReason = /* @__PURE__ */ new Map();
|
|
15639
|
+
for (const a of acks) {
|
|
15640
|
+
if (a.disposition !== "rejected") continue;
|
|
15641
|
+
const key = a.reason ?? "";
|
|
15642
|
+
const group = byReason.get(key) ?? [];
|
|
15643
|
+
group.push(a.deliveryId);
|
|
15644
|
+
byReason.set(key, group);
|
|
15645
|
+
}
|
|
15646
|
+
for (const [reason, group] of byReason) {
|
|
15647
|
+
for (const chunk of chunked(group, ACK_CHUNK)) {
|
|
15648
|
+
await this.client.reject(chunk, reason === "" ? void 0 : reason);
|
|
15649
|
+
settled.push(...chunk);
|
|
15650
|
+
}
|
|
15651
|
+
}
|
|
15505
15652
|
}
|
|
15506
15653
|
onWake(cb) {
|
|
15507
15654
|
if (this.wake === null) return () => void 0;
|
|
@@ -15512,10 +15659,12 @@ var WalletApiDeliveryPort = class {
|
|
|
15512
15659
|
const raw = await this.kv.get(DELIVERY_SEEN_KEY);
|
|
15513
15660
|
return new Set(raw ?? []);
|
|
15514
15661
|
}
|
|
15515
|
-
|
|
15662
|
+
/** SerialChain settles-not-fulfills: a rejected write never bricks later acks. */
|
|
15663
|
+
addSeenAll(deliveryIds) {
|
|
15664
|
+
if (deliveryIds.length === 0) return Promise.resolve();
|
|
15516
15665
|
return this.seenChain.enqueue(async () => {
|
|
15517
15666
|
const seen = await this.readSeen();
|
|
15518
|
-
seen.add(
|
|
15667
|
+
for (const id of deliveryIds) seen.add(id);
|
|
15519
15668
|
await this.kv.set(DELIVERY_SEEN_KEY, [...seen]);
|
|
15520
15669
|
});
|
|
15521
15670
|
}
|