@tinycloud/sdk-core 3.11.0 → 3.12.0-beta.4
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/index.cjs +874 -59
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +149 -72
- package/dist/index.d.ts +149 -72
- package/dist/index.js +874 -59
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -18396,6 +18396,298 @@ async function verifyLocationRecord(input) {
|
|
|
18396
18396
|
}
|
|
18397
18397
|
return false;
|
|
18398
18398
|
}
|
|
18399
|
+
function canonicalLocationPayloadV2(payload) {
|
|
18400
|
+
return JSON.stringify({
|
|
18401
|
+
version: payload.version,
|
|
18402
|
+
subject: payload.subject,
|
|
18403
|
+
nodes: payload.nodes.map(({ did, url }) => ({ did, url })).sort((left, right) => compareAscii(left.did, right.did)),
|
|
18404
|
+
spaces: payload.spaces.map(({ space, nodes }) => ({
|
|
18405
|
+
space,
|
|
18406
|
+
nodes: nodes.map(({ did, mode }) => ({ did, mode })).sort((left, right) => compareAscii(left.did, right.did))
|
|
18407
|
+
})).sort((left, right) => compareAscii(left.space, right.space)),
|
|
18408
|
+
encryptionNode: payload.encryptionNode,
|
|
18409
|
+
updated_at: payload.updated_at,
|
|
18410
|
+
sequence: payload.sequence
|
|
18411
|
+
});
|
|
18412
|
+
}
|
|
18413
|
+
function validateLocationRecordV2Payload(input) {
|
|
18414
|
+
const payload = exactV2Object(
|
|
18415
|
+
input,
|
|
18416
|
+
["version", "subject", "nodes", "spaces", "encryptionNode", "updated_at", "sequence"],
|
|
18417
|
+
"payload"
|
|
18418
|
+
);
|
|
18419
|
+
if (payload.version !== 2) {
|
|
18420
|
+
throw new LocationRecordValidationError("version must be 2");
|
|
18421
|
+
}
|
|
18422
|
+
validateV2OwnerSubject(payload.subject);
|
|
18423
|
+
if (!Array.isArray(payload.nodes)) {
|
|
18424
|
+
throw new LocationRecordValidationError("nodes must be an array");
|
|
18425
|
+
}
|
|
18426
|
+
const nodeDids = /* @__PURE__ */ new Set();
|
|
18427
|
+
const origins = /* @__PURE__ */ new Set();
|
|
18428
|
+
const nodes = [];
|
|
18429
|
+
for (const item of payload.nodes) {
|
|
18430
|
+
const node = exactV2Object(item, ["did", "url"], "node");
|
|
18431
|
+
if (typeof node.did !== "string") {
|
|
18432
|
+
throw new LocationRecordValidationError("node.did must be a string");
|
|
18433
|
+
}
|
|
18434
|
+
validateV2DidKey(node.did, "node.did");
|
|
18435
|
+
if (typeof node.url !== "string") {
|
|
18436
|
+
throw new LocationRecordValidationError("node.url must be a string");
|
|
18437
|
+
}
|
|
18438
|
+
const origin2 = validateV2Origin(node.url);
|
|
18439
|
+
if (nodeDids.has(node.did) || origins.has(origin2)) {
|
|
18440
|
+
throw new LocationRecordValidationError("node DIDs and origins must be unique");
|
|
18441
|
+
}
|
|
18442
|
+
nodeDids.add(node.did);
|
|
18443
|
+
origins.add(origin2);
|
|
18444
|
+
nodes.push({ did: node.did, url: origin2 });
|
|
18445
|
+
}
|
|
18446
|
+
if (!Array.isArray(payload.spaces)) {
|
|
18447
|
+
throw new LocationRecordValidationError("spaces must be an array");
|
|
18448
|
+
}
|
|
18449
|
+
const spaceKeys = /* @__PURE__ */ new Set();
|
|
18450
|
+
const spaces = [];
|
|
18451
|
+
for (const item of payload.spaces) {
|
|
18452
|
+
const space = exactV2Object(item, ["space", "nodes"], "space");
|
|
18453
|
+
if (typeof space.space !== "string" || !isAsciiNonEmpty(space.space)) {
|
|
18454
|
+
throw new LocationRecordValidationError("space.space must be a non-empty ASCII URI");
|
|
18455
|
+
}
|
|
18456
|
+
const identity3 = validateV2SpaceOwner(space.space, payload.subject);
|
|
18457
|
+
if (spaceKeys.has(identity3)) {
|
|
18458
|
+
throw new LocationRecordValidationError("spaces must be unique");
|
|
18459
|
+
}
|
|
18460
|
+
spaceKeys.add(identity3);
|
|
18461
|
+
if (!Array.isArray(space.nodes)) {
|
|
18462
|
+
throw new LocationRecordValidationError("space.nodes must be an array");
|
|
18463
|
+
}
|
|
18464
|
+
const membershipDids = /* @__PURE__ */ new Set();
|
|
18465
|
+
const memberships = [];
|
|
18466
|
+
for (const item2 of space.nodes) {
|
|
18467
|
+
const membership = exactV2Object(item2, ["did", "mode"], "space membership");
|
|
18468
|
+
if (typeof membership.did !== "string") {
|
|
18469
|
+
throw new LocationRecordValidationError("membership.did must be a string");
|
|
18470
|
+
}
|
|
18471
|
+
validateV2DidKey(membership.did, "membership.did");
|
|
18472
|
+
if (!nodeDids.has(membership.did)) {
|
|
18473
|
+
throw new LocationRecordValidationError("space membership must reference a listed node");
|
|
18474
|
+
}
|
|
18475
|
+
if (membershipDids.has(membership.did)) {
|
|
18476
|
+
throw new LocationRecordValidationError("space membership DIDs must be unique");
|
|
18477
|
+
}
|
|
18478
|
+
if (membership.mode !== "read-write" && membership.mode !== "read-only") {
|
|
18479
|
+
throw new LocationRecordValidationError("membership.mode must be read-write or read-only");
|
|
18480
|
+
}
|
|
18481
|
+
membershipDids.add(membership.did);
|
|
18482
|
+
memberships.push({ did: membership.did, mode: membership.mode });
|
|
18483
|
+
}
|
|
18484
|
+
spaces.push({ space: space.space, nodes: memberships });
|
|
18485
|
+
}
|
|
18486
|
+
if (payload.encryptionNode !== null) {
|
|
18487
|
+
if (typeof payload.encryptionNode !== "string") {
|
|
18488
|
+
throw new LocationRecordValidationError("encryptionNode must be a DID or null");
|
|
18489
|
+
}
|
|
18490
|
+
validateV2DidKey(payload.encryptionNode, "encryptionNode");
|
|
18491
|
+
if (!nodeDids.has(payload.encryptionNode)) {
|
|
18492
|
+
throw new LocationRecordValidationError("encryptionNode must reference a listed node");
|
|
18493
|
+
}
|
|
18494
|
+
}
|
|
18495
|
+
if (typeof payload.updated_at !== "string" || !isV2UtcTimestamp(payload.updated_at)) {
|
|
18496
|
+
throw new LocationRecordValidationError("updated_at must be a valid UTC RFC 3339 timestamp with milliseconds");
|
|
18497
|
+
}
|
|
18498
|
+
if (typeof payload.sequence !== "number" || !Number.isSafeInteger(payload.sequence) || payload.sequence < 0) {
|
|
18499
|
+
throw new LocationRecordValidationError("sequence must be a non-negative safe integer");
|
|
18500
|
+
}
|
|
18501
|
+
return {
|
|
18502
|
+
version: 2,
|
|
18503
|
+
subject: payload.subject,
|
|
18504
|
+
nodes,
|
|
18505
|
+
spaces,
|
|
18506
|
+
encryptionNode: payload.encryptionNode,
|
|
18507
|
+
updated_at: payload.updated_at,
|
|
18508
|
+
sequence: payload.sequence
|
|
18509
|
+
};
|
|
18510
|
+
}
|
|
18511
|
+
function validateLocationRecordV2(input) {
|
|
18512
|
+
const record2 = exactV2Object(
|
|
18513
|
+
input,
|
|
18514
|
+
["version", "subject", "nodes", "spaces", "encryptionNode", "updated_at", "sequence", "signature"],
|
|
18515
|
+
"record"
|
|
18516
|
+
);
|
|
18517
|
+
const payload = validateLocationRecordV2Payload({
|
|
18518
|
+
version: record2.version,
|
|
18519
|
+
subject: record2.subject,
|
|
18520
|
+
nodes: record2.nodes,
|
|
18521
|
+
spaces: record2.spaces,
|
|
18522
|
+
encryptionNode: record2.encryptionNode,
|
|
18523
|
+
updated_at: record2.updated_at,
|
|
18524
|
+
sequence: record2.sequence
|
|
18525
|
+
});
|
|
18526
|
+
if (typeof record2.signature !== "string" || !isCanonicalV2Signature(payload.subject, record2.signature)) {
|
|
18527
|
+
throw new LocationRecordValidationError("signature is not in canonical owner-signature format");
|
|
18528
|
+
}
|
|
18529
|
+
return { ...payload, signature: record2.signature };
|
|
18530
|
+
}
|
|
18531
|
+
async function verifyLocationRecordV2(input) {
|
|
18532
|
+
const record2 = validateLocationRecordV2(input);
|
|
18533
|
+
const payload = canonicalLocationPayloadV2(record2);
|
|
18534
|
+
if (record2.subject.startsWith("did:pkh:")) {
|
|
18535
|
+
const address = record2.subject.slice(record2.subject.lastIndexOf(":") + 1);
|
|
18536
|
+
try {
|
|
18537
|
+
return await verifyMessage2({
|
|
18538
|
+
address,
|
|
18539
|
+
message: payload,
|
|
18540
|
+
signature: record2.signature
|
|
18541
|
+
});
|
|
18542
|
+
} catch {
|
|
18543
|
+
return false;
|
|
18544
|
+
}
|
|
18545
|
+
}
|
|
18546
|
+
try {
|
|
18547
|
+
return ed2551911.verify(
|
|
18548
|
+
decodeBase64Url4(record2.signature),
|
|
18549
|
+
new TextEncoder().encode(payload),
|
|
18550
|
+
canonicalV2DidKey(record2.subject),
|
|
18551
|
+
{ zip215: false }
|
|
18552
|
+
);
|
|
18553
|
+
} catch {
|
|
18554
|
+
return false;
|
|
18555
|
+
}
|
|
18556
|
+
}
|
|
18557
|
+
async function signLocationRecordV2(payload, signer) {
|
|
18558
|
+
const validated = validateLocationRecordV2Payload(payload);
|
|
18559
|
+
const message = canonicalLocationPayloadV2(validated);
|
|
18560
|
+
const isPkh = validated.subject.startsWith("did:pkh:");
|
|
18561
|
+
if (signer.type === "did:pkh" !== isPkh) {
|
|
18562
|
+
throw new LocationRecordValidationError("signer type does not match owner subject");
|
|
18563
|
+
}
|
|
18564
|
+
const signature = signer.type === "did:pkh" ? await signer.signMessage(message) : base64UrlEncode3(await signer.signBytes(new TextEncoder().encode(message)));
|
|
18565
|
+
const record2 = validateLocationRecordV2({ ...validated, signature });
|
|
18566
|
+
if (!await verifyLocationRecordV2(record2)) {
|
|
18567
|
+
throw new LocationRecordValidationError("signature does not verify for owner subject");
|
|
18568
|
+
}
|
|
18569
|
+
return record2;
|
|
18570
|
+
}
|
|
18571
|
+
function exactV2Object(input, expected, label) {
|
|
18572
|
+
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
18573
|
+
throw new LocationRecordValidationError(`${label} must be an object`);
|
|
18574
|
+
}
|
|
18575
|
+
const keys = Reflect.ownKeys(input);
|
|
18576
|
+
if (keys.length !== expected.length || keys.some((key) => typeof key !== "string" || !expected.includes(key))) {
|
|
18577
|
+
throw new LocationRecordValidationError(`${label} has missing or unknown fields`);
|
|
18578
|
+
}
|
|
18579
|
+
return input;
|
|
18580
|
+
}
|
|
18581
|
+
function validateV2OwnerSubject(subject) {
|
|
18582
|
+
if (typeof subject !== "string" || !isAsciiNonEmpty(subject)) {
|
|
18583
|
+
throw new LocationRecordValidationError("subject must be a canonical owner DID");
|
|
18584
|
+
}
|
|
18585
|
+
const pkh = /^did:pkh:eip155:([1-9][0-9]*):(0x[a-fA-F0-9]{40})$/.exec(subject);
|
|
18586
|
+
if (pkh !== null) {
|
|
18587
|
+
const chainId = Number(pkh[1]);
|
|
18588
|
+
try {
|
|
18589
|
+
if (Number.isSafeInteger(chainId) && chainId > 0 && pkhDid(pkh[2], chainId) === subject) return;
|
|
18590
|
+
} catch {
|
|
18591
|
+
}
|
|
18592
|
+
throw new LocationRecordValidationError("subject must use a positive safe chain ID and EIP-55 address");
|
|
18593
|
+
}
|
|
18594
|
+
validateV2DidKey(subject, "subject");
|
|
18595
|
+
}
|
|
18596
|
+
function validateV2DidKey(value, label) {
|
|
18597
|
+
try {
|
|
18598
|
+
canonicalV2DidKey(value);
|
|
18599
|
+
} catch {
|
|
18600
|
+
throw new LocationRecordValidationError(`${label} must be a canonical Ed25519 did:key`);
|
|
18601
|
+
}
|
|
18602
|
+
}
|
|
18603
|
+
function canonicalV2DidKey(did) {
|
|
18604
|
+
if (!did.startsWith("did:key:z")) {
|
|
18605
|
+
throw new LocationRecordValidationError("DID must be an Ed25519 did:key");
|
|
18606
|
+
}
|
|
18607
|
+
const identifier = did.slice("did:key:".length);
|
|
18608
|
+
const bytes2 = bases.base58btc.decode(identifier);
|
|
18609
|
+
if (bytes2.length !== 34 || bytes2[0] !== 237 || bytes2[1] !== 1 || bases.base58btc.encode(bytes2) !== identifier) {
|
|
18610
|
+
throw new LocationRecordValidationError("DID must use the canonical Ed25519 multicodec");
|
|
18611
|
+
}
|
|
18612
|
+
return bytes2.slice(2);
|
|
18613
|
+
}
|
|
18614
|
+
function validateV2Origin(input) {
|
|
18615
|
+
if (!isAsciiNonEmpty(input)) {
|
|
18616
|
+
throw new LocationRecordValidationError("node.url must be an ASCII HTTP(S) origin");
|
|
18617
|
+
}
|
|
18618
|
+
let url;
|
|
18619
|
+
try {
|
|
18620
|
+
url = new URL(input);
|
|
18621
|
+
} catch {
|
|
18622
|
+
throw new LocationRecordValidationError("node.url must be an absolute HTTP(S) origin");
|
|
18623
|
+
}
|
|
18624
|
+
if (url.protocol !== "https:" && url.protocol !== "http:" || url.username !== "" || url.password !== "" || url.pathname !== "/" || url.search !== "" || url.hash !== "" || url.origin !== input) {
|
|
18625
|
+
throw new LocationRecordValidationError("node.url must be an exact origin without path, query, fragment, or credentials");
|
|
18626
|
+
}
|
|
18627
|
+
if (url.protocol === "http:" && url.hostname !== "127.0.0.1" && url.hostname !== "[::1]") {
|
|
18628
|
+
throw new LocationRecordValidationError("HTTP node URLs are allowed only for IPv4 or IPv6 loopback");
|
|
18629
|
+
}
|
|
18630
|
+
return url.origin;
|
|
18631
|
+
}
|
|
18632
|
+
function validateV2SpaceOwner(space, subject) {
|
|
18633
|
+
if (!/^[A-Za-z0-9._~!$&'()*+,;=:@%-]+$/.test(space)) {
|
|
18634
|
+
throw new LocationRecordValidationError("space must be a complete normalized TinyCloud space URI");
|
|
18635
|
+
}
|
|
18636
|
+
for (let index = space.indexOf("%"); index !== -1; index = space.indexOf("%", index + 3)) {
|
|
18637
|
+
const octet = space.slice(index + 1, index + 3);
|
|
18638
|
+
if (!/^[0-9A-F]{2}$/.test(octet) || /^[A-Za-z0-9._~-]$/.test(String.fromCharCode(Number.parseInt(octet, 16)))) {
|
|
18639
|
+
throw new LocationRecordValidationError("space URI percent encoding must be normalized");
|
|
18640
|
+
}
|
|
18641
|
+
}
|
|
18642
|
+
if (subject.startsWith("did:key:")) {
|
|
18643
|
+
const prefix = `tinycloud:${subject.slice("did:".length)}:`;
|
|
18644
|
+
if (!space.startsWith(prefix) || space.length === prefix.length || space.slice(prefix.length).includes(":")) {
|
|
18645
|
+
throw new LocationRecordValidationError("space owner must match the did:key subject");
|
|
18646
|
+
}
|
|
18647
|
+
return space;
|
|
18648
|
+
}
|
|
18649
|
+
const parts = /^tinycloud:pkh:eip155:([1-9][0-9]*):(0x[a-fA-F0-9]{40}):([^:]+)$/.exec(space);
|
|
18650
|
+
const ownerAddress = subject.slice(subject.lastIndexOf(":") + 1);
|
|
18651
|
+
if (parts === null || !Number.isSafeInteger(Number(parts[1])) || Number(parts[1]) <= 0 || parts[2].toLowerCase() !== ownerAddress.toLowerCase()) {
|
|
18652
|
+
throw new LocationRecordValidationError("space owner address must match the did:pkh subject");
|
|
18653
|
+
}
|
|
18654
|
+
return `tinycloud:pkh:eip155:${parts[1]}:${parts[2].toLowerCase()}:${parts[3]}`;
|
|
18655
|
+
}
|
|
18656
|
+
function isV2UtcTimestamp(value) {
|
|
18657
|
+
const match = /^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})\.([0-9]{3})Z$/.exec(value);
|
|
18658
|
+
if (match === null) return false;
|
|
18659
|
+
const [year, month, day, hour, minute, second] = match.slice(1, 7).map(Number);
|
|
18660
|
+
const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
18661
|
+
const daysInMonth = [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month - 1];
|
|
18662
|
+
return year > 0 && daysInMonth !== void 0 && day >= 1 && day <= daysInMonth && hour <= 23 && minute <= 59 && second <= 59;
|
|
18663
|
+
}
|
|
18664
|
+
function isCanonicalV2Signature(subject, signature) {
|
|
18665
|
+
if (subject.startsWith("did:pkh:")) {
|
|
18666
|
+
if (!/^0x[0-9a-fA-F]{130}$/.test(signature)) return false;
|
|
18667
|
+
const r = BigInt(`0x${signature.slice(2, 66)}`);
|
|
18668
|
+
const s = BigInt(`0x${signature.slice(66, 130)}`);
|
|
18669
|
+
const v = Number.parseInt(signature.slice(130), 16);
|
|
18670
|
+
const curveOrder = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n;
|
|
18671
|
+
return r > 0n && r < curveOrder && s > 0n && s <= curveOrder / 2n && (v === 27 || v === 28);
|
|
18672
|
+
}
|
|
18673
|
+
if (!/^[A-Za-z0-9_-]{86}$/.test(signature)) return false;
|
|
18674
|
+
try {
|
|
18675
|
+
const bytes2 = decodeBase64Url4(signature);
|
|
18676
|
+
return bytes2.length === 64 && base64UrlEncode3(bytes2) === signature;
|
|
18677
|
+
} catch {
|
|
18678
|
+
return false;
|
|
18679
|
+
}
|
|
18680
|
+
}
|
|
18681
|
+
function isAsciiNonEmpty(value) {
|
|
18682
|
+
if (value.length === 0) return false;
|
|
18683
|
+
for (let index = 0; index < value.length; index++) {
|
|
18684
|
+
if (value.charCodeAt(index) > 127) return false;
|
|
18685
|
+
}
|
|
18686
|
+
return true;
|
|
18687
|
+
}
|
|
18688
|
+
function compareAscii(left, right) {
|
|
18689
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
18690
|
+
}
|
|
18399
18691
|
async function fetchLocationRecord(registryUrl, subject, fetchFn = globalThis.fetch, signal) {
|
|
18400
18692
|
const url = `${registryUrl.replace(/\/$/, "")}/v1/locations/${encodeURIComponent(subject)}`;
|
|
18401
18693
|
const response = await fetchFn(url, {
|
|
@@ -19172,6 +19464,50 @@ async function classifyRegistryLookup(subject, response, options2 = {}) {
|
|
|
19172
19464
|
}
|
|
19173
19465
|
return { status: "unregistered", subject, source: "offchain", hosts: [] };
|
|
19174
19466
|
}
|
|
19467
|
+
if (options2.recordVersion === 2) {
|
|
19468
|
+
let record3;
|
|
19469
|
+
try {
|
|
19470
|
+
record3 = parseRecordResponseV2(response.body);
|
|
19471
|
+
} catch (error) {
|
|
19472
|
+
return unavailable(
|
|
19473
|
+
"malformed-response",
|
|
19474
|
+
error instanceof Error ? error.message : "location registry response is invalid"
|
|
19475
|
+
);
|
|
19476
|
+
}
|
|
19477
|
+
if (registryOwnerKeyV2(record3.subject) !== registryOwnerKeyV2(subject)) {
|
|
19478
|
+
return unavailable(
|
|
19479
|
+
"subject-mismatch",
|
|
19480
|
+
"location record owner does not match requested owner key"
|
|
19481
|
+
);
|
|
19482
|
+
}
|
|
19483
|
+
let signatureValid = false;
|
|
19484
|
+
try {
|
|
19485
|
+
signatureValid = await verifyLocationRecordV2(record3);
|
|
19486
|
+
} catch {
|
|
19487
|
+
signatureValid = false;
|
|
19488
|
+
}
|
|
19489
|
+
if (!signatureValid) {
|
|
19490
|
+
return unavailable("invalid-signature", "location record signature is invalid");
|
|
19491
|
+
}
|
|
19492
|
+
if (remembered !== null && record3.sequence < remembered) {
|
|
19493
|
+
return unavailable(
|
|
19494
|
+
"rollback",
|
|
19495
|
+
`location record sequence ${record3.sequence} is below remembered sequence ${remembered}`
|
|
19496
|
+
);
|
|
19497
|
+
}
|
|
19498
|
+
return {
|
|
19499
|
+
status: "registered",
|
|
19500
|
+
subject,
|
|
19501
|
+
source: "offchain",
|
|
19502
|
+
sequence: record3.sequence,
|
|
19503
|
+
record: record3,
|
|
19504
|
+
hosts: record3.nodes.map(({ did, url }) => ({
|
|
19505
|
+
nodeDid: did,
|
|
19506
|
+
urls: [url],
|
|
19507
|
+
source: "offchain"
|
|
19508
|
+
}))
|
|
19509
|
+
};
|
|
19510
|
+
}
|
|
19175
19511
|
let record2;
|
|
19176
19512
|
try {
|
|
19177
19513
|
record2 = parseRecordResponse(response.body);
|
|
@@ -19348,6 +19684,26 @@ function parseStrictJson(text3) {
|
|
|
19348
19684
|
if (i !== text3.length) fail2("trailing data");
|
|
19349
19685
|
return value;
|
|
19350
19686
|
}
|
|
19687
|
+
function registryOwnerKeyV2(subject) {
|
|
19688
|
+
return subject.startsWith("did:pkh:") ? subject.slice(subject.lastIndexOf(":") + 1) : subject;
|
|
19689
|
+
}
|
|
19690
|
+
function parseRecordResponseV2(body) {
|
|
19691
|
+
const root = parseStrictJson(body);
|
|
19692
|
+
if (!(root instanceof Map) || root.size !== 1 || !root.has("record")) {
|
|
19693
|
+
throw new SyntaxError("location registry v2 response must contain only a record field");
|
|
19694
|
+
}
|
|
19695
|
+
return validateLocationRecordV2(toNativeJson(root.get("record")));
|
|
19696
|
+
}
|
|
19697
|
+
function toNativeJson(value) {
|
|
19698
|
+
if (value instanceof JsonInteger) return Number(value.lexeme);
|
|
19699
|
+
if (Array.isArray(value)) return value.map(toNativeJson);
|
|
19700
|
+
if (value instanceof Map) {
|
|
19701
|
+
return Object.fromEntries(
|
|
19702
|
+
Array.from(value, ([key, entry]) => [key, toNativeJson(entry)])
|
|
19703
|
+
);
|
|
19704
|
+
}
|
|
19705
|
+
return value;
|
|
19706
|
+
}
|
|
19351
19707
|
function parseRecordResponse(body) {
|
|
19352
19708
|
const root = parseStrictJson(body);
|
|
19353
19709
|
const record2 = root instanceof Map ? root.get("record") : void 0;
|
|
@@ -19468,11 +19824,150 @@ function multiaddrOrigin(address) {
|
|
|
19468
19824
|
}
|
|
19469
19825
|
}
|
|
19470
19826
|
|
|
19827
|
+
// src/registry-cache.ts
|
|
19828
|
+
function createInMemoryRegistryCacheStore() {
|
|
19829
|
+
const entries = /* @__PURE__ */ new Map();
|
|
19830
|
+
let tail = Promise.resolve();
|
|
19831
|
+
return {
|
|
19832
|
+
async get(key) {
|
|
19833
|
+
await tail;
|
|
19834
|
+
const record2 = validateRegistryCacheRecord(entries.get(key), key);
|
|
19835
|
+
return record2 && "invalidated" in record2 ? void 0 : record2;
|
|
19836
|
+
},
|
|
19837
|
+
async getRevision(key) {
|
|
19838
|
+
await tail;
|
|
19839
|
+
return validateRegistryCacheRecord(entries.get(key), key)?.revision ?? 0;
|
|
19840
|
+
},
|
|
19841
|
+
update(key, update) {
|
|
19842
|
+
const next = tail.then(async () => {
|
|
19843
|
+
const record2 = validateRegistryCacheRecord(entries.get(key), key);
|
|
19844
|
+
const revision = record2?.revision ?? 0;
|
|
19845
|
+
const entry = { ...await update(record2 && "invalidated" in record2 ? void 0 : record2, revision), revision };
|
|
19846
|
+
entries.set(key, entry);
|
|
19847
|
+
return entry;
|
|
19848
|
+
});
|
|
19849
|
+
tail = next.catch(() => void 0);
|
|
19850
|
+
return next;
|
|
19851
|
+
},
|
|
19852
|
+
delete(key, expectedRevision, maximumSequence) {
|
|
19853
|
+
const next = tail.then(() => {
|
|
19854
|
+
const record2 = validateRegistryCacheRecord(entries.get(key), key);
|
|
19855
|
+
const revision = record2?.revision ?? 0;
|
|
19856
|
+
if (expectedRevision !== void 0 && (!Number.isSafeInteger(expectedRevision) || expectedRevision < 0 || expectedRevision !== revision)) {
|
|
19857
|
+
throw new Error("Registry cache revision changed before invalidation");
|
|
19858
|
+
}
|
|
19859
|
+
if (maximumSequence !== void 0 && (!Number.isSafeInteger(maximumSequence) || maximumSequence < 0 || record2 !== void 0 && !("invalidated" in record2) && record2.sequence > maximumSequence)) {
|
|
19860
|
+
throw new Error("Registry cache sequence advanced before invalidation");
|
|
19861
|
+
}
|
|
19862
|
+
if (revision === Number.MAX_SAFE_INTEGER) throw new Error("Registry cache revision overflow");
|
|
19863
|
+
entries.set(key, { ownerKey: key, revision: revision + 1, invalidated: true });
|
|
19864
|
+
});
|
|
19865
|
+
tail = next.catch(() => void 0);
|
|
19866
|
+
return next;
|
|
19867
|
+
}
|
|
19868
|
+
};
|
|
19869
|
+
}
|
|
19870
|
+
function validateRegistryCacheRecord(value, key) {
|
|
19871
|
+
if (value === void 0) return void 0;
|
|
19872
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || !("ownerKey" in value) || value.ownerKey !== key) {
|
|
19873
|
+
throw new Error("Invalid registry cache metadata");
|
|
19874
|
+
}
|
|
19875
|
+
const revision = "revision" in value ? value.revision : 0;
|
|
19876
|
+
if (typeof revision !== "number" || !Number.isSafeInteger(revision) || revision < 0) {
|
|
19877
|
+
throw new Error("Invalid registry cache revision");
|
|
19878
|
+
}
|
|
19879
|
+
if ("invalidated" in value) {
|
|
19880
|
+
if (value.invalidated !== true || !("revision" in value) || revision === 0 || Object.keys(value).some((field) => field !== "ownerKey" && field !== "revision" && field !== "invalidated")) {
|
|
19881
|
+
throw new Error("Invalid registry cache tombstone");
|
|
19882
|
+
}
|
|
19883
|
+
return { ownerKey: key, revision, invalidated: true };
|
|
19884
|
+
}
|
|
19885
|
+
if (!("responseBody" in value) || typeof value.responseBody !== "string" || !("fetchedAt" in value) || typeof value.fetchedAt !== "number" || !Number.isSafeInteger(value.fetchedAt) || value.fetchedAt < 0 || !("sequence" in value) || typeof value.sequence !== "number" || !Number.isSafeInteger(value.sequence) || value.sequence < 0) {
|
|
19886
|
+
throw new Error("Invalid registry cache metadata");
|
|
19887
|
+
}
|
|
19888
|
+
return value;
|
|
19889
|
+
}
|
|
19890
|
+
var DEFAULT_REGISTRY_CACHE_STORAGE_KEY = "tinycloud.registryCache.v2";
|
|
19891
|
+
function createLocalStorageRegistryCacheStore(storage, storageKey = DEFAULT_REGISTRY_CACHE_STORAGE_KEY) {
|
|
19892
|
+
const browserGlobal = globalThis;
|
|
19893
|
+
const backing = storage ?? browserGlobal.localStorage;
|
|
19894
|
+
if (!backing) return createInMemoryRegistryCacheStore();
|
|
19895
|
+
const read2 = (key) => {
|
|
19896
|
+
const raw = backing.getItem(`${storageKey}:${key}`);
|
|
19897
|
+
if (raw === null) return void 0;
|
|
19898
|
+
const parsed = JSON.parse(raw);
|
|
19899
|
+
return validateRegistryCacheRecord(parsed, key);
|
|
19900
|
+
};
|
|
19901
|
+
let tail = Promise.resolve();
|
|
19902
|
+
return {
|
|
19903
|
+
async get(key) {
|
|
19904
|
+
await tail;
|
|
19905
|
+
const record2 = read2(key);
|
|
19906
|
+
return record2 && "invalidated" in record2 ? void 0 : record2;
|
|
19907
|
+
},
|
|
19908
|
+
async getRevision(key) {
|
|
19909
|
+
await tail;
|
|
19910
|
+
return read2(key)?.revision ?? 0;
|
|
19911
|
+
},
|
|
19912
|
+
update(key, update) {
|
|
19913
|
+
const commit = async () => {
|
|
19914
|
+
const record2 = read2(key);
|
|
19915
|
+
const revision = record2?.revision ?? 0;
|
|
19916
|
+
const entry = { ...await update(record2 && "invalidated" in record2 ? void 0 : record2, revision), revision };
|
|
19917
|
+
backing.setItem(`${storageKey}:${key}`, JSON.stringify(entry));
|
|
19918
|
+
return entry;
|
|
19919
|
+
};
|
|
19920
|
+
const next = tail.then(() => {
|
|
19921
|
+
const navigator = globalThis.navigator;
|
|
19922
|
+
const locks = navigator?.locks;
|
|
19923
|
+
if (!locks) throw new Error("Persistent registry cache requires Web Locks");
|
|
19924
|
+
return locks.request(`${storageKey}:${key}`, commit);
|
|
19925
|
+
});
|
|
19926
|
+
tail = next.catch(() => void 0);
|
|
19927
|
+
return next;
|
|
19928
|
+
},
|
|
19929
|
+
delete(key, expectedRevision, maximumSequence) {
|
|
19930
|
+
const next = tail.then(async () => {
|
|
19931
|
+
const navigator = globalThis.navigator;
|
|
19932
|
+
if (!navigator?.locks) throw new Error("Persistent registry cache requires Web Locks");
|
|
19933
|
+
await navigator.locks.request(`${storageKey}:${key}`, async () => {
|
|
19934
|
+
const record2 = read2(key);
|
|
19935
|
+
const revision = record2?.revision ?? 0;
|
|
19936
|
+
if (expectedRevision !== void 0 && (!Number.isSafeInteger(expectedRevision) || expectedRevision < 0 || expectedRevision !== revision)) {
|
|
19937
|
+
throw new Error("Registry cache revision changed before invalidation");
|
|
19938
|
+
}
|
|
19939
|
+
if (maximumSequence !== void 0 && (!Number.isSafeInteger(maximumSequence) || maximumSequence < 0 || record2 !== void 0 && !("invalidated" in record2) && record2.sequence > maximumSequence)) {
|
|
19940
|
+
throw new Error("Registry cache sequence advanced before invalidation");
|
|
19941
|
+
}
|
|
19942
|
+
if (revision === Number.MAX_SAFE_INTEGER) throw new Error("Registry cache revision overflow");
|
|
19943
|
+
backing.setItem(`${storageKey}:${key}`, JSON.stringify({ ownerKey: key, revision: revision + 1, invalidated: true }));
|
|
19944
|
+
});
|
|
19945
|
+
});
|
|
19946
|
+
tail = next.catch(() => void 0);
|
|
19947
|
+
return next;
|
|
19948
|
+
}
|
|
19949
|
+
};
|
|
19950
|
+
}
|
|
19951
|
+
async function verifyRegistryCacheEntry(subject, ownerKey, entry, rememberedSequence) {
|
|
19952
|
+
if (!entry || ownerKey !== registryOwnerKeyV2(subject) || entry.ownerKey !== ownerKey || typeof entry.responseBody !== "string" || !Number.isSafeInteger(entry.fetchedAt) || entry.fetchedAt < 0 || !Number.isSafeInteger(entry.sequence) || entry.sequence < 0 || entry.revision !== void 0 && (!Number.isSafeInteger(entry.revision) || entry.revision < 0)) {
|
|
19953
|
+
throw new Error("Invalid registry cache metadata");
|
|
19954
|
+
}
|
|
19955
|
+
const verdict = await classifyRegistryLookup(subject, { kind: "http", status: 200, body: entry.responseBody }, {
|
|
19956
|
+
recordVersion: 2,
|
|
19957
|
+
rememberedSequence
|
|
19958
|
+
});
|
|
19959
|
+
if (verdict.status !== "registered" || verdict.sequence !== entry.sequence || !verdict.record) {
|
|
19960
|
+
throw new Error("Registry cache verification failed");
|
|
19961
|
+
}
|
|
19962
|
+
return verdict;
|
|
19963
|
+
}
|
|
19964
|
+
|
|
19471
19965
|
// src/registry-policy.ts
|
|
19472
19966
|
var DEFAULT_REGISTRY_OWNER_CHAIN_ID = 1;
|
|
19473
19967
|
var DEFAULT_NEW_USER_HOST = DEFAULT_TINYCLOUD_FALLBACK_HOST;
|
|
19474
19968
|
var DEFAULT_REGISTRY_LOOKUP_TIMEOUT_MS = 3e3;
|
|
19475
|
-
var DEFAULT_REGISTRY_SEQUENCE_STORAGE_KEY = "tinycloud.registrySequences.
|
|
19969
|
+
var DEFAULT_REGISTRY_SEQUENCE_STORAGE_KEY = "tinycloud.registrySequences.v2";
|
|
19970
|
+
var REGISTRY_CACHE_MAX_AGE_MS = 36e5;
|
|
19476
19971
|
var MAX_SUBJECT_LENGTH = 74;
|
|
19477
19972
|
function registryOwnerSubjectV1(address, registryChainId = DEFAULT_REGISTRY_OWNER_CHAIN_ID) {
|
|
19478
19973
|
return pkhDid(address, registryChainId);
|
|
@@ -19518,33 +20013,272 @@ function createLocalStorageRegistrySequenceStore(storage, storageKey = DEFAULT_R
|
|
|
19518
20013
|
},
|
|
19519
20014
|
set: async (subject, sequence) => {
|
|
19520
20015
|
await memory.set(subject, sequence);
|
|
19521
|
-
const
|
|
19522
|
-
|
|
19523
|
-
|
|
19524
|
-
|
|
20016
|
+
const navigator = globalThis.navigator;
|
|
20017
|
+
const locks = navigator?.locks;
|
|
20018
|
+
if (!locks) throw new Error("Persistent registry sequence store requires Web Locks");
|
|
20019
|
+
await locks.request(storageKey, async () => {
|
|
20020
|
+
const remembered = await memory.get(subject);
|
|
20021
|
+
const sequences = read2();
|
|
20022
|
+
const previous = sequences.get(subject);
|
|
20023
|
+
const highest = Math.max(previous ?? -1, remembered ?? -1, sequence);
|
|
20024
|
+
await memory.set(subject, highest);
|
|
20025
|
+
if (previous !== void 0 && previous >= highest) return;
|
|
20026
|
+
sequences.set(subject, highest);
|
|
20027
|
+
backing.setItem(storageKey, JSON.stringify(Object.fromEntries(sequences)));
|
|
20028
|
+
});
|
|
19525
20029
|
}
|
|
19526
20030
|
};
|
|
19527
20031
|
}
|
|
19528
20032
|
function isSequence(value) {
|
|
19529
20033
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
19530
20034
|
}
|
|
19531
|
-
|
|
19532
|
-
if (typeof subject !== "string" ||
|
|
19533
|
-
subject.length > MAX_SUBJECT_LENGTH || !isCanonicalSubject(subject)) {
|
|
20035
|
+
function assertRegistrySubject(subject) {
|
|
20036
|
+
if (typeof subject !== "string" || subject.length > MAX_SUBJECT_LENGTH || !isCanonicalSubject(subject)) {
|
|
19534
20037
|
throw new RegistryLookupInputError(
|
|
19535
20038
|
"invalid-subject",
|
|
19536
20039
|
"subject must be did:pkh:eip155:<chainId>:<EIP-55 address> or an Ed25519 did:key"
|
|
19537
20040
|
);
|
|
19538
20041
|
}
|
|
20042
|
+
}
|
|
20043
|
+
async function resolveOwnerHosts(subject, options2 = {}) {
|
|
20044
|
+
assertRegistrySubject(subject);
|
|
20045
|
+
const ownerKey = registryOwnerKeyV2(subject);
|
|
20046
|
+
const store = options2.sequenceStore;
|
|
20047
|
+
const remembered = store === void 0 ? void 0 : await store.get(ownerKey);
|
|
20048
|
+
const cache2 = options2.cacheStore;
|
|
20049
|
+
const authority = cache2 ? cacheAuthority(cache2, ownerKey) : void 0;
|
|
20050
|
+
const highWater = Math.max(remembered ?? -1, authority?.sequence ?? -1);
|
|
20051
|
+
if (cache2) {
|
|
20052
|
+
const cached = await cache2.get(ownerKey);
|
|
20053
|
+
if (cached !== void 0) {
|
|
20054
|
+
const verdict = await verifyRegistryCacheEntry(subject, ownerKey, cached, highWater < 0 ? void 0 : highWater);
|
|
20055
|
+
authority.sequence = Math.max(highWater, verdict.sequence);
|
|
20056
|
+
await rememberAcceptedSequence(store, ownerKey, remembered, verdict);
|
|
20057
|
+
}
|
|
20058
|
+
return refreshCachedOwnerRegistry(subject, options2, cache2, ownerKey, Math.max(highWater, authority.sequence) < 0 ? void 0 : Math.max(highWater, authority.sequence));
|
|
20059
|
+
}
|
|
20060
|
+
return (await fetchOwnerRegistry(subject, options2, remembered)).verdict;
|
|
20061
|
+
}
|
|
20062
|
+
var cacheAuthorities = /* @__PURE__ */ new WeakMap();
|
|
20063
|
+
var verdictRevisions = /* @__PURE__ */ new WeakMap();
|
|
20064
|
+
function cacheAuthority(cache2, key) {
|
|
20065
|
+
let owners = cacheAuthorities.get(cache2);
|
|
20066
|
+
if (!owners) cacheAuthorities.set(cache2, owners = /* @__PURE__ */ new Map());
|
|
20067
|
+
let authority = owners.get(key);
|
|
20068
|
+
if (!authority) owners.set(key, authority = { sequence: -1, invalidated: false, invalidationAttempt: 0 });
|
|
20069
|
+
return authority;
|
|
20070
|
+
}
|
|
20071
|
+
var refreshes = /* @__PURE__ */ new WeakMap();
|
|
20072
|
+
async function refreshOwnerRegistry(subject, options2) {
|
|
20073
|
+
return resolveOwnerHosts(subject, options2);
|
|
20074
|
+
}
|
|
20075
|
+
async function refreshCachedOwnerRegistry(subject, options2, cache2, key, remembered) {
|
|
20076
|
+
let active = refreshes.get(cache2);
|
|
20077
|
+
if (!active) refreshes.set(cache2, active = /* @__PURE__ */ new Map());
|
|
20078
|
+
let next = active.get(key);
|
|
20079
|
+
if (!next) {
|
|
20080
|
+
next = (async () => {
|
|
20081
|
+
const current2 = await cache2.get(key);
|
|
20082
|
+
if (current2 !== void 0) {
|
|
20083
|
+
const verified = await verifyRegistryCacheEntry(subject, key, current2, remembered);
|
|
20084
|
+
await rememberAcceptedSequence(options2.sequenceStore, key, remembered, verified);
|
|
20085
|
+
}
|
|
20086
|
+
const highWater2 = Math.max(remembered ?? -1, current2?.sequence ?? -1);
|
|
20087
|
+
return fetchOwnerRegistry(subject, options2, highWater2 < 0 ? void 0 : highWater2, current2);
|
|
20088
|
+
})();
|
|
20089
|
+
active.set(key, next);
|
|
20090
|
+
void next.finally(() => active.delete(key)).catch(() => void 0);
|
|
20091
|
+
}
|
|
20092
|
+
const { verdict, allowCached, revision, invalidationAttempt } = await next;
|
|
20093
|
+
if (verdict.status === "unavailable" && !allowCached) return verdict;
|
|
20094
|
+
const current = await cache2.get(key);
|
|
20095
|
+
const latest = await options2.sequenceStore?.get(key);
|
|
20096
|
+
const authority = cacheAuthority(cache2, key);
|
|
20097
|
+
const highWater = Math.max(remembered ?? -1, latest ?? -1, authority.sequence);
|
|
20098
|
+
assertRegistryRevision(subject, await cache2.getRevision(key), revision);
|
|
20099
|
+
assertInvalidationAttempt(subject, authority.invalidationAttempt, invalidationAttempt);
|
|
20100
|
+
if (current !== void 0) {
|
|
20101
|
+
const verified = await verifyRegistryCacheEntry(subject, key, current, highWater < 0 ? void 0 : highWater);
|
|
20102
|
+
authority.sequence = Math.max(highWater, verified.sequence);
|
|
20103
|
+
await rememberAcceptedSequence(options2.sequenceStore, key, latest, verified);
|
|
20104
|
+
assertRegistryRevision(subject, await cache2.getRevision(key), revision);
|
|
20105
|
+
assertInvalidationAttempt(subject, authority.invalidationAttempt, invalidationAttempt);
|
|
20106
|
+
if (verdict.status === "unavailable") {
|
|
20107
|
+
const age = Date.now() - current.fetchedAt;
|
|
20108
|
+
if (!allowCached || authority.invalidated || age < 0 || age >= REGISTRY_CACHE_MAX_AGE_MS) return verdict;
|
|
20109
|
+
const fallback = { ...verified, offline: true };
|
|
20110
|
+
verdictRevisions.set(fallback, revision);
|
|
20111
|
+
return fallback;
|
|
20112
|
+
}
|
|
20113
|
+
verdictRevisions.set(verified, revision);
|
|
20114
|
+
return verified;
|
|
20115
|
+
}
|
|
20116
|
+
if (verdict.status === "unavailable") return verdict;
|
|
20117
|
+
if (verdict.status === "registered") {
|
|
20118
|
+
if (verdict.sequence < highWater) return {
|
|
20119
|
+
status: "unavailable",
|
|
20120
|
+
subject,
|
|
20121
|
+
source: "offchain",
|
|
20122
|
+
reason: "rollback",
|
|
20123
|
+
message: `registry refresh sequence ${verdict.sequence} is below remembered sequence ${highWater}`,
|
|
20124
|
+
hosts: []
|
|
20125
|
+
};
|
|
20126
|
+
await rememberAcceptedSequence(options2.sequenceStore, key, latest, verdict);
|
|
20127
|
+
assertRegistryRevision(subject, await cache2.getRevision(key), revision);
|
|
20128
|
+
assertInvalidationAttempt(subject, authority.invalidationAttempt, invalidationAttempt);
|
|
20129
|
+
verdictRevisions.set(verdict, revision);
|
|
20130
|
+
return verdict;
|
|
20131
|
+
}
|
|
20132
|
+
if (highWater >= 0) return {
|
|
20133
|
+
status: "unavailable",
|
|
20134
|
+
subject,
|
|
20135
|
+
source: "offchain",
|
|
20136
|
+
reason: "not-found-after-registered",
|
|
20137
|
+
message: "owner registration was remembered before registry absence could authorize onboarding",
|
|
20138
|
+
hosts: []
|
|
20139
|
+
};
|
|
20140
|
+
return verdict;
|
|
20141
|
+
}
|
|
20142
|
+
function assertRegistryRevision(subject, actual, expected) {
|
|
20143
|
+
if (!isSequence(actual) || actual !== expected) {
|
|
20144
|
+
throw new RegistryUnavailableError(subject, void 0, {
|
|
20145
|
+
message: "owner routing was invalidated while the registry lookup was in flight"
|
|
20146
|
+
});
|
|
20147
|
+
}
|
|
20148
|
+
}
|
|
20149
|
+
function assertInvalidationAttempt(subject, actual, expected) {
|
|
20150
|
+
if (!isSequence(actual) || actual !== expected) {
|
|
20151
|
+
throw new RegistryUnavailableError(subject, void 0, {
|
|
20152
|
+
message: "owner routing invalidation was attempted while the registry lookup was in flight"
|
|
20153
|
+
});
|
|
20154
|
+
}
|
|
20155
|
+
}
|
|
20156
|
+
async function fetchOwnerRegistry(subject, options2, remembered, cached) {
|
|
20157
|
+
const ownerKey = registryOwnerKeyV2(subject);
|
|
19539
20158
|
const store = options2.sequenceStore;
|
|
19540
|
-
const remembered = store === void 0 ? void 0 : await store.get(subject);
|
|
19541
20159
|
const registryUrl = options2.registryUrl ?? DEFAULT_TINYCLOUD_LOCATION_REGISTRY_URL;
|
|
19542
|
-
const
|
|
19543
|
-
const
|
|
19544
|
-
|
|
19545
|
-
|
|
19546
|
-
|
|
19547
|
-
);
|
|
20160
|
+
const baseUrl = registryUrl.replace(/\/$/, "");
|
|
20161
|
+
const fetchFn = options2.fetch ?? globalThis.fetch;
|
|
20162
|
+
const timeoutMs = options2.timeoutMs ?? DEFAULT_REGISTRY_LOOKUP_TIMEOUT_MS;
|
|
20163
|
+
const authority = options2.cacheStore ? cacheAuthority(options2.cacheStore, ownerKey) : void 0;
|
|
20164
|
+
const invalidationAttempt = authority?.invalidationAttempt;
|
|
20165
|
+
const revision = options2.cacheStore ? await options2.cacheStore.getRevision(ownerKey) : void 0;
|
|
20166
|
+
if (options2.cacheStore && !isSequence(revision)) throw new RegistryUnavailableError(subject, void 0, {
|
|
20167
|
+
message: "invalid registry cache revision"
|
|
20168
|
+
});
|
|
20169
|
+
if (revision !== void 0 && revision > 0) remembered = Math.max(remembered ?? -1, 0);
|
|
20170
|
+
if (authority) assertInvalidationAttempt(subject, authority.invalidationAttempt, invalidationAttempt);
|
|
20171
|
+
const observation = await observeRegistry(`${baseUrl}/v2/locations/${encodeURIComponent(ownerKey)}`, fetchFn, timeoutMs);
|
|
20172
|
+
const v2 = await classifyObservation(subject, observation, 2, remembered ?? null);
|
|
20173
|
+
if (v2.status === "registered" && options2.cacheStore && observation.kind === "http") {
|
|
20174
|
+
const candidate = { ownerKey, responseBody: observation.body, fetchedAt: Date.now(), sequence: v2.sequence };
|
|
20175
|
+
let accepted;
|
|
20176
|
+
let equalSequenceConflict = false;
|
|
20177
|
+
try {
|
|
20178
|
+
accepted = await options2.cacheStore.update(ownerKey, async (current, currentRevision) => {
|
|
20179
|
+
assertRegistryRevision(subject, currentRevision, revision);
|
|
20180
|
+
assertInvalidationAttempt(subject, authority.invalidationAttempt, invalidationAttempt);
|
|
20181
|
+
const highest = Math.max(await store?.get(ownerKey) ?? -1, authority.sequence);
|
|
20182
|
+
const highWater = highest < 0 ? void 0 : highest;
|
|
20183
|
+
if (current !== void 0) {
|
|
20184
|
+
const previous = await verifyRegistryCacheEntry(subject, ownerKey, current, highWater);
|
|
20185
|
+
assertInvalidationAttempt(subject, authority.invalidationAttempt, invalidationAttempt);
|
|
20186
|
+
if (v2.sequence < previous.sequence) return current;
|
|
20187
|
+
if (v2.sequence === previous.sequence) {
|
|
20188
|
+
if (canonicalLocationPayloadV2(v2.record) !== canonicalLocationPayloadV2(previous.record)) {
|
|
20189
|
+
equalSequenceConflict = true;
|
|
20190
|
+
throw new RegistryUnavailableError(subject, void 0, {
|
|
20191
|
+
message: "equal-sequence registry response changes the accepted signed payload"
|
|
20192
|
+
});
|
|
20193
|
+
}
|
|
20194
|
+
return { ...current, fetchedAt: candidate.fetchedAt };
|
|
20195
|
+
}
|
|
20196
|
+
}
|
|
20197
|
+
await verifyRegistryCacheEntry(subject, ownerKey, candidate, highWater);
|
|
20198
|
+
assertInvalidationAttempt(subject, authority.invalidationAttempt, invalidationAttempt);
|
|
20199
|
+
return candidate;
|
|
20200
|
+
});
|
|
20201
|
+
} catch (error) {
|
|
20202
|
+
if (equalSequenceConflict) await dropOwnerRegistryCache(subject, options2.cacheStore, store, ownerKey, v2.sequence, revision);
|
|
20203
|
+
throw error;
|
|
20204
|
+
}
|
|
20205
|
+
const verdict = await verifyRegistryCacheEntry(subject, ownerKey, accepted);
|
|
20206
|
+
assertInvalidationAttempt(subject, authority.invalidationAttempt, invalidationAttempt);
|
|
20207
|
+
authority.sequence = Math.max(authority.sequence, verdict.sequence);
|
|
20208
|
+
authority.invalidated = false;
|
|
20209
|
+
await rememberAcceptedSequence(store, ownerKey, remembered, verdict);
|
|
20210
|
+
return { verdict, revision, invalidationAttempt };
|
|
20211
|
+
}
|
|
20212
|
+
if (options2.cacheStore && (cached !== void 0 || remembered !== void 0) && observation.kind === "http" && observation.status >= 400 && observation.status < 500) {
|
|
20213
|
+
await dropOwnerRegistryCache(subject, options2.cacheStore, store, ownerKey, Math.max(remembered ?? 0, cached?.sequence ?? 0), revision);
|
|
20214
|
+
}
|
|
20215
|
+
const fresh = cached === void 0 && remembered === void 0;
|
|
20216
|
+
const coldOffline = fresh && v2.status === "unavailable" && registryOutage(observation);
|
|
20217
|
+
if (v2.status !== "unregistered" && !coldOffline) {
|
|
20218
|
+
await rememberAcceptedSequence(store, ownerKey, remembered, v2);
|
|
20219
|
+
const allowCached = v2.status === "unavailable" && (observation.kind === "network-error" || observation.kind === "timeout" || observation.kind === "http" && [502, 503, 504].includes(observation.status));
|
|
20220
|
+
return { verdict: v2, allowCached, revision, invalidationAttempt };
|
|
20221
|
+
}
|
|
20222
|
+
const v1Subject = legacyRegistrySubject(subject, options2.registryChainId);
|
|
20223
|
+
const v1Observation = await observeRegistry(`${baseUrl}/v1/locations/${encodeURIComponent(v1Subject)}`, fetchFn, timeoutMs);
|
|
20224
|
+
const v1 = await classifyObservation(v1Subject, v1Observation, 1, null);
|
|
20225
|
+
if (v1.status === "registered") {
|
|
20226
|
+
await store?.set(ownerKey, Math.max(remembered ?? 0, 0));
|
|
20227
|
+
return { verdict: {
|
|
20228
|
+
status: "unavailable",
|
|
20229
|
+
subject,
|
|
20230
|
+
source: "offchain",
|
|
20231
|
+
reason: "unsupported-format",
|
|
20232
|
+
message: "owner has a v1 location record but no signed-v2 routing record",
|
|
20233
|
+
hosts: []
|
|
20234
|
+
}, revision, invalidationAttempt };
|
|
20235
|
+
}
|
|
20236
|
+
const legacyOffline = fresh && v1.status === "unavailable" && registryOutage(v1Observation);
|
|
20237
|
+
if (v1.status === "unavailable" && !legacyOffline) return { verdict: { ...v1, subject }, revision, invalidationAttempt };
|
|
20238
|
+
return { verdict: { status: "unregistered", subject, source: "offchain", hosts: [], ...coldOffline || legacyOffline ? { offline: true } : {} }, revision, invalidationAttempt };
|
|
20239
|
+
}
|
|
20240
|
+
async function dropOwnerRegistryCache(subject, cache2, store, key, sequence, expectedRevision) {
|
|
20241
|
+
if (!isSequence(expectedRevision)) throw new RegistryUnavailableError(subject, void 0, {
|
|
20242
|
+
message: "registry invalidation has no accepted origin revision"
|
|
20243
|
+
});
|
|
20244
|
+
const authority = cacheAuthority(cache2, key);
|
|
20245
|
+
authority.sequence = Math.max(authority.sequence, sequence);
|
|
20246
|
+
authority.invalidated = true;
|
|
20247
|
+
authority.invalidationAttempt++;
|
|
20248
|
+
try {
|
|
20249
|
+
if (store) await store.set(key, Math.max(authority.sequence, await store.get(key) ?? -1));
|
|
20250
|
+
await cache2.delete(key, expectedRevision, sequence);
|
|
20251
|
+
} catch (cause) {
|
|
20252
|
+
throw new RegistryUnavailableError(subject, void 0, { message: "cached storage settings could not be invalidated", cause });
|
|
20253
|
+
}
|
|
20254
|
+
}
|
|
20255
|
+
var sequenceWriteWarned = false;
|
|
20256
|
+
function registryOutage(observation) {
|
|
20257
|
+
return observation.kind === "network-error" || observation.kind === "timeout" || observation.kind === "http" && observation.status >= 500 && observation.status < 600;
|
|
20258
|
+
}
|
|
20259
|
+
var fallbackWarnings = /* @__PURE__ */ new WeakMap();
|
|
20260
|
+
function warnRegistryDefaultNode(verdict, cacheStore, onRegistryFallback) {
|
|
20261
|
+
if (verdict.status !== "unregistered" || !verdict.offline) return;
|
|
20262
|
+
warnRegistryFallback(verdict.subject, "using_default_node", cacheStore, onRegistryFallback);
|
|
20263
|
+
}
|
|
20264
|
+
function warnRegistryCachedSettings(verdict, options2) {
|
|
20265
|
+
if (verdict.status === "registered" && verdict.offline) {
|
|
20266
|
+
warnRegistryFallback(verdict.subject, "using_cached_settings", options2.cacheStore, options2.onRegistryFallback);
|
|
20267
|
+
}
|
|
20268
|
+
}
|
|
20269
|
+
function warnRegistryFallback(subject, code3, cacheStore, onRegistryFallback) {
|
|
20270
|
+
if (cacheStore) {
|
|
20271
|
+
const key = `${code3}:${registryOwnerKeyV2(subject)}`;
|
|
20272
|
+
let warned = fallbackWarnings.get(cacheStore);
|
|
20273
|
+
if (!warned) fallbackWarnings.set(cacheStore, warned = /* @__PURE__ */ new Set());
|
|
20274
|
+
if (warned.has(key)) return;
|
|
20275
|
+
warned.add(key);
|
|
20276
|
+
}
|
|
20277
|
+
const reason = code3 === "using_default_node" ? "registry unavailable; using the default node" : "Storage settings are unreachable; using cached settings checked less than 1 hour ago.";
|
|
20278
|
+
if (onRegistryFallback) onRegistryFallback(reason, code3);
|
|
20279
|
+
else console.warn(reason);
|
|
20280
|
+
}
|
|
20281
|
+
async function classifyObservation(subject, observed, recordVersion, rememberedSequence) {
|
|
19548
20282
|
if (observed.kind === "oversize" || observed.kind === "invalid-utf8") {
|
|
19549
20283
|
const oversize = observed.kind === "oversize";
|
|
19550
20284
|
return {
|
|
@@ -19556,22 +20290,23 @@ async function resolveOwnerHosts(subject, options2 = {}) {
|
|
|
19556
20290
|
hosts: []
|
|
19557
20291
|
};
|
|
19558
20292
|
}
|
|
19559
|
-
|
|
19560
|
-
|
|
19561
|
-
|
|
19562
|
-
if (store
|
|
19563
|
-
|
|
19564
|
-
|
|
19565
|
-
|
|
19566
|
-
|
|
19567
|
-
|
|
19568
|
-
|
|
19569
|
-
}
|
|
20293
|
+
return classifyRegistryLookup(subject, observed, { rememberedSequence, recordVersion });
|
|
20294
|
+
}
|
|
20295
|
+
async function rememberAcceptedSequence(store, ownerKey, remembered, verdict) {
|
|
20296
|
+
if (store === void 0 || verdict.status !== "registered" || remembered !== void 0 && verdict.sequence <= remembered) return;
|
|
20297
|
+
try {
|
|
20298
|
+
await store.set(ownerKey, verdict.sequence);
|
|
20299
|
+
} catch (error) {
|
|
20300
|
+
if (!sequenceWriteWarned) {
|
|
20301
|
+
sequenceWriteWarned = true;
|
|
20302
|
+
console.warn("[tinycloud:registry] could not remember the registry sequence:", error);
|
|
19570
20303
|
}
|
|
19571
20304
|
}
|
|
19572
|
-
return verdict;
|
|
19573
20305
|
}
|
|
19574
|
-
|
|
20306
|
+
function legacyRegistrySubject(subject, registryChainId) {
|
|
20307
|
+
if (subject.startsWith("did:key:")) return subject;
|
|
20308
|
+
return registryOwnerSubjectV1(subject.slice(subject.lastIndexOf(":") + 1), registryChainId);
|
|
20309
|
+
}
|
|
19575
20310
|
async function observeRegistry(url, fetchFn, timeoutMs) {
|
|
19576
20311
|
const controller = new AbortController();
|
|
19577
20312
|
let cancelTimer = () => void 0;
|
|
@@ -19593,6 +20328,8 @@ async function readRegistry(url, fetchFn, signal) {
|
|
|
19593
20328
|
try {
|
|
19594
20329
|
response = await fetchFn(url, {
|
|
19595
20330
|
redirect: "error",
|
|
20331
|
+
// A browser-cached HTTP 200 must not renew signed-cache freshness.
|
|
20332
|
+
cache: "no-store",
|
|
19596
20333
|
headers: { accept: "application/json" },
|
|
19597
20334
|
signal
|
|
19598
20335
|
});
|
|
@@ -19614,7 +20351,7 @@ async function readRegistry(url, fetchFn, signal) {
|
|
|
19614
20351
|
return {
|
|
19615
20352
|
kind: "http",
|
|
19616
20353
|
status: response.status,
|
|
19617
|
-
body: new TextDecoder("utf-8", { fatal: true }).decode(bytes2)
|
|
20354
|
+
body: new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes2)
|
|
19618
20355
|
};
|
|
19619
20356
|
} catch {
|
|
19620
20357
|
return { kind: "invalid-utf8" };
|
|
@@ -19658,12 +20395,15 @@ async function lookupOwnerRegistryVerdict(subject, options2 = {}) {
|
|
|
19658
20395
|
message: "no location registry is configured"
|
|
19659
20396
|
});
|
|
19660
20397
|
}
|
|
20398
|
+
let verdict;
|
|
19661
20399
|
try {
|
|
19662
|
-
|
|
20400
|
+
verdict = await resolveOwnerHosts(subject, {
|
|
19663
20401
|
registryUrl: options2.registryUrl,
|
|
19664
20402
|
sequenceStore: options2.sequenceStore,
|
|
20403
|
+
cacheStore: options2.cacheStore,
|
|
19665
20404
|
fetch: options2.fetch,
|
|
19666
|
-
timeoutMs: options2.timeoutMs
|
|
20405
|
+
timeoutMs: options2.timeoutMs,
|
|
20406
|
+
registryChainId: options2.registryChainId
|
|
19667
20407
|
});
|
|
19668
20408
|
} catch (error) {
|
|
19669
20409
|
throw new RegistryUnavailableError(subject, void 0, {
|
|
@@ -19671,36 +20411,88 @@ async function lookupOwnerRegistryVerdict(subject, options2 = {}) {
|
|
|
19671
20411
|
cause: error
|
|
19672
20412
|
});
|
|
19673
20413
|
}
|
|
20414
|
+
if (verdict.status === "unregistered" && verdict.offline) {
|
|
20415
|
+
throw Object.assign(new RegistryUnavailableError(subject, void 0, {
|
|
20416
|
+
message: "Couldn't reach your storage settings, and none are saved on this computer yet, so this didn't run. Try again in a few minutes."
|
|
20417
|
+
}), { meta: { reason: "no-saved-settings" } });
|
|
20418
|
+
}
|
|
20419
|
+
return verdict;
|
|
20420
|
+
}
|
|
20421
|
+
async function resolveRegistryResource(subject, resource, access, options2) {
|
|
20422
|
+
const verdict = await lookupOwnerRegistryVerdict(subject, options2);
|
|
20423
|
+
if (verdict.status === "unregistered") return { verdict, hosts: [] };
|
|
20424
|
+
if (verdict.status === "unavailable") throw new RegistryUnavailableError(subject, verdict);
|
|
20425
|
+
const record2 = verdict.record;
|
|
20426
|
+
if (record2 === void 0) {
|
|
20427
|
+
throw new RegistryUnavailableError(subject, void 0, {
|
|
20428
|
+
message: "registered owner has no verified v2 routing record"
|
|
20429
|
+
});
|
|
20430
|
+
}
|
|
20431
|
+
const denyRoute = async () => {
|
|
20432
|
+
if (options2.cacheStore) {
|
|
20433
|
+
await dropOwnerRegistryCache(subject, options2.cacheStore, options2.sequenceStore, registryOwnerKeyV2(subject), verdict.sequence, verdictRevisions.get(verdict));
|
|
20434
|
+
}
|
|
20435
|
+
throw new NodeNotFoundError(subject, verdict);
|
|
20436
|
+
};
|
|
20437
|
+
let targetDids;
|
|
20438
|
+
if (access === "encryption") {
|
|
20439
|
+
if (record2.encryptionNode === null) return denyRoute();
|
|
20440
|
+
targetDids = [record2.encryptionNode];
|
|
20441
|
+
} else {
|
|
20442
|
+
const space = record2.spaces.find(({ space: signedSpace }) => sameSpaceIdentifier(signedSpace, resource));
|
|
20443
|
+
if (space === void 0) return denyRoute();
|
|
20444
|
+
targetDids = space.nodes.filter(({ mode }) => access !== "write" || mode === "read-write").map(({ did }) => did);
|
|
20445
|
+
}
|
|
20446
|
+
const nodeByDid = new Map(record2.nodes.map((node) => [node.did, node.url]));
|
|
20447
|
+
const hosts = targetDids.map((nodeDid) => {
|
|
20448
|
+
const url = nodeByDid.get(nodeDid);
|
|
20449
|
+
if (url === void 0) {
|
|
20450
|
+
throw new RegistryUnavailableError(subject, void 0, {
|
|
20451
|
+
message: "verified membership references a missing node"
|
|
20452
|
+
});
|
|
20453
|
+
}
|
|
20454
|
+
return { nodeDid, urls: [url], source: "offchain" };
|
|
20455
|
+
});
|
|
20456
|
+
if (hosts.length === 0) return denyRoute();
|
|
20457
|
+
warnRegistryCachedSettings(verdict, options2);
|
|
20458
|
+
return { verdict, hosts };
|
|
20459
|
+
}
|
|
20460
|
+
function sameSpaceIdentifier(signedSpace, requestedSpace) {
|
|
20461
|
+
const signed = /^tinycloud:pkh:eip155:([1-9][0-9]*):(0x[a-fA-F0-9]{40}):([^/?#\s]+)$/.exec(signedSpace);
|
|
20462
|
+
const requested = /^tinycloud:pkh:eip155:([1-9][0-9]*):(0x[a-fA-F0-9]{40}):([^/?#\s]+)$/.exec(requestedSpace);
|
|
20463
|
+
if (signed === null || requested === null) return signedSpace === requestedSpace;
|
|
20464
|
+
return signed[1] === requested[1] && signed[2].toLowerCase() === requested[2].toLowerCase() && signed[3] === requested[3];
|
|
19674
20465
|
}
|
|
19675
20466
|
async function resolveFailClosedTinyCloudHosts(address, options2 = {}) {
|
|
19676
20467
|
const newUserHost = options2.newUserHost === null ? null : exactHttpOrigin(options2.newUserHost ?? DEFAULT_NEW_USER_HOST, "newUserHost");
|
|
19677
20468
|
const subject = registryOwnerSubjectV1(address, options2.registryChainId);
|
|
19678
20469
|
const resolve = async () => {
|
|
19679
|
-
|
|
19680
|
-
|
|
20470
|
+
const verdict = await lookupOwnerRegistryVerdict(subject, options2);
|
|
20471
|
+
if (verdict.status === "unregistered" && (options2.autoDiscoverLocalNode ?? true)) {
|
|
20472
|
+
const local = await discoverLocalTinyCloudNode({
|
|
20473
|
+
localNodeUrl: options2.localNodeUrl,
|
|
20474
|
+
localLinkName: options2.localLinkName,
|
|
20475
|
+
expectedNodeDid: options2.expectedNodeDid,
|
|
20476
|
+
identityStore: options2.localNodeIdentityStore,
|
|
20477
|
+
fetch: options2.fetch
|
|
20478
|
+
});
|
|
20479
|
+
if (local) {
|
|
20480
|
+
warnRegistryDefaultNode(verdict, options2.cacheStore, options2.onRegistryFallback);
|
|
20481
|
+
return { hosts: [local.url], verdict };
|
|
20482
|
+
}
|
|
20483
|
+
}
|
|
20484
|
+
let hosts;
|
|
19681
20485
|
try {
|
|
19682
|
-
|
|
20486
|
+
hosts = hostsForVerdict(verdict, newUserHost);
|
|
19683
20487
|
} catch (error) {
|
|
19684
|
-
if (
|
|
19685
|
-
|
|
19686
|
-
}
|
|
19687
|
-
if (options2.autoDiscoverLocalNode ?? true) {
|
|
19688
|
-
const linkNames = verdict?.status === "registered" ? secureRecordHosts(verdict).flatMap((host) => LOCAL_LINK_ORIGIN.exec(host)?.[1] ?? []) : [];
|
|
19689
|
-
for (const candidate of [
|
|
19690
|
-
{ localNodeUrl: options2.localNodeUrl, localLinkName: options2.localLinkName },
|
|
19691
|
-
...linkNames.map((localLinkName) => ({ localLinkName }))
|
|
19692
|
-
]) {
|
|
19693
|
-
const local = await discoverLocalTinyCloudNode({
|
|
19694
|
-
...candidate,
|
|
19695
|
-
expectedNodeDid: options2.expectedNodeDid,
|
|
19696
|
-
identityStore: options2.localNodeIdentityStore,
|
|
19697
|
-
fetch: options2.fetch
|
|
19698
|
-
});
|
|
19699
|
-
if (local) return { hosts: [local.url], ...verdict === void 0 ? {} : { verdict } };
|
|
20488
|
+
if (error instanceof NodeNotFoundError && error.verdict.status === "registered" && options2.cacheStore) {
|
|
20489
|
+
await dropOwnerRegistryCache(subject, options2.cacheStore, options2.sequenceStore, registryOwnerKeyV2(subject), error.verdict.sequence, verdictRevisions.get(error.verdict));
|
|
19700
20490
|
}
|
|
20491
|
+
throw error;
|
|
19701
20492
|
}
|
|
19702
|
-
|
|
19703
|
-
|
|
20493
|
+
warnRegistryCachedSettings(verdict, options2);
|
|
20494
|
+
warnRegistryDefaultNode(verdict, options2.cacheStore, options2.onRegistryFallback);
|
|
20495
|
+
return { hosts, verdict };
|
|
19704
20496
|
};
|
|
19705
20497
|
for (let attempt = 1; ; attempt++) {
|
|
19706
20498
|
let failure;
|
|
@@ -19718,10 +20510,12 @@ async function resolveFailClosedTinyCloudHosts(address, options2 = {}) {
|
|
|
19718
20510
|
case "retry":
|
|
19719
20511
|
continue;
|
|
19720
20512
|
case "use-host":
|
|
19721
|
-
|
|
19722
|
-
|
|
19723
|
-
|
|
19724
|
-
|
|
20513
|
+
if (!(failure instanceof NodeNotFoundError) || failure.verdict.status !== "unregistered") {
|
|
20514
|
+
throw failure;
|
|
20515
|
+
}
|
|
20516
|
+
const host = exactHttpOrigin(decision.host, "onNodeNotFound host");
|
|
20517
|
+
warnRegistryDefaultNode(failure.verdict, options2.cacheStore, options2.onRegistryFallback);
|
|
20518
|
+
return { hosts: [host], verdict: failure.verdict };
|
|
19725
20519
|
case "abort":
|
|
19726
20520
|
throw failure;
|
|
19727
20521
|
default:
|
|
@@ -19729,7 +20523,6 @@ async function resolveFailClosedTinyCloudHosts(address, options2 = {}) {
|
|
|
19729
20523
|
}
|
|
19730
20524
|
}
|
|
19731
20525
|
}
|
|
19732
|
-
var LOCAL_LINK_ORIGIN = /^https:\/\/([a-z0-9-]+)\.local\.tinycloud\.link$/;
|
|
19733
20526
|
function secureRecordHosts(verdict) {
|
|
19734
20527
|
const hosts = /* @__PURE__ */ new Set();
|
|
19735
20528
|
for (const host of verdict.hosts) {
|
|
@@ -19756,7 +20549,7 @@ function hostsForVerdict(verdict, newUserHost) {
|
|
|
19756
20549
|
throw new RegistryUnavailableError(verdict.subject, verdict);
|
|
19757
20550
|
}
|
|
19758
20551
|
}
|
|
19759
|
-
function assertSpaceCreationAllowed(verdict, host) {
|
|
20552
|
+
function assertSpaceCreationAllowed(verdict, host, spaceId) {
|
|
19760
20553
|
switch (verdict.status) {
|
|
19761
20554
|
case "unregistered":
|
|
19762
20555
|
return;
|
|
@@ -19764,7 +20557,14 @@ function assertSpaceCreationAllowed(verdict, host) {
|
|
|
19764
20557
|
throw new RegistryUnavailableError(verdict.subject, verdict);
|
|
19765
20558
|
case "registered": {
|
|
19766
20559
|
const origin2 = httpOrigin(host);
|
|
19767
|
-
if (origin2 !== void 0 &&
|
|
20560
|
+
if (origin2 !== void 0 && verdict.record !== void 0) {
|
|
20561
|
+
const space = spaceId === void 0 ? void 0 : verdict.record.spaces.find(({ space: signedSpace }) => sameSpaceIdentifier(signedSpace, spaceId));
|
|
20562
|
+
const memberNodes = new Set(
|
|
20563
|
+
space?.nodes.map(({ did }) => did) ?? []
|
|
20564
|
+
);
|
|
20565
|
+
const spaceHosts = verdict.record.nodes.filter(({ did }) => memberNodes.has(did)).map(({ url }) => httpOrigin(url));
|
|
20566
|
+
if (spaceHosts.includes(origin2)) return;
|
|
20567
|
+
} else if (origin2 !== void 0 && secureRecordHosts(verdict).includes(origin2)) {
|
|
19768
20568
|
return;
|
|
19769
20569
|
}
|
|
19770
20570
|
throw new NodeRegistryEntryExistsError(verdict.subject, host, verdict);
|
|
@@ -19829,6 +20629,7 @@ export {
|
|
|
19829
20629
|
DEFAULT_MANIFEST_SPACE,
|
|
19830
20630
|
DEFAULT_MANIFEST_VERSION,
|
|
19831
20631
|
DEFAULT_NEW_USER_HOST,
|
|
20632
|
+
DEFAULT_REGISTRY_CACHE_STORAGE_KEY,
|
|
19832
20633
|
DEFAULT_REGISTRY_LOOKUP_TIMEOUT_MS,
|
|
19833
20634
|
DEFAULT_REGISTRY_OWNER_CHAIN_ID,
|
|
19834
20635
|
DEFAULT_REGISTRY_SEQUENCE_STORAGE_KEY,
|
|
@@ -19920,6 +20721,7 @@ export {
|
|
|
19920
20721
|
PermissionNotInManifestError,
|
|
19921
20722
|
PrefixedKVService,
|
|
19922
20723
|
ProtocolMismatchError,
|
|
20724
|
+
REGISTRY_CACHE_MAX_AGE_MS,
|
|
19923
20725
|
REGISTRY_LOOKUP_MAX_BODY_BYTES,
|
|
19924
20726
|
REGISTRY_NOT_FOUND_BODY,
|
|
19925
20727
|
REQUESTER_ENGINE_RETRY_ATTEMPTS,
|
|
@@ -20020,6 +20822,7 @@ export {
|
|
|
20020
20822
|
canonicalEmailDomain,
|
|
20021
20823
|
canonicalHashHex,
|
|
20022
20824
|
canonicalLocationPayload,
|
|
20825
|
+
canonicalLocationPayloadV2,
|
|
20023
20826
|
canonicalMailbox,
|
|
20024
20827
|
canonicalOwnerSharePolicy,
|
|
20025
20828
|
canonicalSignedResponse,
|
|
@@ -20053,8 +20856,10 @@ export {
|
|
|
20053
20856
|
createEmailDomainCredentialRequirement,
|
|
20054
20857
|
createHolderBinding,
|
|
20055
20858
|
createInMemoryLocalNodeIdentityStore,
|
|
20859
|
+
createInMemoryRegistryCacheStore,
|
|
20056
20860
|
createInMemoryRegistrySequenceStore,
|
|
20057
20861
|
createLocalStorageLocalNodeIdentityStore,
|
|
20862
|
+
createLocalStorageRegistryCacheStore,
|
|
20058
20863
|
createLocalStorageRegistrySequenceStore,
|
|
20059
20864
|
createOpenKeyCallbackSigningStrategy,
|
|
20060
20865
|
createOpenKeyManageKeySigningStrategy,
|
|
@@ -20106,6 +20911,7 @@ export {
|
|
|
20106
20911
|
utf8Encode as encryptionUtf8Encode,
|
|
20107
20912
|
ensureNetworkUsableForDecrypt,
|
|
20108
20913
|
err5 as err,
|
|
20914
|
+
exactHttpOrigin,
|
|
20109
20915
|
expandActionShortNames,
|
|
20110
20916
|
expandDelegationPermissionEntries,
|
|
20111
20917
|
expandPermissionEntries,
|
|
@@ -20189,7 +20995,9 @@ export {
|
|
|
20189
20995
|
projectUnifiedPolicyCapability,
|
|
20190
20996
|
publishLocationRecord,
|
|
20191
20997
|
recapCaveatsEqual,
|
|
20998
|
+
refreshOwnerRegistry,
|
|
20192
20999
|
registerPolicyV3,
|
|
21000
|
+
registryOwnerKeyV2,
|
|
20193
21001
|
registryOwnerSubjectV1,
|
|
20194
21002
|
renewPolicyRootStatusV3,
|
|
20195
21003
|
requestTinyCloudManageKeyScope,
|
|
@@ -20198,6 +21006,7 @@ export {
|
|
|
20198
21006
|
resolveManifest,
|
|
20199
21007
|
resolveManifestKnowledgeRoot,
|
|
20200
21008
|
resolveOwnerHosts,
|
|
21009
|
+
resolveRegistryResource,
|
|
20201
21010
|
resolveSecretListPrefix,
|
|
20202
21011
|
resolveSecretPath2 as resolveSecretPath,
|
|
20203
21012
|
resolveTinyCloudHosts,
|
|
@@ -20215,6 +21024,7 @@ export {
|
|
|
20215
21024
|
signCompactUcanAuthorization,
|
|
20216
21025
|
signCompactUcanRootAuthorization,
|
|
20217
21026
|
signLocationRecord,
|
|
21027
|
+
signLocationRecordV2,
|
|
20218
21028
|
signPolicyCredentialPresentationV4,
|
|
20219
21029
|
signShareEnvelope,
|
|
20220
21030
|
signedObjectIdFor,
|
|
@@ -20236,6 +21046,8 @@ export {
|
|
|
20236
21046
|
validateEnvelope,
|
|
20237
21047
|
validateLocationRecord,
|
|
20238
21048
|
validateLocationRecordPayload,
|
|
21049
|
+
validateLocationRecordV2,
|
|
21050
|
+
validateLocationRecordV2Payload,
|
|
20239
21051
|
validateManifest,
|
|
20240
21052
|
validateOwnerSharePolicyRegistration,
|
|
20241
21053
|
validateOwnerSharePolicyRegistrationBytes,
|
|
@@ -20257,17 +21069,20 @@ export {
|
|
|
20257
21069
|
verifyEip191MessageSignature,
|
|
20258
21070
|
verifyIssuedCredential,
|
|
20259
21071
|
verifyLocationRecord,
|
|
21072
|
+
verifyLocationRecordV2,
|
|
20260
21073
|
verifyOwnerNodeBinding,
|
|
20261
21074
|
verifyPolicy,
|
|
20262
21075
|
verifyPolicyEngineRecord,
|
|
20263
21076
|
verifyPolicyRootRevocationV3,
|
|
20264
21077
|
verifyPolicyRootStatusCheckpointV3,
|
|
20265
21078
|
verifyPolicyStatus,
|
|
21079
|
+
verifyRegistryCacheEntry,
|
|
20266
21080
|
verifyShareCid,
|
|
20267
21081
|
verifyShareDeliveryAuthorizationV3Bytes,
|
|
20268
21082
|
verifyShareEnvelope,
|
|
20269
21083
|
verifySignedObject,
|
|
20270
21084
|
verifyStorageReceipt,
|
|
21085
|
+
warnRegistryDefaultNode,
|
|
20271
21086
|
withoutShareLink
|
|
20272
21087
|
};
|
|
20273
21088
|
//# sourceMappingURL=index.js.map
|