@tinycloud/share-sdk 1.3.0-beta.0 → 1.4.0-beta.0
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/README.md +6 -0
- package/dist/{addressed-publish-Db-6rqkA.d.cts → addressed-publish-BP6nsgRq.d.cts} +58 -2
- package/dist/{addressed-publish-CAdGCqiX.d.ts → addressed-publish-CpkwV-Au.d.ts} +58 -2
- package/dist/artifact.cjs +2 -2
- package/dist/artifact.cjs.map +1 -1
- package/dist/artifact.js +2 -2
- package/dist/artifact.js.map +1 -1
- package/dist/{history-ZJFHV-IU.d.cts → history-Bg25JIsi.d.cts} +1 -1
- package/dist/{history-q0ALXTv7.d.ts → history-kwJxrZgq.d.ts} +1 -1
- package/dist/index.cjs +304 -154
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +290 -154
- package/dist/index.js.map +1 -1
- package/dist/{owner-policy-CRFeEC98.d.cts → owner-policy-aPmXIR3e.d.cts} +1 -1
- package/dist/{owner-policy-CRFeEC98.d.ts → owner-policy-aPmXIR3e.d.ts} +1 -1
- package/dist/share-index.cjs +47 -87
- package/dist/share-index.cjs.map +1 -1
- package/dist/share-index.d.cts +28 -30
- package/dist/share-index.d.ts +28 -30
- package/dist/share-index.js +46 -86
- package/dist/share-index.js.map +1 -1
- package/dist/share-root.cjs +2 -2
- package/dist/share-root.cjs.map +1 -1
- package/dist/share-root.d.cts +2 -2
- package/dist/share-root.d.ts +2 -2
- package/dist/share-root.js +2 -2
- package/dist/share-root.js.map +1 -1
- package/dist/{web-recipient-entry-D9IJEyRA.d.cts → web-recipient-entry-CiUzdSQs.d.cts} +9 -1
- package/dist/{web-recipient-entry-CKA_vCi5.d.ts → web-recipient-entry-DVyTQBl0.d.ts} +9 -1
- package/dist/web-recipient.cjs +76 -105
- package/dist/web-recipient.cjs.map +1 -1
- package/dist/web-recipient.d.cts +1 -1
- package/dist/web-recipient.d.ts +1 -1
- package/dist/web-recipient.js +66 -105
- package/dist/web-recipient.js.map +1 -1
- package/package.json +2 -2
package/dist/web-recipient.d.cts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { f as ShareMetadata, p as ShareRecipientClient, s as inspectShare } from './web-recipient-entry-
|
|
1
|
+
export { f as ShareMetadata, p as ShareRecipientClient, s as inspectShare } from './web-recipient-entry-CiUzdSQs.cjs';
|
|
2
2
|
import '@tinycloud/share-envelope';
|
|
3
3
|
import './authorization-CmP1skO8.cjs';
|
package/dist/web-recipient.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { f as ShareMetadata, p as ShareRecipientClient, s as inspectShare } from './web-recipient-entry-
|
|
1
|
+
export { f as ShareMetadata, p as ShareRecipientClient, s as inspectShare } from './web-recipient-entry-DVyTQBl0.js';
|
|
2
2
|
import '@tinycloud/share-envelope';
|
|
3
3
|
import './authorization-CmP1skO8.js';
|
package/dist/web-recipient.js
CHANGED
|
@@ -4921,7 +4921,8 @@ function isCanonicalRawCid(cidString) {
|
|
|
4921
4921
|
} catch {
|
|
4922
4922
|
return false;
|
|
4923
4923
|
}
|
|
4924
|
-
return cid.version === 1 && cid.code === code && cid.multihash.code === SHA256_CODE &&
|
|
4924
|
+
return cid.version === 1 && cid.code === code && cid.multihash.code === SHA256_CODE && // `toString` of a parsed CID gives back the string it was parsed from; a fresh CID is encoded again.
|
|
4925
|
+
CID.createV1(cid.code, cid.multihash).toString() === cidString;
|
|
4925
4926
|
}
|
|
4926
4927
|
var base64 = rfc4648({
|
|
4927
4928
|
prefix: "m",
|
|
@@ -5378,51 +5379,7 @@ function validateV3Invariants(value, ctx) {
|
|
|
5378
5379
|
var unsignedShareEnvelopeV3Schema = unsignedShareEnvelopeV3BaseSchema.superRefine(validateV3Invariants);
|
|
5379
5380
|
var shareEnvelopeV3Schema = unsignedShareEnvelopeV3BaseSchema.extend({ signature: signatureSchema }).strict().superRefine(validateV3Invariants);
|
|
5380
5381
|
var ENVELOPE_AAD_LABEL = "tinycloud-share-envelope-v1";
|
|
5381
|
-
var SEALED_BLOB_VERSION = 1;
|
|
5382
5382
|
var AAD = utf8Bytes(ENVELOPE_AAD_LABEL);
|
|
5383
|
-
var KEY_LENGTH = 32;
|
|
5384
|
-
var NONCE_LENGTH = 12;
|
|
5385
|
-
var TAG_LENGTH = 16;
|
|
5386
|
-
var HEADER_LENGTH = 1;
|
|
5387
|
-
function assertKey(key32) {
|
|
5388
|
-
if (key32.length !== KEY_LENGTH) {
|
|
5389
|
-
throw new TypeError(`key must be ${KEY_LENGTH} bytes, got ${key32.length}`);
|
|
5390
|
-
}
|
|
5391
|
-
}
|
|
5392
|
-
async function importAesKey(key32, usage) {
|
|
5393
|
-
assertKey(key32);
|
|
5394
|
-
return globalThis.crypto.subtle.importKey(
|
|
5395
|
-
"raw",
|
|
5396
|
-
key32,
|
|
5397
|
-
"AES-GCM",
|
|
5398
|
-
false,
|
|
5399
|
-
[usage]
|
|
5400
|
-
);
|
|
5401
|
-
}
|
|
5402
|
-
async function open(blob, key32) {
|
|
5403
|
-
if (blob.length < HEADER_LENGTH + NONCE_LENGTH + TAG_LENGTH) {
|
|
5404
|
-
throw new TypeError(`sealed blob too short: ${blob.length} bytes`);
|
|
5405
|
-
}
|
|
5406
|
-
if (blob[0] !== SEALED_BLOB_VERSION) {
|
|
5407
|
-
throw new TypeError(`unknown sealed blob version: ${blob[0]}`);
|
|
5408
|
-
}
|
|
5409
|
-
const nonce = blob.subarray(HEADER_LENGTH, HEADER_LENGTH + NONCE_LENGTH);
|
|
5410
|
-
const ciphertext = blob.subarray(HEADER_LENGTH + NONCE_LENGTH);
|
|
5411
|
-
return decryptEnvelope(nonce, ciphertext, key32);
|
|
5412
|
-
}
|
|
5413
|
-
async function decryptEnvelope(nonce, ciphertext, key32) {
|
|
5414
|
-
if (nonce.length !== NONCE_LENGTH) {
|
|
5415
|
-
throw new TypeError(`nonce must be ${NONCE_LENGTH} bytes, got ${nonce.length}`);
|
|
5416
|
-
}
|
|
5417
|
-
const key = await importAesKey(key32, "decrypt");
|
|
5418
|
-
return new Uint8Array(
|
|
5419
|
-
await globalThis.crypto.subtle.decrypt(
|
|
5420
|
-
{ name: "AES-GCM", iv: nonce, additionalData: AAD },
|
|
5421
|
-
key,
|
|
5422
|
-
ciphertext
|
|
5423
|
-
)
|
|
5424
|
-
);
|
|
5425
|
-
}
|
|
5426
5383
|
function isPlainRecord(value) {
|
|
5427
5384
|
const proto = Object.getPrototypeOf(value);
|
|
5428
5385
|
return proto === Object.prototype || proto === null;
|
|
@@ -5654,56 +5611,6 @@ function base32Lower(bytes) {
|
|
|
5654
5611
|
if (bits > 0) output += alphabet[buffer << 5 - bits & 31];
|
|
5655
5612
|
return output;
|
|
5656
5613
|
}
|
|
5657
|
-
var KEY_LENGTH2 = 32;
|
|
5658
|
-
var MAX_INLINE_BYTES = 256 * 1024;
|
|
5659
|
-
function assertCanonicalCid(cidString) {
|
|
5660
|
-
const cid = CID.parse(cidString);
|
|
5661
|
-
if (cid.version !== 1 || cid.code !== code || cid.multihash.code !== 18 || // sha2-256 (0x12) only, at the link layer too
|
|
5662
|
-
cid.toString() !== cidString) {
|
|
5663
|
-
throw new TypeError(`not a canonical CIDv1 raw sha2-256 base32 CID: ${cidString}`);
|
|
5664
|
-
}
|
|
5665
|
-
}
|
|
5666
|
-
async function parseSealedInlineShareUrl(url, options = {}) {
|
|
5667
|
-
const parsed = new URL(url);
|
|
5668
|
-
if (parsed.protocol !== "https:" || parsed.username !== "" || parsed.password !== "") throw new TypeError("sealed inline share URL must be canonical HTTPS without userinfo");
|
|
5669
|
-
if (!isCanonicalHttpsOrigin(parsed.origin) || options.expectedOrigin !== void 0 && parsed.origin !== options.expectedOrigin) throw new TypeError("sealed inline share URL origin is not trusted");
|
|
5670
|
-
if (parsed.pathname !== "/s/inline" || parsed.search !== "") throw new TypeError("not a Node sealed-inline share URL");
|
|
5671
|
-
if (url !== `${parsed.origin}/s/inline${parsed.hash}`) throw new TypeError("sealed inline share URL is not lexically canonical");
|
|
5672
|
-
const prefix = "#v=2&p=";
|
|
5673
|
-
if (!parsed.hash.startsWith(prefix)) throw new TypeError("sealed inline URL is missing its canonical fragment");
|
|
5674
|
-
const encoded = parsed.hash.slice(prefix.length);
|
|
5675
|
-
if (encoded.length === 0 || parsed.hash !== `${prefix}${encoded}`) throw new TypeError("sealed inline URL fragment is not canonical");
|
|
5676
|
-
let payloadBytes;
|
|
5677
|
-
try {
|
|
5678
|
-
payloadBytes = fromBase64Url(encoded);
|
|
5679
|
-
} catch {
|
|
5680
|
-
throw new TypeError("sealed inline payload is not canonical base64url");
|
|
5681
|
-
}
|
|
5682
|
-
if (payloadBytes.byteLength === 0 || payloadBytes.byteLength > MAX_INLINE_BYTES * 2) throw new TypeError("sealed inline payload is too large");
|
|
5683
|
-
const payloadText = new TextDecoder("utf-8", { fatal: true }).decode(payloadBytes);
|
|
5684
|
-
let value;
|
|
5685
|
-
try {
|
|
5686
|
-
value = JSON.parse(payloadText);
|
|
5687
|
-
} catch {
|
|
5688
|
-
throw new TypeError("sealed inline payload is not valid JSON");
|
|
5689
|
-
}
|
|
5690
|
-
if (typeof value !== "object" || value === null || Array.isArray(value) || canonicalize(value) !== payloadText) throw new TypeError("sealed inline payload is not canonical JSON");
|
|
5691
|
-
const record = value;
|
|
5692
|
-
if (Object.keys(record).length !== 4 || record.v !== 2 || typeof record.c !== "string" || typeof record.cid !== "string" || typeof record.k !== "string") throw new TypeError("sealed inline payload has invalid fields");
|
|
5693
|
-
let ciphertext;
|
|
5694
|
-
let key32;
|
|
5695
|
-
try {
|
|
5696
|
-
ciphertext = fromBase64Url(record.c);
|
|
5697
|
-
key32 = fromBase64Url(record.k);
|
|
5698
|
-
} catch {
|
|
5699
|
-
throw new TypeError("sealed inline payload has invalid base64url material");
|
|
5700
|
-
}
|
|
5701
|
-
if (ciphertext.byteLength === 0 || ciphertext.byteLength > MAX_INLINE_BYTES) throw new TypeError("sealed inline ciphertext is outside the allowed size");
|
|
5702
|
-
if (key32.byteLength !== KEY_LENGTH2) throw new TypeError("sealed inline key must be 32 bytes");
|
|
5703
|
-
assertCanonicalCid(record.cid);
|
|
5704
|
-
if (await computeCid(ciphertext) !== record.cid) throw new TypeError("sealed inline ciphertext does not match its CID");
|
|
5705
|
-
return { kind: "inline", ciphertextCid: record.cid, ciphertext, key32 };
|
|
5706
|
-
}
|
|
5707
5614
|
|
|
5708
5615
|
// src/artifact-core.ts
|
|
5709
5616
|
var ARTIFACT_ROOT = "artifacts";
|
|
@@ -5818,26 +5725,80 @@ function metadataFor(envelope, origin) {
|
|
|
5818
5725
|
...envelope.artifactPin === void 0 ? {} : { artifact: { id: envelope.artifactPin.artifactId, version: envelope.artifactPin.version } }
|
|
5819
5726
|
};
|
|
5820
5727
|
}
|
|
5728
|
+
function receiveErrorFor(error) {
|
|
5729
|
+
switch (error.code) {
|
|
5730
|
+
case "origin_not_trusted":
|
|
5731
|
+
return new ShareReceiveError("origin-mismatch", "share link origin is not trusted");
|
|
5732
|
+
case "body_over_cap":
|
|
5733
|
+
case "response_too_large":
|
|
5734
|
+
return new ShareReceiveError("max-bytes-exceeded", "sealed policy envelope exceeds the size limit");
|
|
5735
|
+
case "body_hash_mismatch":
|
|
5736
|
+
case "link_cid_mismatch":
|
|
5737
|
+
return new ShareReceiveError("cid-mismatch", "sealed envelope bytes do not match the link CID");
|
|
5738
|
+
case "body_open_failed":
|
|
5739
|
+
return new ShareReceiveError("decrypt-failed", "share envelope could not be opened");
|
|
5740
|
+
case "gone":
|
|
5741
|
+
case "expired":
|
|
5742
|
+
return new ShareReceiveError("expired", "share link has expired, was deleted, or never existed");
|
|
5743
|
+
case "bad_signature":
|
|
5744
|
+
case "issuer_binding_mismatch":
|
|
5745
|
+
return new ShareReceiveError("signature-invalid", "share link signature is invalid");
|
|
5746
|
+
case "service_refused":
|
|
5747
|
+
case "service_unavailable":
|
|
5748
|
+
case "invalid_response":
|
|
5749
|
+
return new ShareReceiveError("fetch-failed", "share link could not be fetched");
|
|
5750
|
+
case "not_a_url":
|
|
5751
|
+
case "not_https":
|
|
5752
|
+
case "userinfo":
|
|
5753
|
+
case "not_canonical":
|
|
5754
|
+
case "query_present":
|
|
5755
|
+
case "removed_version":
|
|
5756
|
+
case "path_grammar":
|
|
5757
|
+
case "fragment_grammar":
|
|
5758
|
+
case "key_noncanonical":
|
|
5759
|
+
case "cid_grammar":
|
|
5760
|
+
case "record_encoding":
|
|
5761
|
+
case "record_json":
|
|
5762
|
+
case "record_not_canonical":
|
|
5763
|
+
case "type_path_mismatch":
|
|
5764
|
+
case "full_non_share":
|
|
5765
|
+
return new ShareReceiveError("invalid-link", "share link format is invalid");
|
|
5766
|
+
default:
|
|
5767
|
+
return new ShareReceiveError("envelope-invalid", "share envelope is invalid");
|
|
5768
|
+
}
|
|
5769
|
+
}
|
|
5821
5770
|
async function resolvePolicyShare(link, options) {
|
|
5822
5771
|
options.signal?.throwIfAborted();
|
|
5823
5772
|
let url;
|
|
5824
|
-
let parsed;
|
|
5825
5773
|
try {
|
|
5826
5774
|
url = new URL(link);
|
|
5827
|
-
parsed = await parseSealedInlineShareUrl(link, { ...options.expectedOrigin === void 0 ? {} : { expectedOrigin: options.expectedOrigin } });
|
|
5828
5775
|
} catch {
|
|
5829
5776
|
throw new ShareReceiveError("invalid-link", "share link format is invalid");
|
|
5830
5777
|
}
|
|
5778
|
+
const links = await import("@tinycloud/share-envelope/links");
|
|
5779
|
+
let resolved;
|
|
5780
|
+
try {
|
|
5781
|
+
resolved = await links.resolveLink(link, {
|
|
5782
|
+
trustedOrigins: [options.expectedOrigin ?? url.origin],
|
|
5783
|
+
apiOrigin: options.apiOrigin ?? links.DEFAULT_LINKS_API_ORIGIN,
|
|
5784
|
+
...options.fetch === void 0 ? {} : { fetch: options.fetch },
|
|
5785
|
+
...options.signal === void 0 ? {} : { signal: options.signal },
|
|
5786
|
+
now: options.now?.() ?? Date.now()
|
|
5787
|
+
});
|
|
5788
|
+
} catch (error) {
|
|
5789
|
+
options.signal?.throwIfAborted();
|
|
5790
|
+
throw error instanceof links.LinkError ? receiveErrorFor(error) : error;
|
|
5791
|
+
}
|
|
5792
|
+
if (resolved.type !== "share") throw new ShareReceiveError("invalid-link", "share link is not a share");
|
|
5831
5793
|
const limit = options.maxSealedBlobBytes ?? DEFAULT_MAX_SEALED_BLOB_BYTES;
|
|
5832
|
-
if (!Number.isSafeInteger(limit) || limit <= 0 ||
|
|
5833
|
-
|
|
5794
|
+
if (!Number.isSafeInteger(limit) || limit <= 0 || resolved.body.byteLength > limit) throw new ShareReceiveError("max-bytes-exceeded", "sealed policy envelope exceeds the configured byte limit");
|
|
5795
|
+
const cid = resolved.header.bodyCid;
|
|
5834
5796
|
options.signal?.throwIfAborted();
|
|
5835
5797
|
let envelope;
|
|
5836
5798
|
try {
|
|
5837
|
-
const encoded = new TextDecoder("utf-8", { fatal: true }).decode(
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
envelope = shareEnvelopeV3Schema.parse(value);
|
|
5799
|
+
const encoded = new TextDecoder("utf-8", { fatal: true }).decode(resolved.plaintext);
|
|
5800
|
+
if (canonicalize(resolved.value) !== encoded) throw new Error("non-canonical envelope");
|
|
5801
|
+
envelope = shareEnvelopeV3Schema.parse(resolved.value);
|
|
5841
5802
|
} catch {
|
|
5842
5803
|
throw new ShareReceiveError("envelope-invalid", "share envelope is invalid");
|
|
5843
5804
|
}
|
|
@@ -5856,8 +5817,8 @@ async function resolvePolicyShare(link, options) {
|
|
|
5856
5817
|
const expiry = Date.parse(envelope.expiry);
|
|
5857
5818
|
if (!Number.isFinite(expiry)) throw new ShareReceiveError("envelope-invalid", "share expiry is invalid");
|
|
5858
5819
|
if (expiry <= (options.now?.() ?? Date.now())) throw new ShareReceiveError("expired", "share has expired", { expiresAt: envelope.expiry });
|
|
5859
|
-
options.onResolvedAddressedEnvelope?.(envelope,
|
|
5860
|
-
return { envelope, origin: url.origin, cid
|
|
5820
|
+
options.onResolvedAddressedEnvelope?.(envelope, cid);
|
|
5821
|
+
return { envelope, origin: url.origin, cid };
|
|
5861
5822
|
}
|
|
5862
5823
|
async function inspectShare(link, options = {}) {
|
|
5863
5824
|
const resolved = await resolvePolicyShare(link, options);
|