@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
|
@@ -456,8 +456,22 @@ interface DeliveryPort {
|
|
|
456
456
|
*/
|
|
457
457
|
incomingEpoch(): string | null;
|
|
458
458
|
ack(deliveryId: string, disposition: 'claimed' | 'rejected', reason?: 'invalid' | 'not-owned' | 'storage-rejected' | 'other'): Promise<void>;
|
|
459
|
+
/** Settle many acks at once (#757). Contract pinned by the delivery-port contract suite. */
|
|
460
|
+
ackBatch?(acks: readonly AckRequest[]): Promise<readonly AckOutcome[]>;
|
|
459
461
|
onWake?(cb: () => void): () => void;
|
|
460
462
|
}
|
|
463
|
+
interface AckRequest {
|
|
464
|
+
readonly deliveryId: string;
|
|
465
|
+
readonly disposition: 'claimed' | 'rejected';
|
|
466
|
+
readonly reason?: 'invalid' | 'not-owned' | 'storage-rejected' | 'other';
|
|
467
|
+
}
|
|
468
|
+
type AckOutcome = {
|
|
469
|
+
readonly deliveryId: string;
|
|
470
|
+
readonly status: 'settled';
|
|
471
|
+
} | {
|
|
472
|
+
readonly deliveryId: string;
|
|
473
|
+
readonly status: 'conflict';
|
|
474
|
+
};
|
|
461
475
|
|
|
462
476
|
type StoragePortClient = Pick<WalletApiV2Client, 'listInventory' | 'blobUrls' | 'uploadUrls' | 'apply' | 'fetchBlob' | 'uploadBlob'>;
|
|
463
477
|
declare class WalletApiStoragePort implements StoragePort {
|
|
@@ -528,9 +542,19 @@ declare class WalletApiDeliveryPort implements DeliveryPort {
|
|
|
528
542
|
private openEnvelope;
|
|
529
543
|
private fetchVerified;
|
|
530
544
|
ack(deliveryId: string, disposition: 'claimed' | 'rejected', reason?: 'invalid' | 'not-owned' | 'storage-rejected' | 'other'): Promise<void>;
|
|
545
|
+
/**
|
|
546
|
+
* §5.7/#757 batched settle. Claims go out grouped, rejects grouped by reason
|
|
547
|
+
* (the endpoint takes one reason per call), and the seen-set is written ONCE
|
|
548
|
+
* after every HTTP call has succeeded — never on a throw, so an aborted batch
|
|
549
|
+
* re-lists rather than being silently forgotten.
|
|
550
|
+
*/
|
|
551
|
+
ackBatch(acks: readonly AckRequest[]): Promise<readonly AckOutcome[]>;
|
|
552
|
+
private settleClaims;
|
|
553
|
+
private settleRejects;
|
|
531
554
|
onWake(cb: () => void): () => void;
|
|
532
555
|
private readSeen;
|
|
533
|
-
|
|
556
|
+
/** SerialChain settles-not-fulfills: a rejected write never bricks later acks. */
|
|
557
|
+
private addSeenAll;
|
|
534
558
|
}
|
|
535
559
|
|
|
536
560
|
/**
|
|
@@ -456,8 +456,22 @@ interface DeliveryPort {
|
|
|
456
456
|
*/
|
|
457
457
|
incomingEpoch(): string | null;
|
|
458
458
|
ack(deliveryId: string, disposition: 'claimed' | 'rejected', reason?: 'invalid' | 'not-owned' | 'storage-rejected' | 'other'): Promise<void>;
|
|
459
|
+
/** Settle many acks at once (#757). Contract pinned by the delivery-port contract suite. */
|
|
460
|
+
ackBatch?(acks: readonly AckRequest[]): Promise<readonly AckOutcome[]>;
|
|
459
461
|
onWake?(cb: () => void): () => void;
|
|
460
462
|
}
|
|
463
|
+
interface AckRequest {
|
|
464
|
+
readonly deliveryId: string;
|
|
465
|
+
readonly disposition: 'claimed' | 'rejected';
|
|
466
|
+
readonly reason?: 'invalid' | 'not-owned' | 'storage-rejected' | 'other';
|
|
467
|
+
}
|
|
468
|
+
type AckOutcome = {
|
|
469
|
+
readonly deliveryId: string;
|
|
470
|
+
readonly status: 'settled';
|
|
471
|
+
} | {
|
|
472
|
+
readonly deliveryId: string;
|
|
473
|
+
readonly status: 'conflict';
|
|
474
|
+
};
|
|
461
475
|
|
|
462
476
|
type StoragePortClient = Pick<WalletApiV2Client, 'listInventory' | 'blobUrls' | 'uploadUrls' | 'apply' | 'fetchBlob' | 'uploadBlob'>;
|
|
463
477
|
declare class WalletApiStoragePort implements StoragePort {
|
|
@@ -528,9 +542,19 @@ declare class WalletApiDeliveryPort implements DeliveryPort {
|
|
|
528
542
|
private openEnvelope;
|
|
529
543
|
private fetchVerified;
|
|
530
544
|
ack(deliveryId: string, disposition: 'claimed' | 'rejected', reason?: 'invalid' | 'not-owned' | 'storage-rejected' | 'other'): Promise<void>;
|
|
545
|
+
/**
|
|
546
|
+
* §5.7/#757 batched settle. Claims go out grouped, rejects grouped by reason
|
|
547
|
+
* (the endpoint takes one reason per call), and the seen-set is written ONCE
|
|
548
|
+
* after every HTTP call has succeeded — never on a throw, so an aborted batch
|
|
549
|
+
* re-lists rather than being silently forgotten.
|
|
550
|
+
*/
|
|
551
|
+
ackBatch(acks: readonly AckRequest[]): Promise<readonly AckOutcome[]>;
|
|
552
|
+
private settleClaims;
|
|
553
|
+
private settleRejects;
|
|
531
554
|
onWake(cb: () => void): () => void;
|
|
532
555
|
private readSeen;
|
|
533
|
-
|
|
556
|
+
/** SerialChain settles-not-fulfills: a rejected write never bricks later acks. */
|
|
557
|
+
private addSeenAll;
|
|
534
558
|
}
|
|
535
559
|
|
|
536
560
|
/**
|
|
@@ -12,6 +12,10 @@ var WalletApiHttpError = class extends Error {
|
|
|
12
12
|
if (retryAfter !== void 0) this.retryAfter = retryAfter;
|
|
13
13
|
}
|
|
14
14
|
};
|
|
15
|
+
var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([0, 408, 425, 429, 500, 502, 503, 504]);
|
|
16
|
+
function isRetryableStatus(err) {
|
|
17
|
+
return err instanceof WalletApiHttpError && RETRYABLE_STATUSES.has(err.status);
|
|
18
|
+
}
|
|
15
19
|
function isWalletApiHttpError(err, status) {
|
|
16
20
|
return err instanceof WalletApiHttpError && (status === void 0 || err.status === status);
|
|
17
21
|
}
|
|
@@ -4049,6 +4053,23 @@ function computeDeliveryId(tokenIdHex, stateHashHex) {
|
|
|
4049
4053
|
return bytesToHex3(sha256(joined));
|
|
4050
4054
|
}
|
|
4051
4055
|
var DELIVERY_SEEN_KEY = "delivery:seen";
|
|
4056
|
+
var ACK_CHUNK = 50;
|
|
4057
|
+
function chunked(items, size) {
|
|
4058
|
+
const out = [];
|
|
4059
|
+
for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));
|
|
4060
|
+
return out;
|
|
4061
|
+
}
|
|
4062
|
+
var RetryableAckFailure = class extends Error {
|
|
4063
|
+
retryable = true;
|
|
4064
|
+
constructor(cause) {
|
|
4065
|
+
super(`mailbox ack batch could not be attempted: ${String(cause)}`);
|
|
4066
|
+
this.name = "RetryableAckFailure";
|
|
4067
|
+
this.cause = cause;
|
|
4068
|
+
}
|
|
4069
|
+
};
|
|
4070
|
+
function asRetryable(err) {
|
|
4071
|
+
return isRetryableStatus(err) ? new RetryableAckFailure(err) : err;
|
|
4072
|
+
}
|
|
4052
4073
|
var WalletApiDeliveryPort = class {
|
|
4053
4074
|
custody;
|
|
4054
4075
|
client;
|
|
@@ -4214,7 +4235,54 @@ var WalletApiDeliveryPort = class {
|
|
|
4214
4235
|
} else {
|
|
4215
4236
|
await this.client.reject([deliveryId], reason);
|
|
4216
4237
|
}
|
|
4217
|
-
await this.
|
|
4238
|
+
await this.addSeenAll([deliveryId]);
|
|
4239
|
+
}
|
|
4240
|
+
/**
|
|
4241
|
+
* §5.7/#757 batched settle. Claims go out grouped, rejects grouped by reason
|
|
4242
|
+
* (the endpoint takes one reason per call), and the seen-set is written ONCE
|
|
4243
|
+
* after every HTTP call has succeeded — never on a throw, so an aborted batch
|
|
4244
|
+
* re-lists rather than being silently forgotten.
|
|
4245
|
+
*/
|
|
4246
|
+
async ackBatch(acks) {
|
|
4247
|
+
const settled = [];
|
|
4248
|
+
const outcomes = [];
|
|
4249
|
+
try {
|
|
4250
|
+
await this.settleClaims(acks, settled, outcomes);
|
|
4251
|
+
await this.settleRejects(acks, settled);
|
|
4252
|
+
} catch (err) {
|
|
4253
|
+
await this.addSeenAll(settled).catch(() => void 0);
|
|
4254
|
+
throw asRetryable(err);
|
|
4255
|
+
}
|
|
4256
|
+
await this.addSeenAll(settled);
|
|
4257
|
+
return [...outcomes, ...settled.map((deliveryId) => ({ deliveryId, status: "settled" }))];
|
|
4258
|
+
}
|
|
4259
|
+
async settleClaims(acks, settled, outcomes) {
|
|
4260
|
+
const ids = acks.filter((a) => a.disposition === "claimed").map((a) => a.deliveryId);
|
|
4261
|
+
for (const chunk of chunked(ids, ACK_CHUNK)) {
|
|
4262
|
+
const result = await this.client.claim(chunk, this.custody === "inventory");
|
|
4263
|
+
settled.push(...result.claimed);
|
|
4264
|
+
settled.push(...result.alreadyClaimed.map((e) => e.entryId));
|
|
4265
|
+
for (const f of result.failed) {
|
|
4266
|
+
if (f.code === "CONFLICT") outcomes.push({ deliveryId: f.entryId, status: "conflict" });
|
|
4267
|
+
else throw new MailboxClaimFailedError(f.entryId, f.code);
|
|
4268
|
+
}
|
|
4269
|
+
}
|
|
4270
|
+
}
|
|
4271
|
+
async settleRejects(acks, settled) {
|
|
4272
|
+
const byReason = /* @__PURE__ */ new Map();
|
|
4273
|
+
for (const a of acks) {
|
|
4274
|
+
if (a.disposition !== "rejected") continue;
|
|
4275
|
+
const key = a.reason ?? "";
|
|
4276
|
+
const group = byReason.get(key) ?? [];
|
|
4277
|
+
group.push(a.deliveryId);
|
|
4278
|
+
byReason.set(key, group);
|
|
4279
|
+
}
|
|
4280
|
+
for (const [reason, group] of byReason) {
|
|
4281
|
+
for (const chunk of chunked(group, ACK_CHUNK)) {
|
|
4282
|
+
await this.client.reject(chunk, reason === "" ? void 0 : reason);
|
|
4283
|
+
settled.push(...chunk);
|
|
4284
|
+
}
|
|
4285
|
+
}
|
|
4218
4286
|
}
|
|
4219
4287
|
onWake(cb) {
|
|
4220
4288
|
if (this.wake === null) return () => void 0;
|
|
@@ -4225,10 +4293,12 @@ var WalletApiDeliveryPort = class {
|
|
|
4225
4293
|
const raw = await this.kv.get(DELIVERY_SEEN_KEY);
|
|
4226
4294
|
return new Set(raw ?? []);
|
|
4227
4295
|
}
|
|
4228
|
-
|
|
4296
|
+
/** SerialChain settles-not-fulfills: a rejected write never bricks later acks. */
|
|
4297
|
+
addSeenAll(deliveryIds) {
|
|
4298
|
+
if (deliveryIds.length === 0) return Promise.resolve();
|
|
4229
4299
|
return this.seenChain.enqueue(async () => {
|
|
4230
4300
|
const seen = await this.readSeen();
|
|
4231
|
-
seen.add(
|
|
4301
|
+
for (const id of deliveryIds) seen.add(id);
|
|
4232
4302
|
await this.kv.set(DELIVERY_SEEN_KEY, [...seen]);
|
|
4233
4303
|
});
|
|
4234
4304
|
}
|