@tinycloud/sdk-core 2.11.0-beta.1 → 2.11.0-beta.10

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.js CHANGED
@@ -1858,13 +1858,12 @@ function digest(input, options2) {
1858
1858
  }
1859
1859
  var identity2 = { code: code2, name, encode: encode4, digest };
1860
1860
 
1861
- // ../../node_modules/multiformats/dist/src/hashes/sha2.js
1862
- var sha2_exports = {};
1863
- __export(sha2_exports, {
1861
+ // ../../node_modules/multiformats/dist/src/hashes/sha2-browser.js
1862
+ var sha2_browser_exports = {};
1863
+ __export(sha2_browser_exports, {
1864
1864
  sha256: () => sha2562,
1865
1865
  sha512: () => sha512
1866
1866
  });
1867
- import crypto2 from "crypto";
1868
1867
 
1869
1868
  // ../../node_modules/multiformats/dist/src/hashes/hasher.js
1870
1869
  var DEFAULT_MIN_DIGEST_LENGTH = 20;
@@ -1914,21 +1913,24 @@ function createDigest(digest4, code3, truncate) {
1914
1913
  return create(code3, digest4);
1915
1914
  }
1916
1915
 
1917
- // ../../node_modules/multiformats/dist/src/hashes/sha2.js
1916
+ // ../../node_modules/multiformats/dist/src/hashes/sha2-browser.js
1917
+ function sha(name2) {
1918
+ return async (data) => new Uint8Array(await crypto.subtle.digest(name2, data));
1919
+ }
1918
1920
  var sha2562 = from2({
1919
1921
  name: "sha2-256",
1920
1922
  code: 18,
1921
- encode: (input) => coerce(crypto2.createHash("sha256").update(input).digest())
1923
+ encode: sha("SHA-256")
1922
1924
  });
1923
1925
  var sha512 = from2({
1924
1926
  name: "sha2-512",
1925
1927
  code: 19,
1926
- encode: (input) => coerce(crypto2.createHash("sha512").update(input).digest())
1928
+ encode: sha("SHA-512")
1927
1929
  });
1928
1930
 
1929
1931
  // ../../node_modules/multiformats/dist/src/basics.js
1930
1932
  var bases = { ...identity_exports, ...base2_exports, ...base8_exports, ...base10_exports, ...base16_exports, ...base32_exports, ...base36_exports, ...base58_exports, ...base64_exports, ...base256emoji_exports };
1931
- var hashes = { ...sha2_exports, ...identity_exports2 };
1933
+ var hashes = { ...sha2_browser_exports, ...identity_exports2 };
1932
1934
 
1933
1935
  // src/policy/signed-object.ts
1934
1936
  import { bytesToHex as bytesToHex2, sha256 as sha2563, verifyMessage } from "viem";
@@ -6360,15 +6362,28 @@ function normalizeSpace(space) {
6360
6362
  function isCapabilitySubset(requested, granted) {
6361
6363
  const missing = [];
6362
6364
  for (const req of requested) {
6363
- const match = granted.find((g) => canonicalizeEntryMatches(req, g));
6364
- if (match === void 0) {
6365
+ const matchingScope = granted.filter(
6366
+ (entry) => canonicalizeEntryScopeMatches(req, entry)
6367
+ );
6368
+ const requestedActions = expandActionShortNames(
6369
+ req.service,
6370
+ req.actions
6371
+ );
6372
+ const grantedActions = matchingScope.flatMap(
6373
+ (entry) => expandActionShortNames(entry.service, entry.actions)
6374
+ );
6375
+ const covered = requestedActions.every(
6376
+ (requestedAction) => grantedActions.some(
6377
+ (grantedAction) => actionContains(grantedAction, requestedAction)
6378
+ )
6379
+ );
6380
+ if (!covered) {
6365
6381
  missing.push(cloneEntry(req));
6366
- continue;
6367
6382
  }
6368
6383
  }
6369
6384
  return { subset: missing.length === 0, missing };
6370
6385
  }
6371
- function canonicalizeEntryMatches(requested, granted) {
6386
+ function canonicalizeEntryScopeMatches(requested, granted) {
6372
6387
  if (requested.service !== granted.service) {
6373
6388
  return false;
6374
6389
  }
@@ -6378,15 +6393,6 @@ function canonicalizeEntryMatches(requested, granted) {
6378
6393
  if (!pathContains2(granted.path, requested.path)) {
6379
6394
  return false;
6380
6395
  }
6381
- const reqActions = new Set(
6382
- expandActionShortNames(requested.service, requested.actions)
6383
- );
6384
- const grantedActions = expandActionShortNames(granted.service, granted.actions);
6385
- for (const a of reqActions) {
6386
- if (!grantedActions.some((grantedAction) => actionContains(grantedAction, a))) {
6387
- return false;
6388
- }
6389
- }
6390
6396
  if (!recapCaveatsEqual(requested.caveats, granted.caveats)) {
6391
6397
  return false;
6392
6398
  }
@@ -6654,6 +6660,10 @@ function normalizeRecipient(recipient) {
6654
6660
  }
6655
6661
  return { kind: recipient.kind, value: domain };
6656
6662
  }
6663
+ if (recipient.kind === "recipientDid") {
6664
+ if (!isCanonicalRecipientDid(recipient.value)) throw new ShareEnvelopeError("invalid-envelope", "recipient DID is invalid");
6665
+ return { kind: recipient.kind, value: recipient.value };
6666
+ }
6657
6667
  const exact = recipient.value.trim();
6658
6668
  const at = exact.lastIndexOf("@");
6659
6669
  const normalizedExact = at > 0 ? `${exact.slice(0, at)}@${exact.slice(at + 1).toLowerCase()}` : exact;
@@ -6662,6 +6672,28 @@ function normalizeRecipient(recipient) {
6662
6672
  }
6663
6673
  return { kind: recipient.kind, value: normalizedExact };
6664
6674
  }
6675
+ function isCanonicalRecipientDid(value) {
6676
+ if (value.length === 0 || value.length > 2048 || /[\u0000-\u0020\u007f]/.test(value)) return false;
6677
+ const parts = value.split(":");
6678
+ if (parts.length < 3 || parts[0] !== "did" || !/^[a-z0-9]+$/.test(parts[1] ?? "")) return false;
6679
+ const identifier = parts.slice(2);
6680
+ if (identifier.some((part) => part.length === 0)) return false;
6681
+ if (parts[1] === "web") {
6682
+ const host = identifier[0] ?? "";
6683
+ if (host.length > 253 || host.split(".").some((label) => !label || label.length > 63 || !/^[A-Za-z0-9-]+$/.test(label) || label.startsWith("-") || label.endsWith("-"))) return false;
6684
+ return identifier.slice(1).every((part) => /^[A-Za-z0-9._%-]+$/.test(part));
6685
+ }
6686
+ if (parts[1] === "pkh") return identifier.length >= 3 && identifier.every((part) => /^[A-Za-z0-9._%-]+$/.test(part));
6687
+ if (parts[1] === "key") {
6688
+ try {
6689
+ const bytes2 = base58btc.decode(identifier.join(":"));
6690
+ return bytes2.length === 34 && bytes2[0] === 237 && bytes2[1] === 1;
6691
+ } catch {
6692
+ return false;
6693
+ }
6694
+ }
6695
+ return false;
6696
+ }
6665
6697
  function normalizeShareRecipientTarget(recipient) {
6666
6698
  const normalized = normalizeRecipient(recipient);
6667
6699
  if (normalized === void 0) throw new ShareEnvelopeError("invalid-envelope", "recipient matcher is required");
@@ -6748,9 +6780,9 @@ function verifyShareEnvelope(envelope, options2 = {}) {
6748
6780
  const recipient = normalizeRecipient(envelope.recipient);
6749
6781
  if (recipient !== void 0 && JSON.stringify(recipient) !== JSON.stringify(envelope.recipient)) throw new ShareEnvelopeError("invalid-envelope", "recipient is not canonical");
6750
6782
  if (recipient?.kind === "exactEmail" || recipient?.kind === "emailDomain") throw new ShareEnvelopeError("unsafe-plaintext", "recipient matchers are not public envelope fields");
6751
- if (envelope.recipientKind !== void 0 && !["exactEmail", "emailDomain", "bearer"].includes(envelope.recipientKind)) throw new ShareEnvelopeError("invalid-envelope", "recipient mode is invalid");
6783
+ if (envelope.recipientKind !== void 0 && !["exactEmail", "emailDomain", "recipientDid", "bearer"].includes(envelope.recipientKind)) throw new ShareEnvelopeError("invalid-envelope", "recipient mode is invalid");
6752
6784
  if (envelope.recipientKind === "bearer" && recipient?.kind !== void 0 && recipient.kind !== "bearer") throw new ShareEnvelopeError("invalid-envelope", "recipient mode does not match recipient");
6753
- if ((envelope.recipientKind === "exactEmail" || envelope.recipientKind === "emailDomain") && !envelope.encrypted) throw new ShareEnvelopeError("unsafe-plaintext", "policy shares require encryption");
6785
+ if ((envelope.recipientKind === "exactEmail" || envelope.recipientKind === "emailDomain" || envelope.recipientKind === "recipientDid") && !envelope.encrypted) throw new ShareEnvelopeError("unsafe-plaintext", "addressed shares require encryption");
6754
6786
  if (typeof envelope.spaceId !== "string" || envelope.spaceId.length === 0 || typeof envelope.content.mimeType !== "string" || envelope.content.mimeType.length === 0 || typeof envelope.content.digest !== "string" || !/^[A-Za-z0-9_-]+$/.test(envelope.content.digest)) throw new ShareEnvelopeError("invalid-envelope", "content metadata is invalid");
6755
6787
  const maxContentBytes = envelope.encrypted ? options2.maxSealedContentBytes ?? MAX_SEALED_SHARE_CONTENT_BYTES : options2.maxContentBytes ?? MAX_SHARE_CONTENT_BYTES;
6756
6788
  if (!Number.isInteger(envelope.content.size) || envelope.content.size < 0 || envelope.content.size > maxContentBytes) throw new ShareEnvelopeError("content-too-large", "share content exceeds the configured limit");
@@ -6889,7 +6921,7 @@ function parsePolicyMetadata(policyBytes, target, expectedExpiry) {
6889
6921
  assertKeys(policy, ["type", "version", "issuerDid", "recipientMatcher", "target", "delegation", "authority", "actions", "resource", "content-source", "expiresAt"], "published canonical policy material");
6890
6922
  if (typeof policy.issuerDid !== "string" || typeof policy.expiresAt !== "string" || policy.expiresAt !== expectedExpiry) throw new ShareEnvelopeError("invalid-envelope", "policy expiry is not bound to the envelope");
6891
6923
  const matcherValue = policy.recipientMatcher;
6892
- if (!isPlainObject(matcherValue) || matcherValue.kind !== "exactEmail" && matcherValue.kind !== "emailDomain" || typeof matcherValue.value !== "string") throw new ShareEnvelopeError("invalid-envelope", "policy matcher is invalid");
6924
+ if (!isPlainObject(matcherValue) || matcherValue.kind !== "exactEmail" && matcherValue.kind !== "emailDomain" && matcherValue.kind !== "recipientDid" || typeof matcherValue.value !== "string") throw new ShareEnvelopeError("invalid-envelope", "policy matcher is invalid");
6893
6925
  const matcher2 = normalizeRecipient(matcherValue);
6894
6926
  if (matcher2 === void 0 || matcher2.kind === "bearer") throw new ShareEnvelopeError("invalid-envelope", "policy matcher is invalid");
6895
6927
  if (!isPlainObject(policy.target)) throw new ShareEnvelopeError("invalid-envelope", "policy target is invalid");
@@ -6976,7 +7008,7 @@ function parseCanonicalPublishedV2(value, options2) {
6976
7008
  if (!Number.isFinite(expiry) || expiry <= (options2.now ?? /* @__PURE__ */ new Date()).getTime() || new Date(expiry).toISOString() !== value.expiry) throw new ShareEnvelopeError("expired", "published share envelope has expired");
6977
7009
  if (typeof delegation.cid !== "string" || typeof delegation.issuerDid !== "string" || typeof delegation.audience !== "string" || typeof delegation.expiresAt !== "string" || Date.parse(delegation.expiresAt) > expiry) throw new ShareEnvelopeError("invalid-envelope", "published v2 delegation is invalid");
6978
7010
  const matcher = value.recipientMatcher;
6979
- if (!isPlainObject(matcher) || matcher.kind !== "exactEmail" && matcher.kind !== "emailDomain" || typeof matcher.value !== "string") throw new ShareEnvelopeError("invalid-envelope", "published v2 recipient matcher is invalid");
7011
+ if (!isPlainObject(matcher) || matcher.kind !== "exactEmail" && matcher.kind !== "emailDomain" && matcher.kind !== "recipientDid" || typeof matcher.value !== "string") throw new ShareEnvelopeError("invalid-envelope", "published v2 recipient matcher is invalid");
6980
7012
  const normalizedMatcher = normalizeRecipient(matcher);
6981
7013
  if (normalizedMatcher === void 0 || normalizedMatcher.kind === "bearer") throw new ShareEnvelopeError("invalid-envelope", "published v2 recipient matcher is invalid");
6982
7014
  if (!Array.isArray(value.actions)) throw new ShareEnvelopeError("invalid-envelope", "published v2 actions are invalid");
@@ -7417,10 +7449,11 @@ var ShareConflict = class extends Error {
7417
7449
  }
7418
7450
  };
7419
7451
  var ShareAccessError = class extends Error {
7420
- constructor(code3, message) {
7452
+ constructor(code3, message, details) {
7421
7453
  super(message ?? "shared access could not be completed");
7422
7454
  this.name = "ShareAccessError";
7423
7455
  this.code = code3;
7456
+ this.details = details;
7424
7457
  }
7425
7458
  };
7426
7459
 
@@ -7429,6 +7462,7 @@ import { z as z5 } from "zod";
7429
7462
  var ShareRecipientTargetSchema = z5.discriminatedUnion("kind", [
7430
7463
  z5.object({ kind: z5.literal("exactEmail"), value: z5.string().min(1) }).strict(),
7431
7464
  z5.object({ kind: z5.literal("emailDomain"), value: z5.string().min(1) }).strict(),
7465
+ z5.object({ kind: z5.literal("recipientDid"), value: z5.string().regex(/^did:[a-z0-9]+:.+$/) }).strict(),
7432
7466
  z5.object({ kind: z5.literal("bearer") }).strict()
7433
7467
  ]);
7434
7468
  var ShareResourceSchema = z5.discriminatedUnion("kind", [
@@ -7472,7 +7506,8 @@ var ShareContentSourceSchema = z5.discriminatedUnion("kind", [
7472
7506
  ]);
7473
7507
  var ShareAddressedRecipientSchema = z5.discriminatedUnion("kind", [
7474
7508
  z5.object({ kind: z5.literal("exactEmail"), value: z5.string().min(1) }).strict(),
7475
- z5.object({ kind: z5.literal("emailDomain"), value: z5.string().min(1) }).strict()
7509
+ z5.object({ kind: z5.literal("emailDomain"), value: z5.string().min(1) }).strict(),
7510
+ z5.object({ kind: z5.literal("recipientDid"), value: z5.string().regex(/^did:[a-z0-9]+:.+$/) }).strict()
7476
7511
  ]);
7477
7512
  var ShareAddressedDelegationRequestV2Schema = z5.object({
7478
7513
  version: z5.literal(2),
@@ -7727,12 +7762,21 @@ var ShareRecipientClient = class {
7727
7762
  const { envelope } = resolved.artifact;
7728
7763
  const { location } = resolved;
7729
7764
  const bearer = isBearerEnvelope(envelope);
7730
- const policy = envelope.recipientKind === "exactEmail" || envelope.recipientKind === "emailDomain";
7765
+ const policy = envelope.recipientKind === "exactEmail" || envelope.recipientKind === "emailDomain" || envelope.authorizationTargetKind === "recipientDid";
7731
7766
  const legacyBearer = !policy && envelope.recipientKind === void 0;
7732
- if (envelope.authorizationTargetKind === "recipientDid") throw new ShareAccessError("SHARE_RECIPIENT_DID_UNSUPPORTED");
7767
+ let recipientDidSession;
7768
+ if (envelope.authorizationTargetKind === "recipientDid") {
7769
+ if (this.options.establishRecipientDidSession === void 0) throw new ShareAccessError("SHARE_DEVICE_AUTH_REQUIRED", "an OpenKey device authorization is required");
7770
+ const authorization = await this.options.establishRecipientDidSession({ envelope, recipientDid: envelope.recipient?.kind === "recipientDid" ? envelope.recipient.value : "" });
7771
+ if ("state" in authorization) {
7772
+ if (authorization.state === "authorization-required") throw new ShareAccessError("SHARE_DEVICE_AUTH_REQUIRED", "an OpenKey device authorization is required", authorization);
7773
+ throw new ShareAccessError("SHARE_DENIED", "recipient DID authorization was denied", authorization);
7774
+ }
7775
+ recipientDidSession = authorization;
7776
+ }
7733
7777
  if (policy && this.options.bearerSession !== void 0) throw new ShareAccessError("SHARE_POLICY_BEARER_MISMATCH");
7734
7778
  if (bearer && this.options.presentation !== void 0) throw new ShareAccessError("SHARE_BEARER_PRESENTATION_MISMATCH");
7735
- const session = bearer || legacyBearer ? this.options.bearerSession : policy && this.options.establishPolicySession !== void 0 ? await this.options.establishPolicySession({ envelope, presentation: this.options.presentation }) : policy ? await this.establishPolicySession(envelope) : void 0;
7779
+ const session = bearer || legacyBearer ? this.options.bearerSession : recipientDidSession ? recipientDidSession : policy && this.options.establishPolicySession !== void 0 ? await this.options.establishPolicySession({ envelope, presentation: this.options.presentation }) : policy ? await this.establishPolicySession(envelope) : void 0;
7736
7780
  const kv = bearer || legacyBearer ? session === void 0 ? void 0 : this.createKV(envelope.origin, session) : void 0;
7737
7781
  const policySession = policy ? session : void 0;
7738
7782
  if (policySession !== void 0) this.assertProvidedPolicySession(policySession, envelope);
@@ -9933,7 +9977,7 @@ function validateShareDeliveryAuthorizationBytes(bytesValue, expected) {
9933
9977
  const authorization = assertObject2(root.authorization, AUTHORIZATION_KEYS, "share delivery authorization");
9934
9978
  const proof = assertObject2(root.proof, ["alg", "kid", "signature"], "share delivery authorization proof");
9935
9979
  const request = expected.request;
9936
- if (authorization.type !== "TinyCloudShareDeliveryAuthorization" || authorization.version !== 2 || authorization.dataAuthority !== false || authorization.jti !== request.jti || authorization.shareCid !== request.shareCid || authorization.shareId !== request.shareId || authorization.registrationCid !== request.registrationCid || authorization.delegationCid !== request.delegationCid || authorization.enforcementDelegationCid !== request.enforcementDelegationCid || authorization.envelopeCid !== request.envelopeCid || authorization.policyCid !== request.policyCid || authorization.deliveryEmail !== request.recipientEmail || authorization.shareUrl !== request.shareUrl || authorization.documentName !== request.documentName || authorization.requestBodyDigest !== request.requestBodyDigest || authorization.expiresAt !== request.expiresAt || authorization.idempotencyKey !== request.jti || authorization.reportAbuseToken !== request.jti || authorization.authorityMaterialHandle !== request.registrationCid) {
9980
+ if (authorization.type !== "TinyCloudShareDeliveryAuthorization" || authorization.version !== 2 || authorization.dataAuthority !== false || authorization.jti !== request.jti || authorization.shareCid !== request.shareCid || authorization.shareId !== request.shareId || authorization.registrationCid !== request.registrationCid || authorization.delegationCid !== request.delegationCid || authorization.enforcementDelegationCid !== request.enforcementDelegationCid || authorization.envelopeCid !== request.envelopeCid || authorization.policyCid !== request.policyCid || authorization.deliveryEmail !== request.recipientEmail || authorization.shareUrl !== request.shareUrl || authorization.documentName !== request.documentName || authorization.requestBodyDigest !== request.requestBodyDigest || authorization.expiresAt !== request.expiresAt || authorization.idempotencyKey !== request.idempotencyKey || authorization.reportAbuseToken !== request.jti || authorization.authorityMaterialHandle !== request.registrationCid) {
9937
9981
  throw new Error("share delivery authorization is not bound to the submitted request");
9938
9982
  }
9939
9983
  if (typeof authorization.issuedAt !== "string" || Date.parse(authorization.issuedAt) > Date.now() + 6e4) throw new Error("share delivery authorization issuedAt is invalid");
@@ -10068,6 +10112,32 @@ var SenderShareStore = class {
10068
10112
  }
10069
10113
  };
10070
10114
 
10115
+ // src/delegations/index.ts
10116
+ import {
10117
+ authorizeShare,
10118
+ authorizationMethodForTarget,
10119
+ notifyShare,
10120
+ revokeShare,
10121
+ listShares,
10122
+ showShare,
10123
+ publishTargetShare,
10124
+ publishPolicyShare,
10125
+ claimShare,
10126
+ resumeShareAuthorization,
10127
+ isLegacyShareLink,
10128
+ receiveLegacyShare,
10129
+ migrateShare,
10130
+ MemoryEncryptedShareHistoryStorage,
10131
+ EncryptedSenderShareHistory
10132
+ } from "@tinycloud/share-sdk";
10133
+ import {
10134
+ inspectShare,
10135
+ receiveShare,
10136
+ ShareReceiveError,
10137
+ toShareErrorInfo,
10138
+ verifyBearerEnvelope
10139
+ } from "@tinycloud/share-sdk";
10140
+
10071
10141
  // src/networkId.ts
10072
10142
  import {
10073
10143
  buildNetworkId,
@@ -11789,7 +11859,8 @@ var TinyCloud = class _TinyCloud {
11789
11859
  import {
11790
11860
  err as err3,
11791
11861
  ok as ok3,
11792
- serviceError as serviceError3
11862
+ serviceError as serviceError3,
11863
+ ErrorCodes as ErrorCodes3
11793
11864
  } from "@tinycloud/sdk-services";
11794
11865
  import { ACCOUNT_INDEX_SCHEMA } from "@tinycloud/bootstrap";
11795
11866
  var SERVICE_NAME2 = "account";
@@ -11799,6 +11870,22 @@ var ACCOUNT_SPACES_PATH = "spaces/";
11799
11870
  var AccountService = class {
11800
11871
  constructor(config) {
11801
11872
  this.config = config;
11873
+ /**
11874
+ * Per-account-space memo for {@link ensureAccountIndex}.
11875
+ *
11876
+ * Applying the index migration is a no-op once the schema exists, but the
11877
+ * SQL service still spends two round trips discovering that (create the
11878
+ * migrations table, then read the applied-migration rows). Account bootstrap
11879
+ * touches the index eight times, so without a memo that is ~14 wasted
11880
+ * requests on the cold sign-in path. The in-flight dedupe inside SQLService
11881
+ * never helps here: the node SDK builds a fresh SQLService per
11882
+ * `getAccountDb()` call, so its lock map is always empty.
11883
+ *
11884
+ * Keyed by account space id, so a different account never reuses this entry.
11885
+ * Failures are evicted so a later call retries instead of inheriting a
11886
+ * permanently poisoned result.
11887
+ */
11888
+ this.accountIndexReady = /* @__PURE__ */ new Map();
11802
11889
  this.applications = {
11803
11890
  list: async (options2 = {}) => {
11804
11891
  if (options2.preferIndex) {
@@ -11832,7 +11919,7 @@ var AccountService = class {
11832
11919
  if (!loaded.ok) return accountErr(loaded.error);
11833
11920
  return ok3(applicationFromRecord(key, loaded.data.data));
11834
11921
  },
11835
- register: async (manifest) => {
11922
+ register: async (manifest, options2 = {}) => {
11836
11923
  const manifests = Array.isArray(manifest) ? manifest : [manifest];
11837
11924
  const request = composeManifestRequest(manifests);
11838
11925
  if (request.registryRecords.length === 0) {
@@ -11850,7 +11937,7 @@ var AccountService = class {
11850
11937
  let registered;
11851
11938
  for (const record of request.registryRecords) {
11852
11939
  const manifestHash = hashJson(record.manifests);
11853
- if (await this.indexHasApplicationHash(record.app_id, manifestHash)) {
11940
+ if (!options2.assumeUnregistered && await this.indexHasApplicationHash(record.app_id, manifestHash)) {
11854
11941
  registered = {
11855
11942
  appId: record.app_id,
11856
11943
  manifests: record.manifests,
@@ -11925,6 +12012,60 @@ var AccountService = class {
11925
12012
  await this.upsertSpaceIndexQuietly(registered);
11926
12013
  return ok3(registered);
11927
12014
  },
12015
+ /**
12016
+ * Register multiple spaces in one KV batch write + one multi-row index
12017
+ * write, instead of one `register()` round trip per space (TC-373).
12018
+ *
12019
+ * The stored records are computed exactly once and reused for both the
12020
+ * batch attempt and any reconciliation below, so a fallback never mints a
12021
+ * fresh timestamp for a space that already has a written record — a
12022
+ * second `spaceRecordFromInput` call would produce a new content hash and
12023
+ * leave the first attempt's blocks orphaned.
12024
+ */
12025
+ registerBatch: async (spaces) => {
12026
+ if (spaces.length === 0) return ok3({ spaces: [] });
12027
+ await this.config.ensureAccountSpaceHosted?.();
12028
+ const kvResult = this.accountKV();
12029
+ if (!kvResult.ok) return kvResult;
12030
+ const stored = spaces.map((space) => spaceRecordFromInput(space));
12031
+ const registered = stored.map(
12032
+ (record) => spaceFromRecord(spaceKey(record.space_id), record)
12033
+ );
12034
+ const items = stored.map((record) => ({
12035
+ key: spaceKey(record.space_id),
12036
+ value: record
12037
+ }));
12038
+ const batchResult = await kvResult.data.batchPut(items);
12039
+ if (batchResult.ok) {
12040
+ await this.upsertSpacesIndexQuietly(registered);
12041
+ return ok3({ spaces: registered });
12042
+ }
12043
+ if (!isAmbiguousBatchFailure(batchResult.error)) {
12044
+ return accountErr(batchResult.error);
12045
+ }
12046
+ const reconcileFailures = [];
12047
+ for (const record of stored) {
12048
+ const written = await kvResult.data.put(spaceKey(record.space_id), record);
12049
+ if (!written.ok) {
12050
+ reconcileFailures.push(`${record.space_id}: ${written.error.message}`);
12051
+ }
12052
+ }
12053
+ if (reconcileFailures.length > 0) {
12054
+ return err3(
12055
+ serviceError3(
12056
+ "KV_BATCH_RECONCILE_FAILED",
12057
+ `KV batch write for ${items.length} space(s) failed ambiguously (${batchResult.error.message}) and per-space reconciliation also failed for: ${reconcileFailures.join("; ")}`,
12058
+ SERVICE_NAME2,
12059
+ { cause: batchResult.error.cause, meta: { originalError: batchResult.error } }
12060
+ )
12061
+ );
12062
+ }
12063
+ console.warn(
12064
+ `[AccountService] KV batch write for ${items.length} space(s) returned an ambiguous error and was reconciled via per-space fallback. Original error: ${batchResult.error.message}`
12065
+ );
12066
+ await this.upsertSpacesIndexQuietly(registered);
12067
+ return ok3({ spaces: registered, recoveredFromBatchError: batchResult.error });
12068
+ },
11928
12069
  syncAccessible: async () => {
11929
12070
  const listed = await this.config.getSpaces().list();
11930
12071
  if (!listed.ok) return accountErr(listed.error);
@@ -12031,6 +12172,7 @@ var AccountService = class {
12031
12172
  const delegations = await this.delegations.list();
12032
12173
  if (!delegations.ok) return delegations;
12033
12174
  const syncedAt = (/* @__PURE__ */ new Date()).toISOString();
12175
+ this.invalidateAccountIndexMemo();
12034
12176
  const schema = await this.ensureAccountIndex(dbResult.data);
12035
12177
  if (!schema.ok) return schema;
12036
12178
  const statements = [
@@ -12310,6 +12452,47 @@ var AccountService = class {
12310
12452
  if (!written.ok) return accountErr(written.error);
12311
12453
  return ok3(void 0);
12312
12454
  }
12455
+ async upsertSpacesIndexQuietly(spaces) {
12456
+ await ignoreIndexFailure(() => this.upsertSpacesIndex(spaces));
12457
+ }
12458
+ /**
12459
+ * Upsert multiple space index rows in ONE multi-row `INSERT OR REPLACE`
12460
+ * statement (TC-373), rather than one statement per space. This matters
12461
+ * because `IDatabaseHandle.batch()` executes its statement list as a plain
12462
+ * loop, not a single database transaction — an N-statement batch is not
12463
+ * atomic. A single multi-row INSERT is one SQLite statement, so it applies
12464
+ * atomically: all rows or none.
12465
+ */
12466
+ async upsertSpacesIndex(spaces) {
12467
+ if (spaces.length === 0) return ok3(void 0);
12468
+ const dbResult = this.accountDb();
12469
+ if (!dbResult.ok) return ok3(void 0);
12470
+ const schema = await this.ensureAccountIndex(dbResult.data);
12471
+ if (!schema.ok) return schema;
12472
+ const placeholders = spaces.map(() => "(?, ?, ?, ?, ?, ?, ?, ?, ?)").join(", ");
12473
+ const params = spaces.flatMap((space) => {
12474
+ const updatedAt = space.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString();
12475
+ return [
12476
+ space.spaceId,
12477
+ space.name,
12478
+ space.ownerDid,
12479
+ space.type,
12480
+ JSON.stringify(space.permissions),
12481
+ space.status,
12482
+ space.registeredAt ?? updatedAt,
12483
+ updatedAt,
12484
+ space.expiresAt?.toISOString() ?? null
12485
+ ];
12486
+ });
12487
+ const written = await dbResult.data.batch([
12488
+ {
12489
+ sql: `INSERT OR REPLACE INTO spaces (space_id, name, owner_did, type, permissions_json, status, registered_at, updated_at, expires_at) VALUES ${placeholders}`,
12490
+ params
12491
+ }
12492
+ ]);
12493
+ if (!written.ok) return accountErr(written.error);
12494
+ return ok3(void 0);
12495
+ }
12313
12496
  async deleteSpaceIndexQuietly(spaceId) {
12314
12497
  await ignoreIndexFailure(() => this.deleteSpaceIndex(spaceId));
12315
12498
  }
@@ -12430,7 +12613,41 @@ var AccountService = class {
12430
12613
  ]);
12431
12614
  });
12432
12615
  }
12616
+ /**
12617
+ * Apply the account index schema at most once per account space.
12618
+ *
12619
+ * The index is a cache over canonical KV state, and every caller already
12620
+ * tolerates it being absent (`ignoreIndexFailure` / `isMissingIndexError`),
12621
+ * so a memo that is wrong in the "already applied" direction degrades to a
12622
+ * `no such table` read that the caller handles. A memo that is wrong in the
12623
+ * other direction would be a real bug, so failures are never cached.
12624
+ */
12433
12625
  async ensureAccountIndex(db) {
12626
+ const key = this.config.getAccountSpaceId() ?? "";
12627
+ const cached = this.accountIndexReady.get(key);
12628
+ if (cached) return cached;
12629
+ const pending = this.applyAccountIndex(db);
12630
+ this.accountIndexReady.set(key, pending);
12631
+ const evict = () => {
12632
+ if (this.accountIndexReady.get(key) === pending) {
12633
+ this.accountIndexReady.delete(key);
12634
+ }
12635
+ };
12636
+ let settled;
12637
+ try {
12638
+ settled = await pending;
12639
+ } catch (error) {
12640
+ evict();
12641
+ throw error;
12642
+ }
12643
+ if (!settled.ok) evict();
12644
+ return settled;
12645
+ }
12646
+ /** Drop the memo so the next {@link ensureAccountIndex} re-applies the schema. */
12647
+ invalidateAccountIndexMemo() {
12648
+ this.accountIndexReady.delete(this.config.getAccountSpaceId() ?? "");
12649
+ }
12650
+ async applyAccountIndex(db) {
12434
12651
  const migrated = await db.migrations.apply({
12435
12652
  namespace: ACCOUNT_INDEX_NAMESPACE,
12436
12653
  migrations: [
@@ -12602,6 +12819,17 @@ function accountErr(error) {
12602
12819
  function isMissingIndexError(error) {
12603
12820
  return /no such table:/i.test(error.message);
12604
12821
  }
12822
+ var AMBIGUOUS_WRITE_STATUSES = /* @__PURE__ */ new Set([500, 502, 503, 504, 520, 522, 524]);
12823
+ function isAmbiguousBatchFailure(error) {
12824
+ if (error.code === ErrorCodes3.NETWORK_ERROR || error.code === ErrorCodes3.TIMEOUT) {
12825
+ return error.meta?.requestMayHaveDispatched === true;
12826
+ }
12827
+ if (error.code === ErrorCodes3.KV_WRITE_FAILED) {
12828
+ const status = error.meta?.status;
12829
+ return typeof status === "number" && AMBIGUOUS_WRITE_STATUSES.has(status);
12830
+ }
12831
+ return false;
12832
+ }
12605
12833
  async function ignoreIndexFailure(task) {
12606
12834
  try {
12607
12835
  await task();
@@ -12617,7 +12845,7 @@ import {
12617
12845
  ok as ok5,
12618
12846
  err as err5,
12619
12847
  serviceError as serviceError5,
12620
- ErrorCodes as ErrorCodes3,
12848
+ ErrorCodes as ErrorCodes4,
12621
12849
  TinyCloudDebugLogger,
12622
12850
  tinyCloudDebugLogger,
12623
12851
  enableTinyCloudDebug,
@@ -13291,6 +13519,279 @@ var AutoApproveSpaceCreationHandler = class {
13291
13519
  };
13292
13520
  var defaultSpaceCreationHandler = new AutoApproveSpaceCreationHandler();
13293
13521
 
13522
+ // src/authorization/openkey-protocol.ts
13523
+ function validateAuthorizationResultV1(value) {
13524
+ if (!value || typeof value !== "object") {
13525
+ return { ok: false, error: "authorization result must be an object" };
13526
+ }
13527
+ const v = value;
13528
+ if (v.protocolVersion !== 1) {
13529
+ return { ok: false, error: `unsupported protocolVersion ${String(v.protocolVersion)}` };
13530
+ }
13531
+ if (typeof v.address !== "string" || !v.address) {
13532
+ return { ok: false, error: "address must be a non-empty string" };
13533
+ }
13534
+ if (typeof v.signature !== "string" || !v.signature) {
13535
+ return { ok: false, error: "signature must be a non-empty string" };
13536
+ }
13537
+ if (typeof v.signedMessage !== "string" || !v.signedMessage) {
13538
+ return { ok: false, error: "signedMessage must be a non-empty string" };
13539
+ }
13540
+ if (!Array.isArray(v.selectedActionKeys) || v.selectedActionKeys.some((k) => typeof k !== "string")) {
13541
+ return { ok: false, error: "selectedActionKeys must be a string[]" };
13542
+ }
13543
+ if (!Array.isArray(v.permissions)) {
13544
+ return { ok: false, error: "permissions must be an array" };
13545
+ }
13546
+ const permissions = [];
13547
+ for (const [index, entry] of v.permissions.entries()) {
13548
+ if (!entry || typeof entry !== "object") {
13549
+ return { ok: false, error: `permissions[${index}] must be an object` };
13550
+ }
13551
+ const e = entry;
13552
+ if (typeof e.service !== "string" || typeof e.space !== "string" || typeof e.path !== "string") {
13553
+ return { ok: false, error: `permissions[${index}] must have string service/space/path` };
13554
+ }
13555
+ if (!Array.isArray(e.actions) || e.actions.some((a) => typeof a !== "string")) {
13556
+ return { ok: false, error: `permissions[${index}].actions must be a string[]` };
13557
+ }
13558
+ permissions.push({
13559
+ service: e.service,
13560
+ space: e.space,
13561
+ path: e.path,
13562
+ actions: e.actions
13563
+ });
13564
+ }
13565
+ return {
13566
+ ok: true,
13567
+ value: {
13568
+ protocolVersion: 1,
13569
+ address: v.address,
13570
+ signature: v.signature,
13571
+ signedMessage: v.signedMessage,
13572
+ selectedActionKeys: v.selectedActionKeys,
13573
+ permissions
13574
+ }
13575
+ };
13576
+ }
13577
+ var OPENKEY_ACTION_ID_SEPARATOR = "\0";
13578
+ function isPlausibleOpenKeyActionId(id) {
13579
+ return typeof id === "string" && id.split(OPENKEY_ACTION_ID_SEPARATOR).length === 4 && id.length > 0;
13580
+ }
13581
+ function parseCanonicalRecapResource(resource) {
13582
+ if (!resource.startsWith("tinycloud:")) {
13583
+ return { space: resource, path: "" };
13584
+ }
13585
+ const firstSlash = resource.indexOf("/");
13586
+ if (firstSlash < 0) {
13587
+ return { space: resource, path: "" };
13588
+ }
13589
+ const space = resource.slice(0, firstSlash);
13590
+ const rest = resource.slice(firstSlash + 1);
13591
+ const secondSlash = rest.indexOf("/");
13592
+ if (secondSlash < 0) {
13593
+ return { space, path: "" };
13594
+ }
13595
+ return { space, path: rest.slice(secondSlash + 1) };
13596
+ }
13597
+ function extractImmutableSiweFields(siwe) {
13598
+ const lines = siwe.split(/\r?\n/);
13599
+ const result = {};
13600
+ const headerLine = lines[0];
13601
+ if (headerLine) {
13602
+ const domainMatch = headerLine.match(/^(.+?)\s+wants you to sign in/);
13603
+ if (domainMatch) {
13604
+ result.domain = domainMatch[1];
13605
+ }
13606
+ }
13607
+ for (let i = 1; i < lines.length && i < 5; i++) {
13608
+ const line = lines[i]?.trim() ?? "";
13609
+ if (/^0x[0-9a-fA-F]{40}$/.test(line)) {
13610
+ result.address = line;
13611
+ break;
13612
+ }
13613
+ }
13614
+ for (const line of lines) {
13615
+ const match = line.match(/^([A-Z][A-Za-z ]*):\s*(.*)$/);
13616
+ if (!match) continue;
13617
+ const [, key, value] = match;
13618
+ switch (key) {
13619
+ case "URI":
13620
+ result.uri = value;
13621
+ break;
13622
+ case "Version":
13623
+ result.version = value;
13624
+ break;
13625
+ case "Chain ID":
13626
+ result.chainId = value;
13627
+ break;
13628
+ case "Nonce":
13629
+ result.nonce = value;
13630
+ break;
13631
+ case "Issued At":
13632
+ result.issuedAt = value;
13633
+ break;
13634
+ // Sol MAJOR-4: complete immutable-field coverage.
13635
+ case "Expiration Time":
13636
+ result.expirationTime = value;
13637
+ break;
13638
+ case "Not Before":
13639
+ result.notBefore = value;
13640
+ break;
13641
+ case "Request ID":
13642
+ result.requestId = value;
13643
+ break;
13644
+ default:
13645
+ break;
13646
+ }
13647
+ }
13648
+ const uriLineIdx = lines.findIndex((l) => /^URI:/.test(l));
13649
+ if (uriLineIdx > 3) {
13650
+ let end = uriLineIdx - 1;
13651
+ while (end > 3 && lines[end] === "") end -= 1;
13652
+ if (end >= 3) {
13653
+ result.statement = lines.slice(3, end + 1).join("\n");
13654
+ } else {
13655
+ result.statement = "";
13656
+ }
13657
+ } else {
13658
+ result.statement = "";
13659
+ }
13660
+ result.nonRecapResources = lines.filter((l) => /^- /.test(l) && !/^- urn:recap:/.test(l)).join("\n");
13661
+ return result;
13662
+ }
13663
+ function diffImmutableSiweFields(original, signed, options2) {
13664
+ const baseKeys = [
13665
+ "domain",
13666
+ "address",
13667
+ "uri",
13668
+ "version",
13669
+ "chainId",
13670
+ "nonce",
13671
+ "issuedAt",
13672
+ "expirationTime",
13673
+ "notBefore",
13674
+ "requestId",
13675
+ "nonRecapResources"
13676
+ ];
13677
+ const keys = options2?.originalHasRecap ? baseKeys : [...baseKeys, "statement"];
13678
+ const diffs = [];
13679
+ for (const key of keys) {
13680
+ const a = original[key] ?? "";
13681
+ const b = signed[key] ?? "";
13682
+ if (a !== b) {
13683
+ diffs.push(key);
13684
+ }
13685
+ }
13686
+ return diffs;
13687
+ }
13688
+ function extractRecapAttenuations(siwe) {
13689
+ const merged = {};
13690
+ const lines = siwe.split(/\r?\n/);
13691
+ for (const line of lines) {
13692
+ const match = line.match(/urn:recap:([A-Za-z0-9_-]+=*)/);
13693
+ if (!match) continue;
13694
+ const encoded = match[1];
13695
+ let decodedJson;
13696
+ try {
13697
+ const normalized = encoded.replace(/-/g, "+").replace(/_/g, "/");
13698
+ const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
13699
+ decodedJson = Buffer.from(padded, "base64").toString("utf8");
13700
+ } catch (err6) {
13701
+ throw new Error(
13702
+ `Failed to decode urn:recap: payload: ${err6.message}`
13703
+ );
13704
+ }
13705
+ let parsed;
13706
+ try {
13707
+ parsed = JSON.parse(decodedJson);
13708
+ } catch (err6) {
13709
+ throw new Error(
13710
+ `urn:recap: payload is not valid JSON: ${err6.message}`
13711
+ );
13712
+ }
13713
+ if (!parsed || typeof parsed !== "object") {
13714
+ throw new Error("urn:recap: payload must be an object");
13715
+ }
13716
+ const att = parsed.att;
13717
+ if (att === void 0) {
13718
+ continue;
13719
+ }
13720
+ if (typeof att !== "object" || att === null || Array.isArray(att)) {
13721
+ throw new Error("urn:recap: att must be an object");
13722
+ }
13723
+ for (const [resource, actionMap] of Object.entries(att)) {
13724
+ if (!actionMap || typeof actionMap !== "object" || Array.isArray(actionMap)) {
13725
+ throw new Error(`urn:recap: att[${resource}] must be an object`);
13726
+ }
13727
+ const existing = merged[resource] ?? {};
13728
+ for (const [action, caveats] of Object.entries(actionMap)) {
13729
+ if (!Array.isArray(caveats)) {
13730
+ throw new Error(
13731
+ `urn:recap: att[${resource}][${action}] must be an array`
13732
+ );
13733
+ }
13734
+ const previous = existing[action] ?? [];
13735
+ existing[action] = previous.concat(caveats);
13736
+ }
13737
+ merged[resource] = existing;
13738
+ }
13739
+ }
13740
+ return merged;
13741
+ }
13742
+ function stableStringify(value) {
13743
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
13744
+ if (Array.isArray(value)) {
13745
+ return `[${value.map(stableStringify).join(",")}]`;
13746
+ }
13747
+ const entries = Object.entries(value).sort(
13748
+ ([a], [b]) => a.localeCompare(b)
13749
+ );
13750
+ return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).join(",")}}`;
13751
+ }
13752
+ function caveatsDeepEqual(a, b) {
13753
+ return stableStringify(a) === stableStringify(b);
13754
+ }
13755
+ function unauthorizedRecapCapabilities(child, parent) {
13756
+ const unauthorized = [];
13757
+ for (const [resource, actions] of Object.entries(child)) {
13758
+ const parentActions = parent[resource];
13759
+ for (const [action, childCaveatsRaw] of Object.entries(actions)) {
13760
+ if (!parentActions || parentActions[action] === void 0) {
13761
+ unauthorized.push({ resource, action });
13762
+ continue;
13763
+ }
13764
+ const parentCaveatsRaw = parentActions[action];
13765
+ const parentCaveats = Array.isArray(parentCaveatsRaw) ? parentCaveatsRaw : [];
13766
+ const childCaveats = Array.isArray(childCaveatsRaw) ? childCaveatsRaw : [];
13767
+ const parentCanonCounts = /* @__PURE__ */ new Map();
13768
+ for (const c of parentCaveats) {
13769
+ const k = stableStringify(c);
13770
+ parentCanonCounts.set(k, (parentCanonCounts.get(k) ?? 0) + 1);
13771
+ }
13772
+ const childCanonCounts = /* @__PURE__ */ new Map();
13773
+ for (const c of childCaveats) {
13774
+ const k = stableStringify(c);
13775
+ childCanonCounts.set(k, (childCanonCounts.get(k) ?? 0) + 1);
13776
+ }
13777
+ let equal = parentCanonCounts.size === childCanonCounts.size;
13778
+ if (equal) {
13779
+ for (const [k, n] of parentCanonCounts) {
13780
+ if (childCanonCounts.get(k) !== n) {
13781
+ equal = false;
13782
+ break;
13783
+ }
13784
+ }
13785
+ }
13786
+ if (!equal) {
13787
+ unauthorized.push({ resource, action });
13788
+ }
13789
+ void caveatsDeepEqual;
13790
+ }
13791
+ }
13792
+ return unauthorized;
13793
+ }
13794
+
13294
13795
  // src/version.ts
13295
13796
  var ProtocolMismatchError = class extends Error {
13296
13797
  constructor(sdkProtocol, nodeProtocol, nodeVersion, host) {
@@ -13402,17 +13903,9 @@ function equals3(a, b) {
13402
13903
  return true;
13403
13904
  }
13404
13905
 
13405
- // ../../node_modules/uint8arrays/dist/src/alloc.node.js
13406
- import { Buffer as Buffer2 } from "buffer";
13407
-
13408
- // ../../node_modules/uint8arrays/dist/src/util/as-uint8array.node.js
13409
- function asUint8Array(buf) {
13410
- return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
13411
- }
13412
-
13413
- // ../../node_modules/uint8arrays/dist/src/alloc.node.js
13906
+ // ../../node_modules/uint8arrays/dist/src/alloc.js
13414
13907
  function allocUnsafe(size = 0) {
13415
- return asUint8Array(Buffer2.allocUnsafe(size));
13908
+ return new Uint8Array(size);
13416
13909
  }
13417
13910
 
13418
13911
  // ../../node_modules/uint8-varint/dist/src/index.js
@@ -13588,14 +14081,24 @@ function decode6(buf, offset = 0) {
13588
14081
  }
13589
14082
  }
13590
14083
 
13591
- // ../../node_modules/uint8arrays/dist/src/concat.node.js
13592
- import { Buffer as Buffer3 } from "buffer";
13593
- function concat(arrays, length2) {
13594
- return asUint8Array(Buffer3.concat(arrays, length2));
14084
+ // ../../node_modules/uint8arrays/dist/src/util/as-uint8array.js
14085
+ function asUint8Array(buf) {
14086
+ return buf;
13595
14087
  }
13596
14088
 
13597
- // ../../node_modules/uint8arrays/dist/src/from-string.node.js
13598
- import { Buffer as Buffer4 } from "buffer";
14089
+ // ../../node_modules/uint8arrays/dist/src/concat.js
14090
+ function concat(arrays, length2) {
14091
+ if (length2 == null) {
14092
+ length2 = arrays.reduce((acc, curr) => acc + curr.length, 0);
14093
+ }
14094
+ const output = allocUnsafe(length2);
14095
+ let offset = 0;
14096
+ for (const arr of arrays) {
14097
+ output.set(arr, offset);
14098
+ offset += arr.length;
14099
+ }
14100
+ return asUint8Array(output);
14101
+ }
13599
14102
 
13600
14103
  // ../../node_modules/uint8arrays/dist/src/util/bases.js
13601
14104
  function createCodec(name2, prefix, encode5, decode7) {
@@ -13644,28 +14147,21 @@ var BASES = {
13644
14147
  };
13645
14148
  var bases_default = BASES;
13646
14149
 
13647
- // ../../node_modules/uint8arrays/dist/src/from-string.node.js
14150
+ // ../../node_modules/uint8arrays/dist/src/from-string.js
13648
14151
  function fromString2(string2, encoding = "utf8") {
13649
14152
  const base3 = bases_default[encoding];
13650
14153
  if (base3 == null) {
13651
14154
  throw new Error(`Unsupported encoding "${encoding}"`);
13652
14155
  }
13653
- if (encoding === "utf8" || encoding === "utf-8") {
13654
- return asUint8Array(Buffer4.from(string2, "utf-8"));
13655
- }
13656
14156
  return base3.decoder.decode(`${base3.prefix}${string2}`);
13657
14157
  }
13658
14158
 
13659
- // ../../node_modules/uint8arrays/dist/src/to-string.node.js
13660
- import { Buffer as Buffer5 } from "buffer";
14159
+ // ../../node_modules/uint8arrays/dist/src/to-string.js
13661
14160
  function toString2(array, encoding = "utf8") {
13662
14161
  const base3 = bases_default[encoding];
13663
14162
  if (base3 == null) {
13664
14163
  throw new Error(`Unsupported encoding "${encoding}"`);
13665
14164
  }
13666
- if (encoding === "utf8" || encoding === "utf-8") {
13667
- return Buffer5.from(array.buffer, array.byteOffset, array.byteLength).toString("utf8");
13668
- }
13669
14165
  return base3.encoder.encode(array).substring(1);
13670
14166
  }
13671
14167
 
@@ -13699,8 +14195,218 @@ var UnknownProtocolError = class extends Error {
13699
14195
  };
13700
14196
  __publicField(UnknownProtocolError, "name", "UnknownProtocolError");
13701
14197
 
13702
- // ../../node_modules/@chainsafe/is-ip/lib/is-ip.node.js
13703
- import { isIPv4, isIPv6, isIP as ipVersion } from "net";
14198
+ // ../../node_modules/@chainsafe/is-ip/lib/parser.js
14199
+ var Parser = class {
14200
+ constructor() {
14201
+ __publicField(this, "index", 0);
14202
+ __publicField(this, "input", "");
14203
+ }
14204
+ new(input) {
14205
+ this.index = 0;
14206
+ this.input = input;
14207
+ return this;
14208
+ }
14209
+ /** Run a parser, and restore the pre-parse state if it fails. */
14210
+ readAtomically(fn) {
14211
+ const index = this.index;
14212
+ const result = fn();
14213
+ if (result === void 0) {
14214
+ this.index = index;
14215
+ }
14216
+ return result;
14217
+ }
14218
+ /** Run a parser, but fail if the entire input wasn't consumed. Doesn't run atomically. */
14219
+ parseWith(fn) {
14220
+ const result = fn();
14221
+ if (this.index !== this.input.length) {
14222
+ return void 0;
14223
+ }
14224
+ return result;
14225
+ }
14226
+ /** Peek the next character from the input */
14227
+ peekChar() {
14228
+ if (this.index >= this.input.length) {
14229
+ return void 0;
14230
+ }
14231
+ return this.input[this.index];
14232
+ }
14233
+ /** Read the next character from the input */
14234
+ readChar() {
14235
+ if (this.index >= this.input.length) {
14236
+ return void 0;
14237
+ }
14238
+ return this.input[this.index++];
14239
+ }
14240
+ /** Read the next character from the input if it matches the target. */
14241
+ readGivenChar(target) {
14242
+ return this.readAtomically(() => {
14243
+ const char = this.readChar();
14244
+ if (char !== target) {
14245
+ return void 0;
14246
+ }
14247
+ return char;
14248
+ });
14249
+ }
14250
+ /**
14251
+ * Helper for reading separators in an indexed loop. Reads the separator
14252
+ * character iff index > 0, then runs the parser. When used in a loop,
14253
+ * the separator character will only be read on index > 0 (see
14254
+ * readIPv4Addr for an example)
14255
+ */
14256
+ readSeparator(sep, index, inner) {
14257
+ return this.readAtomically(() => {
14258
+ if (index > 0) {
14259
+ if (this.readGivenChar(sep) === void 0) {
14260
+ return void 0;
14261
+ }
14262
+ }
14263
+ return inner();
14264
+ });
14265
+ }
14266
+ /**
14267
+ * Read a number off the front of the input in the given radix, stopping
14268
+ * at the first non-digit character or eof. Fails if the number has more
14269
+ * digits than max_digits or if there is no number.
14270
+ */
14271
+ readNumber(radix, maxDigits, allowZeroPrefix, maxBytes) {
14272
+ return this.readAtomically(() => {
14273
+ let result = 0;
14274
+ let digitCount = 0;
14275
+ const leadingChar = this.peekChar();
14276
+ if (leadingChar === void 0) {
14277
+ return void 0;
14278
+ }
14279
+ const hasLeadingZero = leadingChar === "0";
14280
+ const maxValue2 = 2 ** (8 * maxBytes) - 1;
14281
+ while (true) {
14282
+ const digit = this.readAtomically(() => {
14283
+ const char = this.readChar();
14284
+ if (char === void 0) {
14285
+ return void 0;
14286
+ }
14287
+ const num = Number.parseInt(char, radix);
14288
+ if (Number.isNaN(num)) {
14289
+ return void 0;
14290
+ }
14291
+ return num;
14292
+ });
14293
+ if (digit === void 0) {
14294
+ break;
14295
+ }
14296
+ result *= radix;
14297
+ result += digit;
14298
+ if (result > maxValue2) {
14299
+ return void 0;
14300
+ }
14301
+ digitCount += 1;
14302
+ if (maxDigits !== void 0) {
14303
+ if (digitCount > maxDigits) {
14304
+ return void 0;
14305
+ }
14306
+ }
14307
+ }
14308
+ if (digitCount === 0) {
14309
+ return void 0;
14310
+ } else if (!allowZeroPrefix && hasLeadingZero && digitCount > 1) {
14311
+ return void 0;
14312
+ } else {
14313
+ return result;
14314
+ }
14315
+ });
14316
+ }
14317
+ /** Read an IPv4 address. */
14318
+ readIPv4Addr() {
14319
+ return this.readAtomically(() => {
14320
+ const out = new Uint8Array(4);
14321
+ for (let i = 0; i < out.length; i++) {
14322
+ const ix = this.readSeparator(".", i, () => this.readNumber(10, 3, false, 1));
14323
+ if (ix === void 0) {
14324
+ return void 0;
14325
+ }
14326
+ out[i] = ix;
14327
+ }
14328
+ return out;
14329
+ });
14330
+ }
14331
+ /** Read an IPv6 Address. */
14332
+ readIPv6Addr() {
14333
+ const readGroups = (groups) => {
14334
+ for (let i = 0; i < groups.length / 2; i++) {
14335
+ const ix = i * 2;
14336
+ if (i < groups.length - 3) {
14337
+ const ipv4 = this.readSeparator(":", i, () => this.readIPv4Addr());
14338
+ if (ipv4 !== void 0) {
14339
+ groups[ix] = ipv4[0];
14340
+ groups[ix + 1] = ipv4[1];
14341
+ groups[ix + 2] = ipv4[2];
14342
+ groups[ix + 3] = ipv4[3];
14343
+ return [ix + 4, true];
14344
+ }
14345
+ }
14346
+ const group = this.readSeparator(":", i, () => this.readNumber(16, 4, true, 2));
14347
+ if (group === void 0) {
14348
+ return [ix, false];
14349
+ }
14350
+ groups[ix] = group >> 8;
14351
+ groups[ix + 1] = group & 255;
14352
+ }
14353
+ return [groups.length, false];
14354
+ };
14355
+ return this.readAtomically(() => {
14356
+ const head = new Uint8Array(16);
14357
+ const [headSize, headIp4] = readGroups(head);
14358
+ if (headSize === 16) {
14359
+ return head;
14360
+ }
14361
+ if (headIp4) {
14362
+ return void 0;
14363
+ }
14364
+ if (this.readGivenChar(":") === void 0) {
14365
+ return void 0;
14366
+ }
14367
+ if (this.readGivenChar(":") === void 0) {
14368
+ return void 0;
14369
+ }
14370
+ const tail = new Uint8Array(14);
14371
+ const limit = 16 - (headSize + 2);
14372
+ const [tailSize] = readGroups(tail.subarray(0, limit));
14373
+ head.set(tail.subarray(0, tailSize), 16 - tailSize);
14374
+ return head;
14375
+ });
14376
+ }
14377
+ /** Read an IP Address, either IPv4 or IPv6. */
14378
+ readIPAddr() {
14379
+ return this.readIPv4Addr() ?? this.readIPv6Addr();
14380
+ }
14381
+ };
14382
+
14383
+ // ../../node_modules/@chainsafe/is-ip/lib/parse.js
14384
+ var MAX_IPV6_LENGTH = 45;
14385
+ var MAX_IPV4_LENGTH = 15;
14386
+ var parser = new Parser();
14387
+ function parseIPv4(input) {
14388
+ if (input.length > MAX_IPV4_LENGTH) {
14389
+ return void 0;
14390
+ }
14391
+ return parser.new(input).parseWith(() => parser.readIPv4Addr());
14392
+ }
14393
+ function parseIPv6(input) {
14394
+ if (input.includes("%")) {
14395
+ input = input.split("%")[0];
14396
+ }
14397
+ if (input.length > MAX_IPV6_LENGTH) {
14398
+ return void 0;
14399
+ }
14400
+ return parser.new(input).parseWith(() => parser.readIPv6Addr());
14401
+ }
14402
+
14403
+ // ../../node_modules/@chainsafe/is-ip/lib/is-ip.js
14404
+ function isIPv4(input) {
14405
+ return Boolean(parseIPv4(input));
14406
+ }
14407
+ function isIPv6(input) {
14408
+ return Boolean(parseIPv6(input));
14409
+ }
13704
14410
 
13705
14411
  // ../../node_modules/@multiformats/multiaddr/dist/src/constants.js
13706
14412
  var CODE_IP4 = 4;
@@ -14657,25 +15363,17 @@ ipRegex.v4 = (options2) => options2 && options2.exact ? v4exact : new RegExp(`${
14657
15363
  ipRegex.v6 = (options2) => options2 && options2.exact ? v6exact : new RegExp(`${boundry(options2)}${v6}${boundry(options2)}`, "g");
14658
15364
  var ip_regex_default = ipRegex;
14659
15365
 
14660
- // ../../node_modules/function-timeout/index.js
14661
- import vm from "vm";
14662
- function functionTimeout(function_, { timeout } = {}) {
14663
- const script = new vm.Script("returnValue = function_()");
14664
- const wrappedFunction = (...arguments_) => {
14665
- const context = {
14666
- function_: () => function_(...arguments_)
14667
- };
14668
- script.runInNewContext(context, { timeout });
14669
- return context.returnValue;
14670
- };
15366
+ // ../../node_modules/function-timeout/browser.js
15367
+ function functionTimeout(function_) {
15368
+ const wrappedFunction = (...arguments_) => function_(...arguments_);
14671
15369
  Object.defineProperty(wrappedFunction, "name", {
14672
15370
  value: `functionTimeout(${function_.name || "<anonymous>"})`,
14673
15371
  configurable: true
14674
15372
  });
14675
15373
  return wrappedFunction;
14676
15374
  }
14677
- function isTimeoutError(error) {
14678
- return error?.code === "ERR_SCRIPT_EXECUTION_TIMEOUT";
15375
+ function isTimeoutError() {
15376
+ return false;
14679
15377
  }
14680
15378
 
14681
15379
  // ../../node_modules/clone-regexp/node_modules/is-regexp/index.js
@@ -15077,13 +15775,14 @@ async function discoverLocalTinyCloudNode(options2 = {}) {
15077
15775
  }
15078
15776
  const fetchFn = options2.fetch ?? globalThis.fetch;
15079
15777
  const identityStore = options2.identityStore ?? defaultLocalNodeIdentityStore;
15080
- const staticCandidates = [
15081
- {
15778
+ const staticCandidates = [];
15779
+ if (options2.localNodeUrl) {
15780
+ staticCandidates.push({
15082
15781
  source: "local-loopback",
15083
- url: options2.localNodeUrl ?? DEFAULT_LOCAL_NODE_URL,
15782
+ url: options2.localNodeUrl,
15084
15783
  timeoutMs: LOCAL_LOOPBACK_PROBE_TIMEOUT_MS
15085
- }
15086
- ];
15784
+ });
15785
+ }
15087
15786
  if (options2.localLinkName) {
15088
15787
  if (isValidDnsLabel(options2.localLinkName)) {
15089
15788
  staticCandidates.push({
@@ -15536,7 +16235,7 @@ export {
15536
16235
  EXPIRY,
15537
16236
  EncryptionService,
15538
16237
  EnsDataSchema,
15539
- ErrorCodes3 as ErrorCodes,
16238
+ ErrorCodes4 as ErrorCodes,
15540
16239
  HOLDER_KEY_BINDING_PRESENTATION_SCHEMA,
15541
16240
  HOOKS,
15542
16241
  HooksService2 as HooksService,
@@ -15558,6 +16257,7 @@ export {
15558
16257
  MemoryShareCache,
15559
16258
  NETWORK_NAME_PATTERN,
15560
16259
  NetworkIdError,
16260
+ OPENKEY_ACTION_ID_SEPARATOR,
15561
16261
  POLICY_ENGINE_CHALLENGE_REQUEST_SCHEMA,
15562
16262
  POLICY_ENGINE_CHALLENGE_RESPONSE_SCHEMA,
15563
16263
  POLICY_ENGINE_DENIAL_SCHEMA,
@@ -15684,6 +16384,7 @@ export {
15684
16384
  deriveSignedReceiverKey,
15685
16385
  didCacheKey,
15686
16386
  didEquals,
16387
+ diffImmutableSiweFields,
15687
16388
  disableTinyCloudDebug,
15688
16389
  discoverLocalTinyCloudNode,
15689
16390
  discoverNetwork,
@@ -15704,6 +16405,8 @@ export {
15704
16405
  expandActionShortNames,
15705
16406
  expandPermissionEntries,
15706
16407
  expandPermissionEntry,
16408
+ extractImmutableSiweFields,
16409
+ extractRecapAttenuations,
15707
16410
  fetchLocationRecord,
15708
16411
  fetchPeerId,
15709
16412
  generateRandomReceiverKey,
@@ -15716,6 +16419,7 @@ export {
15716
16419
  isCapabilitySubset,
15717
16420
  isEvmAddress,
15718
16421
  isNetworkId,
16422
+ isPlausibleOpenKeyActionId,
15719
16423
  isV2ShareLink,
15720
16424
  jcsCanonicalize,
15721
16425
  loadManifest,
@@ -15733,6 +16437,7 @@ export {
15733
16437
  ok5 as ok,
15734
16438
  openWrappedKey,
15735
16439
  parseCanonicalNetworkId,
16440
+ parseCanonicalRecapResource,
15736
16441
  parseExpiry,
15737
16442
  parseNetworkId2 as parseNetworkId,
15738
16443
  parseNodeNativePortableDelegation,
@@ -15764,6 +16469,8 @@ export {
15764
16469
  submitHostDelegation,
15765
16470
  tinyCloudDebugLogger,
15766
16471
  toSignedObjectError,
16472
+ unauthorizedRecapCapabilities,
16473
+ validateAuthorizationResultV1,
15767
16474
  validateClientSession,
15768
16475
  validateEnvelope,
15769
16476
  validateLocationRecord,