@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.cjs
CHANGED
|
@@ -11303,6 +11303,26 @@ var SerialChain = class {
|
|
|
11303
11303
|
return run;
|
|
11304
11304
|
}
|
|
11305
11305
|
};
|
|
11306
|
+
function coalesced(run) {
|
|
11307
|
+
let inFlight = false;
|
|
11308
|
+
let queued = false;
|
|
11309
|
+
const start = () => {
|
|
11310
|
+
inFlight = true;
|
|
11311
|
+
void run().catch(() => void 0).finally(() => {
|
|
11312
|
+
inFlight = false;
|
|
11313
|
+
if (!queued) return;
|
|
11314
|
+
queued = false;
|
|
11315
|
+
start();
|
|
11316
|
+
});
|
|
11317
|
+
};
|
|
11318
|
+
return () => {
|
|
11319
|
+
if (inFlight) {
|
|
11320
|
+
queued = true;
|
|
11321
|
+
return;
|
|
11322
|
+
}
|
|
11323
|
+
start();
|
|
11324
|
+
};
|
|
11325
|
+
}
|
|
11306
11326
|
|
|
11307
11327
|
// modules/payments-v2/stores.ts
|
|
11308
11328
|
function createScopedKV(storage, network, chainPubkey) {
|
|
@@ -12750,6 +12770,16 @@ var InventoryView = class {
|
|
|
12750
12770
|
this.inFlight.add(tokenId);
|
|
12751
12771
|
this.deps.emit("inventory:updated");
|
|
12752
12772
|
}
|
|
12773
|
+
/** Reserve a whole plan as ONE change — per token emitted 54 events per send. */
|
|
12774
|
+
markInFlightMany(tokenIds) {
|
|
12775
|
+
let changed = false;
|
|
12776
|
+
for (const tokenId of tokenIds) {
|
|
12777
|
+
if (this.inFlight.has(tokenId)) continue;
|
|
12778
|
+
this.inFlight.add(tokenId);
|
|
12779
|
+
changed = true;
|
|
12780
|
+
}
|
|
12781
|
+
if (changed) this.deps.emit("inventory:updated");
|
|
12782
|
+
}
|
|
12753
12783
|
release(tokenId) {
|
|
12754
12784
|
if (this.inFlight.delete(tokenId)) this.deps.emit("inventory:updated");
|
|
12755
12785
|
}
|
|
@@ -12974,6 +13004,11 @@ var InventoryView = class {
|
|
|
12974
13004
|
}
|
|
12975
13005
|
};
|
|
12976
13006
|
|
|
13007
|
+
// modules/payments-v2/ports.ts
|
|
13008
|
+
function isRetryableAckError(err) {
|
|
13009
|
+
return err !== null && typeof err === "object" && err.retryable === true;
|
|
13010
|
+
}
|
|
13011
|
+
|
|
12977
13012
|
// modules/payments-v2/receive/Receive.ts
|
|
12978
13013
|
var ACK_BATCH_SIZE = 200;
|
|
12979
13014
|
var REFRESH_INTERVAL_MS = 2500;
|
|
@@ -13055,6 +13090,7 @@ var Receive = class {
|
|
|
13055
13090
|
}
|
|
13056
13091
|
} catch (err) {
|
|
13057
13092
|
logger.warn("PaymentsV2", "receive drain interrupted \u2014 unacked entries retry next drain:", err);
|
|
13093
|
+
if (isRetryableAckError(err)) return stored;
|
|
13058
13094
|
await flushAcks(deps, pending, pageEpoch).catch((flushErr) => {
|
|
13059
13095
|
logger.warn("PaymentsV2", "receive ack flush failed \u2014 cursor holds at the acked prefix:", flushErr);
|
|
13060
13096
|
});
|
|
@@ -13129,14 +13165,18 @@ async function announce(deps, entry, record) {
|
|
|
13129
13165
|
};
|
|
13130
13166
|
}
|
|
13131
13167
|
async function flushAcks(deps, pending, epochOf) {
|
|
13168
|
+
if (pending.length === 0) return;
|
|
13132
13169
|
let lastAcked = null;
|
|
13133
13170
|
try {
|
|
13134
|
-
|
|
13135
|
-
|
|
13136
|
-
|
|
13137
|
-
lastAcked =
|
|
13138
|
-
|
|
13171
|
+
const settled = await settleAcks(deps, pending);
|
|
13172
|
+
let i = 0;
|
|
13173
|
+
while (i < pending.length && settled.has(pending[i].deliveryId)) {
|
|
13174
|
+
lastAcked = pending[i].cursor;
|
|
13175
|
+
i += 1;
|
|
13139
13176
|
}
|
|
13177
|
+
const stuck = pending.filter((p) => !settled.has(p.deliveryId));
|
|
13178
|
+
pending.length = 0;
|
|
13179
|
+
pending.push(...stuck);
|
|
13140
13180
|
} finally {
|
|
13141
13181
|
if (lastAcked !== null) {
|
|
13142
13182
|
const record = { cursor: lastAcked, syncEpoch: epochOf() };
|
|
@@ -13144,14 +13184,65 @@ async function flushAcks(deps, pending, epochOf) {
|
|
|
13144
13184
|
}
|
|
13145
13185
|
}
|
|
13146
13186
|
}
|
|
13187
|
+
async function settleAcks(deps, pending) {
|
|
13188
|
+
const batch = deps.delivery.ackBatch?.bind(deps.delivery);
|
|
13189
|
+
if (batch === void 0) return settleOneByOne(deps, pending);
|
|
13190
|
+
let outcomes;
|
|
13191
|
+
try {
|
|
13192
|
+
outcomes = await batch(pending.map(toAckRequest));
|
|
13193
|
+
} catch (err) {
|
|
13194
|
+
if (isRetryableAckError(err)) throw err;
|
|
13195
|
+
logger.warn("PaymentsV2", "batched mailbox ack failed \u2014 settling one at a time:", err);
|
|
13196
|
+
return settleOneByOne(deps, pending);
|
|
13197
|
+
}
|
|
13198
|
+
const settled = new Set(outcomes.filter((o) => o.status === "settled").map((o) => o.deliveryId));
|
|
13199
|
+
const conflicts = outcomes.filter((o) => o.status === "conflict").map((o) => o.deliveryId);
|
|
13200
|
+
if (conflicts.length > 0) await resolveConflicts(deps, pending, conflicts, settled);
|
|
13201
|
+
return settled;
|
|
13202
|
+
}
|
|
13203
|
+
async function resolveConflicts(deps, pending, conflicts, settled) {
|
|
13204
|
+
for (const deliveryId of conflicts) {
|
|
13205
|
+
const entry = pending.find((p) => p.deliveryId === deliveryId);
|
|
13206
|
+
try {
|
|
13207
|
+
await rejectStaleClaim(deps, deliveryId, entry?.transferId);
|
|
13208
|
+
} catch (err) {
|
|
13209
|
+
logger.warn("PaymentsV2", `stale-reject failed for ${deliveryId} \u2014 cursor holds here:`, err);
|
|
13210
|
+
continue;
|
|
13211
|
+
}
|
|
13212
|
+
settled.add(deliveryId);
|
|
13213
|
+
}
|
|
13214
|
+
}
|
|
13215
|
+
async function rejectStaleClaim(deps, deliveryId, transferId) {
|
|
13216
|
+
logger.warn("PaymentsV2", `mailbox claim CONFLICT for ${deliveryId} \u2014 rejected('other') as stale`);
|
|
13217
|
+
await deps.delivery.ack(deliveryId, "rejected", "other");
|
|
13218
|
+
deps.attention(transferId ?? "", ATTENTION_CLAIM_CONFLICT, deliveryId);
|
|
13219
|
+
}
|
|
13220
|
+
async function settleOneByOne(deps, pending) {
|
|
13221
|
+
const settled = /* @__PURE__ */ new Set();
|
|
13222
|
+
for (const ack of pending) {
|
|
13223
|
+
try {
|
|
13224
|
+
await ackOne(deps, ack);
|
|
13225
|
+
} catch (err) {
|
|
13226
|
+
logger.warn("PaymentsV2", `mailbox ack failed for ${ack.deliveryId} \u2014 cursor holds here:`, err);
|
|
13227
|
+
break;
|
|
13228
|
+
}
|
|
13229
|
+
settled.add(ack.deliveryId);
|
|
13230
|
+
}
|
|
13231
|
+
return settled;
|
|
13232
|
+
}
|
|
13233
|
+
function toAckRequest(ack) {
|
|
13234
|
+
return {
|
|
13235
|
+
deliveryId: ack.deliveryId,
|
|
13236
|
+
disposition: ack.disposition,
|
|
13237
|
+
...ack.reason !== void 0 ? { reason: ack.reason } : {}
|
|
13238
|
+
};
|
|
13239
|
+
}
|
|
13147
13240
|
async function ackOne(deps, ack) {
|
|
13148
13241
|
try {
|
|
13149
13242
|
await deps.delivery.ack(ack.deliveryId, ack.disposition, ack.reason);
|
|
13150
13243
|
} catch (err) {
|
|
13151
13244
|
if (ack.disposition !== "claimed" || !isClaimConflict(err)) throw err;
|
|
13152
|
-
|
|
13153
|
-
await deps.delivery.ack(ack.deliveryId, "rejected", "other");
|
|
13154
|
-
deps.attention(ack.transferId ?? "", ATTENTION_CLAIM_CONFLICT, ack.deliveryId);
|
|
13245
|
+
await rejectStaleClaim(deps, ack.deliveryId, ack.transferId);
|
|
13155
13246
|
}
|
|
13156
13247
|
}
|
|
13157
13248
|
function claimAck(entry) {
|
|
@@ -13905,16 +13996,6 @@ function parseAmount(raw) {
|
|
|
13905
13996
|
function supportsDeterministicMint(engine) {
|
|
13906
13997
|
return engine.deterministicMint === true;
|
|
13907
13998
|
}
|
|
13908
|
-
function makeSingleFlightRefresh(refresh) {
|
|
13909
|
-
let inFlight = false;
|
|
13910
|
-
return () => {
|
|
13911
|
-
if (inFlight) return;
|
|
13912
|
-
inFlight = true;
|
|
13913
|
-
void refresh().catch(() => void 0).finally(() => {
|
|
13914
|
-
inFlight = false;
|
|
13915
|
-
});
|
|
13916
|
-
};
|
|
13917
|
-
}
|
|
13918
13999
|
function composeFacadeParts(deps, hooks) {
|
|
13919
14000
|
const ownPubkeyBytes = hexToBytes4(deps.ownPubkey);
|
|
13920
14001
|
const ledger = new ReservationLedger();
|
|
@@ -13944,9 +14025,11 @@ function composeFacadeParts(deps, hooks) {
|
|
|
13944
14025
|
const machineDeps = buildMachineDeps(deps, hooks, historyStore);
|
|
13945
14026
|
const machineStores = createMachineStores(deps.kv);
|
|
13946
14027
|
const heldStates = /* @__PURE__ */ new Map();
|
|
14028
|
+
const refreshView = coalesced(() => view.delta());
|
|
13947
14029
|
return {
|
|
13948
14030
|
ownPubkeyBytes,
|
|
13949
14031
|
view,
|
|
14032
|
+
refreshView,
|
|
13950
14033
|
ledger,
|
|
13951
14034
|
pins: buildPins(deps, hooks, { ledger, view, machineStores, machineDeps }),
|
|
13952
14035
|
queue,
|
|
@@ -13955,7 +14038,7 @@ function composeFacadeParts(deps, hooks) {
|
|
|
13955
14038
|
machineDeps,
|
|
13956
14039
|
machine: new TransferMachine(machineDeps),
|
|
13957
14040
|
heldStates,
|
|
13958
|
-
receiveLoop: buildReceive(deps, hooks, historyStore, heldStates,
|
|
14041
|
+
receiveLoop: buildReceive(deps, hooks, historyStore, heldStates, refreshView),
|
|
13959
14042
|
requests: buildRequests(deps, hooks),
|
|
13960
14043
|
restoreDeps: buildRestoreDeps(deps, machineDeps, machineStores, view)
|
|
13961
14044
|
};
|
|
@@ -14022,7 +14105,7 @@ function buildMachineDeps(deps, hooks, historyStore) {
|
|
|
14022
14105
|
}
|
|
14023
14106
|
};
|
|
14024
14107
|
}
|
|
14025
|
-
function buildReceive(deps, hooks, historyStore, heldStates,
|
|
14108
|
+
function buildReceive(deps, hooks, historyStore, heldStates, refreshView) {
|
|
14026
14109
|
return new Receive({
|
|
14027
14110
|
delivery: deps.deliveryPort,
|
|
14028
14111
|
engine: () => hooks.engine(),
|
|
@@ -14036,10 +14119,7 @@ function buildReceive(deps, hooks, historyStore, heldStates, view) {
|
|
|
14036
14119
|
registry: deps.registry,
|
|
14037
14120
|
recordReceived: (record) => recordReceived(historyStore, record),
|
|
14038
14121
|
emit: (event, transfer) => deps.emit(event, transfer),
|
|
14039
|
-
|
|
14040
|
-
// firing per throttle tick regardless of the last one builds a queue that
|
|
14041
|
-
// drains in a burst at the END — a slower version of the bug being fixed.
|
|
14042
|
-
refreshView: makeSingleFlightRefresh(() => view.delta()),
|
|
14122
|
+
refreshView,
|
|
14043
14123
|
attention: (transferId, code, detail) => {
|
|
14044
14124
|
deps.emit(
|
|
14045
14125
|
"transfer:attention",
|
|
@@ -14091,6 +14171,7 @@ var PaymentsFacade = class {
|
|
|
14091
14171
|
isActiveOp: (id) => this.activeMoneyOps.has(id)
|
|
14092
14172
|
});
|
|
14093
14173
|
this.view = parts.view;
|
|
14174
|
+
this.wakeRefresh = parts.refreshView;
|
|
14094
14175
|
this.ledger = parts.ledger;
|
|
14095
14176
|
this.queue = parts.queue;
|
|
14096
14177
|
this.machine = parts.machine;
|
|
@@ -14120,6 +14201,7 @@ var PaymentsFacade = class {
|
|
|
14120
14201
|
now: () => this.nowMs()
|
|
14121
14202
|
});
|
|
14122
14203
|
}
|
|
14204
|
+
wakeRefresh;
|
|
14123
14205
|
prewarmed = new PrewarmCache();
|
|
14124
14206
|
view;
|
|
14125
14207
|
ledger;
|
|
@@ -14158,9 +14240,7 @@ var PaymentsFacade = class {
|
|
|
14158
14240
|
this.deps.session.subscribeStream("mailbox", () => {
|
|
14159
14241
|
this.trackTail(this.receiveLoop.drainOnce());
|
|
14160
14242
|
}),
|
|
14161
|
-
this.deps.session.subscribeStream("inventory",
|
|
14162
|
-
this.trackTail(this.view.delta());
|
|
14163
|
-
}),
|
|
14243
|
+
this.deps.session.subscribeStream("inventory", this.wakeRefresh),
|
|
14164
14244
|
this.deps.session.subscribeStream("payment_requests", () => {
|
|
14165
14245
|
this.trackTail(this.requests.drainIncoming());
|
|
14166
14246
|
})
|
|
@@ -14424,7 +14504,7 @@ var PaymentsFacade = class {
|
|
|
14424
14504
|
...spend.plan.direct,
|
|
14425
14505
|
...spend.plan.split !== void 0 ? [spend.plan.split.tokenId] : []
|
|
14426
14506
|
];
|
|
14427
|
-
|
|
14507
|
+
this.view.markInFlightMany(sourceIds);
|
|
14428
14508
|
if (spend.plan.split !== void 0) {
|
|
14429
14509
|
this.queue.declareExpectedChange(transferId, coinId, spend.plan.split.remainderAmount);
|
|
14430
14510
|
}
|
|
@@ -14710,6 +14790,10 @@ var WalletApiHttpError = class extends Error {
|
|
|
14710
14790
|
if (retryAfter !== void 0) this.retryAfter = retryAfter;
|
|
14711
14791
|
}
|
|
14712
14792
|
};
|
|
14793
|
+
var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([0, 408, 425, 429, 500, 502, 503, 504]);
|
|
14794
|
+
function isRetryableStatus(err) {
|
|
14795
|
+
return err instanceof WalletApiHttpError && RETRYABLE_STATUSES.has(err.status);
|
|
14796
|
+
}
|
|
14713
14797
|
function isWalletApiHttpError(err, status) {
|
|
14714
14798
|
return err instanceof WalletApiHttpError && (status === void 0 || err.status === status);
|
|
14715
14799
|
}
|
|
@@ -15422,6 +15506,23 @@ function computeDeliveryId(tokenIdHex, stateHashHex) {
|
|
|
15422
15506
|
return bytesToHex3(sha256(joined));
|
|
15423
15507
|
}
|
|
15424
15508
|
var DELIVERY_SEEN_KEY = "delivery:seen";
|
|
15509
|
+
var ACK_CHUNK = 50;
|
|
15510
|
+
function chunked(items, size) {
|
|
15511
|
+
const out = [];
|
|
15512
|
+
for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));
|
|
15513
|
+
return out;
|
|
15514
|
+
}
|
|
15515
|
+
var RetryableAckFailure = class extends Error {
|
|
15516
|
+
retryable = true;
|
|
15517
|
+
constructor(cause) {
|
|
15518
|
+
super(`mailbox ack batch could not be attempted: ${String(cause)}`);
|
|
15519
|
+
this.name = "RetryableAckFailure";
|
|
15520
|
+
this.cause = cause;
|
|
15521
|
+
}
|
|
15522
|
+
};
|
|
15523
|
+
function asRetryable(err) {
|
|
15524
|
+
return isRetryableStatus(err) ? new RetryableAckFailure(err) : err;
|
|
15525
|
+
}
|
|
15425
15526
|
var WalletApiDeliveryPort = class {
|
|
15426
15527
|
custody;
|
|
15427
15528
|
client;
|
|
@@ -15587,7 +15688,53 @@ var WalletApiDeliveryPort = class {
|
|
|
15587
15688
|
} else {
|
|
15588
15689
|
await this.client.reject([deliveryId], reason);
|
|
15589
15690
|
}
|
|
15590
|
-
await this.
|
|
15691
|
+
await this.addSeenAll([deliveryId]);
|
|
15692
|
+
}
|
|
15693
|
+
/**
|
|
15694
|
+
* §5.7/#757 batched settle. Claims go out grouped, rejects grouped by reason
|
|
15695
|
+
* (the endpoint takes one reason per call), and the seen-set is written ONCE
|
|
15696
|
+
* after every HTTP call has succeeded — never on a throw, so an aborted batch
|
|
15697
|
+
* re-lists rather than being silently forgotten.
|
|
15698
|
+
*/
|
|
15699
|
+
async ackBatch(acks) {
|
|
15700
|
+
const settled = [];
|
|
15701
|
+
const outcomes = [];
|
|
15702
|
+
try {
|
|
15703
|
+
await this.settleClaims(acks, settled, outcomes);
|
|
15704
|
+
await this.settleRejects(acks, settled);
|
|
15705
|
+
} catch (err) {
|
|
15706
|
+
throw asRetryable(err);
|
|
15707
|
+
}
|
|
15708
|
+
await this.addSeenAll(settled);
|
|
15709
|
+
return [...outcomes, ...settled.map((deliveryId) => ({ deliveryId, status: "settled" }))];
|
|
15710
|
+
}
|
|
15711
|
+
async settleClaims(acks, settled, outcomes) {
|
|
15712
|
+
const ids = acks.filter((a) => a.disposition === "claimed").map((a) => a.deliveryId);
|
|
15713
|
+
for (const chunk of chunked(ids, ACK_CHUNK)) {
|
|
15714
|
+
const result = await this.client.claim(chunk, this.custody === "inventory");
|
|
15715
|
+
settled.push(...result.claimed);
|
|
15716
|
+
settled.push(...result.alreadyClaimed.map((e) => e.entryId));
|
|
15717
|
+
for (const f of result.failed) {
|
|
15718
|
+
if (f.code === "CONFLICT") outcomes.push({ deliveryId: f.entryId, status: "conflict" });
|
|
15719
|
+
else throw new MailboxClaimFailedError(f.entryId, f.code);
|
|
15720
|
+
}
|
|
15721
|
+
}
|
|
15722
|
+
}
|
|
15723
|
+
async settleRejects(acks, settled) {
|
|
15724
|
+
const byReason = /* @__PURE__ */ new Map();
|
|
15725
|
+
for (const a of acks) {
|
|
15726
|
+
if (a.disposition !== "rejected") continue;
|
|
15727
|
+
const key = a.reason ?? "";
|
|
15728
|
+
const group = byReason.get(key) ?? [];
|
|
15729
|
+
group.push(a.deliveryId);
|
|
15730
|
+
byReason.set(key, group);
|
|
15731
|
+
}
|
|
15732
|
+
for (const [reason, group] of byReason) {
|
|
15733
|
+
for (const chunk of chunked(group, ACK_CHUNK)) {
|
|
15734
|
+
await this.client.reject(chunk, reason === "" ? void 0 : reason);
|
|
15735
|
+
settled.push(...chunk);
|
|
15736
|
+
}
|
|
15737
|
+
}
|
|
15591
15738
|
}
|
|
15592
15739
|
onWake(cb) {
|
|
15593
15740
|
if (this.wake === null) return () => void 0;
|
|
@@ -15598,10 +15745,12 @@ var WalletApiDeliveryPort = class {
|
|
|
15598
15745
|
const raw = await this.kv.get(DELIVERY_SEEN_KEY);
|
|
15599
15746
|
return new Set(raw ?? []);
|
|
15600
15747
|
}
|
|
15601
|
-
|
|
15748
|
+
/** SerialChain settles-not-fulfills: a rejected write never bricks later acks. */
|
|
15749
|
+
addSeenAll(deliveryIds) {
|
|
15750
|
+
if (deliveryIds.length === 0) return Promise.resolve();
|
|
15602
15751
|
return this.seenChain.enqueue(async () => {
|
|
15603
15752
|
const seen = await this.readSeen();
|
|
15604
|
-
seen.add(
|
|
15753
|
+
for (const id of deliveryIds) seen.add(id);
|
|
15605
15754
|
await this.kv.set(DELIVERY_SEEN_KEY, [...seen]);
|
|
15606
15755
|
});
|
|
15607
15756
|
}
|