@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/index.js
CHANGED
|
@@ -11233,6 +11233,26 @@ var SerialChain = class {
|
|
|
11233
11233
|
return run;
|
|
11234
11234
|
}
|
|
11235
11235
|
};
|
|
11236
|
+
function coalesced(run) {
|
|
11237
|
+
let inFlight = false;
|
|
11238
|
+
let queued = false;
|
|
11239
|
+
const start = () => {
|
|
11240
|
+
inFlight = true;
|
|
11241
|
+
void run().catch(() => void 0).finally(() => {
|
|
11242
|
+
inFlight = false;
|
|
11243
|
+
if (!queued) return;
|
|
11244
|
+
queued = false;
|
|
11245
|
+
start();
|
|
11246
|
+
});
|
|
11247
|
+
};
|
|
11248
|
+
return () => {
|
|
11249
|
+
if (inFlight) {
|
|
11250
|
+
queued = true;
|
|
11251
|
+
return;
|
|
11252
|
+
}
|
|
11253
|
+
start();
|
|
11254
|
+
};
|
|
11255
|
+
}
|
|
11236
11256
|
|
|
11237
11257
|
// modules/payments-v2/stores.ts
|
|
11238
11258
|
function createScopedKV(storage, network, chainPubkey) {
|
|
@@ -12680,6 +12700,16 @@ var InventoryView = class {
|
|
|
12680
12700
|
this.inFlight.add(tokenId);
|
|
12681
12701
|
this.deps.emit("inventory:updated");
|
|
12682
12702
|
}
|
|
12703
|
+
/** Reserve a whole plan as ONE change — per token emitted 54 events per send. */
|
|
12704
|
+
markInFlightMany(tokenIds) {
|
|
12705
|
+
let changed = false;
|
|
12706
|
+
for (const tokenId of tokenIds) {
|
|
12707
|
+
if (this.inFlight.has(tokenId)) continue;
|
|
12708
|
+
this.inFlight.add(tokenId);
|
|
12709
|
+
changed = true;
|
|
12710
|
+
}
|
|
12711
|
+
if (changed) this.deps.emit("inventory:updated");
|
|
12712
|
+
}
|
|
12683
12713
|
release(tokenId) {
|
|
12684
12714
|
if (this.inFlight.delete(tokenId)) this.deps.emit("inventory:updated");
|
|
12685
12715
|
}
|
|
@@ -12904,6 +12934,11 @@ var InventoryView = class {
|
|
|
12904
12934
|
}
|
|
12905
12935
|
};
|
|
12906
12936
|
|
|
12937
|
+
// modules/payments-v2/ports.ts
|
|
12938
|
+
function isRetryableAckError(err) {
|
|
12939
|
+
return err !== null && typeof err === "object" && err.retryable === true;
|
|
12940
|
+
}
|
|
12941
|
+
|
|
12907
12942
|
// modules/payments-v2/receive/Receive.ts
|
|
12908
12943
|
var ACK_BATCH_SIZE = 200;
|
|
12909
12944
|
var REFRESH_INTERVAL_MS = 2500;
|
|
@@ -12985,6 +13020,7 @@ var Receive = class {
|
|
|
12985
13020
|
}
|
|
12986
13021
|
} catch (err) {
|
|
12987
13022
|
logger.warn("PaymentsV2", "receive drain interrupted \u2014 unacked entries retry next drain:", err);
|
|
13023
|
+
if (isRetryableAckError(err)) return stored;
|
|
12988
13024
|
await flushAcks(deps, pending, pageEpoch).catch((flushErr) => {
|
|
12989
13025
|
logger.warn("PaymentsV2", "receive ack flush failed \u2014 cursor holds at the acked prefix:", flushErr);
|
|
12990
13026
|
});
|
|
@@ -13059,14 +13095,18 @@ async function announce(deps, entry, record) {
|
|
|
13059
13095
|
};
|
|
13060
13096
|
}
|
|
13061
13097
|
async function flushAcks(deps, pending, epochOf) {
|
|
13098
|
+
if (pending.length === 0) return;
|
|
13062
13099
|
let lastAcked = null;
|
|
13063
13100
|
try {
|
|
13064
|
-
|
|
13065
|
-
|
|
13066
|
-
|
|
13067
|
-
lastAcked =
|
|
13068
|
-
|
|
13101
|
+
const settled = await settleAcks(deps, pending);
|
|
13102
|
+
let i = 0;
|
|
13103
|
+
while (i < pending.length && settled.has(pending[i].deliveryId)) {
|
|
13104
|
+
lastAcked = pending[i].cursor;
|
|
13105
|
+
i += 1;
|
|
13069
13106
|
}
|
|
13107
|
+
const stuck = pending.filter((p) => !settled.has(p.deliveryId));
|
|
13108
|
+
pending.length = 0;
|
|
13109
|
+
pending.push(...stuck);
|
|
13070
13110
|
} finally {
|
|
13071
13111
|
if (lastAcked !== null) {
|
|
13072
13112
|
const record = { cursor: lastAcked, syncEpoch: epochOf() };
|
|
@@ -13074,14 +13114,65 @@ async function flushAcks(deps, pending, epochOf) {
|
|
|
13074
13114
|
}
|
|
13075
13115
|
}
|
|
13076
13116
|
}
|
|
13117
|
+
async function settleAcks(deps, pending) {
|
|
13118
|
+
const batch = deps.delivery.ackBatch?.bind(deps.delivery);
|
|
13119
|
+
if (batch === void 0) return settleOneByOne(deps, pending);
|
|
13120
|
+
let outcomes;
|
|
13121
|
+
try {
|
|
13122
|
+
outcomes = await batch(pending.map(toAckRequest));
|
|
13123
|
+
} catch (err) {
|
|
13124
|
+
if (isRetryableAckError(err)) throw err;
|
|
13125
|
+
logger.warn("PaymentsV2", "batched mailbox ack failed \u2014 settling one at a time:", err);
|
|
13126
|
+
return settleOneByOne(deps, pending);
|
|
13127
|
+
}
|
|
13128
|
+
const settled = new Set(outcomes.filter((o) => o.status === "settled").map((o) => o.deliveryId));
|
|
13129
|
+
const conflicts = outcomes.filter((o) => o.status === "conflict").map((o) => o.deliveryId);
|
|
13130
|
+
if (conflicts.length > 0) await resolveConflicts(deps, pending, conflicts, settled);
|
|
13131
|
+
return settled;
|
|
13132
|
+
}
|
|
13133
|
+
async function resolveConflicts(deps, pending, conflicts, settled) {
|
|
13134
|
+
for (const deliveryId of conflicts) {
|
|
13135
|
+
const entry = pending.find((p) => p.deliveryId === deliveryId);
|
|
13136
|
+
try {
|
|
13137
|
+
await rejectStaleClaim(deps, deliveryId, entry?.transferId);
|
|
13138
|
+
} catch (err) {
|
|
13139
|
+
logger.warn("PaymentsV2", `stale-reject failed for ${deliveryId} \u2014 cursor holds here:`, err);
|
|
13140
|
+
continue;
|
|
13141
|
+
}
|
|
13142
|
+
settled.add(deliveryId);
|
|
13143
|
+
}
|
|
13144
|
+
}
|
|
13145
|
+
async function rejectStaleClaim(deps, deliveryId, transferId) {
|
|
13146
|
+
logger.warn("PaymentsV2", `mailbox claim CONFLICT for ${deliveryId} \u2014 rejected('other') as stale`);
|
|
13147
|
+
await deps.delivery.ack(deliveryId, "rejected", "other");
|
|
13148
|
+
deps.attention(transferId ?? "", ATTENTION_CLAIM_CONFLICT, deliveryId);
|
|
13149
|
+
}
|
|
13150
|
+
async function settleOneByOne(deps, pending) {
|
|
13151
|
+
const settled = /* @__PURE__ */ new Set();
|
|
13152
|
+
for (const ack of pending) {
|
|
13153
|
+
try {
|
|
13154
|
+
await ackOne(deps, ack);
|
|
13155
|
+
} catch (err) {
|
|
13156
|
+
logger.warn("PaymentsV2", `mailbox ack failed for ${ack.deliveryId} \u2014 cursor holds here:`, err);
|
|
13157
|
+
break;
|
|
13158
|
+
}
|
|
13159
|
+
settled.add(ack.deliveryId);
|
|
13160
|
+
}
|
|
13161
|
+
return settled;
|
|
13162
|
+
}
|
|
13163
|
+
function toAckRequest(ack) {
|
|
13164
|
+
return {
|
|
13165
|
+
deliveryId: ack.deliveryId,
|
|
13166
|
+
disposition: ack.disposition,
|
|
13167
|
+
...ack.reason !== void 0 ? { reason: ack.reason } : {}
|
|
13168
|
+
};
|
|
13169
|
+
}
|
|
13077
13170
|
async function ackOne(deps, ack) {
|
|
13078
13171
|
try {
|
|
13079
13172
|
await deps.delivery.ack(ack.deliveryId, ack.disposition, ack.reason);
|
|
13080
13173
|
} catch (err) {
|
|
13081
13174
|
if (ack.disposition !== "claimed" || !isClaimConflict(err)) throw err;
|
|
13082
|
-
|
|
13083
|
-
await deps.delivery.ack(ack.deliveryId, "rejected", "other");
|
|
13084
|
-
deps.attention(ack.transferId ?? "", ATTENTION_CLAIM_CONFLICT, ack.deliveryId);
|
|
13175
|
+
await rejectStaleClaim(deps, ack.deliveryId, ack.transferId);
|
|
13085
13176
|
}
|
|
13086
13177
|
}
|
|
13087
13178
|
function claimAck(entry) {
|
|
@@ -13835,16 +13926,6 @@ function parseAmount(raw) {
|
|
|
13835
13926
|
function supportsDeterministicMint(engine) {
|
|
13836
13927
|
return engine.deterministicMint === true;
|
|
13837
13928
|
}
|
|
13838
|
-
function makeSingleFlightRefresh(refresh) {
|
|
13839
|
-
let inFlight = false;
|
|
13840
|
-
return () => {
|
|
13841
|
-
if (inFlight) return;
|
|
13842
|
-
inFlight = true;
|
|
13843
|
-
void refresh().catch(() => void 0).finally(() => {
|
|
13844
|
-
inFlight = false;
|
|
13845
|
-
});
|
|
13846
|
-
};
|
|
13847
|
-
}
|
|
13848
13929
|
function composeFacadeParts(deps, hooks) {
|
|
13849
13930
|
const ownPubkeyBytes = hexToBytes4(deps.ownPubkey);
|
|
13850
13931
|
const ledger = new ReservationLedger();
|
|
@@ -13874,9 +13955,11 @@ function composeFacadeParts(deps, hooks) {
|
|
|
13874
13955
|
const machineDeps = buildMachineDeps(deps, hooks, historyStore);
|
|
13875
13956
|
const machineStores = createMachineStores(deps.kv);
|
|
13876
13957
|
const heldStates = /* @__PURE__ */ new Map();
|
|
13958
|
+
const refreshView = coalesced(() => view.delta());
|
|
13877
13959
|
return {
|
|
13878
13960
|
ownPubkeyBytes,
|
|
13879
13961
|
view,
|
|
13962
|
+
refreshView,
|
|
13880
13963
|
ledger,
|
|
13881
13964
|
pins: buildPins(deps, hooks, { ledger, view, machineStores, machineDeps }),
|
|
13882
13965
|
queue,
|
|
@@ -13885,7 +13968,7 @@ function composeFacadeParts(deps, hooks) {
|
|
|
13885
13968
|
machineDeps,
|
|
13886
13969
|
machine: new TransferMachine(machineDeps),
|
|
13887
13970
|
heldStates,
|
|
13888
|
-
receiveLoop: buildReceive(deps, hooks, historyStore, heldStates,
|
|
13971
|
+
receiveLoop: buildReceive(deps, hooks, historyStore, heldStates, refreshView),
|
|
13889
13972
|
requests: buildRequests(deps, hooks),
|
|
13890
13973
|
restoreDeps: buildRestoreDeps(deps, machineDeps, machineStores, view)
|
|
13891
13974
|
};
|
|
@@ -13952,7 +14035,7 @@ function buildMachineDeps(deps, hooks, historyStore) {
|
|
|
13952
14035
|
}
|
|
13953
14036
|
};
|
|
13954
14037
|
}
|
|
13955
|
-
function buildReceive(deps, hooks, historyStore, heldStates,
|
|
14038
|
+
function buildReceive(deps, hooks, historyStore, heldStates, refreshView) {
|
|
13956
14039
|
return new Receive({
|
|
13957
14040
|
delivery: deps.deliveryPort,
|
|
13958
14041
|
engine: () => hooks.engine(),
|
|
@@ -13966,10 +14049,7 @@ function buildReceive(deps, hooks, historyStore, heldStates, view) {
|
|
|
13966
14049
|
registry: deps.registry,
|
|
13967
14050
|
recordReceived: (record) => recordReceived(historyStore, record),
|
|
13968
14051
|
emit: (event, transfer) => deps.emit(event, transfer),
|
|
13969
|
-
|
|
13970
|
-
// firing per throttle tick regardless of the last one builds a queue that
|
|
13971
|
-
// drains in a burst at the END — a slower version of the bug being fixed.
|
|
13972
|
-
refreshView: makeSingleFlightRefresh(() => view.delta()),
|
|
14052
|
+
refreshView,
|
|
13973
14053
|
attention: (transferId, code, detail) => {
|
|
13974
14054
|
deps.emit(
|
|
13975
14055
|
"transfer:attention",
|
|
@@ -14021,6 +14101,7 @@ var PaymentsFacade = class {
|
|
|
14021
14101
|
isActiveOp: (id) => this.activeMoneyOps.has(id)
|
|
14022
14102
|
});
|
|
14023
14103
|
this.view = parts.view;
|
|
14104
|
+
this.wakeRefresh = parts.refreshView;
|
|
14024
14105
|
this.ledger = parts.ledger;
|
|
14025
14106
|
this.queue = parts.queue;
|
|
14026
14107
|
this.machine = parts.machine;
|
|
@@ -14050,6 +14131,7 @@ var PaymentsFacade = class {
|
|
|
14050
14131
|
now: () => this.nowMs()
|
|
14051
14132
|
});
|
|
14052
14133
|
}
|
|
14134
|
+
wakeRefresh;
|
|
14053
14135
|
prewarmed = new PrewarmCache();
|
|
14054
14136
|
view;
|
|
14055
14137
|
ledger;
|
|
@@ -14088,9 +14170,7 @@ var PaymentsFacade = class {
|
|
|
14088
14170
|
this.deps.session.subscribeStream("mailbox", () => {
|
|
14089
14171
|
this.trackTail(this.receiveLoop.drainOnce());
|
|
14090
14172
|
}),
|
|
14091
|
-
this.deps.session.subscribeStream("inventory",
|
|
14092
|
-
this.trackTail(this.view.delta());
|
|
14093
|
-
}),
|
|
14173
|
+
this.deps.session.subscribeStream("inventory", this.wakeRefresh),
|
|
14094
14174
|
this.deps.session.subscribeStream("payment_requests", () => {
|
|
14095
14175
|
this.trackTail(this.requests.drainIncoming());
|
|
14096
14176
|
})
|
|
@@ -14354,7 +14434,7 @@ var PaymentsFacade = class {
|
|
|
14354
14434
|
...spend.plan.direct,
|
|
14355
14435
|
...spend.plan.split !== void 0 ? [spend.plan.split.tokenId] : []
|
|
14356
14436
|
];
|
|
14357
|
-
|
|
14437
|
+
this.view.markInFlightMany(sourceIds);
|
|
14358
14438
|
if (spend.plan.split !== void 0) {
|
|
14359
14439
|
this.queue.declareExpectedChange(transferId, coinId, spend.plan.split.remainderAmount);
|
|
14360
14440
|
}
|
|
@@ -14640,6 +14720,10 @@ var WalletApiHttpError = class extends Error {
|
|
|
14640
14720
|
if (retryAfter !== void 0) this.retryAfter = retryAfter;
|
|
14641
14721
|
}
|
|
14642
14722
|
};
|
|
14723
|
+
var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([0, 408, 425, 429, 500, 502, 503, 504]);
|
|
14724
|
+
function isRetryableStatus(err) {
|
|
14725
|
+
return err instanceof WalletApiHttpError && RETRYABLE_STATUSES.has(err.status);
|
|
14726
|
+
}
|
|
14643
14727
|
function isWalletApiHttpError(err, status) {
|
|
14644
14728
|
return err instanceof WalletApiHttpError && (status === void 0 || err.status === status);
|
|
14645
14729
|
}
|
|
@@ -15352,6 +15436,23 @@ function computeDeliveryId(tokenIdHex, stateHashHex) {
|
|
|
15352
15436
|
return bytesToHex3(sha256(joined));
|
|
15353
15437
|
}
|
|
15354
15438
|
var DELIVERY_SEEN_KEY = "delivery:seen";
|
|
15439
|
+
var ACK_CHUNK = 50;
|
|
15440
|
+
function chunked(items, size) {
|
|
15441
|
+
const out = [];
|
|
15442
|
+
for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));
|
|
15443
|
+
return out;
|
|
15444
|
+
}
|
|
15445
|
+
var RetryableAckFailure = class extends Error {
|
|
15446
|
+
retryable = true;
|
|
15447
|
+
constructor(cause) {
|
|
15448
|
+
super(`mailbox ack batch could not be attempted: ${String(cause)}`);
|
|
15449
|
+
this.name = "RetryableAckFailure";
|
|
15450
|
+
this.cause = cause;
|
|
15451
|
+
}
|
|
15452
|
+
};
|
|
15453
|
+
function asRetryable(err) {
|
|
15454
|
+
return isRetryableStatus(err) ? new RetryableAckFailure(err) : err;
|
|
15455
|
+
}
|
|
15355
15456
|
var WalletApiDeliveryPort = class {
|
|
15356
15457
|
custody;
|
|
15357
15458
|
client;
|
|
@@ -15517,7 +15618,53 @@ var WalletApiDeliveryPort = class {
|
|
|
15517
15618
|
} else {
|
|
15518
15619
|
await this.client.reject([deliveryId], reason);
|
|
15519
15620
|
}
|
|
15520
|
-
await this.
|
|
15621
|
+
await this.addSeenAll([deliveryId]);
|
|
15622
|
+
}
|
|
15623
|
+
/**
|
|
15624
|
+
* §5.7/#757 batched settle. Claims go out grouped, rejects grouped by reason
|
|
15625
|
+
* (the endpoint takes one reason per call), and the seen-set is written ONCE
|
|
15626
|
+
* after every HTTP call has succeeded — never on a throw, so an aborted batch
|
|
15627
|
+
* re-lists rather than being silently forgotten.
|
|
15628
|
+
*/
|
|
15629
|
+
async ackBatch(acks) {
|
|
15630
|
+
const settled = [];
|
|
15631
|
+
const outcomes = [];
|
|
15632
|
+
try {
|
|
15633
|
+
await this.settleClaims(acks, settled, outcomes);
|
|
15634
|
+
await this.settleRejects(acks, settled);
|
|
15635
|
+
} catch (err) {
|
|
15636
|
+
throw asRetryable(err);
|
|
15637
|
+
}
|
|
15638
|
+
await this.addSeenAll(settled);
|
|
15639
|
+
return [...outcomes, ...settled.map((deliveryId) => ({ deliveryId, status: "settled" }))];
|
|
15640
|
+
}
|
|
15641
|
+
async settleClaims(acks, settled, outcomes) {
|
|
15642
|
+
const ids = acks.filter((a) => a.disposition === "claimed").map((a) => a.deliveryId);
|
|
15643
|
+
for (const chunk of chunked(ids, ACK_CHUNK)) {
|
|
15644
|
+
const result = await this.client.claim(chunk, this.custody === "inventory");
|
|
15645
|
+
settled.push(...result.claimed);
|
|
15646
|
+
settled.push(...result.alreadyClaimed.map((e) => e.entryId));
|
|
15647
|
+
for (const f of result.failed) {
|
|
15648
|
+
if (f.code === "CONFLICT") outcomes.push({ deliveryId: f.entryId, status: "conflict" });
|
|
15649
|
+
else throw new MailboxClaimFailedError(f.entryId, f.code);
|
|
15650
|
+
}
|
|
15651
|
+
}
|
|
15652
|
+
}
|
|
15653
|
+
async settleRejects(acks, settled) {
|
|
15654
|
+
const byReason = /* @__PURE__ */ new Map();
|
|
15655
|
+
for (const a of acks) {
|
|
15656
|
+
if (a.disposition !== "rejected") continue;
|
|
15657
|
+
const key = a.reason ?? "";
|
|
15658
|
+
const group = byReason.get(key) ?? [];
|
|
15659
|
+
group.push(a.deliveryId);
|
|
15660
|
+
byReason.set(key, group);
|
|
15661
|
+
}
|
|
15662
|
+
for (const [reason, group] of byReason) {
|
|
15663
|
+
for (const chunk of chunked(group, ACK_CHUNK)) {
|
|
15664
|
+
await this.client.reject(chunk, reason === "" ? void 0 : reason);
|
|
15665
|
+
settled.push(...chunk);
|
|
15666
|
+
}
|
|
15667
|
+
}
|
|
15521
15668
|
}
|
|
15522
15669
|
onWake(cb) {
|
|
15523
15670
|
if (this.wake === null) return () => void 0;
|
|
@@ -15528,10 +15675,12 @@ var WalletApiDeliveryPort = class {
|
|
|
15528
15675
|
const raw = await this.kv.get(DELIVERY_SEEN_KEY);
|
|
15529
15676
|
return new Set(raw ?? []);
|
|
15530
15677
|
}
|
|
15531
|
-
|
|
15678
|
+
/** SerialChain settles-not-fulfills: a rejected write never bricks later acks. */
|
|
15679
|
+
addSeenAll(deliveryIds) {
|
|
15680
|
+
if (deliveryIds.length === 0) return Promise.resolve();
|
|
15532
15681
|
return this.seenChain.enqueue(async () => {
|
|
15533
15682
|
const seen = await this.readSeen();
|
|
15534
|
-
seen.add(
|
|
15683
|
+
for (const id of deliveryIds) seen.add(id);
|
|
15535
15684
|
await this.kv.set(DELIVERY_SEEN_KEY, [...seen]);
|
|
15536
15685
|
});
|
|
15537
15686
|
}
|