@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
|
@@ -340,8 +340,22 @@ interface DeliveryPort {
|
|
|
340
340
|
*/
|
|
341
341
|
incomingEpoch(): string | null;
|
|
342
342
|
ack(deliveryId: string, disposition: 'claimed' | 'rejected', reason?: 'invalid' | 'not-owned' | 'storage-rejected' | 'other'): Promise<void>;
|
|
343
|
+
/** Settle many acks at once (#757). Contract pinned by the delivery-port contract suite. */
|
|
344
|
+
ackBatch?(acks: readonly AckRequest[]): Promise<readonly AckOutcome[]>;
|
|
343
345
|
onWake?(cb: () => void): () => void;
|
|
344
346
|
}
|
|
347
|
+
interface AckRequest {
|
|
348
|
+
readonly deliveryId: string;
|
|
349
|
+
readonly disposition: 'claimed' | 'rejected';
|
|
350
|
+
readonly reason?: 'invalid' | 'not-owned' | 'storage-rejected' | 'other';
|
|
351
|
+
}
|
|
352
|
+
type AckOutcome = {
|
|
353
|
+
readonly deliveryId: string;
|
|
354
|
+
readonly status: 'settled';
|
|
355
|
+
} | {
|
|
356
|
+
readonly deliveryId: string;
|
|
357
|
+
readonly status: 'conflict';
|
|
358
|
+
};
|
|
345
359
|
|
|
346
360
|
interface IntentPayload {
|
|
347
361
|
v: 2;
|
|
@@ -1044,6 +1058,7 @@ declare const MAX_RESELECT = 8;
|
|
|
1044
1058
|
declare const ATTENTION_MINT_UNRESOLVED = "mint:unresolved";
|
|
1045
1059
|
declare class PaymentsFacade implements PaymentsV2 {
|
|
1046
1060
|
private readonly deps;
|
|
1061
|
+
private readonly wakeRefresh;
|
|
1047
1062
|
private readonly prewarmed;
|
|
1048
1063
|
private readonly view;
|
|
1049
1064
|
private readonly ledger;
|
|
@@ -340,8 +340,22 @@ interface DeliveryPort {
|
|
|
340
340
|
*/
|
|
341
341
|
incomingEpoch(): string | null;
|
|
342
342
|
ack(deliveryId: string, disposition: 'claimed' | 'rejected', reason?: 'invalid' | 'not-owned' | 'storage-rejected' | 'other'): Promise<void>;
|
|
343
|
+
/** Settle many acks at once (#757). Contract pinned by the delivery-port contract suite. */
|
|
344
|
+
ackBatch?(acks: readonly AckRequest[]): Promise<readonly AckOutcome[]>;
|
|
343
345
|
onWake?(cb: () => void): () => void;
|
|
344
346
|
}
|
|
347
|
+
interface AckRequest {
|
|
348
|
+
readonly deliveryId: string;
|
|
349
|
+
readonly disposition: 'claimed' | 'rejected';
|
|
350
|
+
readonly reason?: 'invalid' | 'not-owned' | 'storage-rejected' | 'other';
|
|
351
|
+
}
|
|
352
|
+
type AckOutcome = {
|
|
353
|
+
readonly deliveryId: string;
|
|
354
|
+
readonly status: 'settled';
|
|
355
|
+
} | {
|
|
356
|
+
readonly deliveryId: string;
|
|
357
|
+
readonly status: 'conflict';
|
|
358
|
+
};
|
|
345
359
|
|
|
346
360
|
interface IntentPayload {
|
|
347
361
|
v: 2;
|
|
@@ -1044,6 +1058,7 @@ declare const MAX_RESELECT = 8;
|
|
|
1044
1058
|
declare const ATTENTION_MINT_UNRESOLVED = "mint:unresolved";
|
|
1045
1059
|
declare class PaymentsFacade implements PaymentsV2 {
|
|
1046
1060
|
private readonly deps;
|
|
1061
|
+
private readonly wakeRefresh;
|
|
1047
1062
|
private readonly prewarmed;
|
|
1048
1063
|
private readonly view;
|
|
1049
1064
|
private readonly ledger;
|
|
@@ -536,6 +536,26 @@ var SerialChain = class {
|
|
|
536
536
|
return run;
|
|
537
537
|
}
|
|
538
538
|
};
|
|
539
|
+
function coalesced(run) {
|
|
540
|
+
let inFlight = false;
|
|
541
|
+
let queued = false;
|
|
542
|
+
const start = () => {
|
|
543
|
+
inFlight = true;
|
|
544
|
+
void run().catch(() => void 0).finally(() => {
|
|
545
|
+
inFlight = false;
|
|
546
|
+
if (!queued) return;
|
|
547
|
+
queued = false;
|
|
548
|
+
start();
|
|
549
|
+
});
|
|
550
|
+
};
|
|
551
|
+
return () => {
|
|
552
|
+
if (inFlight) {
|
|
553
|
+
queued = true;
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
start();
|
|
557
|
+
};
|
|
558
|
+
}
|
|
539
559
|
|
|
540
560
|
// modules/payments-v2/machine/journal.ts
|
|
541
561
|
var MAX_DELIVERY_ATTEMPTS = 6;
|
|
@@ -2841,6 +2861,16 @@ var InventoryView = class {
|
|
|
2841
2861
|
this.inFlight.add(tokenId);
|
|
2842
2862
|
this.deps.emit("inventory:updated");
|
|
2843
2863
|
}
|
|
2864
|
+
/** Reserve a whole plan as ONE change — per token emitted 54 events per send. */
|
|
2865
|
+
markInFlightMany(tokenIds) {
|
|
2866
|
+
let changed = false;
|
|
2867
|
+
for (const tokenId of tokenIds) {
|
|
2868
|
+
if (this.inFlight.has(tokenId)) continue;
|
|
2869
|
+
this.inFlight.add(tokenId);
|
|
2870
|
+
changed = true;
|
|
2871
|
+
}
|
|
2872
|
+
if (changed) this.deps.emit("inventory:updated");
|
|
2873
|
+
}
|
|
2844
2874
|
release(tokenId) {
|
|
2845
2875
|
if (this.inFlight.delete(tokenId)) this.deps.emit("inventory:updated");
|
|
2846
2876
|
}
|
|
@@ -3157,6 +3187,11 @@ var logger = {
|
|
|
3157
3187
|
}
|
|
3158
3188
|
};
|
|
3159
3189
|
|
|
3190
|
+
// modules/payments-v2/ports.ts
|
|
3191
|
+
function isRetryableAckError(err) {
|
|
3192
|
+
return err !== null && typeof err === "object" && err.retryable === true;
|
|
3193
|
+
}
|
|
3194
|
+
|
|
3160
3195
|
// modules/payments-v2/receive/Receive.ts
|
|
3161
3196
|
var ACK_BATCH_SIZE = 200;
|
|
3162
3197
|
var REFRESH_INTERVAL_MS = 2500;
|
|
@@ -3238,6 +3273,7 @@ var Receive = class {
|
|
|
3238
3273
|
}
|
|
3239
3274
|
} catch (err) {
|
|
3240
3275
|
logger.warn("PaymentsV2", "receive drain interrupted \u2014 unacked entries retry next drain:", err);
|
|
3276
|
+
if (isRetryableAckError(err)) return stored;
|
|
3241
3277
|
await flushAcks(deps, pending, pageEpoch).catch((flushErr) => {
|
|
3242
3278
|
logger.warn("PaymentsV2", "receive ack flush failed \u2014 cursor holds at the acked prefix:", flushErr);
|
|
3243
3279
|
});
|
|
@@ -3312,14 +3348,18 @@ async function announce(deps, entry, record) {
|
|
|
3312
3348
|
};
|
|
3313
3349
|
}
|
|
3314
3350
|
async function flushAcks(deps, pending, epochOf) {
|
|
3351
|
+
if (pending.length === 0) return;
|
|
3315
3352
|
let lastAcked = null;
|
|
3316
3353
|
try {
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
lastAcked =
|
|
3321
|
-
|
|
3354
|
+
const settled = await settleAcks(deps, pending);
|
|
3355
|
+
let i = 0;
|
|
3356
|
+
while (i < pending.length && settled.has(pending[i].deliveryId)) {
|
|
3357
|
+
lastAcked = pending[i].cursor;
|
|
3358
|
+
i += 1;
|
|
3322
3359
|
}
|
|
3360
|
+
const stuck = pending.filter((p) => !settled.has(p.deliveryId));
|
|
3361
|
+
pending.length = 0;
|
|
3362
|
+
pending.push(...stuck);
|
|
3323
3363
|
} finally {
|
|
3324
3364
|
if (lastAcked !== null) {
|
|
3325
3365
|
const record = { cursor: lastAcked, syncEpoch: epochOf() };
|
|
@@ -3327,14 +3367,65 @@ async function flushAcks(deps, pending, epochOf) {
|
|
|
3327
3367
|
}
|
|
3328
3368
|
}
|
|
3329
3369
|
}
|
|
3370
|
+
async function settleAcks(deps, pending) {
|
|
3371
|
+
const batch = deps.delivery.ackBatch?.bind(deps.delivery);
|
|
3372
|
+
if (batch === void 0) return settleOneByOne(deps, pending);
|
|
3373
|
+
let outcomes;
|
|
3374
|
+
try {
|
|
3375
|
+
outcomes = await batch(pending.map(toAckRequest));
|
|
3376
|
+
} catch (err) {
|
|
3377
|
+
if (isRetryableAckError(err)) throw err;
|
|
3378
|
+
logger.warn("PaymentsV2", "batched mailbox ack failed \u2014 settling one at a time:", err);
|
|
3379
|
+
return settleOneByOne(deps, pending);
|
|
3380
|
+
}
|
|
3381
|
+
const settled = new Set(outcomes.filter((o) => o.status === "settled").map((o) => o.deliveryId));
|
|
3382
|
+
const conflicts = outcomes.filter((o) => o.status === "conflict").map((o) => o.deliveryId);
|
|
3383
|
+
if (conflicts.length > 0) await resolveConflicts(deps, pending, conflicts, settled);
|
|
3384
|
+
return settled;
|
|
3385
|
+
}
|
|
3386
|
+
async function resolveConflicts(deps, pending, conflicts, settled) {
|
|
3387
|
+
for (const deliveryId of conflicts) {
|
|
3388
|
+
const entry = pending.find((p) => p.deliveryId === deliveryId);
|
|
3389
|
+
try {
|
|
3390
|
+
await rejectStaleClaim(deps, deliveryId, entry?.transferId);
|
|
3391
|
+
} catch (err) {
|
|
3392
|
+
logger.warn("PaymentsV2", `stale-reject failed for ${deliveryId} \u2014 cursor holds here:`, err);
|
|
3393
|
+
continue;
|
|
3394
|
+
}
|
|
3395
|
+
settled.add(deliveryId);
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3398
|
+
async function rejectStaleClaim(deps, deliveryId, transferId) {
|
|
3399
|
+
logger.warn("PaymentsV2", `mailbox claim CONFLICT for ${deliveryId} \u2014 rejected('other') as stale`);
|
|
3400
|
+
await deps.delivery.ack(deliveryId, "rejected", "other");
|
|
3401
|
+
deps.attention(transferId ?? "", ATTENTION_CLAIM_CONFLICT, deliveryId);
|
|
3402
|
+
}
|
|
3403
|
+
async function settleOneByOne(deps, pending) {
|
|
3404
|
+
const settled = /* @__PURE__ */ new Set();
|
|
3405
|
+
for (const ack of pending) {
|
|
3406
|
+
try {
|
|
3407
|
+
await ackOne(deps, ack);
|
|
3408
|
+
} catch (err) {
|
|
3409
|
+
logger.warn("PaymentsV2", `mailbox ack failed for ${ack.deliveryId} \u2014 cursor holds here:`, err);
|
|
3410
|
+
break;
|
|
3411
|
+
}
|
|
3412
|
+
settled.add(ack.deliveryId);
|
|
3413
|
+
}
|
|
3414
|
+
return settled;
|
|
3415
|
+
}
|
|
3416
|
+
function toAckRequest(ack) {
|
|
3417
|
+
return {
|
|
3418
|
+
deliveryId: ack.deliveryId,
|
|
3419
|
+
disposition: ack.disposition,
|
|
3420
|
+
...ack.reason !== void 0 ? { reason: ack.reason } : {}
|
|
3421
|
+
};
|
|
3422
|
+
}
|
|
3330
3423
|
async function ackOne(deps, ack) {
|
|
3331
3424
|
try {
|
|
3332
3425
|
await deps.delivery.ack(ack.deliveryId, ack.disposition, ack.reason);
|
|
3333
3426
|
} catch (err) {
|
|
3334
3427
|
if (ack.disposition !== "claimed" || !isClaimConflict(err)) throw err;
|
|
3335
|
-
|
|
3336
|
-
await deps.delivery.ack(ack.deliveryId, "rejected", "other");
|
|
3337
|
-
deps.attention(ack.transferId ?? "", ATTENTION_CLAIM_CONFLICT, ack.deliveryId);
|
|
3428
|
+
await rejectStaleClaim(deps, ack.deliveryId, ack.transferId);
|
|
3338
3429
|
}
|
|
3339
3430
|
}
|
|
3340
3431
|
function claimAck(entry) {
|
|
@@ -4088,16 +4179,6 @@ function parseAmount(raw) {
|
|
|
4088
4179
|
function supportsDeterministicMint(engine) {
|
|
4089
4180
|
return engine.deterministicMint === true;
|
|
4090
4181
|
}
|
|
4091
|
-
function makeSingleFlightRefresh(refresh) {
|
|
4092
|
-
let inFlight = false;
|
|
4093
|
-
return () => {
|
|
4094
|
-
if (inFlight) return;
|
|
4095
|
-
inFlight = true;
|
|
4096
|
-
void refresh().catch(() => void 0).finally(() => {
|
|
4097
|
-
inFlight = false;
|
|
4098
|
-
});
|
|
4099
|
-
};
|
|
4100
|
-
}
|
|
4101
4182
|
function composeFacadeParts(deps, hooks) {
|
|
4102
4183
|
const ownPubkeyBytes = hexToBytes(deps.ownPubkey);
|
|
4103
4184
|
const ledger = new ReservationLedger();
|
|
@@ -4127,9 +4208,11 @@ function composeFacadeParts(deps, hooks) {
|
|
|
4127
4208
|
const machineDeps = buildMachineDeps(deps, hooks, historyStore);
|
|
4128
4209
|
const machineStores = createMachineStores(deps.kv);
|
|
4129
4210
|
const heldStates = /* @__PURE__ */ new Map();
|
|
4211
|
+
const refreshView = coalesced(() => view.delta());
|
|
4130
4212
|
return {
|
|
4131
4213
|
ownPubkeyBytes,
|
|
4132
4214
|
view,
|
|
4215
|
+
refreshView,
|
|
4133
4216
|
ledger,
|
|
4134
4217
|
pins: buildPins(deps, hooks, { ledger, view, machineStores, machineDeps }),
|
|
4135
4218
|
queue,
|
|
@@ -4138,7 +4221,7 @@ function composeFacadeParts(deps, hooks) {
|
|
|
4138
4221
|
machineDeps,
|
|
4139
4222
|
machine: new TransferMachine(machineDeps),
|
|
4140
4223
|
heldStates,
|
|
4141
|
-
receiveLoop: buildReceive(deps, hooks, historyStore, heldStates,
|
|
4224
|
+
receiveLoop: buildReceive(deps, hooks, historyStore, heldStates, refreshView),
|
|
4142
4225
|
requests: buildRequests(deps, hooks),
|
|
4143
4226
|
restoreDeps: buildRestoreDeps(deps, machineDeps, machineStores, view)
|
|
4144
4227
|
};
|
|
@@ -4205,7 +4288,7 @@ function buildMachineDeps(deps, hooks, historyStore) {
|
|
|
4205
4288
|
}
|
|
4206
4289
|
};
|
|
4207
4290
|
}
|
|
4208
|
-
function buildReceive(deps, hooks, historyStore, heldStates,
|
|
4291
|
+
function buildReceive(deps, hooks, historyStore, heldStates, refreshView) {
|
|
4209
4292
|
return new Receive({
|
|
4210
4293
|
delivery: deps.deliveryPort,
|
|
4211
4294
|
engine: () => hooks.engine(),
|
|
@@ -4219,10 +4302,7 @@ function buildReceive(deps, hooks, historyStore, heldStates, view) {
|
|
|
4219
4302
|
registry: deps.registry,
|
|
4220
4303
|
recordReceived: (record) => recordReceived(historyStore, record),
|
|
4221
4304
|
emit: (event, transfer) => deps.emit(event, transfer),
|
|
4222
|
-
|
|
4223
|
-
// firing per throttle tick regardless of the last one builds a queue that
|
|
4224
|
-
// drains in a burst at the END — a slower version of the bug being fixed.
|
|
4225
|
-
refreshView: makeSingleFlightRefresh(() => view.delta()),
|
|
4305
|
+
refreshView,
|
|
4226
4306
|
attention: (transferId, code, detail) => {
|
|
4227
4307
|
deps.emit(
|
|
4228
4308
|
"transfer:attention",
|
|
@@ -4274,6 +4354,7 @@ var PaymentsFacade = class {
|
|
|
4274
4354
|
isActiveOp: (id) => this.activeMoneyOps.has(id)
|
|
4275
4355
|
});
|
|
4276
4356
|
this.view = parts.view;
|
|
4357
|
+
this.wakeRefresh = parts.refreshView;
|
|
4277
4358
|
this.ledger = parts.ledger;
|
|
4278
4359
|
this.queue = parts.queue;
|
|
4279
4360
|
this.machine = parts.machine;
|
|
@@ -4303,6 +4384,7 @@ var PaymentsFacade = class {
|
|
|
4303
4384
|
now: () => this.nowMs()
|
|
4304
4385
|
});
|
|
4305
4386
|
}
|
|
4387
|
+
wakeRefresh;
|
|
4306
4388
|
prewarmed = new PrewarmCache();
|
|
4307
4389
|
view;
|
|
4308
4390
|
ledger;
|
|
@@ -4341,9 +4423,7 @@ var PaymentsFacade = class {
|
|
|
4341
4423
|
this.deps.session.subscribeStream("mailbox", () => {
|
|
4342
4424
|
this.trackTail(this.receiveLoop.drainOnce());
|
|
4343
4425
|
}),
|
|
4344
|
-
this.deps.session.subscribeStream("inventory",
|
|
4345
|
-
this.trackTail(this.view.delta());
|
|
4346
|
-
}),
|
|
4426
|
+
this.deps.session.subscribeStream("inventory", this.wakeRefresh),
|
|
4347
4427
|
this.deps.session.subscribeStream("payment_requests", () => {
|
|
4348
4428
|
this.trackTail(this.requests.drainIncoming());
|
|
4349
4429
|
})
|
|
@@ -4607,7 +4687,7 @@ var PaymentsFacade = class {
|
|
|
4607
4687
|
...spend.plan.direct,
|
|
4608
4688
|
...spend.plan.split !== void 0 ? [spend.plan.split.tokenId] : []
|
|
4609
4689
|
];
|
|
4610
|
-
|
|
4690
|
+
this.view.markInFlightMany(sourceIds);
|
|
4611
4691
|
if (spend.plan.split !== void 0) {
|
|
4612
4692
|
this.queue.declareExpectedChange(transferId, coinId, spend.plan.split.remainderAmount);
|
|
4613
4693
|
}
|