@tinycloud/share-sdk 0.3.0-beta.2 → 1.0.0-beta.3

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
@@ -5359,7 +5359,7 @@ function validateV3Invariants(value, ctx) {
5359
5359
  if (value.contentSource.encryptionNetwork !== value.encryptionNetwork) {
5360
5360
  ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["encryptionNetwork"], message: "encryption network is not bound to the source" });
5361
5361
  }
5362
- if (value.attestedEnforcerBinding.enforcerDid !== value.target.nodeAudience || value.attestedEnforcerBinding.signature.signerDid !== value.attestedEnforcerBinding.nodeAudience || Date.parse(value.attestedEnforcerBinding.expiresAt) < Date.parse(value.expiry)) {
5362
+ if (value.attestedEnforcerBinding.nodeAudience !== value.target.nodeAudience || value.attestedEnforcerBinding.signature.signerDid !== value.attestedEnforcerBinding.nodeAudience || Date.parse(value.attestedEnforcerBinding.expiresAt) < Date.parse(value.expiry)) {
5363
5363
  ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["attestedEnforcerBinding"], message: "enforcer binding does not cover the target and share lifetime" });
5364
5364
  }
5365
5365
  if (value.policy.contentSource.shareId !== value.contentSource.shareId || value.policy.contentSource.kvResource !== value.contentSource.kvResource || value.policy.contentSource.selector !== value.contentSource.selector || value.policy.contentSource.encryptionNetwork !== value.encryptionNetwork || value.policy.contentSource.encryptedSymmetricKeyDigestHex !== value.contentSource.encryptedSymmetricKeyDigestHex || value.policy.contentSource.keyVersion !== value.contentSource.keyVersion || value.policy.contentSource.mode !== value.contentSource.mode || value.policy.contentSource.initialCiphertextDigestHex !== value.contentSource.initialCiphertextDigestHex) {
@@ -5554,105 +5554,8 @@ function checkBearerDelegation(envelope, options = {}) {
5554
5554
  }
5555
5555
  return { ok: true, delegateeDid: audience };
5556
5556
  }
5557
- function mintBearerDelegation(options) {
5558
- const { issuerPrivateKey, audienceDid, resourceUri, expiresAtSeconds } = options;
5559
- if (!audienceDid.startsWith("did:")) {
5560
- throw new TypeError(`audienceDid must be a DID, got ${audienceDid}`);
5561
- }
5562
- if (resourceUri.length === 0) {
5563
- throw new TypeError("resourceUri must be non-empty");
5564
- }
5565
- if (!Number.isSafeInteger(expiresAtSeconds) || expiresAtSeconds <= 0) {
5566
- throw new TypeError(
5567
- `expiresAtSeconds must be a positive integer (epoch seconds), got ${expiresAtSeconds}`
5568
- );
5569
- }
5570
- const issuerDid = didKeyFromEd25519PublicKey(
5571
- ed25519.getPublicKey(issuerPrivateKey)
5572
- );
5573
- const header = { alg: "EdDSA", typ: "JWT", ucv: "0.9.1" };
5574
- const payload = {
5575
- iss: issuerDid,
5576
- aud: audienceDid,
5577
- att: [{ with: resourceUri, can: BEARER_READ_ABILITY }],
5578
- prf: [],
5579
- exp: expiresAtSeconds
5580
- };
5581
- const signingInput = `${toBase64Url(utf8Bytes(JSON.stringify(header)))}.${toBase64Url(
5582
- utf8Bytes(JSON.stringify(payload))
5583
- )}`;
5584
- const signature = ed25519.sign(utf8Bytes(signingInput), issuerPrivateKey);
5585
- return `${signingInput}.${toBase64Url(signature)}`;
5586
- }
5587
5557
  var ENVELOPE_AAD_LABEL = "tinycloud-share-envelope-v1";
5588
- var SEALED_BLOB_VERSION = 1;
5589
5558
  var AAD = utf8Bytes(ENVELOPE_AAD_LABEL);
5590
- var KEY_LENGTH = 32;
5591
- var NONCE_LENGTH = 12;
5592
- var TAG_LENGTH = 16;
5593
- var HEADER_LENGTH = 1;
5594
- function assertKey(key32) {
5595
- if (key32.length !== KEY_LENGTH) {
5596
- throw new TypeError(`key must be ${KEY_LENGTH} bytes, got ${key32.length}`);
5597
- }
5598
- }
5599
- async function importAesKey(key32, usage) {
5600
- assertKey(key32);
5601
- return globalThis.crypto.subtle.importKey(
5602
- "raw",
5603
- key32,
5604
- "AES-GCM",
5605
- false,
5606
- [usage]
5607
- );
5608
- }
5609
- function generateKey() {
5610
- return globalThis.crypto.getRandomValues(new Uint8Array(KEY_LENGTH));
5611
- }
5612
- async function seal(plaintextBytes, key32) {
5613
- const { nonce, ciphertext } = await encryptEnvelope(plaintextBytes, key32);
5614
- const blob = new Uint8Array(HEADER_LENGTH + nonce.length + ciphertext.length);
5615
- blob[0] = SEALED_BLOB_VERSION;
5616
- blob.set(nonce, HEADER_LENGTH);
5617
- blob.set(ciphertext, HEADER_LENGTH + nonce.length);
5618
- return { blob, cid: await computeCid(blob) };
5619
- }
5620
- async function open(blob, key32) {
5621
- if (blob.length < HEADER_LENGTH + NONCE_LENGTH + TAG_LENGTH) {
5622
- throw new TypeError(`sealed blob too short: ${blob.length} bytes`);
5623
- }
5624
- if (blob[0] !== SEALED_BLOB_VERSION) {
5625
- throw new TypeError(`unknown sealed blob version: ${blob[0]}`);
5626
- }
5627
- const nonce = blob.subarray(HEADER_LENGTH, HEADER_LENGTH + NONCE_LENGTH);
5628
- const ciphertext = blob.subarray(HEADER_LENGTH + NONCE_LENGTH);
5629
- return decryptEnvelope(nonce, ciphertext, key32);
5630
- }
5631
- async function encryptEnvelope(plaintextBytes, key32) {
5632
- const key = await importAesKey(key32, "encrypt");
5633
- const nonce = globalThis.crypto.getRandomValues(new Uint8Array(NONCE_LENGTH));
5634
- const ciphertext = new Uint8Array(
5635
- await globalThis.crypto.subtle.encrypt(
5636
- { name: "AES-GCM", iv: nonce, additionalData: AAD },
5637
- key,
5638
- plaintextBytes
5639
- )
5640
- );
5641
- return { nonce, ciphertext };
5642
- }
5643
- async function decryptEnvelope(nonce, ciphertext, key32) {
5644
- if (nonce.length !== NONCE_LENGTH) {
5645
- throw new TypeError(`nonce must be ${NONCE_LENGTH} bytes, got ${nonce.length}`);
5646
- }
5647
- const key = await importAesKey(key32, "decrypt");
5648
- return new Uint8Array(
5649
- await globalThis.crypto.subtle.decrypt(
5650
- { name: "AES-GCM", iv: nonce, additionalData: AAD },
5651
- key,
5652
- ciphertext
5653
- )
5654
- );
5655
- }
5656
5559
  function isPlainRecord(value) {
5657
5560
  const proto = Object.getPrototypeOf(value);
5658
5561
  return proto === Object.prototype || proto === null;
@@ -5783,8 +5686,6 @@ function equal(left, right) {
5783
5686
  return left.length === right.length && left.every((byte, index) => byte === right[index]);
5784
5687
  }
5785
5688
  var ED25519_VERIFY_OPTS2 = { zip215: false };
5786
- var ENVELOPE_SIGNATURE_DOMAIN = "xyz.tinycloud.share/envelope/v1\0";
5787
- var ENVELOPE_V2_SIGNATURE_DOMAIN = "xyz.tinycloud.share/envelope/v2\0";
5788
5689
  var ENVELOPE_V3_SIGNATURE_DOMAIN = "xyz.tinycloud.share/envelope/v3\0";
5789
5690
  var POLICY_V1_SIGNATURE_DOMAIN = "xyz.tinycloud.policy/policy/v1\0";
5790
5691
  var POLICY_V2_SIGNATURE_DOMAIN = "xyz.tinycloud.policy/policy/v2\0";
@@ -5792,22 +5693,6 @@ var CONTENT_SOURCE_V1_DOMAIN = "xyz.tinycloud.policy/ContentSource/v1\0";
5792
5693
  var POLICY_CAPABILITY_V1_DOMAIN = "xyz.tinycloud.policy/PolicyCapability/v1\0";
5793
5694
  var NATIVE_PROJECTION_V1_DOMAIN = "xyz.tinycloud.policy/NativeProjection/v1\0";
5794
5695
  var ATTESTED_ENFORCER_V2_DOMAIN = "xyz.tinycloud.policy/AttestedEnforcerBinding/v2\0";
5795
- function signingBytes(unsigned) {
5796
- const domain = utf8Bytes(ENVELOPE_SIGNATURE_DOMAIN);
5797
- const body = utf8Bytes(canonicalize(unsigned));
5798
- const bytes3 = new Uint8Array(domain.length + body.length);
5799
- bytes3.set(domain);
5800
- bytes3.set(body, domain.length);
5801
- return bytes3;
5802
- }
5803
- function signingBytesV2(unsigned) {
5804
- const domain = utf8Bytes(ENVELOPE_V2_SIGNATURE_DOMAIN);
5805
- const body = utf8Bytes(canonicalize(unsigned));
5806
- const bytes3 = new Uint8Array(domain.length + body.length);
5807
- bytes3.set(domain);
5808
- bytes3.set(body, domain.length);
5809
- return bytes3;
5810
- }
5811
5696
  function signingBytesV3(unsigned) {
5812
5697
  const domain = utf8Bytes(ENVELOPE_V3_SIGNATURE_DOMAIN);
5813
5698
  const body = utf8Bytes(canonicalize(unsigned));
@@ -5816,53 +5701,6 @@ function signingBytesV3(unsigned) {
5816
5701
  bytes3.set(body, domain.length);
5817
5702
  return bytes3;
5818
5703
  }
5819
- function signEnvelope(envelopeWithoutSig, ed25519PrivKey) {
5820
- const unsigned = unsignedShareEnvelopeSchema.parse(envelopeWithoutSig);
5821
- const publicKey = ed255193.getPublicKey(ed25519PrivKey);
5822
- const signature = ed255193.sign(signingBytes(unsigned), ed25519PrivKey);
5823
- return {
5824
- ...unsigned,
5825
- signature: {
5826
- signerDid: didKeyFromEd25519PublicKey(publicKey),
5827
- algorithm: "Ed25519",
5828
- value: toBase64Url(signature)
5829
- }
5830
- };
5831
- }
5832
- function verifyEnvelopeSignatureOnly(envelope) {
5833
- const parsed = shareEnvelopeSchema.parse(envelope);
5834
- const { signature, ...unsigned } = parsed;
5835
- const publicKey = ed25519PublicKeyFromDidKey(signature.signerDid);
5836
- return ed255193.verify(
5837
- fromBase64Url(signature.value),
5838
- signingBytes(unsigned),
5839
- publicKey,
5840
- ED25519_VERIFY_OPTS2
5841
- );
5842
- }
5843
- async function verifyEnvelope(envelope, options) {
5844
- const parsed = shareEnvelopeSchema.parse(envelope);
5845
- if (parsed.signature.signerDid !== options.expectedSignerDid) return false;
5846
- if (!verifyEnvelopeSignatureOnly(parsed)) return false;
5847
- if (parsed.authorizationTarget.kind === "policy") {
5848
- const policyBytes = fromBase64Url(parsed.authorizationTarget.policyBytes);
5849
- if (await computeCid(policyBytes) !== parsed.authorizationTarget.policyCid) {
5850
- return false;
5851
- }
5852
- }
5853
- return true;
5854
- }
5855
- function verifyEnvelopeV2SignatureOnly(envelope) {
5856
- const parsed = shareEnvelopeV2Schema.parse(envelope);
5857
- const { signature, ...unsigned } = parsed;
5858
- return ed255193.verify(fromBase64Url(signature.value), signingBytesV2(unsigned), ed25519PublicKeyFromDidKey(signature.signerDid), ED25519_VERIFY_OPTS2);
5859
- }
5860
- async function verifyEnvelopeV2(envelope, options) {
5861
- const parsed = shareEnvelopeV2Schema.parse(envelope);
5862
- if (parsed.signature.signerDid !== options.expectedSignerDid || !verifyEnvelopeV2SignatureOnly(parsed)) return false;
5863
- if (parsed.authorizationTarget.kind === "policy") return await computeCid(fromBase64Url(parsed.authorizationTarget.policyBytes)) === parsed.authorizationTarget.policyCid;
5864
- return true;
5865
- }
5866
5704
  function verifyEnvelopeV3SignatureOnly(envelope) {
5867
5705
  const parsed = shareEnvelopeV3Schema.parse(envelope);
5868
5706
  const { signature, ...unsigned } = parsed;
@@ -5908,7 +5746,7 @@ async function verifyEnvelopeV3(envelope, options) {
5908
5746
  const binding = parsed.attestedEnforcerBinding;
5909
5747
  const { signature: bindingSignature, ...unsignedBinding } = binding;
5910
5748
  const expectedBindingDigestHex = hex(sha2562(new TextEncoder().encode(canonicalize({ enforcerDid: binding.enforcerDid, nodeAudience: binding.nodeAudience }))));
5911
- if (binding.enforcerDid !== parsed.target.nodeAudience || binding.attestationBindingDigestHex !== expectedBindingDigestHex || bindingSignature.signerDid !== binding.nodeAudience || bindingSignature.suite !== "Ed25519" || Date.parse(binding.issuedAt) > Date.now() || Date.parse(binding.expiresAt) <= Date.now() || Date.parse(binding.expiresAt) < Date.parse(parsed.expiry)) return false;
5749
+ if (binding.nodeAudience !== parsed.target.nodeAudience || binding.attestationBindingDigestHex !== expectedBindingDigestHex || bindingSignature.signerDid !== binding.nodeAudience || bindingSignature.suite !== "Ed25519" || Date.parse(binding.issuedAt) > Date.now() || Date.parse(binding.expiresAt) <= Date.now() || Date.parse(binding.expiresAt) < Date.parse(parsed.expiry)) return false;
5912
5750
  try {
5913
5751
  const digest3 = sha2562(new TextEncoder().encode(`${ATTESTED_ENFORCER_V2_DOMAIN}${canonicalize(unsignedBinding)}`));
5914
5752
  if (!ed255193.verify(fromBase64Url(bindingSignature.value), digest3, ed25519PublicKeyFromDidKey(binding.nodeAudience), ED25519_VERIFY_OPTS2)) return false;
@@ -5950,7 +5788,8 @@ function base32Lower(bytes3) {
5950
5788
  return output;
5951
5789
  }
5952
5790
  var KEY_LENGTH2 = 32;
5953
- var INLINE_PREFIX = "#v=2&p=";
5791
+ var INLINE_PREFIX = "#tc2=";
5792
+ var PUBLIC_INLINE_PARAMETER = "tc2";
5954
5793
  var MAX_INLINE_BYTES = 256 * 1024;
5955
5794
  function assertCanonicalCid(cidString) {
5956
5795
  const cid2 = CID.parse(cidString);
@@ -5959,77 +5798,30 @@ function assertCanonicalCid(cidString) {
5959
5798
  throw new TypeError(`not a canonical CIDv1 raw sha2-256 base32 CID: ${cidString}`);
5960
5799
  }
5961
5800
  }
5962
- function encodeShareUrl({ origin, ciphertextCid, key32 }) {
5963
- if (key32.length !== KEY_LENGTH2) {
5964
- throw new TypeError(`key must be ${KEY_LENGTH2} bytes, got ${key32.length}`);
5965
- }
5966
- assertCanonicalCid(ciphertextCid);
5967
- if (!isCanonicalHttpsOrigin(origin)) {
5968
- throw new TypeError(`origin must be a canonical https origin, got ${origin}`);
5969
- }
5970
- return `${origin}/s/${ciphertextCid}#k=${toBase64Url(key32)}`;
5971
- }
5972
- function parseShareUrl(url, options = {}) {
5973
- const parsed = new URL(url);
5974
- if (parsed.protocol !== "https:") {
5975
- throw new TypeError(`share URL must be https, got ${parsed.protocol}`);
5976
- }
5977
- if (parsed.username !== "" || parsed.password !== "") {
5978
- throw new TypeError("share URL must not carry userinfo");
5979
- }
5980
- if (parsed.search !== "") {
5981
- throw new TypeError("share URL must not have a query string");
5982
- }
5983
- if (options.expectedOrigin !== void 0) {
5984
- if (!isCanonicalHttpsOrigin(options.expectedOrigin)) {
5985
- throw new TypeError(
5986
- `expectedOrigin must be a canonical https origin, got ${options.expectedOrigin}`
5987
- );
5988
- }
5989
- if (parsed.origin !== options.expectedOrigin) {
5990
- throw new TypeError(
5991
- `share URL origin ${parsed.origin} does not match expected ${options.expectedOrigin}`
5992
- );
5993
- }
5994
- }
5995
- const match = /^\/s\/([a-z2-7]+)$/.exec(parsed.pathname);
5996
- if (!match || match[1] === void 0) {
5997
- throw new TypeError(`not a share URL path: ${parsed.pathname}`);
5998
- }
5999
- const ciphertextCid = match[1];
6000
- assertCanonicalCid(ciphertextCid);
6001
- if (!parsed.hash.startsWith("#k=")) {
6002
- throw new TypeError("share URL is missing the #k= key fragment");
6003
- }
6004
- const key32 = fromBase64Url(parsed.hash.slice("#k=".length));
6005
- if (key32.length !== KEY_LENGTH2) {
6006
- throw new TypeError(`fragment key must be ${KEY_LENGTH2} bytes, got ${key32.length}`);
6007
- }
6008
- return { ciphertextCid, key32 };
6009
- }
6010
- async function encodeInlineShareUrl(parts) {
5801
+ async function encodePublicInlineShareUrl(parts) {
6011
5802
  if (!isCanonicalHttpsOrigin(parts.origin)) throw new TypeError("origin must be a canonical https origin");
6012
- if (parts.ciphertext.byteLength === 0 || parts.ciphertext.byteLength > MAX_INLINE_BYTES) throw new RangeError("inline ciphertext is outside the allowed size");
6013
- if (parts.key32 !== void 0 && parts.key32.length !== KEY_LENGTH2) throw new TypeError("inline key must be 32 bytes");
6014
- const ciphertextCid = await computeCid(parts.ciphertext);
6015
- const payload = canonicalize({
5803
+ if (parts.plaintext.byteLength === 0 || parts.plaintext.byteLength > MAX_INLINE_BYTES) throw new RangeError("public inline envelope is outside the allowed size");
5804
+ const ciphertextCid = await computeCid(parts.plaintext);
5805
+ const payloadBytes = new TextEncoder().encode(canonicalize({
6016
5806
  v: 2,
6017
- c: toBase64Url(parts.ciphertext),
6018
- cid: ciphertextCid,
6019
- ...parts.key32 === void 0 ? {} : { k: toBase64Url(parts.key32) }
6020
- });
6021
- const payloadBytes = new TextEncoder().encode(payload);
5807
+ c: toBase64Url(parts.plaintext),
5808
+ cid: ciphertextCid
5809
+ }));
6022
5810
  if (payloadBytes.byteLength > MAX_INLINE_BYTES * 2) throw new RangeError("inline URL is too large");
6023
- return `${parts.origin}/s/inline${INLINE_PREFIX}${toBase64Url(payloadBytes)}`;
5811
+ return `${parts.origin}/viewer?${PUBLIC_INLINE_PARAMETER}=${toBase64Url(payloadBytes)}`;
6024
5812
  }
6025
5813
  function parseInlineShareUrl(url, options = {}) {
6026
5814
  const parsed = new URL(url);
6027
- if (parsed.protocol !== "https:" || parsed.username !== "" || parsed.password !== "" || parsed.search !== "") throw new TypeError("inline share URL must be canonical HTTPS without query or userinfo");
5815
+ if (parsed.protocol !== "https:" || parsed.username !== "" || parsed.password !== "") throw new TypeError("inline share URL must be canonical HTTPS without userinfo");
6028
5816
  if (!isCanonicalHttpsOrigin(parsed.origin) || options.expectedOrigin !== void 0 && parsed.origin !== options.expectedOrigin) throw new TypeError("inline share URL origin is not trusted");
6029
- if (parsed.pathname !== "/s/inline" || !parsed.hash.startsWith(INLINE_PREFIX)) throw new TypeError("not an inline v2 share URL");
5817
+ if (parsed.pathname !== "/viewer") throw new TypeError("not a TinyCloud policy share URL");
5818
+ const secretPayload = parsed.search === "" && parsed.hash.startsWith(INLINE_PREFIX) ? parsed.hash.slice(INLINE_PREFIX.length) : void 0;
5819
+ const publicPayload = parsed.hash === "" && parsed.searchParams.size === 1 ? parsed.searchParams.get(PUBLIC_INLINE_PARAMETER) ?? void 0 : void 0;
5820
+ if (secretPayload === void 0 && publicPayload === void 0) throw new TypeError("not a canonical TinyCloud policy share URL");
5821
+ if (publicPayload !== void 0 && parsed.search !== `?${PUBLIC_INLINE_PARAMETER}=${publicPayload}`) throw new TypeError("public inline URL is not canonical");
6030
5822
  let payloadBytes;
6031
5823
  try {
6032
- payloadBytes = fromBase64Url(parsed.hash.slice(INLINE_PREFIX.length));
5824
+ payloadBytes = fromBase64Url(secretPayload ?? publicPayload);
6033
5825
  } catch {
6034
5826
  throw new TypeError("inline payload is not canonical base64url");
6035
5827
  }
@@ -6042,37 +5834,32 @@ function parseInlineShareUrl(url, options = {}) {
6042
5834
  }
6043
5835
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new TypeError("inline payload must be an object");
6044
5836
  if (canonicalize(value) !== new TextDecoder("utf-8").decode(payloadBytes)) throw new TypeError("inline payload is not canonical JSON");
6045
- const record2 = value;
6046
- const keys = Object.keys(record2);
6047
- if (!keys.every((key) => key === "v" || key === "c" || key === "cid" || key === "k") || record2.v !== 2 || typeof record2.c !== "string" || typeof record2.cid !== "string") throw new TypeError("inline payload has invalid fields");
5837
+ const record = value;
5838
+ const keys = Object.keys(record);
5839
+ if (!keys.every((key) => key === "v" || key === "c" || key === "cid" || key === "k") || record.v !== 2 || typeof record.c !== "string" || typeof record.cid !== "string" || publicPayload !== void 0 && (keys.length !== 3 || record.k !== void 0)) throw new TypeError("inline payload has invalid fields");
6048
5840
  let ciphertext;
6049
5841
  try {
6050
- ciphertext = fromBase64Url(record2.c);
5842
+ ciphertext = fromBase64Url(record.c);
6051
5843
  } catch {
6052
5844
  throw new TypeError("inline ciphertext is not canonical base64url");
6053
5845
  }
6054
5846
  if (ciphertext.byteLength === 0 || ciphertext.byteLength > MAX_INLINE_BYTES) throw new TypeError("inline ciphertext is outside the allowed size");
6055
- assertCanonicalCid(record2.cid);
6056
- if (record2.k !== void 0 && typeof record2.k !== "string") throw new TypeError("inline key must be base64url");
5847
+ assertCanonicalCid(record.cid);
5848
+ if (record.k !== void 0 && typeof record.k !== "string") throw new TypeError("inline key must be base64url");
6057
5849
  let key32;
6058
5850
  try {
6059
- key32 = record2.k === void 0 ? void 0 : fromBase64Url(record2.k);
5851
+ key32 = record.k === void 0 ? void 0 : fromBase64Url(record.k);
6060
5852
  } catch {
6061
5853
  throw new TypeError("inline key is not canonical base64url");
6062
5854
  }
6063
5855
  if (key32 !== void 0 && key32.length !== KEY_LENGTH2) throw new TypeError("inline key must be 32 bytes");
6064
- return { kind: "inline", ciphertextCid: record2.cid, ciphertext, ...key32 === void 0 ? {} : { key32 } };
6065
- }
6066
- function parseCompactOrInlineShareUrl(url, options = {}) {
6067
- if (new URL(url).pathname === "/s/inline") return parseInlineShareUrl(url, options);
6068
- return { kind: "compact", ...parseShareUrl(url, options) };
5856
+ return { kind: "inline", ciphertextCid: record.cid, ciphertext, ...key32 === void 0 ? {} : { key32 } };
6069
5857
  }
6070
5858
 
6071
5859
  // src/receive.ts
6072
5860
  var SHARE_RESULT_VERSION = 1;
6073
5861
  var DEFAULT_MAX_SEALED_BLOB_BYTES = 100 * 1024 * 1024 + 29;
6074
5862
  var DEFAULT_MAX_CONTENT_BLOB_BYTES = DEFAULT_MAX_SEALED_BLOB_BYTES;
6075
- var CONTENT_SEALED_OVERHEAD = 1 + 12 + 16;
6076
5863
  var ShareReceiveError = class extends Error {
6077
5864
  code;
6078
5865
  details;
@@ -6082,7 +5869,6 @@ var ShareReceiveError = class extends Error {
6082
5869
  this.code = code3;
6083
5870
  this.details = details;
6084
5871
  }
6085
- /** Machine output is deliberately code-only; diagnostics belong on stderr. */
6086
5872
  toJSON() {
6087
5873
  return { protocol: "tinycloud-share", version: SHARE_RESULT_VERSION, error: { code: this.code } };
6088
5874
  }
@@ -6091,338 +5877,79 @@ function toShareErrorInfo(error) {
6091
5877
  if (error instanceof ShareReceiveError) return error.toJSON();
6092
5878
  return { protocol: "tinycloud-share", version: SHARE_RESULT_VERSION, error: { code: "fetch-failed" } };
6093
5879
  }
6094
- function boundedBytes(value, limit, code3) {
6095
- if (!Number.isSafeInteger(limit) || limit <= 0) throw new ShareReceiveError("fetch-failed", "share byte limit is invalid");
6096
- if (!(value instanceof Uint8Array)) throw new ShareReceiveError("fetch-failed", "registry returned invalid bytes");
6097
- if (value.byteLength > limit) throw new ShareReceiveError(code3, "share blob exceeds the configured byte limit");
6098
- return value;
6099
- }
6100
- async function readResponseBytes(response, limit, tooLargeCode) {
6101
- const contentLength = response.headers.get("content-length");
6102
- if (contentLength !== null && Number.isSafeInteger(Number(contentLength)) && Number(contentLength) > limit) {
6103
- throw new ShareReceiveError(tooLargeCode, "share blob exceeds the configured byte limit");
6104
- }
6105
- if (response.body === null) {
6106
- return boundedBytes(new Uint8Array(await response.arrayBuffer()), limit, tooLargeCode);
6107
- }
6108
- const reader = response.body.getReader();
6109
- const chunks = [];
6110
- let total = 0;
6111
- try {
6112
- for (; ; ) {
6113
- const part = await reader.read();
6114
- if (part.done) break;
6115
- total += part.value.byteLength;
6116
- if (total > limit) {
6117
- await reader.cancel();
6118
- throw new ShareReceiveError(tooLargeCode, "share blob exceeds the configured byte limit");
6119
- }
6120
- chunks.push(part.value);
6121
- }
6122
- } finally {
6123
- reader.releaseLock();
6124
- }
6125
- const bytes3 = new Uint8Array(total);
6126
- let offset = 0;
6127
- for (const chunk of chunks) {
6128
- bytes3.set(chunk, offset);
6129
- offset += chunk.byteLength;
6130
- }
6131
- return bytes3;
6132
- }
6133
- function registryFetcher(options, limit, tooLargeCode = "max-bytes-exceeded", stage = "envelope") {
6134
- if (options.fetchBlob !== void 0) return async (input) => {
6135
- try {
6136
- options.signal?.throwIfAborted();
6137
- const bytes3 = await options.fetchBlob({ ...input, ...options.signal === void 0 ? {} : { signal: options.signal } });
6138
- options.signal?.throwIfAborted();
6139
- return boundedBytes(bytes3, limit, tooLargeCode);
6140
- } catch (error) {
6141
- if (options.signal?.aborted) throw options.signal.reason ?? error;
6142
- if (error instanceof ShareReceiveError) throw error;
6143
- throw new ShareReceiveError("fetch-failed", "registry unavailable", { stage });
6144
- }
6145
- };
6146
- if (options.registryBaseUrl === void 0) throw new ShareReceiveError("fetch-failed", "a registry fetch adapter is required");
6147
- const fetchFn = options.fetchFn ?? globalThis.fetch;
6148
- const base3 = options.registryBaseUrl.replace(/\/+$/, "");
6149
- return async ({ cid: cid2 }) => {
6150
- try {
6151
- options.signal?.throwIfAborted();
6152
- const response = await fetchFn(`${base3}/ipfs/${cid2}?format=raw`, {
6153
- headers: { accept: "application/vnd.ipld.raw" },
6154
- redirect: "error",
6155
- ...options.signal === void 0 ? {} : { signal: options.signal }
6156
- });
6157
- options.signal?.throwIfAborted();
6158
- if (!response.ok) throw new ShareReceiveError("fetch-failed", `registry returned ${response.status}`, { stage });
6159
- const bytes3 = await readResponseBytes(response, limit, tooLargeCode);
6160
- options.signal?.throwIfAborted();
6161
- return bytes3;
6162
- } catch (error) {
6163
- if (options.signal?.aborted) throw options.signal.reason ?? error;
6164
- if (error instanceof ShareReceiveError) throw error;
6165
- throw new ShareReceiveError("fetch-failed", "registry unavailable", { stage });
6166
- }
6167
- };
6168
- }
6169
5880
  function metadataFor(envelope, origin) {
6170
- if (envelope.version === 2 || envelope.version === 3) {
6171
- const targetKind3 = envelope.recipientMatcher.kind === "recipientDid" ? "recipientDid" : envelope.recipientMatcher.kind === "exactEmail" ? "email" : envelope.recipientMatcher.kind === "emailDomain" ? "emailDomain" : "bearer";
6172
- return {
6173
- protocol: "tinycloud-share",
6174
- version: 1,
6175
- shareId: envelope.shareId,
6176
- origin,
6177
- target: { ...envelope.target, kind: targetKind3 },
6178
- resource: envelope.resource,
6179
- actions: envelope.actions,
6180
- expiresAt: envelope.expiry,
6181
- display: {
6182
- ...envelope.display.senderName === void 0 ? {} : { senderName: envelope.display.senderName },
6183
- ...envelope.display.filename === void 0 ? {} : { filename: envelope.display.filename },
6184
- ...envelope.display.mode === void 0 ? {} : { mode: envelope.display.mode }
6185
- },
6186
- ...envelope.version === 2 && envelope.content !== void 0 ? { content: { cid: envelope.content.cid } } : {}
6187
- };
6188
- }
5881
+ const kind = envelope.recipientMatcher.kind === "recipientDid" ? "recipientDid" : envelope.recipientMatcher.kind === "exactEmail" ? "email" : "emailDomain";
6189
5882
  return {
6190
5883
  protocol: "tinycloud-share",
6191
5884
  version: 1,
6192
5885
  shareId: envelope.shareId,
6193
5886
  origin,
6194
- target: { ...envelope.target, kind: "bearer" },
6195
- resource: envelope.target.resource,
6196
- actions: ["read"],
5887
+ target: { ...envelope.target, kind },
5888
+ resource: { ...envelope.resource },
5889
+ actions: [...envelope.actions],
6197
5890
  expiresAt: envelope.expiry,
6198
5891
  display: {
6199
5892
  ...envelope.display.senderName === void 0 ? {} : { senderName: envelope.display.senderName },
6200
5893
  ...envelope.display.filename === void 0 ? {} : { filename: envelope.display.filename },
6201
5894
  ...envelope.display.mode === void 0 ? {} : { mode: envelope.display.mode }
6202
- },
6203
- ...envelope.content === void 0 ? {} : { content: { cid: envelope.content.cid } }
5895
+ }
6204
5896
  };
6205
5897
  }
6206
- function parseEnvelope(bytes3) {
6207
- try {
6208
- const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes3);
6209
- const value = JSON.parse(text);
6210
- if (canonicalize(value) !== text) throw new Error("share envelope is not canonical JSON");
6211
- if (typeof value === "object" && value !== null && !Array.isArray(value)) {
6212
- const version = value.version;
6213
- if (version === 3) return shareEnvelopeV3Schema.parse(value);
6214
- if (version === 2) return shareEnvelopeV2Schema.parse(value);
6215
- }
6216
- return shareEnvelopeSchema.parse(value);
6217
- } catch {
6218
- throw new ShareReceiveError("envelope-invalid", "share envelope is invalid");
6219
- }
6220
- }
6221
- async function resolveShareEnvelope(link, options = {}) {
5898
+ async function resolvePolicyShare(link, options) {
5899
+ options.signal?.throwIfAborted();
5900
+ let url;
6222
5901
  let parsed;
6223
5902
  try {
6224
- parsed = parseCompactOrInlineShareUrl(link, { ...options.expectedOrigin === void 0 ? {} : { expectedOrigin: options.expectedOrigin } });
5903
+ url = new URL(link);
5904
+ if (url.pathname !== "/viewer" || url.hash !== "" || url.searchParams.size !== 1 || !url.searchParams.has("tc2")) throw new Error("not public Policy/v3");
5905
+ parsed = parseInlineShareUrl(link, { ...options.expectedOrigin === void 0 ? {} : { expectedOrigin: options.expectedOrigin } });
5906
+ if (parsed.key32 !== void 0) throw new Error("secret inline transport is retired");
6225
5907
  } catch {
6226
5908
  throw new ShareReceiveError("invalid-link", "share link format is invalid");
6227
5909
  }
5910
+ const limit = options.maxSealedBlobBytes ?? DEFAULT_MAX_SEALED_BLOB_BYTES;
5911
+ if (!Number.isSafeInteger(limit) || limit <= 0 || parsed.ciphertext.byteLength > limit) throw new ShareReceiveError("max-bytes-exceeded", "public policy envelope exceeds the configured byte limit");
5912
+ if (await computeCid(parsed.ciphertext) !== parsed.ciphertextCid) throw new ShareReceiveError("cid-mismatch", "public envelope bytes do not match the link CID");
5913
+ options.signal?.throwIfAborted();
5914
+ let envelope;
6228
5915
  try {
6229
- if (parsed.key32 !== void 0) options.onKeyParsed?.(parsed.key32);
6230
- const url = new URL(link);
6231
- const sealed = parsed.kind === "inline" ? boundedBytes(parsed.ciphertext, options.maxSealedBlobBytes ?? DEFAULT_MAX_SEALED_BLOB_BYTES, "max-bytes-exceeded") : await registryFetcher(options, options.maxSealedBlobBytes ?? DEFAULT_MAX_SEALED_BLOB_BYTES)({ origin: url.origin, cid: parsed.ciphertextCid });
6232
- if (await computeCid(sealed) !== parsed.ciphertextCid) throw new ShareReceiveError("cid-mismatch", "registry bytes do not match the link CID");
6233
- let plaintext;
6234
- try {
6235
- plaintext = parsed.key32 === void 0 ? sealed : await open(sealed, parsed.key32);
6236
- } catch {
6237
- throw new ShareReceiveError("decrypt-failed", "share envelope could not be opened");
6238
- }
6239
- return { envelope: parseEnvelope(plaintext), origin: url.origin, cid: parsed.ciphertextCid, kind: parsed.kind };
6240
- } finally {
6241
- parsed.key32?.fill(0);
6242
- }
6243
- }
6244
- function actionName(value) {
6245
- return value === "read" ? "tinycloud.kv/get" : value === "list" ? "tinycloud.kv/list" : value === "edit" ? "tinycloud.kv/put" : value;
6246
- }
6247
- function resourceContains(outer, inner) {
6248
- if (outer.kind === "exact") return inner.kind === "exact" && outer.path === inner.path;
6249
- const prefix = outer.path.endsWith("/") ? outer.path : `${outer.path}/`;
6250
- return inner.path === outer.path || inner.path.startsWith(prefix);
6251
- }
6252
- function policyEvidenceMatches(envelope, evidence) {
6253
- const registeredActions = new Set(evidence.actions.map(actionName));
6254
- return evidence.policyCid === (envelope.authorizationTarget.kind === "policy" ? envelope.authorizationTarget.policyCid : "") && evidence.signerDid === envelope.signature.signerDid && evidence.registrationCid.length > 0 && evidence.shareId === envelope.shareId && canonicalize(evidence.recipientMatcher) === canonicalize(envelope.recipientMatcher) && canonicalize(evidence.target) === canonicalize(envelope.target) && resourceContains(evidence.resource, envelope.resource) && envelope.actions.every((action) => registeredActions.has(actionName(action))) && canonicalize(evidence.contentSource) === canonicalize(envelope.contentSource) && evidence.contentSourceDigest === envelope.contentSourceDigest && evidence.delegationCid === envelope.delegationCid && evidence.authorityMaterialHandle === envelope.authorityMaterialHandle && evidence.authorityMaterialDigest === envelope.authorityMaterialDigest && evidence.expiresAt === envelope.expiry && Number.isFinite(Date.parse(evidence.expiresAt)) && Date.parse(evidence.expiresAt) === Date.parse(envelope.expiry);
6255
- }
6256
- async function verifyV2Envelope(envelope, linkOrigin, options) {
6257
- if (options.expectedOrigin !== void 0 && linkOrigin !== options.expectedOrigin) {
6258
- throw new ShareReceiveError("origin-mismatch", "share link origin does not match the trusted origin");
6259
- }
6260
- try {
6261
- if (!await verifyEnvelopeV2(envelope, { expectedSignerDid: envelope.signature.signerDid })) throw new Error("signature");
5916
+ const encoded = new TextDecoder("utf-8", { fatal: true }).decode(parsed.ciphertext);
5917
+ const value = JSON.parse(encoded);
5918
+ if (canonicalize(value) !== encoded) throw new Error("non-canonical envelope");
5919
+ envelope = shareEnvelopeV3Schema.parse(value);
6262
5920
  } catch {
6263
- throw new ShareReceiveError("signature-invalid", "share signature is invalid");
6264
- }
6265
- const expiry = Date.parse(envelope.expiry);
6266
- if (!Number.isFinite(expiry)) throw new ShareReceiveError("envelope-invalid", "share expiry is invalid");
6267
- if (expiry <= (options.now?.() ?? Date.now())) throw new ShareReceiveError("expired", "share has expired", { expiresAt: envelope.expiry });
6268
- if (envelope.authorizationTarget.kind === "policy") {
6269
- if (options.trustedPolicyAuthority === void 0) throw new ShareReceiveError("envelope-invalid", "an external policy authority is required");
6270
- let evidence;
6271
- try {
6272
- evidence = await options.trustedPolicyAuthority.resolve({ policyCid: envelope.authorizationTarget.policyCid, envelope });
6273
- } catch {
6274
- evidence = void 0;
6275
- }
6276
- if (evidence === void 0 || !policyEvidenceMatches(envelope, evidence)) throw new ShareReceiveError("capability-invalid", "share policy authority evidence is not bound to the envelope");
6277
- if (options.trustedSignerDid !== void 0 && (evidence.signerDid !== options.trustedSignerDid || envelope.signature.signerDid !== options.trustedSignerDid)) {
6278
- throw new ShareReceiveError("signature-invalid", "share signer is not trusted");
6279
- }
6280
- } else if (envelope.authorizationTarget.kind === "recipientDid" && envelope.recipientMatcher.kind === "recipientDid" && envelope.authorizationTarget.did !== envelope.recipientMatcher.value) {
6281
- throw new ShareReceiveError("capability-invalid", "recipient authorization does not match the signed recipient");
6282
- } else {
6283
- if (options.trustedSignerDid === void 0 || envelope.signature.signerDid !== options.trustedSignerDid) throw new ShareReceiveError("signature-invalid", "an addressed signer trust root is required");
6284
- }
6285
- }
6286
- async function verifyV3Envelope(envelope, linkOrigin, options) {
6287
- if (options.expectedOrigin !== void 0 && linkOrigin !== options.expectedOrigin) {
6288
- throw new ShareReceiveError("origin-mismatch", "share link origin does not match the trusted origin");
5921
+ throw new ShareReceiveError("envelope-invalid", "share envelope is invalid");
6289
5922
  }
6290
5923
  try {
6291
5924
  if (!await verifyEnvelopeV3(envelope, { expectedSignerDid: envelope.policy.ownerDid })) throw new Error("signature");
6292
5925
  } catch {
6293
5926
  throw new ShareReceiveError("signature-invalid", "share signature is invalid");
6294
5927
  }
5928
+ options.signal?.throwIfAborted();
6295
5929
  const expiry = Date.parse(envelope.expiry);
6296
5930
  if (!Number.isFinite(expiry)) throw new ShareReceiveError("envelope-invalid", "share expiry is invalid");
6297
5931
  if (expiry <= (options.now?.() ?? Date.now())) throw new ShareReceiveError("expired", "share has expired", { expiresAt: envelope.expiry });
6298
- }
6299
- async function verifyV1PolicyEnvelope(envelope, linkOrigin, options) {
6300
- if (envelope.authorizationTarget.kind !== "policy") throw new ShareReceiveError("unsupported-target", "share target is not an addressed policy", { reason: "policy-target" });
6301
- if (envelope.target.origin !== linkOrigin || options.expectedOrigin !== void 0 && (linkOrigin !== options.expectedOrigin || envelope.target.origin !== options.expectedOrigin)) {
6302
- throw new ShareReceiveError("origin-mismatch", "share origin does not match the trusted origin");
6303
- }
6304
- throw new ShareReceiveError("unsupported-target", "share policy target is not supported", { reason: "policy-target" });
6305
- }
6306
- async function verifyBearerEnvelope(envelope, linkOrigin, options = {}) {
6307
- if (envelope.target.origin !== linkOrigin || options.expectedOrigin !== void 0 && (linkOrigin !== options.expectedOrigin || envelope.target.origin !== options.expectedOrigin)) {
6308
- throw new ShareReceiveError("origin-mismatch", "share origin does not match the trusted origin");
6309
- }
6310
- if (envelope.authorizationTarget.kind !== "bearerKey") throw new ShareReceiveError("unsupported-target", "this receive path only handles bearer shares", { reason: envelope.authorizationTarget.kind === "policy" ? "policy-target" : "recipient-did-target" });
6311
- if (envelope.target.resource.kind !== "exact") throw new ShareReceiveError("unsupported-target", "this receive path only handles exact resources", { reason: "prefix-resource" });
6312
- const expiry = Date.parse(envelope.expiry);
6313
- if (!Number.isFinite(expiry)) throw new ShareReceiveError("envelope-invalid", "share expiry is invalid");
6314
- try {
6315
- if (!await verifyEnvelope(envelope, { expectedSignerDid: envelope.signature.signerDid })) throw new Error("signature");
6316
- } catch {
6317
- throw new ShareReceiveError("signature-invalid", "share signature is invalid");
6318
- }
6319
- if (expiry <= (options.now?.() ?? Date.now())) throw new ShareReceiveError("expired", "share has expired", { expiresAt: envelope.expiry });
6320
- const capability = checkBearerDelegation(envelope, options.now === void 0 ? {} : { now: options.now });
6321
- if (!capability.ok) throw new ShareReceiveError("capability-invalid", "share delegation does not authorize the target");
6322
- }
6323
- async function verifyResolved(envelope, linkOrigin, options) {
6324
- await verifyBearerEnvelope(envelope, linkOrigin, options);
5932
+ options.onResolvedAddressedEnvelope?.(envelope, parsed.ciphertextCid);
5933
+ return { envelope, origin: url.origin, cid: parsed.ciphertextCid };
6325
5934
  }
6326
5935
  async function inspectShare(link, options = {}) {
6327
- const resolved = await resolveShareEnvelope(link, options);
6328
- if (resolved.envelope.version === 1 && resolved.envelope.authorizationTarget.kind === "policy") await verifyV1PolicyEnvelope(resolved.envelope, resolved.origin, options);
6329
- else if (resolved.envelope.version === 1) await verifyResolved(resolved.envelope, resolved.origin, options);
6330
- else if (resolved.envelope.version === 2) await verifyV2Envelope(resolved.envelope, resolved.origin, options);
6331
- else await verifyV3Envelope(resolved.envelope, resolved.origin, options);
6332
- if (resolved.envelope.version === 2 || resolved.envelope.version === 3 || resolved.envelope.authorizationTarget.kind === "policy") options.onResolvedAddressedEnvelope?.(resolved.envelope, resolved.cid);
6333
- return { metadata: metadataFor(resolved.envelope, resolved.origin), link: { origin: resolved.origin, cid: resolved.cid, kind: resolved.kind } };
5936
+ const resolved = await resolvePolicyShare(link, options);
5937
+ return { metadata: metadataFor(resolved.envelope, resolved.origin), link: { origin: resolved.origin, cid: resolved.cid, kind: "policy" } };
6334
5938
  }
6335
5939
  async function receiveShare(link, options = {}) {
6336
- const resolved = await resolveShareEnvelope(link, options);
6337
- if (resolved.envelope.version === 3) {
6338
- await verifyV3Envelope(resolved.envelope, resolved.origin, options);
6339
- options.onResolvedAddressedEnvelope?.(resolved.envelope, resolved.cid);
6340
- return { state: "authorization-required", method: resolved.envelope.recipientMatcher.kind === "recipientDid" ? "openkey-device" : "email-claim" };
6341
- }
6342
- if (resolved.envelope.version === 2) {
6343
- await verifyV2Envelope(resolved.envelope, resolved.origin, options);
6344
- options.onResolvedAddressedEnvelope?.(resolved.envelope, resolved.cid);
6345
- const method = resolved.envelope.recipientMatcher.kind === "recipientDid" ? "openkey-device" : "email-claim";
6346
- if (options.authorization === void 0) return { state: "authorization-required", method };
6347
- const result = options.authorizationResumeToken === void 0 ? await options.authorization.begin({ envelope: resolved.envelope, method }) : await options.authorization.resume({ envelope: resolved.envelope, method, resumeToken: options.authorizationResumeToken, ...options.authorizationProof === void 0 ? {} : { proof: options.authorizationProof } });
6348
- if (result.state === "authorization-required") return result;
6349
- if (result.state === "denied") throw new ShareReceiveError("authorization-denied", "share authorization was denied");
6350
- if (result.value === null || typeof result.value !== "object" || !(result.value.bytes instanceof Uint8Array) || result.value.proof === void 0 || options.authorization.verifyResult === void 0) {
6351
- throw new ShareReceiveError("content-integrity-failed", "share authorization returned an unsigned content result");
6352
- }
6353
- const authorized = result.value;
6354
- try {
6355
- if (!await options.authorization.verifyResult({ envelope: resolved.envelope, value: authorized, proof: authorized.proof })) throw new Error("node proof");
6356
- } catch {
6357
- throw new ShareReceiveError("content-integrity-failed", "node authorization proof is invalid");
6358
- }
6359
- const bytes4 = authorized.bytes.slice();
6360
- const maxBytes = options.maxContentBlobBytes ?? DEFAULT_MAX_CONTENT_BLOB_BYTES;
6361
- if (bytes4.byteLength > maxBytes) throw new ShareReceiveError("max-bytes-exceeded", "shared content exceeds the configured byte limit");
6362
- const digestBytes3 = new Uint8Array(await crypto.subtle.digest("SHA-256", bytes4));
6363
- const bodyDigest = toBase64Url(digestBytes3);
6364
- if (authorized.bodyDigest !== bodyDigest) throw new ShareReceiveError("content-integrity-failed", "authorized content digest does not match");
6365
- if (authorized.contentSourceDigest !== resolved.envelope.contentSourceDigest) throw new ShareReceiveError("content-integrity-failed", "authorized content source does not match");
6366
- const binding = authorized.binding;
6367
- if (binding.shareId !== resolved.envelope.shareId || binding.delegationCid !== resolved.envelope.delegationCid || binding.authorityMaterialHandle !== resolved.envelope.authorityMaterialHandle || binding.authorityMaterialDigest !== resolved.envelope.authorityMaterialDigest || binding.resource.kind !== resolved.envelope.resource.kind || binding.resource.path !== resolved.envelope.resource.path || binding.action !== void 0 && !resolved.envelope.actions.some((action) => action === binding.action || `tinycloud.kv/${action}` === binding.action)) throw new ShareReceiveError("content-integrity-failed", "authorized content binding does not match");
6368
- if (resolved.envelope.metadata.byteLength !== void 0 && resolved.envelope.metadata.byteLength !== bytes4.byteLength) {
6369
- throw new ShareReceiveError("content-integrity-failed", "authorized content length does not match");
6370
- }
6371
- let text2;
6372
- try {
6373
- text2 = new TextDecoder("utf-8", { fatal: true }).decode(bytes4);
6374
- } catch {
6375
- }
6376
- return { metadata: metadataFor(resolved.envelope, resolved.origin), link: { origin: resolved.origin, cid: resolved.cid, kind: resolved.kind }, bytes: bytes4, ...text2 === void 0 ? {} : { text: text2 } };
6377
- }
6378
- if (resolved.envelope.authorizationTarget.kind === "policy") {
6379
- await verifyV1PolicyEnvelope(resolved.envelope, resolved.origin, options);
6380
- options.onResolvedAddressedEnvelope?.(resolved.envelope, resolved.cid);
6381
- return { state: "authorization-required", method: "email-claim" };
6382
- }
6383
- await verifyResolved(resolved.envelope, resolved.origin, options);
6384
- let bytes3 = new Uint8Array();
6385
- if (resolved.envelope.content !== void 0) {
6386
- bytes3 = await registryFetcher(
6387
- options,
6388
- (options.maxContentBlobBytes ?? DEFAULT_MAX_CONTENT_BLOB_BYTES) + CONTENT_SEALED_OVERHEAD,
6389
- "max-bytes-exceeded",
6390
- "content"
6391
- )({ origin: resolved.origin, cid: resolved.envelope.content.cid });
6392
- if (await computeCid(bytes3) !== resolved.envelope.content.cid) throw new ShareReceiveError("content-integrity-failed", "shared content CID does not match");
6393
- const key = fromBase64Url(resolved.envelope.content.key);
6394
- try {
6395
- bytes3 = await open(bytes3, key);
6396
- } catch {
6397
- throw new ShareReceiveError("content-integrity-failed", "shared content could not be opened");
6398
- } finally {
6399
- key.fill(0);
6400
- }
6401
- if (bytes3.byteLength > (options.maxContentBlobBytes ?? DEFAULT_MAX_CONTENT_BLOB_BYTES)) {
6402
- throw new ShareReceiveError("max-bytes-exceeded", "shared content exceeds the configured byte limit");
6403
- }
6404
- }
6405
- let text;
6406
- if (bytes3.byteLength !== 0) {
6407
- try {
6408
- text = new TextDecoder("utf-8", { fatal: true }).decode(bytes3);
6409
- } catch {
6410
- }
6411
- }
6412
- return { metadata: metadataFor(resolved.envelope, resolved.origin), link: { origin: resolved.origin, cid: resolved.cid, kind: resolved.kind }, bytes: bytes3, ...text === void 0 ? {} : { text } };
5940
+ const resolved = await resolvePolicyShare(link, options);
5941
+ return { state: "authorization-required", method: resolved.envelope.recipientMatcher.kind === "recipientDid" ? "openkey-device" : "email-claim" };
6413
5942
  }
6414
5943
 
6415
5944
  // src/publish.ts
6416
- import { ed25519 as ed255194 } from "@noble/curves/ed25519";
6417
5945
  var SHARE_CONTENT_LIMIT = 100 * 1024 * 1024;
6418
- var SHARE_SEALED_OVERHEAD = 1 + 12 + 16;
6419
5946
  var SHARE_PUBLISH_RESULT_VERSION = 1;
6420
5947
  var DEFAULT_SHARE_LIFETIME_MS = 7 * 24 * 60 * 60 * 1e3;
6421
5948
  function redactPublishedShare(result) {
6422
5949
  return {
6423
5950
  protocol: "tinycloud-share",
6424
5951
  version: SHARE_PUBLISH_RESULT_VERSION,
6425
- link: { kind: result.link.kind, cid: result.link.cid },
5952
+ link: { ...result.link },
6426
5953
  metadata: {
6427
5954
  protocol: "tinycloud-share",
6428
5955
  version: 1,
@@ -6432,10 +5959,8 @@ function redactPublishedShare(result) {
6432
5959
  resource: { ...result.metadata.resource },
6433
5960
  actions: [...result.metadata.actions],
6434
5961
  expiresAt: result.metadata.expiresAt,
6435
- display: { ...result.metadata.display },
6436
- ...result.metadata.content === void 0 ? {} : { content: { cid: result.metadata.content.cid } }
6437
- },
6438
- registryDeleteAfter: result.registryDeleteAfter
5962
+ display: { ...result.metadata.display }
5963
+ }
6439
5964
  };
6440
5965
  }
6441
5966
  var SharePublishError = class extends Error {
@@ -6446,237 +5971,6 @@ var SharePublishError = class extends Error {
6446
5971
  this.code = code3;
6447
5972
  }
6448
5973
  };
6449
- function assertSafeFilename(filename) {
6450
- if (filename.length === 0 || filename === "." || filename === ".." || /[/\\\u0000-\u001f\u007f]/.test(filename)) {
6451
- throw new SharePublishError("invalid-argument", "filename must be one safe path segment");
6452
- }
6453
- }
6454
- function assertOrigin(origin, label) {
6455
- if (!isCanonicalHttpsOrigin(origin)) {
6456
- throw new SharePublishError("invalid-argument", `${label} must be a canonical HTTPS origin`);
6457
- }
6458
- }
6459
- async function snapshotSource(source, limit) {
6460
- if (!Number.isSafeInteger(limit) || limit <= 0) {
6461
- throw new SharePublishError("invalid-argument", "maxBytes must be a positive safe integer");
6462
- }
6463
- if (source instanceof Uint8Array) {
6464
- if (source.byteLength > limit) throw new SharePublishError("max-bytes-exceeded", "share content exceeds the configured byte limit");
6465
- return source.slice();
6466
- }
6467
- const chunks = [];
6468
- let total = 0;
6469
- try {
6470
- for await (const chunk of source) {
6471
- if (!(chunk instanceof Uint8Array)) throw new SharePublishError("invalid-argument", "share source yielded invalid bytes");
6472
- total += chunk.byteLength;
6473
- if (total > limit) throw new SharePublishError("max-bytes-exceeded", "share content exceeds the configured byte limit");
6474
- chunks.push(chunk);
6475
- }
6476
- } catch (error) {
6477
- if (error instanceof SharePublishError) throw error;
6478
- throw new SharePublishError("upload-failed", "share source could not be read");
6479
- }
6480
- const bytes3 = new Uint8Array(total);
6481
- let offset = 0;
6482
- for (const chunk of chunks) {
6483
- bytes3.set(chunk, offset);
6484
- offset += chunk.byteLength;
6485
- }
6486
- return bytes3;
6487
- }
6488
- function assertTarget(target) {
6489
- if (target.kind !== "bearer") {
6490
- throw new SharePublishError("unsupported-target", "this release publishes bearer shares only");
6491
- }
6492
- }
6493
- function assertRegistryUrl(value, allowInsecure) {
6494
- let url;
6495
- try {
6496
- url = new URL(value);
6497
- } catch {
6498
- throw new SharePublishError("invalid-argument", "registry URL is invalid");
6499
- }
6500
- if (url.username || url.password || url.search || url.hash) throw new SharePublishError("invalid-argument", "registry URL must not contain credentials, query, or fragment");
6501
- if (!allowInsecure && url.protocol !== "https:") throw new SharePublishError("invalid-argument", "production registry uploads require HTTPS");
6502
- if (url.protocol !== "https:" && url.hostname !== "127.0.0.1" && url.hostname !== "localhost") throw new SharePublishError("invalid-argument", "insecure registry uploads are limited to localhost");
6503
- return url;
6504
- }
6505
- async function defaultUpload(options, input) {
6506
- if (options.registryBaseUrl === void 0) throw new SharePublishError("upload-auth-required", "an authenticated share registry is required");
6507
- const base3 = assertRegistryUrl(options.registryBaseUrl, options.allowInsecureRegistry === true).toString().replace(/\/$/, "");
6508
- const localInsecure = options.allowInsecureRegistry === true && (new URL(base3).hostname === "127.0.0.1" || new URL(base3).hostname === "localhost");
6509
- if (options.authorizeUpload === void 0 && !localInsecure) {
6510
- throw new SharePublishError("upload-auth-required", "share upload authorization is required");
6511
- }
6512
- let authorization;
6513
- if (options.authorizeUpload !== void 0) {
6514
- try {
6515
- authorization = await options.authorizeUpload(input);
6516
- } catch {
6517
- throw new SharePublishError("upload-auth-required", "share upload authorization was rejected");
6518
- }
6519
- }
6520
- if (authorization !== void 0 && options.authorizationOrigin !== void 0) {
6521
- let authorizationUrl;
6522
- try {
6523
- authorizationUrl = new URL(options.authorizationOrigin);
6524
- } catch {
6525
- throw new SharePublishError("invalid-argument", "upload authorization origin is invalid");
6526
- }
6527
- if (authorizationUrl.origin !== new URL(base3).origin) throw new SharePublishError("upload-auth-required", "upload authorization is scoped to another origin");
6528
- }
6529
- const headers = new Headers({
6530
- "content-type": "application/vnd.ipld.raw",
6531
- "if-none-match": "*",
6532
- "x-delete-after": input.deleteAfter
6533
- });
6534
- if (typeof authorization === "string") {
6535
- headers.set("x-tinycloud-upload-attestation", authorization);
6536
- try {
6537
- const attestation = JSON.parse(authorization);
6538
- if (attestation.retention === void 0) throw new Error("retention");
6539
- headers.set("x-tinycloud-retention", canonicalize(attestation.retention));
6540
- } catch {
6541
- throw new SharePublishError("upload-auth-required", "share upload authorization was rejected");
6542
- }
6543
- } else if (authorization !== void 0) {
6544
- new Headers(authorization).forEach((value, key) => headers.set(key, value));
6545
- }
6546
- let response;
6547
- try {
6548
- response = await (options.fetchFn ?? globalThis.fetch)(`${base3}/blobs`, {
6549
- method: "POST",
6550
- headers,
6551
- body: input.blob,
6552
- ...options.credentials === void 0 ? {} : { credentials: options.credentials },
6553
- redirect: "error",
6554
- referrerPolicy: "no-referrer"
6555
- });
6556
- } catch {
6557
- throw new SharePublishError("upload-failed", "share registry is unavailable");
6558
- }
6559
- if (response.status === 401 || response.status === 403) throw new SharePublishError("upload-auth-required", "share upload authorization was rejected");
6560
- if (!response.ok) throw new SharePublishError("upload-failed", "share registry rejected the encrypted blob");
6561
- let body;
6562
- try {
6563
- body = await response.json();
6564
- } catch {
6565
- throw new SharePublishError("upload-failed", "share registry returned an invalid response");
6566
- }
6567
- const record2 = typeof body === "object" && body !== null ? body : {};
6568
- if (record2.cid !== input.cid || typeof record2.deleteAfter !== "string") throw new SharePublishError("upload-failed", "share registry returned an unexpected CID or retention value");
6569
- return { cid: input.cid, deleteAfter: record2.deleteAfter };
6570
- }
6571
- async function uploadShareBlob(options, input) {
6572
- const result = await (options.uploadBlob ?? ((value) => defaultUpload(options, value)))(input);
6573
- if (result.cid !== input.cid || typeof result.deleteAfter !== "string") throw new SharePublishError("upload-failed", "share uploader returned an invalid result");
6574
- return result;
6575
- }
6576
- async function publishShare(options) {
6577
- const target = options.target ?? { kind: "bearer" };
6578
- assertTarget(target);
6579
- assertSafeFilename(options.filename);
6580
- assertOrigin(options.origin, "share origin");
6581
- if (options.mediaType !== void 0 && options.mediaType.length === 0) throw new SharePublishError("invalid-argument", "mediaType must not be empty");
6582
- const maxBytes = options.maxBytes ?? SHARE_CONTENT_LIMIT;
6583
- if (maxBytes > SHARE_CONTENT_LIMIT) throw new SharePublishError("max-bytes-exceeded", "share content exceeds the maximum 100 MiB limit");
6584
- const content = await snapshotSource(options.source, maxBytes);
6585
- if (content.byteLength === 0) throw new SharePublishError("invalid-argument", "share content is empty");
6586
- if (options.allowBinary !== true) {
6587
- try {
6588
- new TextDecoder("utf-8", { fatal: true }).decode(content);
6589
- } catch {
6590
- throw new SharePublishError("invalid-argument", "Markdown input must be valid UTF-8");
6591
- }
6592
- }
6593
- const nowMs = options.now?.() ?? Date.now();
6594
- const expiresAt = options.expiresAt ?? new Date(nowMs + DEFAULT_SHARE_LIFETIME_MS);
6595
- if (!Number.isFinite(expiresAt.getTime()) || expiresAt.getTime() <= nowMs) throw new SharePublishError("invalid-argument", "expiresAt must be a valid future time");
6596
- const expiry = expiresAt.toISOString();
6597
- const nodeAudience = options.nodeAudience ?? "did:web:node.tinycloud.xyz";
6598
- const spaceId = options.spaceId ?? "bearer";
6599
- const shareId = toBase64Url(globalThis.crypto.getRandomValues(new Uint8Array(16)));
6600
- const path = `shares/${shareId}/${options.filename}`;
6601
- const sessionPrivateKey = ed255194.utils.randomPrivateKey();
6602
- const senderPrivateKey = ed255194.utils.randomPrivateKey();
6603
- let contentKey;
6604
- let envelopeKey;
6605
- try {
6606
- const sessionPublicKey = ed255194.getPublicKey(sessionPrivateKey);
6607
- const sessionDid = didKeyFromEd25519PublicKey(sessionPublicKey);
6608
- const delegation = mintBearerDelegation({
6609
- issuerPrivateKey: senderPrivateKey,
6610
- audienceDid: sessionDid,
6611
- resourceUri: bearerResourceUri(options.origin, spaceId, path),
6612
- expiresAtSeconds: Math.ceil(expiresAt.getTime() / 1e3)
6613
- });
6614
- contentKey = generateKey();
6615
- const sealedContent = await seal(content, contentKey);
6616
- const unsigned = {
6617
- version: 1,
6618
- shareId,
6619
- delegation,
6620
- authorizationTarget: {
6621
- kind: "bearerKey",
6622
- sessionJwk: { kty: "OKP", crv: "Ed25519", x: toBase64Url(sessionPublicKey), d: toBase64Url(sessionPrivateKey) }
6623
- },
6624
- target: { origin: options.origin, nodeAudience, spaceId, resource: { kind: "exact", path } },
6625
- display: { filename: options.filename },
6626
- expiry,
6627
- content: { cid: sealedContent.cid, key: toBase64Url(contentKey) }
6628
- };
6629
- const envelope = signEnvelope(unsigned, senderPrivateKey);
6630
- envelopeKey = generateKey();
6631
- const sealedEnvelope = await seal(new TextEncoder().encode(canonicalize(envelope)), envelopeKey);
6632
- let inlineUrl;
6633
- if (options.inline === true) {
6634
- try {
6635
- inlineUrl = await encodeInlineShareUrl({ origin: options.origin, ciphertext: sealedEnvelope.blob, key32: envelopeKey });
6636
- } catch (error) {
6637
- throw new SharePublishError("inline-too-large", error instanceof Error ? error.message : "inline share is too large");
6638
- }
6639
- }
6640
- const contentUpload = await uploadShareBlob(options, { blob: sealedContent.blob, cid: sealedContent.cid, deleteAfter: expiry, contentLength: sealedContent.blob.byteLength });
6641
- let url;
6642
- let kind;
6643
- let retention = contentUpload.deleteAfter;
6644
- if (options.inline === true) {
6645
- if (inlineUrl === void 0) throw new SharePublishError("inline-too-large", "inline share is too large");
6646
- url = inlineUrl;
6647
- kind = "inline";
6648
- } else {
6649
- const envelopeUpload = await uploadShareBlob(options, { blob: sealedEnvelope.blob, cid: sealedEnvelope.cid, deleteAfter: expiry, contentLength: sealedEnvelope.blob.byteLength });
6650
- url = encodeShareUrl({ origin: options.origin, ciphertextCid: envelopeUpload.cid, key32: envelopeKey });
6651
- kind = "compact";
6652
- retention = envelopeUpload.deleteAfter;
6653
- }
6654
- const metadata = {
6655
- protocol: "tinycloud-share",
6656
- version: 1,
6657
- shareId,
6658
- origin: options.origin,
6659
- target: { kind: "bearer", origin: options.origin, nodeAudience, spaceId },
6660
- resource: { kind: "exact", path },
6661
- actions: ["read"],
6662
- expiresAt: expiry,
6663
- display: { filename: options.filename },
6664
- content: { cid: sealedContent.cid }
6665
- };
6666
- const result = { protocol: "tinycloud-share", version: SHARE_PUBLISH_RESULT_VERSION, link: { kind, cid: sealedEnvelope.cid }, metadata, registryDeleteAfter: retention };
6667
- Object.defineProperty(result, "toJSON", {
6668
- enumerable: false,
6669
- value: () => redactPublishedShare(result)
6670
- });
6671
- Object.defineProperty(result, "url", { enumerable: false, value: url });
6672
- return result;
6673
- } finally {
6674
- sessionPrivateKey.fill(0);
6675
- senderPrivateKey.fill(0);
6676
- contentKey?.fill(0);
6677
- envelopeKey?.fill(0);
6678
- }
6679
- }
6680
5974
 
6681
5975
  // src/addressed.ts
6682
5976
  async function parseAddressedEnvelope(value) {
@@ -7120,8 +6414,8 @@ function targetAuthorizationMethod(target) {
7120
6414
  }
7121
6415
  async function publishTargetShare(input) {
7122
6416
  const target = normalizeShareTarget(input.target);
7123
- if (target.kind === "bearer") return publishShare(input);
7124
6417
  if (input.targetAdapter === void 0) {
6418
+ if (target.kind === "bearer") throw new SharePublishError("authority-required", "native bearer publication requires an authenticated TinyCloud node");
7125
6419
  return {
7126
6420
  state: "authorization-required",
7127
6421
  method: targetAuthorizationMethod(target)
@@ -7131,9 +6425,9 @@ async function publishTargetShare(input) {
7131
6425
  const chunks = [];
7132
6426
  let size = 0;
7133
6427
  for await (const chunk of input.source) {
7134
- if (!(chunk instanceof Uint8Array)) throw new TypeError("addressed publication source yielded invalid bytes");
6428
+ if (!(chunk instanceof Uint8Array)) throw new TypeError("share publication source yielded invalid bytes");
7135
6429
  size += chunk.byteLength;
7136
- if (size > (input.maxBytes ?? 100 * 1024 * 1024)) throw new TypeError("addressed publication exceeds maxBytes");
6430
+ if (size > (input.maxBytes ?? SHARE_CONTENT_LIMIT)) throw new SharePublishError("max-bytes-exceeded", "share publication exceeds maxBytes");
7137
6431
  chunks.push(chunk.slice());
7138
6432
  }
7139
6433
  const bytes3 = new Uint8Array(size);
@@ -7144,15 +6438,29 @@ async function publishTargetShare(input) {
7144
6438
  }
7145
6439
  return bytes3;
7146
6440
  })();
6441
+ if (source.byteLength === 0) throw new SharePublishError("invalid-argument", "share content is empty");
6442
+ if (!input.filename || input.filename === "." || input.filename === ".." || /[/\\\u0000-\u001f\u007f]/.test(input.filename)) {
6443
+ throw new SharePublishError("invalid-argument", "filename must be one safe path segment");
6444
+ }
6445
+ if (target.kind === "bearer" && input.allowBinary !== true) {
6446
+ try {
6447
+ new TextDecoder("utf-8", { fatal: true }).decode(source);
6448
+ } catch {
6449
+ throw new SharePublishError("invalid-argument", "Markdown input must be valid UTF-8");
6450
+ }
6451
+ }
7147
6452
  const files = input.files === void 0 || input.files.length === 0 ? [{ bytes: source }] : input.files;
7148
6453
  const limit = input.maxBytes ?? SHARE_CONTENT_LIMIT;
7149
6454
  let totalBytes = 0;
7150
6455
  for (const file of files) {
7151
6456
  totalBytes += file.bytes.byteLength;
7152
6457
  if (!Number.isSafeInteger(totalBytes) || totalBytes > limit) {
7153
- throw new SharePublishError("max-bytes-exceeded", "addressed publication exceeds the combined byte limit");
6458
+ throw new SharePublishError("max-bytes-exceeded", "share publication exceeds the combined byte limit");
7154
6459
  }
7155
6460
  }
6461
+ const nowMs = input.now?.() ?? Date.now();
6462
+ const expiresAt = input.expiresAt ?? new Date(nowMs + DEFAULT_SHARE_LIFETIME_MS);
6463
+ if (!Number.isFinite(expiresAt.getTime()) || expiresAt.getTime() <= nowMs) throw new SharePublishError("invalid-argument", "expiresAt must be a valid future time");
7156
6464
  return input.targetAdapter.publish({
7157
6465
  source,
7158
6466
  filename: input.filename,
@@ -7161,18 +6469,8 @@ async function publishTargetShare(input) {
7161
6469
  ...input.resourceKind === void 0 ? {} : { resourceKind: input.resourceKind },
7162
6470
  ...input.actions === void 0 ? {} : { actions: input.actions },
7163
6471
  target,
7164
- expiresAt: input.expiresAt ?? new Date((input.now?.() ?? Date.now()) + 7 * 24 * 60 * 60 * 1e3),
6472
+ expiresAt,
7165
6473
  origin: input.origin,
7166
- ...input.inline === void 0 ? {} : { inline: input.inline },
7167
- upload: {
7168
- ...input.registryBaseUrl === void 0 ? {} : { registryBaseUrl: input.registryBaseUrl },
7169
- ...input.fetchFn === void 0 ? {} : { fetchFn: input.fetchFn },
7170
- ...input.authorizeUpload === void 0 ? {} : { authorizeUpload: input.authorizeUpload },
7171
- ...input.authorizationOrigin === void 0 ? {} : { authorizationOrigin: input.authorizationOrigin },
7172
- ...input.credentials === void 0 ? {} : { credentials: input.credentials },
7173
- ...input.allowInsecureRegistry === void 0 ? {} : { allowInsecureRegistry: input.allowInsecureRegistry },
7174
- ...input.uploadBlob === void 0 ? {} : { uploadBlob: input.uploadBlob }
7175
- },
7176
6474
  ...input.notify === void 0 ? {} : { notify: input.notify }
7177
6475
  });
7178
6476
  }
@@ -7264,7 +6562,7 @@ function publicationResult(input) {
7264
6562
  protocol: "tinycloud-share",
7265
6563
  version: SHARE_PUBLISH_RESULT_VERSION,
7266
6564
  url: input.url,
7267
- link: { kind: input.options.inline === true ? "inline" : "compact", cid: input.envelopeCid },
6565
+ link: { kind: "policy", cid: input.envelopeCid },
7268
6566
  metadata: {
7269
6567
  protocol: "tinycloud-share",
7270
6568
  version: 1,
@@ -7283,8 +6581,7 @@ function publicationResult(input) {
7283
6581
  enforcerDid: input.enforcerDid,
7284
6582
  envelopeCid: input.envelopeCid,
7285
6583
  shareCid: input.envelopeCid
7286
- },
7287
- registryDeleteAfter: input.retention
6584
+ }
7288
6585
  };
7289
6586
  Object.defineProperty(result, "toJSON", { enumerable: false, value: () => redactPublishedShare(result) });
7290
6587
  Object.defineProperty(result, "url", { enumerable: false, value: input.url });
@@ -7366,40 +6663,89 @@ async function publishAddressedShare(options) {
7366
6663
  if (envelopeSignature.byteLength !== 64) throw new TypeError("v3 envelope signature must be Ed25519");
7367
6664
  const envelope = { ...unsigned, signature: { signerDid: options.authority.ownerDid, algorithm: "Ed25519", value: toBase64Url(envelopeSignature) } };
7368
6665
  shareEnvelopeV3Schema.parse(envelope);
7369
- const envelopeKey = generateKey();
7370
- try {
7371
- const sealed = await seal(textEncoder.encode(canonicalize(envelope)), envelopeKey);
7372
- let url;
7373
- let retention = options.expiresAt.toISOString();
7374
- if (options.inline === true) url = await encodeInlineShareUrl({ origin: options.shareOrigin, ciphertext: sealed.blob, key32: envelopeKey });
7375
- else {
7376
- const uploaded = await uploadShareBlob({ source: new Uint8Array([1]), filename: options.filename, origin: options.shareOrigin, ...options.upload }, { blob: sealed.blob, cid: sealed.cid, deleteAfter: retention, contentLength: sealed.blob.byteLength });
7377
- retention = uploaded.deleteAfter;
7378
- url = encodeShareUrl({ origin: options.shareOrigin, ciphertextCid: uploaded.cid, key32: envelopeKey });
7379
- }
7380
- await options.publishBinding?.({
7381
- version: 3,
7382
- shareCid: sealed.cid,
7383
- shareId: options.shareId,
7384
- policyCid: created.policyCid,
7385
- policyRootCid: policyRoot.cid,
7386
- enforcementRootCid: enforcementRoot.cid,
7387
- contentSourceDigestHex
7388
- });
7389
- const deliveryMaterial = { envelope, sealedEnvelope: toBase64Url(sealed.blob), envelopeKey: toBase64Url(envelopeKey), shareCid: sealed.cid };
7390
- options.onDeliveryMaterial?.(deliveryMaterial);
7391
- return publicationResult({ options, url, envelopeCid: sealed.cid, matcher, policyCid: created.policyCid, policyRootCid: policyRoot.cid, enforcementRootCid: enforcementRoot.cid, enforcerDid: registration.attestedEnforcerBinding.enforcerDid, expiry, retention, deliveryMaterial });
7392
- } finally {
7393
- envelopeKey.fill(0);
7394
- }
6666
+ const envelopeBytes = textEncoder.encode(canonicalize(envelope));
6667
+ const envelopeCid = await computeCid(envelopeBytes);
6668
+ const url = await encodePublicInlineShareUrl({ origin: options.shareOrigin, plaintext: envelopeBytes });
6669
+ const deliveryMaterial = { envelope, shareCid: envelopeCid };
6670
+ options.onDeliveryMaterial?.(deliveryMaterial);
6671
+ return publicationResult({
6672
+ options,
6673
+ url,
6674
+ envelopeCid,
6675
+ matcher,
6676
+ policyCid: created.policyCid,
6677
+ policyRootCid: policyRoot.cid,
6678
+ enforcementRootCid: enforcementRoot.cid,
6679
+ enforcerDid: registration.attestedEnforcerBinding.enforcerDid,
6680
+ expiry,
6681
+ deliveryMaterial
6682
+ });
7395
6683
  }
7396
6684
 
7397
- // src/policy-authority.ts
7398
- import { ed25519 as ed255196 } from "@noble/curves/ed25519";
6685
+ // src/cache.ts
6686
+ import { sha256 as sha2564 } from "@noble/hashes/sha256";
6687
+ function cacheKey(key) {
6688
+ if (!key.cid || !key.expiresAt) throw new Error("share cache keys require CID and expiry");
6689
+ return `${key.cid}\0${key.expiresAt}`;
6690
+ }
6691
+ function digest(bytes3) {
6692
+ return sha2564(bytes3);
6693
+ }
6694
+ function equalBytes(left, right) {
6695
+ if (left.byteLength !== right.byteLength) return false;
6696
+ return left.every((byte, index) => byte === right[index]);
6697
+ }
6698
+ var MemoryShareCache = class {
6699
+ constructor(now = () => /* @__PURE__ */ new Date()) {
6700
+ this.now = now;
6701
+ }
6702
+ entries = /* @__PURE__ */ new Map();
6703
+ async get(key, now = /* @__PURE__ */ new Date()) {
6704
+ const mapKey = cacheKey(key);
6705
+ const entry = this.entries.get(mapKey);
6706
+ if (entry === void 0) return void 0;
6707
+ if (Date.parse(entry.key.expiresAt) <= now.getTime() || !equalBytes(entry.digest, digest(entry.bytes))) {
6708
+ this.entries.delete(mapKey);
6709
+ return void 0;
6710
+ }
6711
+ return entry.bytes.slice();
6712
+ }
6713
+ async getByCid(cid2, now = /* @__PURE__ */ new Date()) {
6714
+ const entries = [...this.entries.values()].filter((entry) => entry.key.cid === cid2);
6715
+ for (const entry of entries) {
6716
+ if (Date.parse(entry.key.expiresAt) <= now.getTime() || !equalBytes(entry.digest, digest(entry.bytes))) {
6717
+ this.entries.delete(cacheKey(entry.key));
6718
+ continue;
6719
+ }
6720
+ return {
6721
+ key: { ...entry.key },
6722
+ bytes: entry.bytes.slice(),
6723
+ digest: entry.digest.slice(),
6724
+ metadata: { ...entry.metadata }
6725
+ };
6726
+ }
6727
+ return void 0;
6728
+ }
6729
+ async set(key, bytes3, metadata) {
6730
+ if (Date.parse(key.expiresAt) <= this.now().getTime()) return;
6731
+ this.entries.set(cacheKey(key), {
6732
+ key: { cid: key.cid, expiresAt: key.expiresAt },
6733
+ bytes: bytes3.slice(),
6734
+ digest: digest(bytes3),
6735
+ metadata: { ...metadata }
6736
+ });
6737
+ }
6738
+ async delete(key) {
6739
+ this.entries.delete(cacheKey(key));
6740
+ }
6741
+ async clear() {
6742
+ this.entries.clear();
6743
+ }
6744
+ };
7399
6745
 
7400
6746
  // src/owner-policy.ts
7401
- import { ed25519 as ed255195 } from "@noble/curves/ed25519";
7402
- import { sha256 as sha2564 } from "@noble/hashes/sha256";
6747
+ import { ed25519 as ed255194 } from "@noble/curves/ed25519";
6748
+ import { sha256 as sha2565 } from "@noble/hashes/sha256";
7403
6749
 
7404
6750
  // ../../node_modules/multiformats/dist/src/bases/base32.js
7405
6751
  var base322 = rfc46482({
@@ -7953,10 +7299,10 @@ function fromB64(value) {
7953
7299
  throw new Error("base64url decoding is unavailable");
7954
7300
  }
7955
7301
  function cid(bytesValue) {
7956
- return CID2.createV1(code2, create2(SHA256_CODE2, sha2564(bytesValue))).toString(base322.encoder);
7302
+ return CID2.createV1(code2, create2(SHA256_CODE2, sha2565(bytesValue))).toString(base322.encoder);
7957
7303
  }
7958
- function digest(bytesValue) {
7959
- return b64(sha2564(bytesValue));
7304
+ function digest2(bytesValue) {
7305
+ return b64(sha2565(bytesValue));
7960
7306
  }
7961
7307
  function didKeyFromEd25519PublicKey2(publicKey) {
7962
7308
  if (publicKey.length !== 32) throw new Error("share key public key must be 32 bytes");
@@ -8030,9 +7376,9 @@ function dagCborEncode(value) {
8030
7376
  }
8031
7377
  function assertObject(value, keys, label) {
8032
7378
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${label} is invalid`);
8033
- const record2 = value;
8034
- if (Object.keys(record2).length !== keys.length || keys.some((key) => !Object.prototype.hasOwnProperty.call(record2, key))) throw new Error(`${label} has unknown or missing fields`);
8035
- return record2;
7379
+ const record = value;
7380
+ if (Object.keys(record).length !== keys.length || keys.some((key) => !Object.prototype.hasOwnProperty.call(record, key))) throw new Error(`${label} has unknown or missing fields`);
7381
+ return record;
8036
7382
  }
8037
7383
  async function createDelegatedShareKey(options) {
8038
7384
  if (!globalThis.crypto?.subtle) throw new Error("WebCrypto Ed25519 is required for delegated share keys");
@@ -8095,7 +7441,7 @@ async function createPolicyEnforcementDelegation(input) {
8095
7441
  async function canonicalOwnerSharePolicy(policy) {
8096
7442
  const text = canonicalize({ domain: POLICY_DOMAIN, policy });
8097
7443
  const policyBytes = new TextEncoder().encode(text);
8098
- return { bytes: policyBytes, cid: cid(policyBytes), digest: digest(policyBytes) };
7444
+ return { bytes: policyBytes, cid: cid(policyBytes), digest: digest2(policyBytes) };
8099
7445
  }
8100
7446
  function computeOwnerShareRegistrationCid(registration) {
8101
7447
  return cid(new TextEncoder().encode(canonicalize(registration)));
@@ -8134,7 +7480,7 @@ function validateOwnerSharePolicyRegistration(value, expected) {
8134
7480
  const publicKey = encodedKid.length === 34 ? encodedKid.slice(2) : encodedKid;
8135
7481
  const signatureBytes = fromB64(proof.signature);
8136
7482
  const signedBytes = new TextEncoder().encode(`${OWNER_SHARE_REGISTRATION_DOMAIN}${canonicalize(registrationCore)}`);
8137
- if (signatureBytes.length !== 64 || !ed255195.verify(signatureBytes, signedBytes, publicKey)) throw new Error("owner-share registration proof signature is invalid");
7483
+ if (signatureBytes.length !== 64 || !ed255194.verify(signatureBytes, signedBytes, publicKey)) throw new Error("owner-share registration proof signature is invalid");
8138
7484
  return { registration, proof };
8139
7485
  }
8140
7486
  function validateOwnerSharePolicyRegistrationBytes(bytesValue, expected) {
@@ -8147,318 +7493,6 @@ function validateOwnerSharePolicyRegistrationBytes(bytesValue, expected) {
8147
7493
  return validateOwnerSharePolicyRegistration(value, expected);
8148
7494
  }
8149
7495
 
8150
- // src/policy-authority.ts
8151
- var ENVELOPE_DOMAIN = "xyz.tinycloud.share/envelope/v2\0";
8152
- function record(value, keys, label) {
8153
- if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${label} is invalid`);
8154
- const output = value;
8155
- if (Object.keys(output).length !== keys.length || keys.some((key) => !Object.hasOwn(output, key))) throw new Error(`${label} has unknown or missing fields`);
8156
- return output;
8157
- }
8158
- function requiredString(value, label) {
8159
- if (typeof value !== "string" || value.length === 0) throw new Error(`${label} is invalid`);
8160
- return value;
8161
- }
8162
- function stringArray(value, label) {
8163
- if (!Array.isArray(value) || value.length === 0 || !value.every((item) => typeof item === "string" && item.length > 0)) throw new Error(`${label} is invalid`);
8164
- return value;
8165
- }
8166
- function parseEnforcement(value) {
8167
- const delegation = record(value, ["cid", "dagCbor", "issuerDid", "audienceDid", "facts", "signature"], "policy enforcement delegation");
8168
- const facts = record(delegation.facts, ["ownerDelegationCid", "policyCid", "shareId", "shareKeyDid", "enforcerDid", "nodeAudience", "spaceId", "path", "actions", "contentSourceDigest", "expiresAt"], "policy enforcement facts");
8169
- return {
8170
- cid: requiredString(delegation.cid, "policy enforcement CID"),
8171
- dagCbor: requiredString(delegation.dagCbor, "policy enforcement bytes"),
8172
- issuerDid: requiredString(delegation.issuerDid, "policy enforcement issuer"),
8173
- audienceDid: requiredString(delegation.audienceDid, "policy enforcement audience"),
8174
- facts: {
8175
- ownerDelegationCid: requiredString(facts.ownerDelegationCid, "owner delegation CID"),
8176
- policyCid: requiredString(facts.policyCid, "policy CID"),
8177
- shareId: requiredString(facts.shareId, "share id"),
8178
- shareKeyDid: requiredString(facts.shareKeyDid, "share key DID"),
8179
- enforcerDid: requiredString(facts.enforcerDid, "enforcer DID"),
8180
- nodeAudience: requiredString(facts.nodeAudience, "node audience"),
8181
- spaceId: requiredString(facts.spaceId, "space id"),
8182
- path: requiredString(facts.path, "resource path"),
8183
- actions: stringArray(facts.actions, "policy actions"),
8184
- contentSourceDigest: requiredString(facts.contentSourceDigest, "content source digest"),
8185
- expiresAt: requiredString(facts.expiresAt, "policy expiry")
8186
- },
8187
- signature: requiredString(delegation.signature, "policy enforcement signature")
8188
- };
8189
- }
8190
- async function verifyEnforcement(delegation) {
8191
- try {
8192
- const dagCbor = fromBase64Url(delegation.dagCbor);
8193
- if (await computeCid(dagCbor) !== delegation.cid) return false;
8194
- const expectedDagCbor = dagCborEncode({
8195
- domain: POLICY_ENFORCEMENT_DOMAIN,
8196
- unsigned: {
8197
- type: "TinyCloudSharePolicyEnforcement",
8198
- version: 2,
8199
- issuerDid: delegation.issuerDid,
8200
- audienceDid: delegation.audienceDid,
8201
- facts: delegation.facts
8202
- }
8203
- });
8204
- if (expectedDagCbor.length !== dagCbor.length || expectedDagCbor.some((byte, index) => byte !== dagCbor[index])) return false;
8205
- const signature = fromBase64Url(delegation.signature);
8206
- return signature.length === 64 && ed255196.verify(
8207
- signature,
8208
- dagCbor,
8209
- ed25519PublicKeyFromDidKey(delegation.issuerDid),
8210
- { zip215: false }
8211
- );
8212
- } catch {
8213
- return false;
8214
- }
8215
- }
8216
- async function verifyOuterEnvelope(value, envelope, policyCid, expectedEnforcerDid, expectedActions) {
8217
- try {
8218
- const hasDecryption = typeof value === "object" && value !== null && Object.hasOwn(value, "decryption");
8219
- const outer = record(value, ["schema", "version", "shareId", "delegationCid", "policyCid", "target", "resource", "actions", ...hasDecryption ? ["decryption"] : [], "contentSource", "contentSourceDigest", "expiresAt", "envelopeCid", "shareCid", "signature"], "outer envelope");
8220
- const target = record(outer.target, ["origin", "nodeAudience", "enforcerDid", "spaceId"], "outer target");
8221
- const signature = record(outer.signature, ["signerDid", "algorithm", "value"], "outer envelope signature");
8222
- const envelopeCid = requiredString(outer.envelopeCid, "outer envelope CID");
8223
- const shareCid = requiredString(outer.shareCid, "outer share CID");
8224
- const { signature: _signature, envelopeCid: _envelopeCid, shareCid: _shareCid, ...identity } = outer;
8225
- if (outer.schema !== "xyz.tinycloud.share/envelope/v2" || outer.version !== 2 || outer.shareId !== envelope.shareId || outer.delegationCid !== envelope.delegationCid || outer.policyCid !== policyCid || target.origin !== envelope.target.origin || target.nodeAudience !== envelope.target.nodeAudience || target.enforcerDid !== expectedEnforcerDid || target.spaceId !== envelope.target.spaceId || canonicalize(outer.resource) !== canonicalize(envelope.resource) || canonicalize(outer.actions) !== canonicalize(expectedActions) || (hasDecryption ? envelope.decryption === void 0 || canonicalize(outer.decryption) !== canonicalize(envelope.decryption) : envelope.decryption !== void 0) || canonicalize(outer.contentSource) !== canonicalize(envelope.contentSource) || outer.contentSourceDigest !== envelope.contentSourceDigest || outer.expiresAt !== envelope.expiry || signature.signerDid !== envelope.signature.signerDid || signature.algorithm !== "Ed25519") return void 0;
8226
- if (envelopeCid !== await computeCid(new TextEncoder().encode(canonicalize(identity)))) return void 0;
8227
- if (shareCid !== await computeCid(new TextEncoder().encode(canonicalize({ version: 2, shareId: envelope.shareId, policyCid, envelopeCid })))) return void 0;
8228
- const unsigned = { ...identity, envelopeCid, shareCid };
8229
- const signatureBytes = fromBase64Url(requiredString(signature.value, "outer signature"));
8230
- if (signatureBytes.length !== 64 || !ed255196.verify(
8231
- signatureBytes,
8232
- new TextEncoder().encode(`${ENVELOPE_DOMAIN}${canonicalize(unsigned)}`),
8233
- ed25519PublicKeyFromDidKey(envelope.signature.signerDid),
8234
- { zip215: false }
8235
- )) return void 0;
8236
- return { envelopeCid, shareCid };
8237
- } catch {
8238
- return void 0;
8239
- }
8240
- }
8241
- function createRegisteredPolicyAuthority(options) {
8242
- const proofKey = options.nodeProof.publicKey.slice();
8243
- return {
8244
- async resolve({ policyCid, envelope }) {
8245
- try {
8246
- if (envelope.authorizationTarget.kind !== "policy" || envelope.authorizationTarget.policyCid !== policyCid) return void 0;
8247
- const ownerAuthority = envelope.ownerAuthority;
8248
- if (ownerAuthority?.registrationReceipt === void 0) return void 0;
8249
- const enforcement = parseEnforcement(ownerAuthority.enforcementDelegation);
8250
- if (!await verifyEnforcement(enforcement)) return void 0;
8251
- const policyBytes = fromBase64Url(envelope.authorizationTarget.policyBytes);
8252
- const ownerDelegation = {
8253
- delegationCid: envelope.delegationCid,
8254
- signedDagCbor: new Uint8Array(),
8255
- delegation: {
8256
- delegateDID: envelope.signature.signerDid,
8257
- spaceId: envelope.target.spaceId,
8258
- path: envelope.resource.path,
8259
- actions: envelope.actions,
8260
- expiry: new Date(envelope.expiry)
8261
- }
8262
- };
8263
- const receipt = validateOwnerSharePolicyRegistration(ownerAuthority.registrationReceipt, {
8264
- policy: { bytes: policyBytes, cid: policyCid, proof: "" },
8265
- ownerDelegation,
8266
- enforcementDelegation: enforcement,
8267
- contentSourceDigest: envelope.contentSourceDigest,
8268
- nodeProof: { kid: options.nodeProof.kid, publicKey: proofKey }
8269
- });
8270
- const registration = receipt.registration;
8271
- const outer = await verifyOuterEnvelope(ownerAuthority.outerEnvelope, envelope, policyCid, options.expectedTarget.enforcerDid, registration.actions);
8272
- if (outer === void 0 || ownerAuthority.registrationCid !== registration.registrationCid || ownerAuthority.envelopeCid !== outer.envelopeCid || ownerAuthority.shareCid !== outer.shareCid || envelope.authorityMaterialHandle !== registration.registrationCid || envelope.signature.signerDid !== registration.shareKeyDid || registration.target.origin !== options.expectedTarget.origin || registration.target.nodeAudience !== options.expectedTarget.nodeAudience || registration.enforcerDid !== options.expectedTarget.enforcerDid || enforcement.issuerDid !== registration.shareKeyDid || enforcement.audienceDid !== registration.enforcerDid) return void 0;
8273
- return {
8274
- policyCid: registration.policyCid,
8275
- signerDid: registration.shareKeyDid,
8276
- registrationCid: registration.registrationCid,
8277
- shareId: registration.shareId,
8278
- recipientMatcher: registration.recipientMatcher,
8279
- target: registration.target,
8280
- resource: registration.resource,
8281
- actions: registration.actions,
8282
- contentSource: registration.contentSource,
8283
- contentSourceDigest: registration.contentSourceDigest,
8284
- delegationCid: registration.ownerDelegationCid,
8285
- authorityMaterialHandle: registration.registrationCid,
8286
- authorityMaterialDigest: envelope.authorityMaterialDigest,
8287
- expiresAt: registration.expiresAt
8288
- };
8289
- } catch {
8290
- return void 0;
8291
- }
8292
- }
8293
- };
8294
- }
8295
-
8296
- // src/cache.ts
8297
- import { sha256 as sha2565 } from "@noble/hashes/sha256";
8298
- function cacheKey(key) {
8299
- if (!key.cid || !key.expiresAt) throw new Error("share cache keys require CID and expiry");
8300
- return `${key.cid}\0${key.expiresAt}`;
8301
- }
8302
- function digest2(bytes3) {
8303
- return sha2565(bytes3);
8304
- }
8305
- function equalBytes(left, right) {
8306
- if (left.byteLength !== right.byteLength) return false;
8307
- return left.every((byte, index) => byte === right[index]);
8308
- }
8309
- var MemoryShareCache = class {
8310
- constructor(now = () => /* @__PURE__ */ new Date()) {
8311
- this.now = now;
8312
- }
8313
- entries = /* @__PURE__ */ new Map();
8314
- async get(key, now = /* @__PURE__ */ new Date()) {
8315
- const mapKey = cacheKey(key);
8316
- const entry = this.entries.get(mapKey);
8317
- if (entry === void 0) return void 0;
8318
- if (Date.parse(entry.key.expiresAt) <= now.getTime() || !equalBytes(entry.digest, digest2(entry.bytes))) {
8319
- this.entries.delete(mapKey);
8320
- return void 0;
8321
- }
8322
- return entry.bytes.slice();
8323
- }
8324
- async getByCid(cid2, now = /* @__PURE__ */ new Date()) {
8325
- const entries = [...this.entries.values()].filter((entry) => entry.key.cid === cid2);
8326
- for (const entry of entries) {
8327
- if (Date.parse(entry.key.expiresAt) <= now.getTime() || !equalBytes(entry.digest, digest2(entry.bytes))) {
8328
- this.entries.delete(cacheKey(entry.key));
8329
- continue;
8330
- }
8331
- return {
8332
- key: { ...entry.key },
8333
- bytes: entry.bytes.slice(),
8334
- digest: entry.digest.slice(),
8335
- metadata: { ...entry.metadata }
8336
- };
8337
- }
8338
- return void 0;
8339
- }
8340
- async set(key, bytes3, metadata) {
8341
- if (Date.parse(key.expiresAt) <= this.now().getTime()) return;
8342
- this.entries.set(cacheKey(key), {
8343
- key: { cid: key.cid, expiresAt: key.expiresAt },
8344
- bytes: bytes3.slice(),
8345
- digest: digest2(bytes3),
8346
- metadata: { ...metadata }
8347
- });
8348
- }
8349
- async delete(key) {
8350
- this.entries.delete(cacheKey(key));
8351
- }
8352
- async clear() {
8353
- this.entries.clear();
8354
- }
8355
- };
8356
-
8357
- // src/delivery.ts
8358
- import { ed25519 as ed255197 } from "@noble/curves/ed25519";
8359
- var SHARE_DELIVERY_AUTHORIZATION_DOMAIN = "xyz.tinycloud.share/delivery-authorization/v2\0";
8360
- function isCanonicalHttpsOrigin2(value) {
8361
- let url;
8362
- try {
8363
- url = new URL(value);
8364
- } catch {
8365
- return false;
8366
- }
8367
- return url.protocol === "https:" && url.origin === value && url.pathname === "/" && url.search === "" && url.hash === "" && url.username === "" && url.password === "";
8368
- }
8369
- var AUTHORIZATION_KEYS = [
8370
- "type",
8371
- "version",
8372
- "jti",
8373
- "shareCid",
8374
- "shareId",
8375
- "registrationCid",
8376
- "delegationCid",
8377
- "enforcementDelegationCid",
8378
- "envelopeCid",
8379
- "policyCid",
8380
- "nodeAudience",
8381
- "targetOrigin",
8382
- "openCredentialsAudience",
8383
- "holder",
8384
- "recipientMatcher",
8385
- "deliveryEmail",
8386
- "shareUrl",
8387
- "returnOrigin",
8388
- "documentName",
8389
- "senderDid",
8390
- "senderTrust",
8391
- "contentSource",
8392
- "contentSourceDigest",
8393
- "shareExpiresAt",
8394
- "issuedAt",
8395
- "reportAbuseToken",
8396
- "actions",
8397
- "resource",
8398
- "authorityMaterialHandle",
8399
- "authorityMaterialDigest",
8400
- "requestBodyDigest",
8401
- "idempotencyKey",
8402
- "expiresAt",
8403
- "dataAuthority"
8404
- ];
8405
- function assertObject2(value, keys, label) {
8406
- if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${label} is invalid`);
8407
- const record2 = value;
8408
- if (Object.keys(record2).length !== keys.length || keys.some((key) => !Object.prototype.hasOwnProperty.call(record2, key))) throw new Error(`${label} has unknown or missing fields`);
8409
- return record2;
8410
- }
8411
- function fromB642(value) {
8412
- if (!/^[A-Za-z0-9_-]+$/.test(value)) throw new Error("share delivery value is not canonical base64url");
8413
- if (typeof atob === "function") {
8414
- const padded = value.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - value.length % 4) % 4);
8415
- return Uint8Array.from(atob(padded), (character) => character.charCodeAt(0));
8416
- }
8417
- if (typeof Buffer !== "undefined") return new Uint8Array(Buffer.from(value, "base64url"));
8418
- throw new Error("base64url decoding is unavailable");
8419
- }
8420
- function validateShareDeliveryAuthorizationBytes(bytesValue, expected) {
8421
- let value;
8422
- try {
8423
- value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytesValue));
8424
- } catch {
8425
- throw new Error("share delivery authorization response is not valid UTF-8 JSON");
8426
- }
8427
- const root = assertObject2(value, ["authorization", "proof"], "share delivery authorization response");
8428
- const authorization = assertObject2(root.authorization, AUTHORIZATION_KEYS, "share delivery authorization");
8429
- const proof = assertObject2(root.proof, ["alg", "kid", "signature"], "share delivery authorization proof");
8430
- const request = expected.request;
8431
- 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) {
8432
- throw new Error("share delivery authorization is not bound to the submitted request");
8433
- }
8434
- const now = Date.now();
8435
- const issuedAt = typeof authorization.issuedAt === "string" ? Date.parse(authorization.issuedAt) : Number.NaN;
8436
- const expiresAt = typeof authorization.expiresAt === "string" ? Date.parse(authorization.expiresAt) : Number.NaN;
8437
- if (!Number.isFinite(issuedAt) || issuedAt > now + 6e4 || issuedAt > expiresAt) throw new Error("share delivery authorization issuedAt is invalid");
8438
- if (!Number.isFinite(expiresAt) || expiresAt <= now) throw new Error("share delivery authorization is expired");
8439
- if (new Date(expiresAt).toISOString() !== authorization.expiresAt) throw new Error("share delivery authorization expiresAt is non-canonical");
8440
- if (typeof authorization.openCredentialsAudience !== "string" || !isCanonicalHttpsOrigin2(authorization.openCredentialsAudience) || authorization.openCredentialsAudience !== expected.credentialsAudience || authorization.openCredentialsAudience === authorization.nodeAudience || authorization.openCredentialsAudience === authorization.returnOrigin) {
8441
- throw new Error("share delivery authorization openCredentialsAudience is missing or untrusted");
8442
- }
8443
- if (proof.alg !== "EdDSA" || typeof proof.kid !== "string" || typeof proof.signature !== "string") throw new Error("share delivery authorization proof is invalid");
8444
- if (proof.kid !== expected.nodeProof.kid) throw new Error("share delivery authorization proof key is not trusted");
8445
- const encodedKid = expected.nodeProof.publicKey;
8446
- const publicKey = encodedKid.length === 34 && encodedKid[0] === 237 && encodedKid[1] === 1 ? encodedKid.slice(2) : encodedKid;
8447
- if (publicKey.length !== 32) throw new Error("share delivery authorization proof key is invalid");
8448
- const signatureBytes = fromB642(proof.signature);
8449
- const signedBytes = new TextEncoder().encode(`${SHARE_DELIVERY_AUTHORIZATION_DOMAIN}${canonicalize(authorization)}`);
8450
- if (signatureBytes.length !== 64 || !ed255197.verify(signatureBytes, signedBytes, publicKey)) throw new Error("share delivery authorization proof signature is invalid");
8451
- return { authorization, proof };
8452
- }
8453
- function shareDeliveryTrustedKid(publicKey) {
8454
- if (publicKey.length !== 32) throw new Error("share delivery node key must be a raw 32-byte Ed25519 public key");
8455
- const prefixed = new Uint8Array(34);
8456
- prefixed.set([237, 1]);
8457
- prefixed.set(publicKey, 2);
8458
- const did = `did:key:${base58btc2.encode(prefixed)}`;
8459
- return `${did}#${did.slice("did:key:".length)}`;
8460
- }
8461
-
8462
7496
  // src/history.ts
8463
7497
  var MemoryEncryptedShareHistoryStorage = class {
8464
7498
  values = [];
@@ -8508,8 +7542,8 @@ function historyRecordForPublishedShare(result, now = /* @__PURE__ */ new Date()
8508
7542
  }
8509
7543
  var MemorySenderShareRecordStorage = class {
8510
7544
  records = /* @__PURE__ */ new Map();
8511
- async put(record2) {
8512
- this.records.set(record2.shareId, record2);
7545
+ async put(record) {
7546
+ this.records.set(record.shareId, record);
8513
7547
  }
8514
7548
  async list() {
8515
7549
  return [...this.records.values()];
@@ -8546,7 +7580,7 @@ var SenderShareStore = class {
8546
7580
  async save(receipt, key) {
8547
7581
  const registration = receipt.registration;
8548
7582
  if (registration.shareKeyDid !== key.did) throw new Error("share key does not match the registered receipt");
8549
- const record2 = {
7583
+ const record = {
8550
7584
  shareId: registration.shareId,
8551
7585
  registrationCid: registration.registrationCid,
8552
7586
  policyCid: registration.policyCid,
@@ -8563,15 +7597,15 @@ var SenderShareStore = class {
8563
7597
  registeredAt: registration.registeredAt,
8564
7598
  expiresAt: registration.expiresAt
8565
7599
  };
8566
- await this.records.put(record2);
8567
- await this.keys.put(record2.shareId, { cryptoKey: key.cryptoKey, publicKey: key.publicKey, extractable: key.extractable });
8568
- return record2;
7600
+ await this.records.put(record);
7601
+ await this.keys.put(record.shareId, { cryptoKey: key.cryptoKey, publicKey: key.publicKey, extractable: key.extractable });
7602
+ return record;
8569
7603
  }
8570
7604
  /** List shares created in any session, most recent first. */
8571
7605
  async list(options) {
8572
7606
  const now = this.now().getTime();
8573
7607
  const records = await this.records.list();
8574
- return records.filter((record2) => (options?.includeRevoked ?? false) || record2.revokedAt === void 0).filter((record2) => (options?.includeExpired ?? false) || Date.parse(record2.expiresAt) > now).sort((left, right) => Date.parse(right.registeredAt) - Date.parse(left.registeredAt));
7608
+ return records.filter((record) => (options?.includeRevoked ?? false) || record.revokedAt === void 0).filter((record) => (options?.includeExpired ?? false) || Date.parse(record.expiresAt) > now).sort((left, right) => Date.parse(right.registeredAt) - Date.parse(left.registeredAt));
8575
7609
  }
8576
7610
  async get(shareId) {
8577
7611
  return this.records.get(shareId);
@@ -8594,17 +7628,17 @@ var SenderShareStore = class {
8594
7628
  * returned CID, then call {@link markRevoked} once that succeeds.
8595
7629
  */
8596
7630
  async revocationTarget(shareId, scope) {
8597
- const record2 = await this.records.get(shareId);
8598
- if (record2 === void 0) throw new Error(`Unknown share: ${shareId}`);
8599
- const delegationCid = scope === "direct" ? record2.enforcementDelegationCid : record2.ownerDelegationCid;
7631
+ const record = await this.records.get(shareId);
7632
+ if (record === void 0) throw new Error(`Unknown share: ${shareId}`);
7633
+ const delegationCid = scope === "direct" ? record.enforcementDelegationCid : record.ownerDelegationCid;
8600
7634
  if (delegationCid === void 0) throw new Error("share has no node-enforced delegation");
8601
7635
  return delegationCid;
8602
7636
  }
8603
7637
  /** Mark a share revoked locally after the caller's node-side revoke call succeeds. */
8604
7638
  async markRevoked(shareId, revokedAt = this.now().toISOString()) {
8605
- const record2 = await this.records.get(shareId);
8606
- if (record2 === void 0) throw new Error(`Unknown share: ${shareId}`);
8607
- const revoked = { ...record2, revokedAt };
7639
+ const record = await this.records.get(shareId);
7640
+ if (record === void 0) throw new Error(`Unknown share: ${shareId}`);
7641
+ const revoked = { ...record, revokedAt };
8608
7642
  await this.records.put(revoked);
8609
7643
  return revoked;
8610
7644
  }
@@ -8620,9 +7654,9 @@ var EncryptedSenderShareHistory = class {
8620
7654
  this.key = key;
8621
7655
  this.now = now;
8622
7656
  }
8623
- async put(record2) {
7657
+ async put(record) {
8624
7658
  const iv = crypto.getRandomValues(new Uint8Array(12));
8625
- const payload = new TextEncoder().encode(JSON.stringify(record2));
7659
+ const payload = new TextEncoder().encode(JSON.stringify(record));
8626
7660
  const ciphertext = new Uint8Array(await crypto.subtle.encrypt({ name: "AES-GCM", iv }, this.key, payload));
8627
7661
  const encoded = new Uint8Array(iv.length + ciphertext.length);
8628
7662
  encoded.set(iv);
@@ -8655,8 +7689,8 @@ var ShareNotifyError = class extends Error {
8655
7689
  this.name = "ShareNotifyError";
8656
7690
  }
8657
7691
  };
8658
- function recipientMatchesShareRecord(record2, recipient) {
8659
- const matcher = record2.recipientMatcher;
7692
+ function recipientMatchesShareRecord(record, recipient) {
7693
+ const matcher = record.recipientMatcher;
8660
7694
  if (matcher.kind === "bearer" || matcher.kind === "recipientDid") return false;
8661
7695
  if (matcher.kind === "exactEmail") return matcher.value === recipient;
8662
7696
  const at = recipient.lastIndexOf("@");
@@ -8695,59 +7729,66 @@ async function defaultIdempotencyKey(shareId, recipient) {
8695
7729
  }
8696
7730
 
8697
7731
  // src/lifecycle.ts
8698
- function targetKind2(record2) {
8699
- if (record2.targetKind !== void 0) return record2.targetKind;
8700
- return record2.recipientMatcher.kind === "exactEmail" ? "email" : record2.recipientMatcher.kind === "emailDomain" ? "emailDomain" : record2.recipientMatcher.kind === "recipientDid" ? "recipientDid" : "bearer";
7732
+ function targetKind2(record) {
7733
+ if (record.targetKind !== void 0) return record.targetKind;
7734
+ return record.recipientMatcher.kind === "exactEmail" ? "email" : record.recipientMatcher.kind === "emailDomain" ? "emailDomain" : record.recipientMatcher.kind === "recipientDid" ? "recipientDid" : "bearer";
7735
+ }
7736
+ function policyNodeAudience(record) {
7737
+ const envelope = record.deliveryMaterial?.envelope;
7738
+ if (typeof envelope !== "object" || envelope === null || Array.isArray(envelope)) return record.target.nodeAudience;
7739
+ const binding = envelope.attestedEnforcerBinding;
7740
+ if (typeof binding !== "object" || binding === null || Array.isArray(binding)) return record.target.nodeAudience;
7741
+ const nodeAudience = binding.nodeAudience;
7742
+ return typeof nodeAudience === "string" ? nodeAudience : record.target.nodeAudience;
8701
7743
  }
8702
7744
  async function revokeShare(input) {
8703
7745
  const target = targetKind2(input.record);
8704
- if (target === "bearer") return { state: "retention-only", target, reason: "bearer-capability-cannot-be-revoked" };
8705
7746
  if (input.adapter === void 0) return { state: "unsupported", target, reason: "node revocation authority is required", code: "unsupported-target" };
8706
7747
  const scope = input.scope ?? "direct";
8707
7748
  const delegationCid = scope === "ancestor" ? input.record.ownerDelegationCid : input.record.enforcementDelegationCid;
8708
7749
  if (delegationCid === void 0) return { state: "unsupported", target, reason: "share has no node-enforced delegation receipt", code: "unsupported-target" };
8709
- await input.adapter.revokeDelegation({ delegationCid, scope });
7750
+ if (target === "bearer") {
7751
+ if (input.adapter.revokeDelegation === void 0) return { state: "unsupported", target, reason: "native delegation revocation authority is required", code: "unsupported-target" };
7752
+ await input.adapter.revokeDelegation({ delegationCid, scope });
7753
+ } else {
7754
+ if (input.record.ownerDid === void 0) return { state: "unsupported", target, reason: "share has no Policy/v3 owner receipt", code: "unsupported-target" };
7755
+ if (input.adapter.revokePolicyRoot === void 0) return { state: "unsupported", target, reason: "Policy/v3 root revocation authority is required", code: "unsupported-target" };
7756
+ await input.adapter.revokePolicyRoot({
7757
+ rootCid: delegationCid,
7758
+ targetRole: scope === "ancestor" ? "policy-authority" : "policy-enforcement",
7759
+ ownerDid: input.record.ownerDid,
7760
+ nodeOrigin: input.record.target.origin,
7761
+ nodeAudience: policyNodeAudience(input.record)
7762
+ });
7763
+ }
8710
7764
  const revokedAt = (input.now?.() ?? /* @__PURE__ */ new Date()).toISOString();
8711
7765
  if (input.records !== void 0) await input.records.put({ ...input.record, revokedAt });
8712
7766
  return { state: "revoked", target, delegationCid, revokedAt };
8713
7767
  }
8714
- function redactRecord(record2, revealLink, link) {
8715
- const matcher = record2.recipientMatcher;
8716
- const revealedLink = revealLink ? link ?? record2.link : void 0;
7768
+ function redactRecord(record, revealLink, link) {
7769
+ const matcher = record.recipientMatcher;
7770
+ const revealedLink = revealLink ? link ?? record.link : void 0;
8717
7771
  return {
8718
- shareId: record2.shareId,
7772
+ shareId: record.shareId,
8719
7773
  target: matcher.kind === "exactEmail" ? "email" : matcher.kind === "emailDomain" ? "email-domain" : matcher.kind === "recipientDid" ? "recipient-did" : "bearer",
8720
7774
  ...matcher.kind === "exactEmail" ? { recipient: matcher.value } : matcher.kind === "emailDomain" ? { recipient: `*@${matcher.value}` } : matcher.kind === "recipientDid" ? { recipient: matcher.value } : {},
8721
- expiresAt: record2.expiresAt,
8722
- revoked: record2.revokedAt !== void 0,
7775
+ expiresAt: record.expiresAt,
7776
+ revoked: record.revokedAt !== void 0,
8723
7777
  ...revealedLink === void 0 ? {} : { link: revealedLink }
8724
7778
  };
8725
7779
  }
8726
7780
  async function listShares(storage) {
8727
7781
  const records = await storage.list();
8728
- return records.map((record2) => redactRecord(record2, false));
7782
+ return records.map((record) => redactRecord(record, false));
8729
7783
  }
8730
7784
  async function showShare(input) {
8731
- const record2 = await input.storage.get(input.shareId);
8732
- if (record2 === void 0) throw new Error("share not found");
8733
- return redactRecord(record2, input.revealLink === true, input.link);
8734
- }
8735
-
8736
- // src/legacy.ts
8737
- function isLegacyShareLink(value) {
8738
- return value.startsWith("tc1:");
8739
- }
8740
- async function receiveLegacyShare(link, reader) {
8741
- if (!isLegacyShareLink(link)) throw new Error("legacy link must use tc1:");
8742
- return reader.read(link);
8743
- }
8744
- async function migrateShare(input) {
8745
- const value = await receiveLegacyShare(input.link, input.reader);
8746
- return { protocol: "tinycloud-share", version: 1, legacy: true, value, migrated: await input.publish(value) };
7785
+ const record = await input.storage.get(input.shareId);
7786
+ if (record === void 0) throw new Error("share not found");
7787
+ return redactRecord(record, input.revealLink === true, input.link);
8747
7788
  }
8748
7789
 
8749
7790
  // src/recipient.ts
8750
- import { ed25519 as ed255198, x25519 } from "@noble/curves/ed25519";
7791
+ import { ed25519 as ed255195, x25519 } from "@noble/curves/ed25519";
8751
7792
  import { sha256 as sha2566 } from "@noble/hashes/sha256";
8752
7793
 
8753
7794
  // src/protocol.ts
@@ -8923,6 +7964,15 @@ function verifyV3PolicyAuthorization(input) {
8923
7964
  function hex3(value) {
8924
7965
  return [...value].map((byte) => byte.toString(16).padStart(2, "0")).join("");
8925
7966
  }
7967
+ function matchesInitialCiphertextDigest(bytes3, expected) {
7968
+ if (typeof expected !== "string" || !LOWER_SHA256_HEX.test(expected)) return false;
7969
+ const actual = sha2566(bytes3);
7970
+ let mismatch = 0;
7971
+ for (let index = 0; index < actual.length; index += 1) {
7972
+ mismatch |= actual[index] ^ Number.parseInt(expected.slice(index * 2, index * 2 + 2), 16);
7973
+ }
7974
+ return mismatch === 0;
7975
+ }
8926
7976
  function canonicalHashHex(value) {
8927
7977
  return hex3(sha2566(new TextEncoder().encode(canonicalize(value))));
8928
7978
  }
@@ -8949,12 +7999,12 @@ function parseV3InlineEncryptedEnvelope(bytes3, expected) {
8949
7999
  throw new Error("encrypted content envelope is malformed");
8950
8000
  }
8951
8001
  if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error("encrypted content envelope is malformed");
8952
- const record2 = value;
8002
+ const record = value;
8953
8003
  const allowed = ["v", "networkId", "alg", "keyVersion", "encryptedSymmetricKey", "encryptedSymmetricKeyHash", "ciphertext", "metadata"];
8954
- if (Object.keys(record2).some((key) => !allowed.includes(key)) || record2.v !== 1 || record2.networkId !== expected.encryptionNetwork || record2.alg !== "x25519-aes256gcm/v1" || record2.keyVersion !== expected.contentSource.keyVersion || typeof record2.encryptedSymmetricKey !== "string" || typeof record2.encryptedSymmetricKeyHash !== "string" || record2.encryptedSymmetricKeyHash !== expected.contentSource.encryptedSymmetricKeyDigestHex || record2.encryptedSymmetricKeyHash !== canonicalHashHex(record2.encryptedSymmetricKey) || typeof record2.ciphertext !== "string" || record2.metadata !== void 0 && (record2.metadata === null || typeof record2.metadata !== "object" || Array.isArray(record2.metadata) || Object.values(record2.metadata).some((entry) => typeof entry !== "string"))) {
8004
+ if (Object.keys(record).some((key) => !allowed.includes(key)) || record.v !== 1 || record.networkId !== expected.encryptionNetwork || record.alg !== "x25519-aes256gcm/v1" || record.keyVersion !== expected.contentSource.keyVersion || typeof record.encryptedSymmetricKey !== "string" || typeof record.encryptedSymmetricKeyHash !== "string" || record.encryptedSymmetricKeyHash !== expected.contentSource.encryptedSymmetricKeyDigestHex || record.encryptedSymmetricKeyHash !== canonicalHashHex(record.encryptedSymmetricKey) || typeof record.ciphertext !== "string" || record.metadata !== void 0 && (record.metadata === null || typeof record.metadata !== "object" || Array.isArray(record.metadata) || Object.values(record.metadata).some((entry) => typeof entry !== "string"))) {
8955
8005
  throw new Error("encrypted content envelope binding is invalid");
8956
8006
  }
8957
- return record2;
8007
+ return record;
8958
8008
  }
8959
8009
  var ShareRecipientClient = class {
8960
8010
  constructor(options) {
@@ -9066,6 +8116,7 @@ var ShareRecipientClient = class {
9066
8116
  const envelope = this.options.envelope;
9067
8117
  const signer = this.nativeSigner ?? this.signer;
9068
8118
  if (envelope.version !== 3 || this.session === void 0 || this.v3Authorization === void 0 || this.v3NodeAudience === void 0 || signer === void 0) throw new Error("v3 policy session signer is required");
8119
+ if (!matchesInitialCiphertextDigest(bytes3, envelope.contentSource.initialCiphertextDigestHex)) throw new Error("v3 owner KV ciphertext digest mismatch");
9069
8120
  const encrypted = parseV3InlineEncryptedEnvelope(bytes3, envelope);
9070
8121
  const receiverPrivateKey = crypto.getRandomValues(new Uint8Array(32));
9071
8122
  const receiverPublicKey = toBase64(x25519.getPublicKey(receiverPrivateKey));
@@ -9095,7 +8146,7 @@ var ShareRecipientClient = class {
9095
8146
  const unsigned = { ...value };
9096
8147
  delete unsigned.nodeSignature;
9097
8148
  const signature = fromBase64(value.nodeSignature, "v3 decrypt response signature");
9098
- if (signature.length !== 64 || !ed255198.verify(signature, new TextEncoder().encode(canonicalize(unsigned)), ed25519PublicKeyFromDidKey(body.targetNode), { zip215: false })) throw new Error("v3 decrypt response signature is invalid");
8149
+ if (signature.length !== 64 || !ed255195.verify(signature, new TextEncoder().encode(canonicalize(unsigned)), ed25519PublicKeyFromDidKey(body.targetNode), { zip215: false })) throw new Error("v3 decrypt response signature is invalid");
9099
8150
  const wrapped = fromBase64(value.wrappedKey, "v3 wrapped content key");
9100
8151
  if (wrapped.length < 61 || wrapped[32] !== 1) throw new Error("v3 wrapped content key is malformed");
9101
8152
  const shared = x25519.getSharedSecret(receiverPrivateKey, wrapped.slice(0, 32));
@@ -9182,10 +8233,10 @@ function createAddressedAuthorization(input) {
9182
8233
  const envelopeAction = nativeAction("read");
9183
8234
  if (response.type !== "TinyCloudShareInvokeResponse" || response.version !== 2 || response.action !== envelopeAction || response.resource !== envelope.resource.path || typeof response.bodyDigest !== "string" || response.bodyDigest !== value.bodyDigest || response.bodyDigest !== await digestBytes2(value.bytes)) return false;
9184
8235
  const detached = object2(wrapper.detached, "share read detached proof");
9185
- if (detached.alg !== "EdDSA" || detached.kid !== input.trustedNode.invitationKid) return false;
8236
+ if (input.trustedNode === void 0 || detached.alg !== "EdDSA" || detached.kid !== input.trustedNode.invitationKid) return false;
9186
8237
  const unsigned = { ...response };
9187
8238
  delete unsigned.proof;
9188
- return ed255198.verify(bytes2(detached.signature, "share read signature"), new TextEncoder().encode(`${SHARE_V2_PROTOCOL.readResponseDomain}${canonicalize(unsigned)}`), trustedPublicKey(input.trustedNode));
8239
+ return ed255195.verify(bytes2(detached.signature, "share read signature"), new TextEncoder().encode(`${SHARE_V2_PROTOCOL.readResponseDomain}${canonicalize(unsigned)}`), trustedPublicKey(input.trustedNode));
9189
8240
  } catch {
9190
8241
  return false;
9191
8242
  }
@@ -9217,6 +8268,37 @@ async function claimShare(input) {
9217
8268
  async function resumeShareAuthorization(input) {
9218
8269
  return authorizeShare(input);
9219
8270
  }
8271
+
8272
+ // src/native.ts
8273
+ var NATIVE_SHARE_FRAGMENT_PARAMETER = "tc1";
8274
+ function canonicalViewerUrl(viewerOrigin) {
8275
+ const url = new URL(viewerOrigin);
8276
+ if (url.protocol !== "https:" || url.origin !== viewerOrigin || url.pathname !== "/" || url.search || url.hash) throw new TypeError("viewer origin must be a canonical HTTPS origin");
8277
+ url.pathname = "/viewer";
8278
+ return url;
8279
+ }
8280
+ async function createNativeShare(sharing, input) {
8281
+ if (!input.path || input.path.startsWith("/") || input.path.endsWith("/") || input.path.includes("..") || input.path.includes("//")) throw new TypeError("native share path must be one canonical TinyCloud KV key");
8282
+ const generated = await sharing.generate({ path: input.path, actions: ["tinycloud.kv/get"], expiry: input.expiresAt });
8283
+ if (generated.ok !== true) throw new Error(typeof generated.error?.message === "string" ? generated.error.message : "TinyCloud sharing service rejected delegation generation");
8284
+ if (typeof generated.data?.token !== "string" || typeof generated.data.delegation?.cid !== "string" || !(generated.data.expiresAt instanceof Date)) throw new Error("TinyCloud sharing service returned incomplete delegation metadata");
8285
+ const delegation = sharing.decodeLink(generated.data.token);
8286
+ if (typeof delegation.spaceId !== "string" || delegation.spaceId.length === 0 || typeof delegation.path !== "string" || delegation.path.length === 0) throw new Error("TinyCloud sharing service returned a non-canonical delegation authority");
8287
+ const url = canonicalViewerUrl(input.viewerOrigin);
8288
+ url.hash = `${NATIVE_SHARE_FRAGMENT_PARAMETER}=${encodeURIComponent(generated.data.token)}`;
8289
+ return { url: url.toString(), delegationCid: generated.data.delegation.cid, expiresAt: generated.data.expiresAt, spaceId: delegation.spaceId };
8290
+ }
8291
+ function parseNativeShareUrl(value) {
8292
+ const url = new URL(value);
8293
+ if (url.search || url.pathname !== "/viewer") throw new TypeError("native shares must carry tc1 only in the /viewer URL fragment");
8294
+ const fragment = new URLSearchParams(url.hash.slice(1));
8295
+ const token = fragment.get(NATIVE_SHARE_FRAGMENT_PARAMETER);
8296
+ if (!token || fragment.size !== 1) throw new TypeError("missing native share fragment");
8297
+ return token;
8298
+ }
8299
+ async function openNativeShare(sharing, link) {
8300
+ return sharing.receive(parseNativeShareUrl(link), { autoSubdelegate: false, useSessionKey: false });
8301
+ }
9220
8302
  export {
9221
8303
  DEFAULT_MAX_CONTENT_BLOB_BYTES,
9222
8304
  DEFAULT_MAX_SEALED_BLOB_BYTES,
@@ -9227,12 +8309,11 @@ export {
9227
8309
  MemorySenderShareKeyStorage,
9228
8310
  MemorySenderShareRecordStorage,
9229
8311
  MemoryShareCache,
8312
+ NATIVE_SHARE_FRAGMENT_PARAMETER,
9230
8313
  OWNER_SHARE_REGISTRATION_DOMAIN,
9231
8314
  SHARE_CONTENT_LIMIT,
9232
- SHARE_DELIVERY_AUTHORIZATION_DOMAIN,
9233
8315
  SHARE_PUBLISH_RESULT_VERSION,
9234
8316
  SHARE_RESULT_VERSION,
9235
- SHARE_SEALED_OVERHEAD,
9236
8317
  SHARE_V2_PROTOCOL,
9237
8318
  SenderShareStore,
9238
8319
  ShareNotifyError,
@@ -9248,23 +8329,21 @@ export {
9248
8329
  computeOwnerShareRegistrationCid,
9249
8330
  createAddressedAuthorization,
9250
8331
  createDelegatedShareKey,
8332
+ createNativeShare,
9251
8333
  createPolicyEnforcementDelegation,
9252
- createRegisteredPolicyAuthority,
9253
8334
  createShareV2HolderBindingArtifact,
9254
8335
  fromBase64Url,
9255
8336
  historyRecordForPublishedShare,
9256
8337
  inspectShare,
9257
- isLegacyShareLink,
9258
8338
  listShares,
9259
- migrateShare,
9260
8339
  normalizeShareTarget,
9261
8340
  notifyShare,
8341
+ openNativeShare,
9262
8342
  parseAddressedEnvelope,
8343
+ parseNativeShareUrl,
9263
8344
  publishAddressedShare,
9264
8345
  publishPolicyShare,
9265
- publishShare,
9266
8346
  publishTargetShare,
9267
- receiveLegacyShare,
9268
8347
  receiveShare,
9269
8348
  recipientMatchesShareRecord,
9270
8349
  redactPublishedShare,
@@ -9273,14 +8352,11 @@ export {
9273
8352
  restoreDelegatedShareKey,
9274
8353
  resumeShareAuthorization,
9275
8354
  revokeShare,
9276
- shareDeliveryTrustedKid,
9277
8355
  showShare,
9278
8356
  targetAuthorizationMethod,
9279
8357
  toBase64Url,
9280
8358
  toShareErrorInfo,
9281
8359
  validateOwnerSharePolicyRegistration,
9282
- validateOwnerSharePolicyRegistrationBytes,
9283
- validateShareDeliveryAuthorizationBytes,
9284
- verifyBearerEnvelope
8360
+ validateOwnerSharePolicyRegistrationBytes
9285
8361
  };
9286
8362
  //# sourceMappingURL=index.js.map