@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.cjs
CHANGED
|
@@ -11361,6 +11361,26 @@ var SerialChain = class {
|
|
|
11361
11361
|
return run;
|
|
11362
11362
|
}
|
|
11363
11363
|
};
|
|
11364
|
+
function coalesced(run) {
|
|
11365
|
+
let inFlight = false;
|
|
11366
|
+
let queued = false;
|
|
11367
|
+
const start = () => {
|
|
11368
|
+
inFlight = true;
|
|
11369
|
+
void run().catch(() => void 0).finally(() => {
|
|
11370
|
+
inFlight = false;
|
|
11371
|
+
if (!queued) return;
|
|
11372
|
+
queued = false;
|
|
11373
|
+
start();
|
|
11374
|
+
});
|
|
11375
|
+
};
|
|
11376
|
+
return () => {
|
|
11377
|
+
if (inFlight) {
|
|
11378
|
+
queued = true;
|
|
11379
|
+
return;
|
|
11380
|
+
}
|
|
11381
|
+
start();
|
|
11382
|
+
};
|
|
11383
|
+
}
|
|
11364
11384
|
|
|
11365
11385
|
// modules/payments-v2/stores.ts
|
|
11366
11386
|
function createScopedKV(storage, network, chainPubkey) {
|
|
@@ -12808,6 +12828,16 @@ var InventoryView = class {
|
|
|
12808
12828
|
this.inFlight.add(tokenId);
|
|
12809
12829
|
this.deps.emit("inventory:updated");
|
|
12810
12830
|
}
|
|
12831
|
+
/** Reserve a whole plan as ONE change — per token emitted 54 events per send. */
|
|
12832
|
+
markInFlightMany(tokenIds) {
|
|
12833
|
+
let changed = false;
|
|
12834
|
+
for (const tokenId of tokenIds) {
|
|
12835
|
+
if (this.inFlight.has(tokenId)) continue;
|
|
12836
|
+
this.inFlight.add(tokenId);
|
|
12837
|
+
changed = true;
|
|
12838
|
+
}
|
|
12839
|
+
if (changed) this.deps.emit("inventory:updated");
|
|
12840
|
+
}
|
|
12811
12841
|
release(tokenId) {
|
|
12812
12842
|
if (this.inFlight.delete(tokenId)) this.deps.emit("inventory:updated");
|
|
12813
12843
|
}
|
|
@@ -13032,6 +13062,11 @@ var InventoryView = class {
|
|
|
13032
13062
|
}
|
|
13033
13063
|
};
|
|
13034
13064
|
|
|
13065
|
+
// modules/payments-v2/ports.ts
|
|
13066
|
+
function isRetryableAckError(err) {
|
|
13067
|
+
return err !== null && typeof err === "object" && err.retryable === true;
|
|
13068
|
+
}
|
|
13069
|
+
|
|
13035
13070
|
// modules/payments-v2/receive/Receive.ts
|
|
13036
13071
|
var ACK_BATCH_SIZE = 200;
|
|
13037
13072
|
var REFRESH_INTERVAL_MS = 2500;
|
|
@@ -13113,6 +13148,7 @@ var Receive = class {
|
|
|
13113
13148
|
}
|
|
13114
13149
|
} catch (err) {
|
|
13115
13150
|
logger.warn("PaymentsV2", "receive drain interrupted \u2014 unacked entries retry next drain:", err);
|
|
13151
|
+
if (isRetryableAckError(err)) return stored;
|
|
13116
13152
|
await flushAcks(deps, pending, pageEpoch).catch((flushErr) => {
|
|
13117
13153
|
logger.warn("PaymentsV2", "receive ack flush failed \u2014 cursor holds at the acked prefix:", flushErr);
|
|
13118
13154
|
});
|
|
@@ -13187,14 +13223,18 @@ async function announce(deps, entry, record) {
|
|
|
13187
13223
|
};
|
|
13188
13224
|
}
|
|
13189
13225
|
async function flushAcks(deps, pending, epochOf) {
|
|
13226
|
+
if (pending.length === 0) return;
|
|
13190
13227
|
let lastAcked = null;
|
|
13191
13228
|
try {
|
|
13192
|
-
|
|
13193
|
-
|
|
13194
|
-
|
|
13195
|
-
lastAcked =
|
|
13196
|
-
|
|
13229
|
+
const settled = await settleAcks(deps, pending);
|
|
13230
|
+
let i = 0;
|
|
13231
|
+
while (i < pending.length && settled.has(pending[i].deliveryId)) {
|
|
13232
|
+
lastAcked = pending[i].cursor;
|
|
13233
|
+
i += 1;
|
|
13197
13234
|
}
|
|
13235
|
+
const stuck = pending.filter((p) => !settled.has(p.deliveryId));
|
|
13236
|
+
pending.length = 0;
|
|
13237
|
+
pending.push(...stuck);
|
|
13198
13238
|
} finally {
|
|
13199
13239
|
if (lastAcked !== null) {
|
|
13200
13240
|
const record = { cursor: lastAcked, syncEpoch: epochOf() };
|
|
@@ -13202,14 +13242,65 @@ async function flushAcks(deps, pending, epochOf) {
|
|
|
13202
13242
|
}
|
|
13203
13243
|
}
|
|
13204
13244
|
}
|
|
13245
|
+
async function settleAcks(deps, pending) {
|
|
13246
|
+
const batch = deps.delivery.ackBatch?.bind(deps.delivery);
|
|
13247
|
+
if (batch === void 0) return settleOneByOne(deps, pending);
|
|
13248
|
+
let outcomes;
|
|
13249
|
+
try {
|
|
13250
|
+
outcomes = await batch(pending.map(toAckRequest));
|
|
13251
|
+
} catch (err) {
|
|
13252
|
+
if (isRetryableAckError(err)) throw err;
|
|
13253
|
+
logger.warn("PaymentsV2", "batched mailbox ack failed \u2014 settling one at a time:", err);
|
|
13254
|
+
return settleOneByOne(deps, pending);
|
|
13255
|
+
}
|
|
13256
|
+
const settled = new Set(outcomes.filter((o) => o.status === "settled").map((o) => o.deliveryId));
|
|
13257
|
+
const conflicts = outcomes.filter((o) => o.status === "conflict").map((o) => o.deliveryId);
|
|
13258
|
+
if (conflicts.length > 0) await resolveConflicts(deps, pending, conflicts, settled);
|
|
13259
|
+
return settled;
|
|
13260
|
+
}
|
|
13261
|
+
async function resolveConflicts(deps, pending, conflicts, settled) {
|
|
13262
|
+
for (const deliveryId of conflicts) {
|
|
13263
|
+
const entry = pending.find((p) => p.deliveryId === deliveryId);
|
|
13264
|
+
try {
|
|
13265
|
+
await rejectStaleClaim(deps, deliveryId, entry?.transferId);
|
|
13266
|
+
} catch (err) {
|
|
13267
|
+
logger.warn("PaymentsV2", `stale-reject failed for ${deliveryId} \u2014 cursor holds here:`, err);
|
|
13268
|
+
continue;
|
|
13269
|
+
}
|
|
13270
|
+
settled.add(deliveryId);
|
|
13271
|
+
}
|
|
13272
|
+
}
|
|
13273
|
+
async function rejectStaleClaim(deps, deliveryId, transferId) {
|
|
13274
|
+
logger.warn("PaymentsV2", `mailbox claim CONFLICT for ${deliveryId} \u2014 rejected('other') as stale`);
|
|
13275
|
+
await deps.delivery.ack(deliveryId, "rejected", "other");
|
|
13276
|
+
deps.attention(transferId ?? "", ATTENTION_CLAIM_CONFLICT, deliveryId);
|
|
13277
|
+
}
|
|
13278
|
+
async function settleOneByOne(deps, pending) {
|
|
13279
|
+
const settled = /* @__PURE__ */ new Set();
|
|
13280
|
+
for (const ack of pending) {
|
|
13281
|
+
try {
|
|
13282
|
+
await ackOne(deps, ack);
|
|
13283
|
+
} catch (err) {
|
|
13284
|
+
logger.warn("PaymentsV2", `mailbox ack failed for ${ack.deliveryId} \u2014 cursor holds here:`, err);
|
|
13285
|
+
break;
|
|
13286
|
+
}
|
|
13287
|
+
settled.add(ack.deliveryId);
|
|
13288
|
+
}
|
|
13289
|
+
return settled;
|
|
13290
|
+
}
|
|
13291
|
+
function toAckRequest(ack) {
|
|
13292
|
+
return {
|
|
13293
|
+
deliveryId: ack.deliveryId,
|
|
13294
|
+
disposition: ack.disposition,
|
|
13295
|
+
...ack.reason !== void 0 ? { reason: ack.reason } : {}
|
|
13296
|
+
};
|
|
13297
|
+
}
|
|
13205
13298
|
async function ackOne(deps, ack) {
|
|
13206
13299
|
try {
|
|
13207
13300
|
await deps.delivery.ack(ack.deliveryId, ack.disposition, ack.reason);
|
|
13208
13301
|
} catch (err) {
|
|
13209
13302
|
if (ack.disposition !== "claimed" || !isClaimConflict(err)) throw err;
|
|
13210
|
-
|
|
13211
|
-
await deps.delivery.ack(ack.deliveryId, "rejected", "other");
|
|
13212
|
-
deps.attention(ack.transferId ?? "", ATTENTION_CLAIM_CONFLICT, ack.deliveryId);
|
|
13303
|
+
await rejectStaleClaim(deps, ack.deliveryId, ack.transferId);
|
|
13213
13304
|
}
|
|
13214
13305
|
}
|
|
13215
13306
|
function claimAck(entry) {
|
|
@@ -13963,16 +14054,6 @@ function parseAmount(raw) {
|
|
|
13963
14054
|
function supportsDeterministicMint(engine) {
|
|
13964
14055
|
return engine.deterministicMint === true;
|
|
13965
14056
|
}
|
|
13966
|
-
function makeSingleFlightRefresh(refresh) {
|
|
13967
|
-
let inFlight = false;
|
|
13968
|
-
return () => {
|
|
13969
|
-
if (inFlight) return;
|
|
13970
|
-
inFlight = true;
|
|
13971
|
-
void refresh().catch(() => void 0).finally(() => {
|
|
13972
|
-
inFlight = false;
|
|
13973
|
-
});
|
|
13974
|
-
};
|
|
13975
|
-
}
|
|
13976
14057
|
function composeFacadeParts(deps, hooks) {
|
|
13977
14058
|
const ownPubkeyBytes = hexToBytes4(deps.ownPubkey);
|
|
13978
14059
|
const ledger = new ReservationLedger();
|
|
@@ -14002,9 +14083,11 @@ function composeFacadeParts(deps, hooks) {
|
|
|
14002
14083
|
const machineDeps = buildMachineDeps(deps, hooks, historyStore);
|
|
14003
14084
|
const machineStores = createMachineStores(deps.kv);
|
|
14004
14085
|
const heldStates = /* @__PURE__ */ new Map();
|
|
14086
|
+
const refreshView = coalesced(() => view.delta());
|
|
14005
14087
|
return {
|
|
14006
14088
|
ownPubkeyBytes,
|
|
14007
14089
|
view,
|
|
14090
|
+
refreshView,
|
|
14008
14091
|
ledger,
|
|
14009
14092
|
pins: buildPins(deps, hooks, { ledger, view, machineStores, machineDeps }),
|
|
14010
14093
|
queue,
|
|
@@ -14013,7 +14096,7 @@ function composeFacadeParts(deps, hooks) {
|
|
|
14013
14096
|
machineDeps,
|
|
14014
14097
|
machine: new TransferMachine(machineDeps),
|
|
14015
14098
|
heldStates,
|
|
14016
|
-
receiveLoop: buildReceive(deps, hooks, historyStore, heldStates,
|
|
14099
|
+
receiveLoop: buildReceive(deps, hooks, historyStore, heldStates, refreshView),
|
|
14017
14100
|
requests: buildRequests(deps, hooks),
|
|
14018
14101
|
restoreDeps: buildRestoreDeps(deps, machineDeps, machineStores, view)
|
|
14019
14102
|
};
|
|
@@ -14080,7 +14163,7 @@ function buildMachineDeps(deps, hooks, historyStore) {
|
|
|
14080
14163
|
}
|
|
14081
14164
|
};
|
|
14082
14165
|
}
|
|
14083
|
-
function buildReceive(deps, hooks, historyStore, heldStates,
|
|
14166
|
+
function buildReceive(deps, hooks, historyStore, heldStates, refreshView) {
|
|
14084
14167
|
return new Receive({
|
|
14085
14168
|
delivery: deps.deliveryPort,
|
|
14086
14169
|
engine: () => hooks.engine(),
|
|
@@ -14094,10 +14177,7 @@ function buildReceive(deps, hooks, historyStore, heldStates, view) {
|
|
|
14094
14177
|
registry: deps.registry,
|
|
14095
14178
|
recordReceived: (record) => recordReceived(historyStore, record),
|
|
14096
14179
|
emit: (event, transfer) => deps.emit(event, transfer),
|
|
14097
|
-
|
|
14098
|
-
// firing per throttle tick regardless of the last one builds a queue that
|
|
14099
|
-
// drains in a burst at the END — a slower version of the bug being fixed.
|
|
14100
|
-
refreshView: makeSingleFlightRefresh(() => view.delta()),
|
|
14180
|
+
refreshView,
|
|
14101
14181
|
attention: (transferId, code, detail) => {
|
|
14102
14182
|
deps.emit(
|
|
14103
14183
|
"transfer:attention",
|
|
@@ -14149,6 +14229,7 @@ var PaymentsFacade = class {
|
|
|
14149
14229
|
isActiveOp: (id) => this.activeMoneyOps.has(id)
|
|
14150
14230
|
});
|
|
14151
14231
|
this.view = parts.view;
|
|
14232
|
+
this.wakeRefresh = parts.refreshView;
|
|
14152
14233
|
this.ledger = parts.ledger;
|
|
14153
14234
|
this.queue = parts.queue;
|
|
14154
14235
|
this.machine = parts.machine;
|
|
@@ -14178,6 +14259,7 @@ var PaymentsFacade = class {
|
|
|
14178
14259
|
now: () => this.nowMs()
|
|
14179
14260
|
});
|
|
14180
14261
|
}
|
|
14262
|
+
wakeRefresh;
|
|
14181
14263
|
prewarmed = new PrewarmCache();
|
|
14182
14264
|
view;
|
|
14183
14265
|
ledger;
|
|
@@ -14216,9 +14298,7 @@ var PaymentsFacade = class {
|
|
|
14216
14298
|
this.deps.session.subscribeStream("mailbox", () => {
|
|
14217
14299
|
this.trackTail(this.receiveLoop.drainOnce());
|
|
14218
14300
|
}),
|
|
14219
|
-
this.deps.session.subscribeStream("inventory",
|
|
14220
|
-
this.trackTail(this.view.delta());
|
|
14221
|
-
}),
|
|
14301
|
+
this.deps.session.subscribeStream("inventory", this.wakeRefresh),
|
|
14222
14302
|
this.deps.session.subscribeStream("payment_requests", () => {
|
|
14223
14303
|
this.trackTail(this.requests.drainIncoming());
|
|
14224
14304
|
})
|
|
@@ -14482,7 +14562,7 @@ var PaymentsFacade = class {
|
|
|
14482
14562
|
...spend.plan.direct,
|
|
14483
14563
|
...spend.plan.split !== void 0 ? [spend.plan.split.tokenId] : []
|
|
14484
14564
|
];
|
|
14485
|
-
|
|
14565
|
+
this.view.markInFlightMany(sourceIds);
|
|
14486
14566
|
if (spend.plan.split !== void 0) {
|
|
14487
14567
|
this.queue.declareExpectedChange(transferId, coinId, spend.plan.split.remainderAmount);
|
|
14488
14568
|
}
|
|
@@ -14768,6 +14848,10 @@ var WalletApiHttpError = class extends Error {
|
|
|
14768
14848
|
if (retryAfter !== void 0) this.retryAfter = retryAfter;
|
|
14769
14849
|
}
|
|
14770
14850
|
};
|
|
14851
|
+
var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([0, 408, 425, 429, 500, 502, 503, 504]);
|
|
14852
|
+
function isRetryableStatus(err) {
|
|
14853
|
+
return err instanceof WalletApiHttpError && RETRYABLE_STATUSES.has(err.status);
|
|
14854
|
+
}
|
|
14771
14855
|
function isWalletApiHttpError(err, status) {
|
|
14772
14856
|
return err instanceof WalletApiHttpError && (status === void 0 || err.status === status);
|
|
14773
14857
|
}
|
|
@@ -15480,6 +15564,23 @@ function computeDeliveryId(tokenIdHex, stateHashHex) {
|
|
|
15480
15564
|
return bytesToHex3(sha256(joined));
|
|
15481
15565
|
}
|
|
15482
15566
|
var DELIVERY_SEEN_KEY = "delivery:seen";
|
|
15567
|
+
var ACK_CHUNK = 50;
|
|
15568
|
+
function chunked(items, size) {
|
|
15569
|
+
const out = [];
|
|
15570
|
+
for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));
|
|
15571
|
+
return out;
|
|
15572
|
+
}
|
|
15573
|
+
var RetryableAckFailure = class extends Error {
|
|
15574
|
+
retryable = true;
|
|
15575
|
+
constructor(cause) {
|
|
15576
|
+
super(`mailbox ack batch could not be attempted: ${String(cause)}`);
|
|
15577
|
+
this.name = "RetryableAckFailure";
|
|
15578
|
+
this.cause = cause;
|
|
15579
|
+
}
|
|
15580
|
+
};
|
|
15581
|
+
function asRetryable(err) {
|
|
15582
|
+
return isRetryableStatus(err) ? new RetryableAckFailure(err) : err;
|
|
15583
|
+
}
|
|
15483
15584
|
var WalletApiDeliveryPort = class {
|
|
15484
15585
|
custody;
|
|
15485
15586
|
client;
|
|
@@ -15645,7 +15746,53 @@ var WalletApiDeliveryPort = class {
|
|
|
15645
15746
|
} else {
|
|
15646
15747
|
await this.client.reject([deliveryId], reason);
|
|
15647
15748
|
}
|
|
15648
|
-
await this.
|
|
15749
|
+
await this.addSeenAll([deliveryId]);
|
|
15750
|
+
}
|
|
15751
|
+
/**
|
|
15752
|
+
* §5.7/#757 batched settle. Claims go out grouped, rejects grouped by reason
|
|
15753
|
+
* (the endpoint takes one reason per call), and the seen-set is written ONCE
|
|
15754
|
+
* after every HTTP call has succeeded — never on a throw, so an aborted batch
|
|
15755
|
+
* re-lists rather than being silently forgotten.
|
|
15756
|
+
*/
|
|
15757
|
+
async ackBatch(acks) {
|
|
15758
|
+
const settled = [];
|
|
15759
|
+
const outcomes = [];
|
|
15760
|
+
try {
|
|
15761
|
+
await this.settleClaims(acks, settled, outcomes);
|
|
15762
|
+
await this.settleRejects(acks, settled);
|
|
15763
|
+
} catch (err) {
|
|
15764
|
+
throw asRetryable(err);
|
|
15765
|
+
}
|
|
15766
|
+
await this.addSeenAll(settled);
|
|
15767
|
+
return [...outcomes, ...settled.map((deliveryId) => ({ deliveryId, status: "settled" }))];
|
|
15768
|
+
}
|
|
15769
|
+
async settleClaims(acks, settled, outcomes) {
|
|
15770
|
+
const ids = acks.filter((a) => a.disposition === "claimed").map((a) => a.deliveryId);
|
|
15771
|
+
for (const chunk of chunked(ids, ACK_CHUNK)) {
|
|
15772
|
+
const result = await this.client.claim(chunk, this.custody === "inventory");
|
|
15773
|
+
settled.push(...result.claimed);
|
|
15774
|
+
settled.push(...result.alreadyClaimed.map((e) => e.entryId));
|
|
15775
|
+
for (const f of result.failed) {
|
|
15776
|
+
if (f.code === "CONFLICT") outcomes.push({ deliveryId: f.entryId, status: "conflict" });
|
|
15777
|
+
else throw new MailboxClaimFailedError(f.entryId, f.code);
|
|
15778
|
+
}
|
|
15779
|
+
}
|
|
15780
|
+
}
|
|
15781
|
+
async settleRejects(acks, settled) {
|
|
15782
|
+
const byReason = /* @__PURE__ */ new Map();
|
|
15783
|
+
for (const a of acks) {
|
|
15784
|
+
if (a.disposition !== "rejected") continue;
|
|
15785
|
+
const key = a.reason ?? "";
|
|
15786
|
+
const group = byReason.get(key) ?? [];
|
|
15787
|
+
group.push(a.deliveryId);
|
|
15788
|
+
byReason.set(key, group);
|
|
15789
|
+
}
|
|
15790
|
+
for (const [reason, group] of byReason) {
|
|
15791
|
+
for (const chunk of chunked(group, ACK_CHUNK)) {
|
|
15792
|
+
await this.client.reject(chunk, reason === "" ? void 0 : reason);
|
|
15793
|
+
settled.push(...chunk);
|
|
15794
|
+
}
|
|
15795
|
+
}
|
|
15649
15796
|
}
|
|
15650
15797
|
onWake(cb) {
|
|
15651
15798
|
if (this.wake === null) return () => void 0;
|
|
@@ -15656,10 +15803,12 @@ var WalletApiDeliveryPort = class {
|
|
|
15656
15803
|
const raw = await this.kv.get(DELIVERY_SEEN_KEY);
|
|
15657
15804
|
return new Set(raw ?? []);
|
|
15658
15805
|
}
|
|
15659
|
-
|
|
15806
|
+
/** SerialChain settles-not-fulfills: a rejected write never bricks later acks. */
|
|
15807
|
+
addSeenAll(deliveryIds) {
|
|
15808
|
+
if (deliveryIds.length === 0) return Promise.resolve();
|
|
15660
15809
|
return this.seenChain.enqueue(async () => {
|
|
15661
15810
|
const seen = await this.readSeen();
|
|
15662
|
-
seen.add(
|
|
15811
|
+
for (const id of deliveryIds) seen.add(id);
|
|
15663
15812
|
await this.kv.set(DELIVERY_SEEN_KEY, [...seen]);
|
|
15664
15813
|
});
|
|
15665
15814
|
}
|