@tinycloud/sdk-core 3.3.0-beta.17 → 3.3.0-beta.20
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 +731 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +336 -1
- package/dist/index.d.ts +336 -1
- package/dist/index.js +731 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -86,6 +86,10 @@ __export(index_exports, {
|
|
|
86
86
|
DEFAULT_LOCAL_NODE_URL: () => DEFAULT_LOCAL_NODE_URL,
|
|
87
87
|
DEFAULT_MANIFEST_SPACE: () => DEFAULT_MANIFEST_SPACE,
|
|
88
88
|
DEFAULT_MANIFEST_VERSION: () => DEFAULT_MANIFEST_VERSION,
|
|
89
|
+
DEFAULT_NEW_USER_HOST: () => DEFAULT_NEW_USER_HOST,
|
|
90
|
+
DEFAULT_REGISTRY_LOOKUP_TIMEOUT_MS: () => DEFAULT_REGISTRY_LOOKUP_TIMEOUT_MS,
|
|
91
|
+
DEFAULT_REGISTRY_OWNER_CHAIN_ID: () => DEFAULT_REGISTRY_OWNER_CHAIN_ID,
|
|
92
|
+
DEFAULT_REGISTRY_SEQUENCE_STORAGE_KEY: () => DEFAULT_REGISTRY_SEQUENCE_STORAGE_KEY,
|
|
89
93
|
DEFAULT_SIGNED_READ_URL_EXPIRY_MS: () => DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
|
|
90
94
|
DEFAULT_TINYCLOUD_FALLBACK_HOST: () => DEFAULT_TINYCLOUD_FALLBACK_HOST,
|
|
91
95
|
DEFAULT_TINYCLOUD_LOCATION_REGISTRY_URL: () => DEFAULT_TINYCLOUD_LOCATION_REGISTRY_URL,
|
|
@@ -142,6 +146,8 @@ __export(index_exports, {
|
|
|
142
146
|
NATIVE_PROJECTION_V1_DOMAIN: () => NATIVE_PROJECTION_V1_DOMAIN,
|
|
143
147
|
NETWORK_NAME_PATTERN: () => import_sdk_services9.NETWORK_NAME_PATTERN,
|
|
144
148
|
NetworkIdError: () => import_sdk_services9.NetworkIdError,
|
|
149
|
+
NodeNotFoundError: () => NodeNotFoundError,
|
|
150
|
+
NodeRegistryEntryExistsError: () => NodeRegistryEntryExistsError,
|
|
145
151
|
OPENKEY_ACTION_ID_SEPARATOR: () => OPENKEY_ACTION_ID_SEPARATOR,
|
|
146
152
|
OWNER_NODE_BINDING_TIMEOUT_MS: () => OWNER_NODE_BINDING_TIMEOUT_MS,
|
|
147
153
|
OpenKeyManageKeyError: () => OpenKeyManageKeyError,
|
|
@@ -170,6 +176,8 @@ __export(index_exports, {
|
|
|
170
176
|
PermissionNotInManifestError: () => PermissionNotInManifestError,
|
|
171
177
|
PrefixedKVService: () => import_sdk_services9.PrefixedKVService,
|
|
172
178
|
ProtocolMismatchError: () => ProtocolMismatchError,
|
|
179
|
+
REGISTRY_LOOKUP_MAX_BODY_BYTES: () => REGISTRY_LOOKUP_MAX_BODY_BYTES,
|
|
180
|
+
REGISTRY_NOT_FOUND_BODY: () => REGISTRY_NOT_FOUND_BODY,
|
|
173
181
|
REQUESTER_ENGINE_RETRY_ATTEMPTS: () => REQUESTER_ENGINE_RETRY_ATTEMPTS,
|
|
174
182
|
REQUESTER_ENGINE_RETRY_MAX_DELAY_MS: () => REQUESTER_ENGINE_RETRY_MAX_DELAY_MS,
|
|
175
183
|
REQUESTER_NEAR_EXPIRY_SECONDS: () => REQUESTER_NEAR_EXPIRY_SECONDS,
|
|
@@ -178,6 +186,8 @@ __export(index_exports, {
|
|
|
178
186
|
ROOT_STATUS_CHECKPOINT_V1_SCHEMA: () => ROOT_STATUS_CHECKPOINT_V1_SCHEMA,
|
|
179
187
|
ROOT_STATUS_RENEWAL_V1_DOMAIN: () => ROOT_STATUS_RENEWAL_V1_DOMAIN,
|
|
180
188
|
ROOT_STATUS_V1_DOMAIN: () => ROOT_STATUS_V1_DOMAIN,
|
|
189
|
+
RegistryLookupInputError: () => RegistryLookupInputError,
|
|
190
|
+
RegistryUnavailableError: () => RegistryUnavailableError,
|
|
181
191
|
SECRETS_SPACE: () => SECRETS_SPACE,
|
|
182
192
|
SECRET_NAME_RE: () => import_sdk_services9.SECRET_NAME_RE,
|
|
183
193
|
SECRET_RECORDS_SCHEMA: () => import_bootstrap4.SECRET_RECORDS_SCHEMA,
|
|
@@ -247,6 +257,7 @@ __export(index_exports, {
|
|
|
247
257
|
admitPolicyCredentialV4: () => admitPolicyCredentialV4,
|
|
248
258
|
appSecretScope: () => import_sdk_services9.appSecretScope,
|
|
249
259
|
applyPrefix: () => applyPrefix,
|
|
260
|
+
assertSpaceCreationAllowed: () => assertSpaceCreationAllowed,
|
|
250
261
|
authorizationVerdictOf: () => import_sdk_services9.authorizationVerdictOf,
|
|
251
262
|
bootstrapEncryptionNetworkId: () => import_bootstrap4.bootstrapEncryptionNetworkId,
|
|
252
263
|
bootstrapSpaceId: () => import_bootstrap4.bootstrapSpaceId,
|
|
@@ -277,6 +288,7 @@ __export(index_exports, {
|
|
|
277
288
|
canonicalizeUnifiedPolicyCapability: () => canonicalizeUnifiedPolicyCapability,
|
|
278
289
|
checkDecryptInvocationInput: () => import_sdk_services9.checkDecryptInvocationInput,
|
|
279
290
|
checkNodeInfo: () => checkNodeInfo,
|
|
291
|
+
classifyRegistryLookup: () => classifyRegistryLookup,
|
|
280
292
|
clearTinyCloudDebugLogs: () => import_sdk_services9.clearTinyCloudDebugLogs,
|
|
281
293
|
compactAttenuationContains: () => compactAttenuationContains,
|
|
282
294
|
composeBootstrapSpaceManifest: () => import_bootstrap4.composeBootstrapSpaceManifest,
|
|
@@ -294,7 +306,9 @@ __export(index_exports, {
|
|
|
294
306
|
createEmailDomainCredentialRequirement: () => createEmailDomainCredentialRequirement,
|
|
295
307
|
createHolderBinding: () => createHolderBinding,
|
|
296
308
|
createInMemoryLocalNodeIdentityStore: () => createInMemoryLocalNodeIdentityStore,
|
|
309
|
+
createInMemoryRegistrySequenceStore: () => createInMemoryRegistrySequenceStore,
|
|
297
310
|
createLocalStorageLocalNodeIdentityStore: () => createLocalStorageLocalNodeIdentityStore,
|
|
311
|
+
createLocalStorageRegistrySequenceStore: () => createLocalStorageRegistrySequenceStore,
|
|
298
312
|
createOpenKeyCallbackSigningStrategy: () => createOpenKeyCallbackSigningStrategy,
|
|
299
313
|
createOpenKeyManageKeySigningStrategy: () => createOpenKeyManageKeySigningStrategy,
|
|
300
314
|
createPolicyEnforcementDelegation: () => createPolicyEnforcementDelegation,
|
|
@@ -378,6 +392,7 @@ __export(index_exports, {
|
|
|
378
392
|
jcsCanonicalize: () => jcsCanonicalize,
|
|
379
393
|
loadManifest: () => loadManifest,
|
|
380
394
|
locationPayloadForRecord: () => locationPayloadForRecord,
|
|
395
|
+
lookupOwnerRegistryVerdict: () => lookupOwnerRegistryVerdict,
|
|
381
396
|
mailboxBelongsToDomain: () => import_share_envelope.mailboxBelongsToDomain,
|
|
382
397
|
makePkhSpaceId: () => makePkhSpaceId,
|
|
383
398
|
makePublicSpaceId: () => makePublicSpaceId,
|
|
@@ -426,11 +441,14 @@ __export(index_exports, {
|
|
|
426
441
|
publishLocationRecord: () => publishLocationRecord,
|
|
427
442
|
recapCaveatsEqual: () => recapCaveatsEqual,
|
|
428
443
|
registerPolicyV3: () => registerPolicyV3,
|
|
444
|
+
registryOwnerSubjectV1: () => registryOwnerSubjectV1,
|
|
429
445
|
renewPolicyRootStatusV3: () => renewPolicyRootStatusV3,
|
|
430
446
|
requestTinyCloudManageKeyScope: () => requestTinyCloudManageKeyScope,
|
|
431
447
|
resolveCloudLocation: () => resolveCloudLocation,
|
|
448
|
+
resolveFailClosedTinyCloudHosts: () => resolveFailClosedTinyCloudHosts,
|
|
432
449
|
resolveManifest: () => resolveManifest,
|
|
433
450
|
resolveManifestKnowledgeRoot: () => resolveManifestKnowledgeRoot,
|
|
451
|
+
resolveOwnerHosts: () => resolveOwnerHosts,
|
|
434
452
|
resolveSecretListPrefix: () => import_sdk_services9.resolveSecretListPrefix,
|
|
435
453
|
resolveSecretPath: () => import_sdk_services9.resolveSecretPath,
|
|
436
454
|
resolveTinyCloudHosts: () => resolveTinyCloudHosts,
|
|
@@ -18706,11 +18724,16 @@ async function resolveTinyCloudHosts(subject, options2 = {}) {
|
|
|
18706
18724
|
fetch: fetchFn,
|
|
18707
18725
|
verifyRecords: options2.verifyRecords
|
|
18708
18726
|
});
|
|
18709
|
-
|
|
18710
|
-
|
|
18711
|
-
|
|
18712
|
-
|
|
18727
|
+
const hosts = location.multiaddrs.map((addr) => multiaddrToHttpUrl(addr));
|
|
18728
|
+
if (location.source === "fallback" && !fallbackDeprecationWarned) {
|
|
18729
|
+
fallbackDeprecationWarned = true;
|
|
18730
|
+
console.warn(
|
|
18731
|
+
`[tinycloud:location] Deprecated: no registry host for this account, using fallback ${hosts[0]}. The silent fallback will be removed in a future major; opt in to registryPolicy "fail-closed" now.`
|
|
18732
|
+
);
|
|
18733
|
+
}
|
|
18734
|
+
return { hosts, location };
|
|
18713
18735
|
}
|
|
18736
|
+
var fallbackDeprecationWarned = false;
|
|
18714
18737
|
function memoizeFetch(fetchFn) {
|
|
18715
18738
|
const cache2 = /* @__PURE__ */ new Map();
|
|
18716
18739
|
return (async (input, init) => {
|
|
@@ -18923,11 +18946,18 @@ function publishedHttpOrigins(record2) {
|
|
|
18923
18946
|
return origins;
|
|
18924
18947
|
}
|
|
18925
18948
|
async function boundedResponseText(response, label, maxBytes) {
|
|
18949
|
+
const bytes2 = await boundedResponseBytes(response, maxBytes);
|
|
18950
|
+
if (bytes2 === null) {
|
|
18951
|
+
throw new LocationRecordValidationError(`${label} response is too large`);
|
|
18952
|
+
}
|
|
18953
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes2);
|
|
18954
|
+
}
|
|
18955
|
+
async function boundedResponseBytes(response, maxBytes) {
|
|
18926
18956
|
const declaredLength = response.headers.get("content-length");
|
|
18927
18957
|
if (declaredLength !== null && (!/^\d+$/.test(declaredLength) || Number(declaredLength) > maxBytes)) {
|
|
18928
|
-
|
|
18958
|
+
return null;
|
|
18929
18959
|
}
|
|
18930
|
-
if (response.body === null) return
|
|
18960
|
+
if (response.body === null) return new Uint8Array();
|
|
18931
18961
|
const reader = response.body.getReader();
|
|
18932
18962
|
const chunks = [];
|
|
18933
18963
|
let total = 0;
|
|
@@ -18939,7 +18969,7 @@ async function boundedResponseText(response, label, maxBytes) {
|
|
|
18939
18969
|
total += value.byteLength;
|
|
18940
18970
|
if (total > maxBytes) {
|
|
18941
18971
|
await reader.cancel().catch(() => void 0);
|
|
18942
|
-
|
|
18972
|
+
return null;
|
|
18943
18973
|
}
|
|
18944
18974
|
chunks.push(value);
|
|
18945
18975
|
}
|
|
@@ -18952,7 +18982,7 @@ async function boundedResponseText(response, label, maxBytes) {
|
|
|
18952
18982
|
bytes2.set(chunk, offset);
|
|
18953
18983
|
offset += chunk.byteLength;
|
|
18954
18984
|
}
|
|
18955
|
-
return
|
|
18985
|
+
return bytes2;
|
|
18956
18986
|
}
|
|
18957
18987
|
function boundedSignal(parent, timeoutMs) {
|
|
18958
18988
|
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
@@ -19250,4 +19280,697 @@ function decodeBase64Url4(value) {
|
|
|
19250
19280
|
}
|
|
19251
19281
|
return Uint8Array.from(bytes2);
|
|
19252
19282
|
}
|
|
19283
|
+
|
|
19284
|
+
// src/registry-verdict.ts
|
|
19285
|
+
var import_ed2551912 = require("@noble/curves/ed25519");
|
|
19286
|
+
var import_viem7 = require("viem");
|
|
19287
|
+
var RegistryLookupInputError = class extends TypeError {
|
|
19288
|
+
constructor(code3, message) {
|
|
19289
|
+
super(message);
|
|
19290
|
+
this.code = code3;
|
|
19291
|
+
this.name = "RegistryLookupInputError";
|
|
19292
|
+
}
|
|
19293
|
+
};
|
|
19294
|
+
var REGISTRY_LOOKUP_MAX_BODY_BYTES = 64 * 1024;
|
|
19295
|
+
var REGISTRY_NOT_FOUND_BODY = '{"error":"location record not found"}';
|
|
19296
|
+
var MAX_JSON_DEPTH = 32;
|
|
19297
|
+
var JSON_SIMPLE_ESCAPES = {
|
|
19298
|
+
'"': '"',
|
|
19299
|
+
"\\": "\\",
|
|
19300
|
+
"/": "/",
|
|
19301
|
+
b: "\b",
|
|
19302
|
+
f: "\f",
|
|
19303
|
+
n: "\n",
|
|
19304
|
+
r: "\r",
|
|
19305
|
+
t: " "
|
|
19306
|
+
};
|
|
19307
|
+
var SECP256K1_N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n;
|
|
19308
|
+
var BASE64URL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
19309
|
+
var IPV4_OCTET = "(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])";
|
|
19310
|
+
var IPV4 = new RegExp(`^${IPV4_OCTET}(\\.${IPV4_OCTET}){3}$`);
|
|
19311
|
+
var DNS_LABEL = "[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?";
|
|
19312
|
+
var DNS_NAME = new RegExp(`^${DNS_LABEL}(\\.${DNS_LABEL})*$`);
|
|
19313
|
+
var HOST_MULTIADDR = /^\/(ip4|ip6|dns|dns4|dns6)\/([^/]+)\/tcp\/([1-9][0-9]{0,4})\/(https|http|tls\/http)$/;
|
|
19314
|
+
var RFC3339_DATE_TIME = /^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(\.[0-9]+)?(Z|[+-]([0-9]{2}):([0-9]{2}))$/;
|
|
19315
|
+
async function classifyRegistryLookup(subject, response, options2 = {}) {
|
|
19316
|
+
if (!isCanonicalSubject(subject)) {
|
|
19317
|
+
throw new RegistryLookupInputError(
|
|
19318
|
+
"invalid-subject",
|
|
19319
|
+
"subject must be did:pkh:eip155:<chainId>:<EIP-55 address> or an Ed25519 did:key"
|
|
19320
|
+
);
|
|
19321
|
+
}
|
|
19322
|
+
const remembered = options2.rememberedSequence ?? null;
|
|
19323
|
+
if (remembered !== null && (!Number.isSafeInteger(remembered) || remembered < 0)) {
|
|
19324
|
+
throw new RegistryLookupInputError(
|
|
19325
|
+
"invalid-remembered-sequence",
|
|
19326
|
+
"rememberedSequence must be a non-negative safe integer"
|
|
19327
|
+
);
|
|
19328
|
+
}
|
|
19329
|
+
const unavailable = (reason, message) => ({
|
|
19330
|
+
status: "unavailable",
|
|
19331
|
+
subject,
|
|
19332
|
+
source: "offchain",
|
|
19333
|
+
reason,
|
|
19334
|
+
message,
|
|
19335
|
+
hosts: []
|
|
19336
|
+
});
|
|
19337
|
+
if (response.kind === "network-error") {
|
|
19338
|
+
return unavailable("network-error", "location registry could not be reached");
|
|
19339
|
+
}
|
|
19340
|
+
if (response.kind === "timeout") {
|
|
19341
|
+
return unavailable("timeout", "location registry lookup timed out");
|
|
19342
|
+
}
|
|
19343
|
+
if (response.status !== 200 && response.status !== 404) {
|
|
19344
|
+
return unavailable("http-status", `location registry returned HTTP ${response.status}`);
|
|
19345
|
+
}
|
|
19346
|
+
if (response.body.length > REGISTRY_LOOKUP_MAX_BODY_BYTES || new TextEncoder().encode(response.body).byteLength > REGISTRY_LOOKUP_MAX_BODY_BYTES) {
|
|
19347
|
+
return unavailable("oversize-response", "location registry response is too large");
|
|
19348
|
+
}
|
|
19349
|
+
if (response.status === 404) {
|
|
19350
|
+
const body = response.body;
|
|
19351
|
+
let start = 0;
|
|
19352
|
+
let end = body.length;
|
|
19353
|
+
while (start < end && " \n\r".includes(body[start])) start++;
|
|
19354
|
+
while (end > start && " \n\r".includes(body[end - 1])) end--;
|
|
19355
|
+
if (body.slice(start, end) !== REGISTRY_NOT_FOUND_BODY) {
|
|
19356
|
+
return unavailable(
|
|
19357
|
+
"malformed-response",
|
|
19358
|
+
"location registry 404 is not the registry's not-found response"
|
|
19359
|
+
);
|
|
19360
|
+
}
|
|
19361
|
+
if (remembered !== null) {
|
|
19362
|
+
return unavailable(
|
|
19363
|
+
"not-found-after-registered",
|
|
19364
|
+
`location registry has no record, but sequence ${remembered} was seen before`
|
|
19365
|
+
);
|
|
19366
|
+
}
|
|
19367
|
+
return { status: "unregistered", subject, source: "offchain", hosts: [] };
|
|
19368
|
+
}
|
|
19369
|
+
let record2;
|
|
19370
|
+
try {
|
|
19371
|
+
record2 = parseRecordResponse(response.body);
|
|
19372
|
+
} catch (error) {
|
|
19373
|
+
return unavailable(
|
|
19374
|
+
"malformed-response",
|
|
19375
|
+
error instanceof Error ? error.message : "location registry response is invalid"
|
|
19376
|
+
);
|
|
19377
|
+
}
|
|
19378
|
+
if (record2.subject !== subject) {
|
|
19379
|
+
return unavailable(
|
|
19380
|
+
"subject-mismatch",
|
|
19381
|
+
"location record subject does not match requested subject"
|
|
19382
|
+
);
|
|
19383
|
+
}
|
|
19384
|
+
if (!await verifyRecordSignature(record2)) {
|
|
19385
|
+
return unavailable("invalid-signature", "location record signature is invalid");
|
|
19386
|
+
}
|
|
19387
|
+
if (remembered !== null && record2.sequence < remembered) {
|
|
19388
|
+
return unavailable(
|
|
19389
|
+
"rollback",
|
|
19390
|
+
`location record sequence ${record2.sequence} is below remembered sequence ${remembered}`
|
|
19391
|
+
);
|
|
19392
|
+
}
|
|
19393
|
+
const hosts = [];
|
|
19394
|
+
for (const address of record2.multiaddrs) {
|
|
19395
|
+
const origin2 = multiaddrOrigin(address);
|
|
19396
|
+
if (origin2 !== null) hosts.push({ urls: [origin2], source: "offchain" });
|
|
19397
|
+
}
|
|
19398
|
+
return {
|
|
19399
|
+
status: "registered",
|
|
19400
|
+
subject,
|
|
19401
|
+
source: "offchain",
|
|
19402
|
+
sequence: record2.sequence,
|
|
19403
|
+
hosts
|
|
19404
|
+
};
|
|
19405
|
+
}
|
|
19406
|
+
function isCanonicalSubject(subject) {
|
|
19407
|
+
if (typeof subject !== "string") return false;
|
|
19408
|
+
const pkh = /^did:pkh:eip155:([1-9][0-9]*):(0x[0-9a-fA-F]{40})$/.exec(subject);
|
|
19409
|
+
if (pkh !== null) {
|
|
19410
|
+
try {
|
|
19411
|
+
return pkhDid(pkh[2], Number(pkh[1])) === subject;
|
|
19412
|
+
} catch {
|
|
19413
|
+
return false;
|
|
19414
|
+
}
|
|
19415
|
+
}
|
|
19416
|
+
return ed25519KeyFromDidKey(subject) !== null;
|
|
19417
|
+
}
|
|
19418
|
+
function ed25519KeyFromDidKey(did) {
|
|
19419
|
+
if (!did.startsWith("did:key:z")) return null;
|
|
19420
|
+
const identifier = did.slice("did:key:".length);
|
|
19421
|
+
let bytes2;
|
|
19422
|
+
try {
|
|
19423
|
+
bytes2 = bases.base58btc.decode(identifier);
|
|
19424
|
+
} catch {
|
|
19425
|
+
return null;
|
|
19426
|
+
}
|
|
19427
|
+
if (bytes2.length !== 34 || bytes2[0] !== 237 || bytes2[1] !== 1) {
|
|
19428
|
+
return null;
|
|
19429
|
+
}
|
|
19430
|
+
return bytes2.slice(2);
|
|
19431
|
+
}
|
|
19432
|
+
var JsonInteger = class {
|
|
19433
|
+
constructor(lexeme) {
|
|
19434
|
+
this.lexeme = lexeme;
|
|
19435
|
+
}
|
|
19436
|
+
};
|
|
19437
|
+
function parseStrictJson(text3) {
|
|
19438
|
+
let i = 0;
|
|
19439
|
+
const fail2 = (what) => {
|
|
19440
|
+
throw new SyntaxError(`location registry response is not strict JSON: ${what} at ${i}`);
|
|
19441
|
+
};
|
|
19442
|
+
const skipWhitespace2 = () => {
|
|
19443
|
+
while (i < text3.length && " \n\r".includes(text3[i])) i++;
|
|
19444
|
+
};
|
|
19445
|
+
const readHex4 = () => {
|
|
19446
|
+
const hex3 = text3.slice(i, i + 4);
|
|
19447
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex3)) fail2("bad \\u escape");
|
|
19448
|
+
i += 4;
|
|
19449
|
+
return parseInt(hex3, 16);
|
|
19450
|
+
};
|
|
19451
|
+
const parseString = () => {
|
|
19452
|
+
i++;
|
|
19453
|
+
let out = "";
|
|
19454
|
+
for (; ; ) {
|
|
19455
|
+
if (i >= text3.length) fail2("unterminated string");
|
|
19456
|
+
const code3 = text3.charCodeAt(i);
|
|
19457
|
+
if (code3 === 34) {
|
|
19458
|
+
i++;
|
|
19459
|
+
return out;
|
|
19460
|
+
}
|
|
19461
|
+
if (code3 < 32) fail2("control character in string");
|
|
19462
|
+
if (code3 === 92) {
|
|
19463
|
+
const escape2 = text3[i + 1];
|
|
19464
|
+
i += 2;
|
|
19465
|
+
if (escape2 !== void 0 && Object.prototype.hasOwnProperty.call(JSON_SIMPLE_ESCAPES, escape2)) {
|
|
19466
|
+
out += JSON_SIMPLE_ESCAPES[escape2];
|
|
19467
|
+
continue;
|
|
19468
|
+
}
|
|
19469
|
+
if (escape2 !== "u") fail2("bad escape");
|
|
19470
|
+
const unit = readHex4();
|
|
19471
|
+
if (unit >= 55296 && unit <= 56319) {
|
|
19472
|
+
if (text3[i] !== "\\" || text3[i + 1] !== "u") fail2("unpaired surrogate");
|
|
19473
|
+
i += 2;
|
|
19474
|
+
const low = readHex4();
|
|
19475
|
+
if (low < 56320 || low > 57343) fail2("unpaired surrogate");
|
|
19476
|
+
out += String.fromCharCode(unit, low);
|
|
19477
|
+
} else if (unit >= 56320 && unit <= 57343) {
|
|
19478
|
+
fail2("unpaired surrogate");
|
|
19479
|
+
} else {
|
|
19480
|
+
out += String.fromCharCode(unit);
|
|
19481
|
+
}
|
|
19482
|
+
continue;
|
|
19483
|
+
}
|
|
19484
|
+
out += text3[i];
|
|
19485
|
+
i++;
|
|
19486
|
+
}
|
|
19487
|
+
};
|
|
19488
|
+
const parseValue = (depth) => {
|
|
19489
|
+
skipWhitespace2();
|
|
19490
|
+
const c = text3[i];
|
|
19491
|
+
if (c === "{" || c === "[") {
|
|
19492
|
+
if (depth >= MAX_JSON_DEPTH) fail2("nesting too deep");
|
|
19493
|
+
i++;
|
|
19494
|
+
skipWhitespace2();
|
|
19495
|
+
const close = c === "{" ? "}" : "]";
|
|
19496
|
+
const object4 = /* @__PURE__ */ new Map();
|
|
19497
|
+
const array = [];
|
|
19498
|
+
if (text3[i] === close) {
|
|
19499
|
+
i++;
|
|
19500
|
+
return c === "{" ? object4 : array;
|
|
19501
|
+
}
|
|
19502
|
+
for (; ; ) {
|
|
19503
|
+
if (c === "{") {
|
|
19504
|
+
skipWhitespace2();
|
|
19505
|
+
if (text3[i] !== '"') fail2("expected key");
|
|
19506
|
+
const key = parseString();
|
|
19507
|
+
if (object4.has(key)) fail2("duplicate key");
|
|
19508
|
+
skipWhitespace2();
|
|
19509
|
+
if (text3[i] !== ":") fail2("expected ':'");
|
|
19510
|
+
i++;
|
|
19511
|
+
object4.set(key, parseValue(depth + 1));
|
|
19512
|
+
} else {
|
|
19513
|
+
array.push(parseValue(depth + 1));
|
|
19514
|
+
}
|
|
19515
|
+
skipWhitespace2();
|
|
19516
|
+
if (text3[i] === ",") {
|
|
19517
|
+
i++;
|
|
19518
|
+
continue;
|
|
19519
|
+
}
|
|
19520
|
+
if (text3[i] !== close) fail2(`expected ',' or '${close}'`);
|
|
19521
|
+
i++;
|
|
19522
|
+
return c === "{" ? object4 : array;
|
|
19523
|
+
}
|
|
19524
|
+
}
|
|
19525
|
+
if (c === '"') return parseString();
|
|
19526
|
+
for (const [literal, value2] of [["true", true], ["false", false], ["null", null]]) {
|
|
19527
|
+
if (text3.startsWith(literal, i)) {
|
|
19528
|
+
i += literal.length;
|
|
19529
|
+
return value2;
|
|
19530
|
+
}
|
|
19531
|
+
}
|
|
19532
|
+
const number = /-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?/y;
|
|
19533
|
+
number.lastIndex = i;
|
|
19534
|
+
const match = number.exec(text3);
|
|
19535
|
+
if (match === null) return fail2("unexpected token");
|
|
19536
|
+
if (match[2] !== void 0 || match[3] !== void 0) fail2("non-integer number");
|
|
19537
|
+
i += match[0].length;
|
|
19538
|
+
return new JsonInteger(match[0]);
|
|
19539
|
+
};
|
|
19540
|
+
const value = parseValue(0);
|
|
19541
|
+
skipWhitespace2();
|
|
19542
|
+
if (i !== text3.length) fail2("trailing data");
|
|
19543
|
+
return value;
|
|
19544
|
+
}
|
|
19545
|
+
function parseRecordResponse(body) {
|
|
19546
|
+
const root = parseStrictJson(body);
|
|
19547
|
+
const record2 = root instanceof Map ? root.get("record") : void 0;
|
|
19548
|
+
if (!(record2 instanceof Map)) {
|
|
19549
|
+
throw new SyntaxError("location registry response has no record object");
|
|
19550
|
+
}
|
|
19551
|
+
const version = record2.get("version");
|
|
19552
|
+
const subject = record2.get("subject");
|
|
19553
|
+
const multiaddrs = record2.get("multiaddrs");
|
|
19554
|
+
const updatedAt = record2.get("updated_at");
|
|
19555
|
+
const sequence = record2.get("sequence");
|
|
19556
|
+
const signature = record2.get("signature");
|
|
19557
|
+
if (!(version instanceof JsonInteger) || version.lexeme !== "1") {
|
|
19558
|
+
throw new SyntaxError("record.version must be the integer 1");
|
|
19559
|
+
}
|
|
19560
|
+
if (typeof subject !== "string") {
|
|
19561
|
+
throw new SyntaxError("record.subject must be a string");
|
|
19562
|
+
}
|
|
19563
|
+
if (!Array.isArray(multiaddrs) || !multiaddrs.every((entry) => typeof entry === "string" && entry.length > 0)) {
|
|
19564
|
+
throw new SyntaxError("record.multiaddrs must be an array of non-empty strings");
|
|
19565
|
+
}
|
|
19566
|
+
if (typeof updatedAt !== "string" || !isRfc3339DateTime(updatedAt)) {
|
|
19567
|
+
throw new SyntaxError("record.updated_at must be an RFC 3339 date-time");
|
|
19568
|
+
}
|
|
19569
|
+
if (!(sequence instanceof JsonInteger) || !/^(0|[1-9][0-9]*)$/.test(sequence.lexeme) || !Number.isSafeInteger(Number(sequence.lexeme))) {
|
|
19570
|
+
throw new SyntaxError("record.sequence must be a non-negative safe integer");
|
|
19571
|
+
}
|
|
19572
|
+
if (typeof signature !== "string" || signature.length === 0) {
|
|
19573
|
+
throw new SyntaxError("record.signature must be a non-empty string");
|
|
19574
|
+
}
|
|
19575
|
+
return {
|
|
19576
|
+
subject,
|
|
19577
|
+
multiaddrs,
|
|
19578
|
+
updated_at: updatedAt,
|
|
19579
|
+
sequence: Number(sequence.lexeme),
|
|
19580
|
+
signature
|
|
19581
|
+
};
|
|
19582
|
+
}
|
|
19583
|
+
function isRfc3339DateTime(value) {
|
|
19584
|
+
const match = RFC3339_DATE_TIME.exec(value);
|
|
19585
|
+
if (match === null) return false;
|
|
19586
|
+
const [year, month, day, hour, minute, second] = match.slice(1, 7).map(Number);
|
|
19587
|
+
const leap = year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
|
|
19588
|
+
const daysInMonth = [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month - 1];
|
|
19589
|
+
return daysInMonth !== void 0 && day >= 1 && day <= daysInMonth && hour <= 23 && minute <= 59 && second <= 59 && (match[8] === "Z" || Number(match[9]) <= 23 && Number(match[10]) <= 59);
|
|
19590
|
+
}
|
|
19591
|
+
async function verifyRecordSignature(record2) {
|
|
19592
|
+
const payload = canonicalLocationPayload({
|
|
19593
|
+
version: 1,
|
|
19594
|
+
subject: record2.subject,
|
|
19595
|
+
multiaddrs: record2.multiaddrs,
|
|
19596
|
+
updated_at: record2.updated_at,
|
|
19597
|
+
sequence: record2.sequence
|
|
19598
|
+
});
|
|
19599
|
+
const { signature } = record2;
|
|
19600
|
+
if (record2.subject.startsWith("did:pkh:")) {
|
|
19601
|
+
if (!/^0x[0-9a-fA-F]{130}$/.test(signature)) return false;
|
|
19602
|
+
const r = BigInt(`0x${signature.slice(2, 66)}`);
|
|
19603
|
+
const s = BigInt(`0x${signature.slice(66, 130)}`);
|
|
19604
|
+
const v = parseInt(signature.slice(130), 16);
|
|
19605
|
+
if (r === 0n || r >= SECP256K1_N || s === 0n || s > SECP256K1_N / 2n) return false;
|
|
19606
|
+
if (v !== 0 && v !== 1 && v !== 27 && v !== 28) return false;
|
|
19607
|
+
const address = record2.subject.slice(record2.subject.lastIndexOf(":") + 1);
|
|
19608
|
+
try {
|
|
19609
|
+
return await (0, import_viem7.verifyMessage)({
|
|
19610
|
+
address,
|
|
19611
|
+
message: payload,
|
|
19612
|
+
signature
|
|
19613
|
+
});
|
|
19614
|
+
} catch {
|
|
19615
|
+
return false;
|
|
19616
|
+
}
|
|
19617
|
+
}
|
|
19618
|
+
const publicKey = ed25519KeyFromDidKey(record2.subject);
|
|
19619
|
+
if (publicKey === null || !/^[A-Za-z0-9_-]{86}$/.test(signature)) return false;
|
|
19620
|
+
const bytes2 = new Uint8Array(64);
|
|
19621
|
+
let buffer = 0;
|
|
19622
|
+
let bits = 0;
|
|
19623
|
+
let length2 = 0;
|
|
19624
|
+
for (const char of signature) {
|
|
19625
|
+
buffer = (buffer << 6 | BASE64URL.indexOf(char)) & 65535;
|
|
19626
|
+
bits += 6;
|
|
19627
|
+
if (bits >= 8) {
|
|
19628
|
+
bits -= 8;
|
|
19629
|
+
bytes2[length2++] = buffer >> bits & 255;
|
|
19630
|
+
}
|
|
19631
|
+
}
|
|
19632
|
+
try {
|
|
19633
|
+
return import_ed2551912.ed25519.verify(bytes2, new TextEncoder().encode(payload), publicKey, { zip215: false });
|
|
19634
|
+
} catch {
|
|
19635
|
+
return false;
|
|
19636
|
+
}
|
|
19637
|
+
}
|
|
19638
|
+
function multiaddrOrigin(address) {
|
|
19639
|
+
const match = HOST_MULTIADDR.exec(address);
|
|
19640
|
+
if (match === null) return null;
|
|
19641
|
+
const [, protocol, host, portText, transport] = match;
|
|
19642
|
+
const port = Number(portText);
|
|
19643
|
+
if (port > 65535) return null;
|
|
19644
|
+
let authority;
|
|
19645
|
+
if (protocol === "ip4") {
|
|
19646
|
+
if (!IPV4.test(host)) return null;
|
|
19647
|
+
authority = host;
|
|
19648
|
+
} else if (protocol === "ip6") {
|
|
19649
|
+
if (!/^[0-9A-Fa-f:.]+$/.test(host) || !host.includes(":")) return null;
|
|
19650
|
+
authority = `[${host}]`;
|
|
19651
|
+
} else {
|
|
19652
|
+
if (host.length > 253 || !DNS_NAME.test(host) || /^(0[xX][0-9a-fA-F]*|[0-9]+)$/.test(host.slice(host.lastIndexOf(".") + 1))) {
|
|
19653
|
+
return null;
|
|
19654
|
+
}
|
|
19655
|
+
authority = host;
|
|
19656
|
+
}
|
|
19657
|
+
const scheme = transport === "http" ? "http" : "https";
|
|
19658
|
+
try {
|
|
19659
|
+
return new URL(`${scheme}://${authority}:${port}`).origin;
|
|
19660
|
+
} catch {
|
|
19661
|
+
return null;
|
|
19662
|
+
}
|
|
19663
|
+
}
|
|
19664
|
+
|
|
19665
|
+
// src/registry-policy.ts
|
|
19666
|
+
var DEFAULT_REGISTRY_OWNER_CHAIN_ID = 1;
|
|
19667
|
+
var DEFAULT_NEW_USER_HOST = DEFAULT_TINYCLOUD_FALLBACK_HOST;
|
|
19668
|
+
var DEFAULT_REGISTRY_LOOKUP_TIMEOUT_MS = 3e3;
|
|
19669
|
+
var DEFAULT_REGISTRY_SEQUENCE_STORAGE_KEY = "tinycloud.registrySequences.v1";
|
|
19670
|
+
var MAX_SUBJECT_LENGTH = 74;
|
|
19671
|
+
function registryOwnerSubjectV1(address, registryChainId = DEFAULT_REGISTRY_OWNER_CHAIN_ID) {
|
|
19672
|
+
return pkhDid(address, registryChainId);
|
|
19673
|
+
}
|
|
19674
|
+
function createInMemoryRegistrySequenceStore() {
|
|
19675
|
+
const sequences = /* @__PURE__ */ new Map();
|
|
19676
|
+
return {
|
|
19677
|
+
get: (subject) => sequences.get(subject),
|
|
19678
|
+
set: (subject, sequence) => {
|
|
19679
|
+
if ((sequences.get(subject) ?? -1) < sequence) sequences.set(subject, sequence);
|
|
19680
|
+
}
|
|
19681
|
+
};
|
|
19682
|
+
}
|
|
19683
|
+
function createLocalStorageRegistrySequenceStore(storage, storageKey = DEFAULT_REGISTRY_SEQUENCE_STORAGE_KEY) {
|
|
19684
|
+
const backing = storage ?? defaultWebStorage();
|
|
19685
|
+
const memory = createInMemoryRegistrySequenceStore();
|
|
19686
|
+
if (!backing) {
|
|
19687
|
+
return memory;
|
|
19688
|
+
}
|
|
19689
|
+
const read2 = () => {
|
|
19690
|
+
const sequences = /* @__PURE__ */ new Map();
|
|
19691
|
+
const raw = backing.getItem(storageKey);
|
|
19692
|
+
if (raw === null) return sequences;
|
|
19693
|
+
let parsed;
|
|
19694
|
+
try {
|
|
19695
|
+
parsed = JSON.parse(raw);
|
|
19696
|
+
} catch {
|
|
19697
|
+
parsed = void 0;
|
|
19698
|
+
}
|
|
19699
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
19700
|
+
for (const [subject, sequence] of Object.entries(parsed)) {
|
|
19701
|
+
if (!isSequence(sequence)) break;
|
|
19702
|
+
sequences.set(subject, sequence);
|
|
19703
|
+
}
|
|
19704
|
+
if (sequences.size === Object.keys(parsed).length) return sequences;
|
|
19705
|
+
}
|
|
19706
|
+
throw new Error(`Corrupt registry sequence store "${storageKey}"`);
|
|
19707
|
+
};
|
|
19708
|
+
return {
|
|
19709
|
+
get: async (subject) => {
|
|
19710
|
+
const sequence = Math.max(read2().get(subject) ?? -1, await memory.get(subject) ?? -1);
|
|
19711
|
+
return sequence < 0 ? void 0 : sequence;
|
|
19712
|
+
},
|
|
19713
|
+
set: async (subject, sequence) => {
|
|
19714
|
+
await memory.set(subject, sequence);
|
|
19715
|
+
const sequences = read2();
|
|
19716
|
+
if ((sequences.get(subject) ?? -1) >= sequence) return;
|
|
19717
|
+
sequences.set(subject, sequence);
|
|
19718
|
+
backing.setItem(storageKey, JSON.stringify(Object.fromEntries(sequences)));
|
|
19719
|
+
}
|
|
19720
|
+
};
|
|
19721
|
+
}
|
|
19722
|
+
function isSequence(value) {
|
|
19723
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
19724
|
+
}
|
|
19725
|
+
async function resolveOwnerHosts(subject, options2 = {}) {
|
|
19726
|
+
if (typeof subject !== "string" || // Bounds the work below; covers any did:pkh with a safe-integer chain ID.
|
|
19727
|
+
subject.length > MAX_SUBJECT_LENGTH || !isCanonicalSubject(subject)) {
|
|
19728
|
+
throw new RegistryLookupInputError(
|
|
19729
|
+
"invalid-subject",
|
|
19730
|
+
"subject must be did:pkh:eip155:<chainId>:<EIP-55 address> or an Ed25519 did:key"
|
|
19731
|
+
);
|
|
19732
|
+
}
|
|
19733
|
+
const store = options2.sequenceStore;
|
|
19734
|
+
const remembered = store === void 0 ? void 0 : await store.get(subject);
|
|
19735
|
+
const registryUrl = options2.registryUrl ?? DEFAULT_TINYCLOUD_LOCATION_REGISTRY_URL;
|
|
19736
|
+
const url = `${registryUrl.replace(/\/$/, "")}/v1/locations/${encodeURIComponent(subject)}`;
|
|
19737
|
+
const observed = await observeRegistry(
|
|
19738
|
+
url,
|
|
19739
|
+
options2.fetch ?? globalThis.fetch,
|
|
19740
|
+
options2.timeoutMs ?? DEFAULT_REGISTRY_LOOKUP_TIMEOUT_MS
|
|
19741
|
+
);
|
|
19742
|
+
if (observed.kind === "oversize" || observed.kind === "invalid-utf8") {
|
|
19743
|
+
const oversize = observed.kind === "oversize";
|
|
19744
|
+
return {
|
|
19745
|
+
status: "unavailable",
|
|
19746
|
+
subject,
|
|
19747
|
+
source: "offchain",
|
|
19748
|
+
reason: oversize ? "oversize-response" : "malformed-response",
|
|
19749
|
+
message: oversize ? "location registry response is too large" : "location registry response is not UTF-8",
|
|
19750
|
+
hosts: []
|
|
19751
|
+
};
|
|
19752
|
+
}
|
|
19753
|
+
const verdict = await classifyRegistryLookup(subject, observed, {
|
|
19754
|
+
rememberedSequence: remembered ?? null
|
|
19755
|
+
});
|
|
19756
|
+
if (store !== void 0 && verdict.status === "registered" && (remembered === void 0 || verdict.sequence > remembered)) {
|
|
19757
|
+
try {
|
|
19758
|
+
await store.set(subject, verdict.sequence);
|
|
19759
|
+
} catch (error) {
|
|
19760
|
+
if (!sequenceWriteWarned) {
|
|
19761
|
+
sequenceWriteWarned = true;
|
|
19762
|
+
console.warn("[tinycloud:registry] could not remember the registry sequence:", error);
|
|
19763
|
+
}
|
|
19764
|
+
}
|
|
19765
|
+
}
|
|
19766
|
+
return verdict;
|
|
19767
|
+
}
|
|
19768
|
+
var sequenceWriteWarned = false;
|
|
19769
|
+
async function observeRegistry(url, fetchFn, timeoutMs) {
|
|
19770
|
+
const controller = new AbortController();
|
|
19771
|
+
let cancelTimer = () => void 0;
|
|
19772
|
+
const timedOut = new Promise((resolve) => {
|
|
19773
|
+
const timer = setTimeout(() => {
|
|
19774
|
+
controller.abort();
|
|
19775
|
+
resolve({ kind: "timeout" });
|
|
19776
|
+
}, timeoutMs);
|
|
19777
|
+
cancelTimer = () => clearTimeout(timer);
|
|
19778
|
+
});
|
|
19779
|
+
try {
|
|
19780
|
+
return await Promise.race([readRegistry(url, fetchFn, controller.signal), timedOut]);
|
|
19781
|
+
} finally {
|
|
19782
|
+
cancelTimer();
|
|
19783
|
+
}
|
|
19784
|
+
}
|
|
19785
|
+
async function readRegistry(url, fetchFn, signal) {
|
|
19786
|
+
let response;
|
|
19787
|
+
try {
|
|
19788
|
+
response = await fetchFn(url, {
|
|
19789
|
+
redirect: "error",
|
|
19790
|
+
headers: { accept: "application/json" },
|
|
19791
|
+
signal
|
|
19792
|
+
});
|
|
19793
|
+
} catch {
|
|
19794
|
+
return { kind: "network-error" };
|
|
19795
|
+
}
|
|
19796
|
+
if (response.status !== 200 && response.status !== 404) {
|
|
19797
|
+
await response.body?.cancel().catch(() => void 0);
|
|
19798
|
+
return { kind: "http", status: response.status, body: "" };
|
|
19799
|
+
}
|
|
19800
|
+
let bytes2;
|
|
19801
|
+
try {
|
|
19802
|
+
bytes2 = await boundedResponseBytes(response, REGISTRY_LOOKUP_MAX_BODY_BYTES);
|
|
19803
|
+
} catch {
|
|
19804
|
+
return { kind: "network-error" };
|
|
19805
|
+
}
|
|
19806
|
+
if (bytes2 === null) return { kind: "oversize" };
|
|
19807
|
+
try {
|
|
19808
|
+
return {
|
|
19809
|
+
kind: "http",
|
|
19810
|
+
status: response.status,
|
|
19811
|
+
body: new TextDecoder("utf-8", { fatal: true }).decode(bytes2)
|
|
19812
|
+
};
|
|
19813
|
+
} catch {
|
|
19814
|
+
return { kind: "invalid-utf8" };
|
|
19815
|
+
}
|
|
19816
|
+
}
|
|
19817
|
+
var NodeNotFoundError = class extends Error {
|
|
19818
|
+
constructor(subject, verdict) {
|
|
19819
|
+
super(
|
|
19820
|
+
verdict.status === "registered" ? `No usable TinyCloud host is registered for ${subject}` : `${subject} is not registered and newUserHost is null`
|
|
19821
|
+
);
|
|
19822
|
+
this.subject = subject;
|
|
19823
|
+
this.verdict = verdict;
|
|
19824
|
+
this.code = "node_not_found";
|
|
19825
|
+
this.name = "NodeNotFoundError";
|
|
19826
|
+
}
|
|
19827
|
+
};
|
|
19828
|
+
var RegistryUnavailableError = class extends Error {
|
|
19829
|
+
constructor(subject, verdict, detail = {}) {
|
|
19830
|
+
super(`Location registry unavailable for ${subject}: ${detail.message ?? verdict?.message}`);
|
|
19831
|
+
this.subject = subject;
|
|
19832
|
+
this.verdict = verdict;
|
|
19833
|
+
this.code = "registry_unavailable";
|
|
19834
|
+
this.name = "RegistryUnavailableError";
|
|
19835
|
+
if (detail.cause !== void 0) this.cause = detail.cause;
|
|
19836
|
+
}
|
|
19837
|
+
};
|
|
19838
|
+
var NodeRegistryEntryExistsError = class extends Error {
|
|
19839
|
+
constructor(subject, host, verdict) {
|
|
19840
|
+
super(`The registry entry for ${subject} does not list ${host}`);
|
|
19841
|
+
this.subject = subject;
|
|
19842
|
+
this.host = host;
|
|
19843
|
+
this.verdict = verdict;
|
|
19844
|
+
this.code = "node_registry_entry_exists";
|
|
19845
|
+
this.name = "NodeRegistryEntryExistsError";
|
|
19846
|
+
this.registeredHosts = secureRecordHosts(verdict);
|
|
19847
|
+
}
|
|
19848
|
+
};
|
|
19849
|
+
async function lookupOwnerRegistryVerdict(subject, options2 = {}) {
|
|
19850
|
+
if (options2.registryUrl === null) {
|
|
19851
|
+
throw new RegistryUnavailableError(subject, void 0, {
|
|
19852
|
+
message: "no location registry is configured"
|
|
19853
|
+
});
|
|
19854
|
+
}
|
|
19855
|
+
try {
|
|
19856
|
+
return await resolveOwnerHosts(subject, {
|
|
19857
|
+
registryUrl: options2.registryUrl,
|
|
19858
|
+
sequenceStore: options2.sequenceStore,
|
|
19859
|
+
fetch: options2.fetch,
|
|
19860
|
+
timeoutMs: options2.timeoutMs
|
|
19861
|
+
});
|
|
19862
|
+
} catch (error) {
|
|
19863
|
+
throw new RegistryUnavailableError(subject, void 0, {
|
|
19864
|
+
message: error instanceof Error ? error.message : String(error),
|
|
19865
|
+
cause: error
|
|
19866
|
+
});
|
|
19867
|
+
}
|
|
19868
|
+
}
|
|
19869
|
+
async function resolveFailClosedTinyCloudHosts(address, options2 = {}) {
|
|
19870
|
+
const newUserHost = options2.newUserHost === null ? null : exactHttpOrigin(options2.newUserHost ?? DEFAULT_NEW_USER_HOST, "newUserHost");
|
|
19871
|
+
const subject = registryOwnerSubjectV1(address, options2.registryChainId);
|
|
19872
|
+
const resolve = async () => {
|
|
19873
|
+
let verdict;
|
|
19874
|
+
let unavailable;
|
|
19875
|
+
try {
|
|
19876
|
+
verdict = await lookupOwnerRegistryVerdict(subject, options2);
|
|
19877
|
+
} catch (error) {
|
|
19878
|
+
if (!(error instanceof RegistryUnavailableError)) throw error;
|
|
19879
|
+
unavailable = error;
|
|
19880
|
+
}
|
|
19881
|
+
if (options2.autoDiscoverLocalNode ?? true) {
|
|
19882
|
+
const linkNames = verdict?.status === "registered" ? secureRecordHosts(verdict).flatMap((host) => LOCAL_LINK_ORIGIN.exec(host)?.[1] ?? []) : [];
|
|
19883
|
+
for (const candidate of [
|
|
19884
|
+
{ localNodeUrl: options2.localNodeUrl, localLinkName: options2.localLinkName },
|
|
19885
|
+
...linkNames.map((localLinkName) => ({ localLinkName }))
|
|
19886
|
+
]) {
|
|
19887
|
+
const local = await discoverLocalTinyCloudNode({
|
|
19888
|
+
...candidate,
|
|
19889
|
+
expectedNodeDid: options2.expectedNodeDid,
|
|
19890
|
+
identityStore: options2.localNodeIdentityStore,
|
|
19891
|
+
fetch: options2.fetch
|
|
19892
|
+
});
|
|
19893
|
+
if (local) return { hosts: [local.url], ...verdict === void 0 ? {} : { verdict } };
|
|
19894
|
+
}
|
|
19895
|
+
}
|
|
19896
|
+
if (verdict === void 0) throw unavailable;
|
|
19897
|
+
return { hosts: hostsForVerdict(verdict, newUserHost), verdict };
|
|
19898
|
+
};
|
|
19899
|
+
for (let attempt = 1; ; attempt++) {
|
|
19900
|
+
let failure;
|
|
19901
|
+
try {
|
|
19902
|
+
return await resolve();
|
|
19903
|
+
} catch (error) {
|
|
19904
|
+
if (!(error instanceof NodeNotFoundError) && !(error instanceof RegistryUnavailableError)) {
|
|
19905
|
+
throw error;
|
|
19906
|
+
}
|
|
19907
|
+
failure = error;
|
|
19908
|
+
}
|
|
19909
|
+
if (!options2.onNodeNotFound) throw failure;
|
|
19910
|
+
const decision = await options2.onNodeNotFound({ error: failure, attempt });
|
|
19911
|
+
switch (decision?.action) {
|
|
19912
|
+
case "retry":
|
|
19913
|
+
continue;
|
|
19914
|
+
case "use-host":
|
|
19915
|
+
return {
|
|
19916
|
+
hosts: [exactHttpOrigin(decision.host, "onNodeNotFound host")],
|
|
19917
|
+
...failure.verdict === void 0 ? {} : { verdict: failure.verdict }
|
|
19918
|
+
};
|
|
19919
|
+
case "abort":
|
|
19920
|
+
throw failure;
|
|
19921
|
+
default:
|
|
19922
|
+
throw new TypeError("onNodeNotFound returned an unknown action");
|
|
19923
|
+
}
|
|
19924
|
+
}
|
|
19925
|
+
}
|
|
19926
|
+
var LOCAL_LINK_ORIGIN = /^https:\/\/([a-z0-9-]+)\.local\.tinycloud\.link$/;
|
|
19927
|
+
function secureRecordHosts(verdict) {
|
|
19928
|
+
const hosts = /* @__PURE__ */ new Set();
|
|
19929
|
+
for (const host of verdict.hosts) {
|
|
19930
|
+
for (const url of host.urls) {
|
|
19931
|
+
try {
|
|
19932
|
+
hosts.add(exactHttpOrigin(url, "registry host"));
|
|
19933
|
+
} catch {
|
|
19934
|
+
}
|
|
19935
|
+
}
|
|
19936
|
+
}
|
|
19937
|
+
return [...hosts];
|
|
19938
|
+
}
|
|
19939
|
+
function hostsForVerdict(verdict, newUserHost) {
|
|
19940
|
+
switch (verdict.status) {
|
|
19941
|
+
case "registered": {
|
|
19942
|
+
const hosts = secureRecordHosts(verdict);
|
|
19943
|
+
if (hosts.length === 0) throw new NodeNotFoundError(verdict.subject, verdict);
|
|
19944
|
+
return hosts;
|
|
19945
|
+
}
|
|
19946
|
+
case "unregistered":
|
|
19947
|
+
if (newUserHost === null) throw new NodeNotFoundError(verdict.subject, verdict);
|
|
19948
|
+
return [newUserHost];
|
|
19949
|
+
case "unavailable":
|
|
19950
|
+
throw new RegistryUnavailableError(verdict.subject, verdict);
|
|
19951
|
+
}
|
|
19952
|
+
}
|
|
19953
|
+
function assertSpaceCreationAllowed(verdict, host) {
|
|
19954
|
+
switch (verdict.status) {
|
|
19955
|
+
case "unregistered":
|
|
19956
|
+
return;
|
|
19957
|
+
case "unavailable":
|
|
19958
|
+
throw new RegistryUnavailableError(verdict.subject, verdict);
|
|
19959
|
+
case "registered": {
|
|
19960
|
+
const origin2 = httpOrigin(host);
|
|
19961
|
+
if (origin2 !== void 0 && secureRecordHosts(verdict).includes(origin2)) {
|
|
19962
|
+
return;
|
|
19963
|
+
}
|
|
19964
|
+
throw new NodeRegistryEntryExistsError(verdict.subject, host, verdict);
|
|
19965
|
+
}
|
|
19966
|
+
}
|
|
19967
|
+
}
|
|
19968
|
+
function httpOrigin(host) {
|
|
19969
|
+
try {
|
|
19970
|
+
const url = new URL(host);
|
|
19971
|
+
return url.protocol === "https:" || url.protocol === "http:" ? url.origin : void 0;
|
|
19972
|
+
} catch {
|
|
19973
|
+
return void 0;
|
|
19974
|
+
}
|
|
19975
|
+
}
|
|
19253
19976
|
//# sourceMappingURL=index.cjs.map
|