@tinycloud/sdk-core 3.3.0-beta.18 → 3.3.0-beta.21

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.
@@ -44,4 +44,4 @@ declare function canonicalizePolicyCapability(input: unknown): PolicyCapability;
44
44
  declare function policyCapabilityDigestHex(input: unknown): string;
45
45
  declare function policyCapabilityContains(authority: unknown, request: unknown): boolean;
46
46
 
47
- export { type JsonObject as J, PolicyCapabilityError as P, type JsonValue as a, type PolicyCapability as b, type PolicyCapabilityErrorCode as c, canonicalizePolicyCapability as d, normalizePolicyCapability as e, policyCapabilityDigestHex as f, jcsCanonicalize as j, normalizeJson as n, policyCapabilityContains as p, serialize as s };
47
+ export { type JsonObject as J, type PolicyCapability as P, PolicyCapabilityError as a, type JsonValue as b, type PolicyCapabilityErrorCode as c, canonicalizePolicyCapability as d, normalizePolicyCapability as e, policyCapabilityDigestHex as f, jcsCanonicalize as j, normalizeJson as n, policyCapabilityContains as p, serialize as s };
@@ -44,4 +44,4 @@ declare function canonicalizePolicyCapability(input: unknown): PolicyCapability;
44
44
  declare function policyCapabilityDigestHex(input: unknown): string;
45
45
  declare function policyCapabilityContains(authority: unknown, request: unknown): boolean;
46
46
 
47
- export { type JsonObject as J, PolicyCapabilityError as P, type JsonValue as a, type PolicyCapability as b, type PolicyCapabilityErrorCode as c, canonicalizePolicyCapability as d, normalizePolicyCapability as e, policyCapabilityDigestHex as f, jcsCanonicalize as j, normalizeJson as n, policyCapabilityContains as p, serialize as s };
47
+ export { type JsonObject as J, type PolicyCapability as P, PolicyCapabilityError as a, type JsonValue as b, type PolicyCapabilityErrorCode as c, canonicalizePolicyCapability as d, normalizePolicyCapability as e, policyCapabilityDigestHex as f, jcsCanonicalize as j, normalizeJson as n, policyCapabilityContains as p, serialize as s };
@@ -1,4 +1,4 @@
1
- import { a as JsonValue } from './capability-BjzXwtNw.cjs';
1
+ import { b as JsonValue } from './capability-BA49Uboq.cjs';
2
2
  import { z } from 'zod';
3
3
 
4
4
  declare const CREDENTIAL_ACQUISITION_PROTOCOL: "tinycloud.credentials/acquisition/v1";
@@ -1,4 +1,4 @@
1
- import { a as JsonValue } from './capability-BjzXwtNw.js';
1
+ import { b as JsonValue } from './capability-BA49Uboq.js';
2
2
  import { z } from 'zod';
3
3
 
4
4
  declare const CREDENTIAL_ACQUISITION_PROTOCOL: "tinycloud.credentials/acquisition/v1";
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,
@@ -181,6 +187,7 @@ __export(index_exports, {
181
187
  ROOT_STATUS_RENEWAL_V1_DOMAIN: () => ROOT_STATUS_RENEWAL_V1_DOMAIN,
182
188
  ROOT_STATUS_V1_DOMAIN: () => ROOT_STATUS_V1_DOMAIN,
183
189
  RegistryLookupInputError: () => RegistryLookupInputError,
190
+ RegistryUnavailableError: () => RegistryUnavailableError,
184
191
  SECRETS_SPACE: () => SECRETS_SPACE,
185
192
  SECRET_NAME_RE: () => import_sdk_services9.SECRET_NAME_RE,
186
193
  SECRET_RECORDS_SCHEMA: () => import_bootstrap4.SECRET_RECORDS_SCHEMA,
@@ -250,6 +257,7 @@ __export(index_exports, {
250
257
  admitPolicyCredentialV4: () => admitPolicyCredentialV4,
251
258
  appSecretScope: () => import_sdk_services9.appSecretScope,
252
259
  applyPrefix: () => applyPrefix,
260
+ assertSpaceCreationAllowed: () => assertSpaceCreationAllowed,
253
261
  authorizationVerdictOf: () => import_sdk_services9.authorizationVerdictOf,
254
262
  bootstrapEncryptionNetworkId: () => import_bootstrap4.bootstrapEncryptionNetworkId,
255
263
  bootstrapSpaceId: () => import_bootstrap4.bootstrapSpaceId,
@@ -298,7 +306,9 @@ __export(index_exports, {
298
306
  createEmailDomainCredentialRequirement: () => createEmailDomainCredentialRequirement,
299
307
  createHolderBinding: () => createHolderBinding,
300
308
  createInMemoryLocalNodeIdentityStore: () => createInMemoryLocalNodeIdentityStore,
309
+ createInMemoryRegistrySequenceStore: () => createInMemoryRegistrySequenceStore,
301
310
  createLocalStorageLocalNodeIdentityStore: () => createLocalStorageLocalNodeIdentityStore,
311
+ createLocalStorageRegistrySequenceStore: () => createLocalStorageRegistrySequenceStore,
302
312
  createOpenKeyCallbackSigningStrategy: () => createOpenKeyCallbackSigningStrategy,
303
313
  createOpenKeyManageKeySigningStrategy: () => createOpenKeyManageKeySigningStrategy,
304
314
  createPolicyEnforcementDelegation: () => createPolicyEnforcementDelegation,
@@ -382,6 +392,7 @@ __export(index_exports, {
382
392
  jcsCanonicalize: () => jcsCanonicalize,
383
393
  loadManifest: () => loadManifest,
384
394
  locationPayloadForRecord: () => locationPayloadForRecord,
395
+ lookupOwnerRegistryVerdict: () => lookupOwnerRegistryVerdict,
385
396
  mailboxBelongsToDomain: () => import_share_envelope.mailboxBelongsToDomain,
386
397
  makePkhSpaceId: () => makePkhSpaceId,
387
398
  makePublicSpaceId: () => makePublicSpaceId,
@@ -430,11 +441,14 @@ __export(index_exports, {
430
441
  publishLocationRecord: () => publishLocationRecord,
431
442
  recapCaveatsEqual: () => recapCaveatsEqual,
432
443
  registerPolicyV3: () => registerPolicyV3,
444
+ registryOwnerSubjectV1: () => registryOwnerSubjectV1,
433
445
  renewPolicyRootStatusV3: () => renewPolicyRootStatusV3,
434
446
  requestTinyCloudManageKeyScope: () => requestTinyCloudManageKeyScope,
435
447
  resolveCloudLocation: () => resolveCloudLocation,
448
+ resolveFailClosedTinyCloudHosts: () => resolveFailClosedTinyCloudHosts,
436
449
  resolveManifest: () => resolveManifest,
437
450
  resolveManifestKnowledgeRoot: () => resolveManifestKnowledgeRoot,
451
+ resolveOwnerHosts: () => resolveOwnerHosts,
438
452
  resolveSecretListPrefix: () => import_sdk_services9.resolveSecretListPrefix,
439
453
  resolveSecretPath: () => import_sdk_services9.resolveSecretPath,
440
454
  resolveTinyCloudHosts: () => resolveTinyCloudHosts,
@@ -18710,11 +18724,16 @@ async function resolveTinyCloudHosts(subject, options2 = {}) {
18710
18724
  fetch: fetchFn,
18711
18725
  verifyRecords: options2.verifyRecords
18712
18726
  });
18713
- return {
18714
- hosts: location.multiaddrs.map((addr) => multiaddrToHttpUrl(addr)),
18715
- location
18716
- };
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 };
18717
18735
  }
18736
+ var fallbackDeprecationWarned = false;
18718
18737
  function memoizeFetch(fetchFn) {
18719
18738
  const cache2 = /* @__PURE__ */ new Map();
18720
18739
  return (async (input, init) => {
@@ -18927,11 +18946,18 @@ function publishedHttpOrigins(record2) {
18927
18946
  return origins;
18928
18947
  }
18929
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) {
18930
18956
  const declaredLength = response.headers.get("content-length");
18931
18957
  if (declaredLength !== null && (!/^\d+$/.test(declaredLength) || Number(declaredLength) > maxBytes)) {
18932
- throw new LocationRecordValidationError(`${label} response is too large`);
18958
+ return null;
18933
18959
  }
18934
- if (response.body === null) return "";
18960
+ if (response.body === null) return new Uint8Array();
18935
18961
  const reader = response.body.getReader();
18936
18962
  const chunks = [];
18937
18963
  let total = 0;
@@ -18943,7 +18969,7 @@ async function boundedResponseText(response, label, maxBytes) {
18943
18969
  total += value.byteLength;
18944
18970
  if (total > maxBytes) {
18945
18971
  await reader.cancel().catch(() => void 0);
18946
- throw new LocationRecordValidationError(`${label} response is too large`);
18972
+ return null;
18947
18973
  }
18948
18974
  chunks.push(value);
18949
18975
  }
@@ -18956,7 +18982,7 @@ async function boundedResponseText(response, label, maxBytes) {
18956
18982
  bytes2.set(chunk, offset);
18957
18983
  offset += chunk.byteLength;
18958
18984
  }
18959
- return new TextDecoder("utf-8", { fatal: true }).decode(bytes2);
18985
+ return bytes2;
18960
18986
  }
18961
18987
  function boundedSignal(parent, timeoutMs) {
18962
18988
  if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
@@ -19635,4 +19661,316 @@ function multiaddrOrigin(address) {
19635
19661
  return null;
19636
19662
  }
19637
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
+ }
19638
19976
  //# sourceMappingURL=index.cjs.map