@unicitylabs/sphere-sdk 0.14.11-dev.6 → 0.14.11-dev.8
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 +201 -46
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.js +201 -46
- 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 +73 -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 +73 -3
- package/dist/impl/wallet-api-v2/index.js.map +1 -1
- package/dist/index.cjs +201 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +201 -46
- package/dist/index.js.map +1 -1
- package/dist/modules/payments-v2/index.cjs +128 -43
- package/dist/modules/payments-v2/index.cjs.map +1 -1
- package/dist/modules/payments-v2/index.d.cts +14 -0
- package/dist/modules/payments-v2/index.d.ts +14 -0
- package/dist/modules/payments-v2/index.js +128 -43
- package/dist/modules/payments-v2/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -581,6 +581,33 @@ var SerialChain = class {
|
|
|
581
581
|
return run;
|
|
582
582
|
}
|
|
583
583
|
};
|
|
584
|
+
function coalesced(run, observe) {
|
|
585
|
+
let inFlight = false;
|
|
586
|
+
let queued = false;
|
|
587
|
+
const start = () => {
|
|
588
|
+
inFlight = true;
|
|
589
|
+
let running;
|
|
590
|
+
try {
|
|
591
|
+
running = run();
|
|
592
|
+
} catch {
|
|
593
|
+
running = Promise.resolve();
|
|
594
|
+
}
|
|
595
|
+
observe?.(running);
|
|
596
|
+
void running.catch(() => void 0).finally(() => {
|
|
597
|
+
inFlight = false;
|
|
598
|
+
if (!queued) return;
|
|
599
|
+
queued = false;
|
|
600
|
+
start();
|
|
601
|
+
});
|
|
602
|
+
};
|
|
603
|
+
return () => {
|
|
604
|
+
if (inFlight) {
|
|
605
|
+
queued = true;
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
start();
|
|
609
|
+
};
|
|
610
|
+
}
|
|
584
611
|
|
|
585
612
|
// modules/payments-v2/machine/journal.ts
|
|
586
613
|
var MAX_DELIVERY_ATTEMPTS = 6;
|
|
@@ -2886,6 +2913,16 @@ var InventoryView = class {
|
|
|
2886
2913
|
this.inFlight.add(tokenId);
|
|
2887
2914
|
this.deps.emit("inventory:updated");
|
|
2888
2915
|
}
|
|
2916
|
+
/** Reserve a whole plan as ONE change — per token emitted 54 events per send. */
|
|
2917
|
+
markInFlightMany(tokenIds) {
|
|
2918
|
+
let changed = false;
|
|
2919
|
+
for (const tokenId of tokenIds) {
|
|
2920
|
+
if (this.inFlight.has(tokenId)) continue;
|
|
2921
|
+
this.inFlight.add(tokenId);
|
|
2922
|
+
changed = true;
|
|
2923
|
+
}
|
|
2924
|
+
if (changed) this.deps.emit("inventory:updated");
|
|
2925
|
+
}
|
|
2889
2926
|
release(tokenId) {
|
|
2890
2927
|
if (this.inFlight.delete(tokenId)) this.deps.emit("inventory:updated");
|
|
2891
2928
|
}
|
|
@@ -3202,6 +3239,11 @@ var logger = {
|
|
|
3202
3239
|
}
|
|
3203
3240
|
};
|
|
3204
3241
|
|
|
3242
|
+
// modules/payments-v2/ports.ts
|
|
3243
|
+
function isRetryableAckError(err) {
|
|
3244
|
+
return err !== null && typeof err === "object" && err.retryable === true;
|
|
3245
|
+
}
|
|
3246
|
+
|
|
3205
3247
|
// modules/payments-v2/receive/Receive.ts
|
|
3206
3248
|
var ACK_BATCH_SIZE = 200;
|
|
3207
3249
|
var REFRESH_INTERVAL_MS = 2500;
|
|
@@ -3258,6 +3300,17 @@ var Receive = class {
|
|
|
3258
3300
|
await flushAcks(deps, pending, epochOf);
|
|
3259
3301
|
deps.refreshView();
|
|
3260
3302
|
}
|
|
3303
|
+
/** One listing pass: process each entry, flushing and refreshing as it goes. */
|
|
3304
|
+
async drainPages(ctx, basis) {
|
|
3305
|
+
const { deps, engine, pending, stored, pageEpoch } = ctx;
|
|
3306
|
+
for await (const entry of deps.delivery.incoming(basis === null ? void 0 : String(basis.cursor))) {
|
|
3307
|
+
const storedBefore = stored.length;
|
|
3308
|
+
await this.processEntry(deps, engine, entry, pending, stored);
|
|
3309
|
+
if (pending.length >= ACK_BATCH_SIZE) await flushAcks(deps, pending, pageEpoch);
|
|
3310
|
+
if (stored.length > storedBefore) await this.maybeRefresh(deps, pending, pageEpoch);
|
|
3311
|
+
}
|
|
3312
|
+
await flushAcks(deps, pending, pageEpoch);
|
|
3313
|
+
}
|
|
3261
3314
|
async doDrain() {
|
|
3262
3315
|
const deps = this.deps;
|
|
3263
3316
|
this.lastRefreshAt = deps.now?.() ?? Date.now();
|
|
@@ -3269,13 +3322,7 @@ var Receive = class {
|
|
|
3269
3322
|
const record = await deps.kv.get(STORE_KEYS.streamCursor("mailbox"));
|
|
3270
3323
|
let basis = record !== null && record.syncEpoch === deps.syncEpoch() ? record : null;
|
|
3271
3324
|
for (let pass = 0; pass < 2; pass++) {
|
|
3272
|
-
|
|
3273
|
-
const storedBefore = stored.length;
|
|
3274
|
-
await this.processEntry(deps, engine, entry, pending, stored);
|
|
3275
|
-
if (pending.length >= ACK_BATCH_SIZE) await flushAcks(deps, pending, pageEpoch);
|
|
3276
|
-
if (stored.length > storedBefore) await this.maybeRefresh(deps, pending, pageEpoch);
|
|
3277
|
-
}
|
|
3278
|
-
await flushAcks(deps, pending, pageEpoch);
|
|
3325
|
+
await this.drainPages({ deps, engine, pending, stored, pageEpoch }, basis);
|
|
3279
3326
|
const served = deps.delivery.incomingEpoch();
|
|
3280
3327
|
if (basis === null || served === null || served === basis.syncEpoch) break;
|
|
3281
3328
|
await deps.kv.remove(STORE_KEYS.streamCursor("mailbox"));
|
|
@@ -3283,10 +3330,12 @@ var Receive = class {
|
|
|
3283
3330
|
}
|
|
3284
3331
|
} catch (err) {
|
|
3285
3332
|
logger.warn("PaymentsV2", "receive drain interrupted \u2014 unacked entries retry next drain:", err);
|
|
3333
|
+
if (isRetryableAckError(err)) return stored;
|
|
3286
3334
|
await flushAcks(deps, pending, pageEpoch).catch((flushErr) => {
|
|
3287
3335
|
logger.warn("PaymentsV2", "receive ack flush failed \u2014 cursor holds at the acked prefix:", flushErr);
|
|
3288
3336
|
});
|
|
3289
3337
|
}
|
|
3338
|
+
if (stored.length > 0) deps.refreshView?.();
|
|
3290
3339
|
return stored;
|
|
3291
3340
|
}
|
|
3292
3341
|
async processEntry(deps, engine, entry, pending, stored) {
|
|
@@ -3357,14 +3406,18 @@ async function announce(deps, entry, record) {
|
|
|
3357
3406
|
};
|
|
3358
3407
|
}
|
|
3359
3408
|
async function flushAcks(deps, pending, epochOf) {
|
|
3409
|
+
if (pending.length === 0) return;
|
|
3360
3410
|
let lastAcked = null;
|
|
3361
3411
|
try {
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
lastAcked =
|
|
3366
|
-
|
|
3412
|
+
const settled = await settleAcks(deps, pending);
|
|
3413
|
+
let i = 0;
|
|
3414
|
+
while (i < pending.length && settled.has(pending[i].deliveryId)) {
|
|
3415
|
+
lastAcked = pending[i].cursor;
|
|
3416
|
+
i += 1;
|
|
3367
3417
|
}
|
|
3418
|
+
const stuck = pending.filter((p) => !settled.has(p.deliveryId));
|
|
3419
|
+
pending.length = 0;
|
|
3420
|
+
pending.push(...stuck);
|
|
3368
3421
|
} finally {
|
|
3369
3422
|
if (lastAcked !== null) {
|
|
3370
3423
|
const record = { cursor: lastAcked, syncEpoch: epochOf() };
|
|
@@ -3372,14 +3425,65 @@ async function flushAcks(deps, pending, epochOf) {
|
|
|
3372
3425
|
}
|
|
3373
3426
|
}
|
|
3374
3427
|
}
|
|
3428
|
+
async function settleAcks(deps, pending) {
|
|
3429
|
+
const batch = deps.delivery.ackBatch?.bind(deps.delivery);
|
|
3430
|
+
if (batch === void 0) return settleOneByOne(deps, pending);
|
|
3431
|
+
let outcomes;
|
|
3432
|
+
try {
|
|
3433
|
+
outcomes = await batch(pending.map(toAckRequest));
|
|
3434
|
+
} catch (err) {
|
|
3435
|
+
if (isRetryableAckError(err)) throw err;
|
|
3436
|
+
logger.warn("PaymentsV2", "batched mailbox ack failed \u2014 settling one at a time:", err);
|
|
3437
|
+
return settleOneByOne(deps, pending);
|
|
3438
|
+
}
|
|
3439
|
+
const settled = new Set(outcomes.filter((o) => o.status === "settled").map((o) => o.deliveryId));
|
|
3440
|
+
const conflicts = outcomes.filter((o) => o.status === "conflict").map((o) => o.deliveryId);
|
|
3441
|
+
if (conflicts.length > 0) await resolveConflicts(deps, pending, conflicts, settled);
|
|
3442
|
+
return settled;
|
|
3443
|
+
}
|
|
3444
|
+
async function resolveConflicts(deps, pending, conflicts, settled) {
|
|
3445
|
+
for (const deliveryId of conflicts) {
|
|
3446
|
+
const entry = pending.find((p) => p.deliveryId === deliveryId);
|
|
3447
|
+
try {
|
|
3448
|
+
await rejectStaleClaim(deps, deliveryId, entry?.transferId);
|
|
3449
|
+
} catch (err) {
|
|
3450
|
+
logger.warn("PaymentsV2", `stale-reject failed for ${deliveryId} \u2014 cursor holds here:`, err);
|
|
3451
|
+
continue;
|
|
3452
|
+
}
|
|
3453
|
+
settled.add(deliveryId);
|
|
3454
|
+
}
|
|
3455
|
+
}
|
|
3456
|
+
async function rejectStaleClaim(deps, deliveryId, transferId) {
|
|
3457
|
+
logger.warn("PaymentsV2", `mailbox claim CONFLICT for ${deliveryId} \u2014 rejected('other') as stale`);
|
|
3458
|
+
await deps.delivery.ack(deliveryId, "rejected", "other");
|
|
3459
|
+
deps.attention(transferId ?? "", ATTENTION_CLAIM_CONFLICT, deliveryId);
|
|
3460
|
+
}
|
|
3461
|
+
async function settleOneByOne(deps, pending) {
|
|
3462
|
+
const settled = /* @__PURE__ */ new Set();
|
|
3463
|
+
for (const ack of pending) {
|
|
3464
|
+
try {
|
|
3465
|
+
await ackOne(deps, ack);
|
|
3466
|
+
} catch (err) {
|
|
3467
|
+
logger.warn("PaymentsV2", `mailbox ack failed for ${ack.deliveryId} \u2014 cursor holds here:`, err);
|
|
3468
|
+
break;
|
|
3469
|
+
}
|
|
3470
|
+
settled.add(ack.deliveryId);
|
|
3471
|
+
}
|
|
3472
|
+
return settled;
|
|
3473
|
+
}
|
|
3474
|
+
function toAckRequest(ack) {
|
|
3475
|
+
return {
|
|
3476
|
+
deliveryId: ack.deliveryId,
|
|
3477
|
+
disposition: ack.disposition,
|
|
3478
|
+
...ack.reason !== void 0 ? { reason: ack.reason } : {}
|
|
3479
|
+
};
|
|
3480
|
+
}
|
|
3375
3481
|
async function ackOne(deps, ack) {
|
|
3376
3482
|
try {
|
|
3377
3483
|
await deps.delivery.ack(ack.deliveryId, ack.disposition, ack.reason);
|
|
3378
3484
|
} catch (err) {
|
|
3379
3485
|
if (ack.disposition !== "claimed" || !isClaimConflict(err)) throw err;
|
|
3380
|
-
|
|
3381
|
-
await deps.delivery.ack(ack.deliveryId, "rejected", "other");
|
|
3382
|
-
deps.attention(ack.transferId ?? "", ATTENTION_CLAIM_CONFLICT, ack.deliveryId);
|
|
3486
|
+
await rejectStaleClaim(deps, ack.deliveryId, ack.transferId);
|
|
3383
3487
|
}
|
|
3384
3488
|
}
|
|
3385
3489
|
function claimAck(entry) {
|
|
@@ -4133,26 +4237,6 @@ function parseAmount(raw) {
|
|
|
4133
4237
|
function supportsDeterministicMint(engine) {
|
|
4134
4238
|
return engine.deterministicMint === true;
|
|
4135
4239
|
}
|
|
4136
|
-
function makeCoalescedRefresh(refresh) {
|
|
4137
|
-
let inFlight = false;
|
|
4138
|
-
let queued = false;
|
|
4139
|
-
const run = () => {
|
|
4140
|
-
inFlight = true;
|
|
4141
|
-
void refresh().catch(() => void 0).finally(() => {
|
|
4142
|
-
inFlight = false;
|
|
4143
|
-
if (!queued) return;
|
|
4144
|
-
queued = false;
|
|
4145
|
-
run();
|
|
4146
|
-
});
|
|
4147
|
-
};
|
|
4148
|
-
return () => {
|
|
4149
|
-
if (inFlight) {
|
|
4150
|
-
queued = true;
|
|
4151
|
-
return;
|
|
4152
|
-
}
|
|
4153
|
-
run();
|
|
4154
|
-
};
|
|
4155
|
-
}
|
|
4156
4240
|
function composeFacadeParts(deps, hooks) {
|
|
4157
4241
|
const ownPubkeyBytes = hexToBytes(deps.ownPubkey);
|
|
4158
4242
|
const ledger = new ReservationLedger();
|
|
@@ -4182,9 +4266,11 @@ function composeFacadeParts(deps, hooks) {
|
|
|
4182
4266
|
const machineDeps = buildMachineDeps(deps, hooks, historyStore);
|
|
4183
4267
|
const machineStores = createMachineStores(deps.kv);
|
|
4184
4268
|
const heldStates = /* @__PURE__ */ new Map();
|
|
4269
|
+
const refreshView = coalesced(() => view.delta(), hooks.track);
|
|
4185
4270
|
return {
|
|
4186
4271
|
ownPubkeyBytes,
|
|
4187
4272
|
view,
|
|
4273
|
+
refreshView,
|
|
4188
4274
|
ledger,
|
|
4189
4275
|
pins: buildPins(deps, hooks, { ledger, view, machineStores, machineDeps }),
|
|
4190
4276
|
queue,
|
|
@@ -4193,7 +4279,7 @@ function composeFacadeParts(deps, hooks) {
|
|
|
4193
4279
|
machineDeps,
|
|
4194
4280
|
machine: new TransferMachine(machineDeps),
|
|
4195
4281
|
heldStates,
|
|
4196
|
-
receiveLoop: buildReceive(deps, hooks, historyStore, heldStates,
|
|
4282
|
+
receiveLoop: buildReceive(deps, hooks, historyStore, heldStates, refreshView),
|
|
4197
4283
|
requests: buildRequests(deps, hooks),
|
|
4198
4284
|
restoreDeps: buildRestoreDeps(deps, machineDeps, machineStores, view)
|
|
4199
4285
|
};
|
|
@@ -4260,7 +4346,7 @@ function buildMachineDeps(deps, hooks, historyStore) {
|
|
|
4260
4346
|
}
|
|
4261
4347
|
};
|
|
4262
4348
|
}
|
|
4263
|
-
function buildReceive(deps, hooks, historyStore, heldStates,
|
|
4349
|
+
function buildReceive(deps, hooks, historyStore, heldStates, refreshView) {
|
|
4264
4350
|
return new Receive({
|
|
4265
4351
|
delivery: deps.deliveryPort,
|
|
4266
4352
|
engine: () => hooks.engine(),
|
|
@@ -4274,8 +4360,7 @@ function buildReceive(deps, hooks, historyStore, heldStates, view) {
|
|
|
4274
4360
|
registry: deps.registry,
|
|
4275
4361
|
recordReceived: (record) => recordReceived(historyStore, record),
|
|
4276
4362
|
emit: (event, transfer) => deps.emit(event, transfer),
|
|
4277
|
-
|
|
4278
|
-
refreshView: makeCoalescedRefresh(() => view.delta()),
|
|
4363
|
+
refreshView,
|
|
4279
4364
|
attention: (transferId, code, detail) => {
|
|
4280
4365
|
deps.emit(
|
|
4281
4366
|
"transfer:attention",
|
|
@@ -4324,10 +4409,11 @@ var PaymentsFacade = class {
|
|
|
4324
4409
|
const parts = composeFacadeParts(deps, {
|
|
4325
4410
|
engine: () => this.engine(),
|
|
4326
4411
|
send: (request) => this.send(request),
|
|
4327
|
-
isActiveOp: (id) => this.activeMoneyOps.has(id)
|
|
4412
|
+
isActiveOp: (id) => this.activeMoneyOps.has(id),
|
|
4413
|
+
track: (op) => this.trackTail(op)
|
|
4328
4414
|
});
|
|
4329
4415
|
this.view = parts.view;
|
|
4330
|
-
this.wakeRefresh =
|
|
4416
|
+
this.wakeRefresh = parts.refreshView;
|
|
4331
4417
|
this.ledger = parts.ledger;
|
|
4332
4418
|
this.queue = parts.queue;
|
|
4333
4419
|
this.machine = parts.machine;
|
|
@@ -4464,7 +4550,6 @@ var PaymentsFacade = class {
|
|
|
4464
4550
|
}
|
|
4465
4551
|
async receive() {
|
|
4466
4552
|
const transfers = await this.track(this.receiveLoop.drainOnce());
|
|
4467
|
-
if (transfers.length > 0) this.trackTail(this.view.delta());
|
|
4468
4553
|
return { transfers };
|
|
4469
4554
|
}
|
|
4470
4555
|
mint(coinId, amount) {
|
|
@@ -4660,7 +4745,7 @@ var PaymentsFacade = class {
|
|
|
4660
4745
|
...spend.plan.direct,
|
|
4661
4746
|
...spend.plan.split !== void 0 ? [spend.plan.split.tokenId] : []
|
|
4662
4747
|
];
|
|
4663
|
-
|
|
4748
|
+
this.view.markInFlightMany(sourceIds);
|
|
4664
4749
|
if (spend.plan.split !== void 0) {
|
|
4665
4750
|
this.queue.declareExpectedChange(transferId, coinId, spend.plan.split.remainderAmount);
|
|
4666
4751
|
}
|