@tinycloud/share-sdk 1.0.0-beta.6 → 1.0.0-beta.8

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.d.cts CHANGED
@@ -1,6 +1,10 @@
1
1
  import { ShareEnvelopeV2, ShareEnvelopeV3, UnifiedPolicyCapability, ShareAction, UnifiedContentSource, PolicyCredentialRequirementV1 } from '@tinycloud/share-envelope';
2
2
  export { CheckBearerDelegationOptions, DelegationCheckResult, canonicalize, checkBearerDelegation, fromBase64Url, requiredResourceUri, resourceUriCovers, toBase64Url } from '@tinycloud/share-envelope';
3
3
 
4
+ declare function hasUnsafeFilenameCodePoint(value: string): boolean;
5
+ /** A single display/download filename, never a path. Returns the NFC form. */
6
+ declare function canonicalShareFilename(value: string): string;
7
+
4
8
  /** The only interactive boundary exposed by the headless SDK. */
5
9
  type ShareAuthorizationMethod = "openkey-device" | "email-claim" | "email-otp";
6
10
  interface ShareAuthorizationRequired {
@@ -590,7 +594,30 @@ interface AddressedSharePublishOptions {
590
594
  readonly onDeliveryMaterial?: (input: PublishedShareDeliveryMaterial) => void;
591
595
  readonly authority: AddressedPublishAuthority;
592
596
  }
593
- declare function publishAddressedShare(options: AddressedSharePublishOptions): Promise<PublishedShare>;
597
+ type MailboxTarget = Extract<ShareTarget, {
598
+ readonly kind: "email" | "emailDomain";
599
+ }>;
600
+ /** The Policy/v2 credential commitment a share addressed to `target` must carry. */
601
+ declare function addressedCredentialRequirement(target: MailboxTarget): PolicyCredentialRequirementV1;
602
+ /** Facts every addressed-share check needs, available before any side effect. */
603
+ type AddressedShareRequest = Pick<AddressedSharePublishOptions, "target" | "actions" | "policyActions" | "filename" | "deliveryEmail">;
604
+ interface PreparedAddressedShare {
605
+ /** The canonical target the envelope's recipient matcher will carry. */
606
+ readonly target: Exclude<ShareTarget, {
607
+ readonly kind: "bearer";
608
+ }>;
609
+ /** Present for mailbox targets: the Policy/v2 commitment to pass to `publishAddressedShare`. */
610
+ readonly credentialRequirement?: PolicyCredentialRequirementV1;
611
+ }
612
+ /**
613
+ * Runs every argument and target check of `publishAddressedShare` that does
614
+ * not depend on stored content, and derives the credential commitment. It has
615
+ * no side effects, so senders call it before uploading content or publishing
616
+ * a location record; a refused share then leaves nothing behind.
617
+ */
618
+ declare function prepareAddressedShare(request: AddressedShareRequest): PreparedAddressedShare;
619
+ /** Canonical application-neutral Policy/v3 addressed publisher shared by browser and CLI. */
620
+ declare function publishAddressedShare(input: AddressedSharePublishOptions): Promise<PublishedShare>;
594
621
 
595
622
  interface ShareCacheKey {
596
623
  readonly cid: string;
@@ -798,7 +825,11 @@ declare class ShareNotifyError extends Error {
798
825
  readonly code: "delivery-failed";
799
826
  constructor(message?: string);
800
827
  }
801
- /** Match a delivery address against the recipient constraint recorded when the share was created. */
828
+ /**
829
+ * Match a delivery address against the recipient constraint recorded when the
830
+ * share was created. Both sides compare in canonical mailbox form, which is
831
+ * the only form the credential issuer accepts.
832
+ */
802
833
  declare function recipientMatchesShareRecord(record: SenderShareRecord, recipient: string): boolean;
803
834
  /**
804
835
  * Delivery is deliberately separate from publication. A failed notification
@@ -1093,4 +1124,4 @@ declare function parseNativeShareUrl(value: string): string;
1093
1124
  /** Receive through the public SDK without creating another transport protocol. */
1094
1125
  declare function openNativeShare(sharing: NativeSharingService, link: string): Promise<unknown>;
1095
1126
 
1096
- export { type AddressedOwnerRootInput, type AddressedOwnerRootReceipt, type AddressedPolicyRegistrationInput, type AddressedPolicyRegistrationReceipt, type AddressedPublishAuthority, type AddressedSharePublishOptions, type CreateOwnerDelegationParams, CredentialInvitationError, type CredentialInvitationErrorCode, type CredentialInvitationReceipt, type CredentialInvitationResult, DEFAULT_MAX_CONTENT_BLOB_BYTES, DEFAULT_MAX_SEALED_BLOB_BYTES, DEFAULT_SHARE_LIFETIME_MS, type DelegatedShareKey, EncryptedSenderShareHistory, type EncryptedShareHistoryStorage, MAX_CONTENT_BYTES, MemoryEncryptedShareHistoryStorage, MemorySenderShareKeyStorage, MemorySenderShareRecordStorage, MemoryShareCache, NATIVE_SHARE_FRAGMENT_PARAMETER, type NativeShareResult, type NativeSharingService, OWNER_SHARE_REGISTRATION_DOMAIN, type OwnerDelegationPermission, type OwnerDelegationReceipt, type OwnerShareAction, type OwnerShareDecryption, type OwnerShareMatcher, type OwnerSharePolicyRegistration, type OwnerSharePolicyRegistrationReceipt, type OwnerSharePolicyV2, type ParsedAddressedEnvelope, type PublishedShare, type PublishedShareDeliveryMaterial, type PublishedShareMetadata, type RegisterOwnerSharePolicyParams, SHARE_CONTENT_LIMIT, SHARE_PUBLISH_RESULT_VERSION, SHARE_RESULT_VERSION, SHARE_V2_PROTOCOL, type SenderShareKeyStorage, type SenderShareRecord, type SenderShareRecordStorage, type SenderShareRevocationScope, SenderShareStore, type ShareAuthorizationAdapter, type ShareAuthorizationDenied, type ShareAuthorizationMethod, type ShareAuthorizationReady, type ShareAuthorizationRequired, type ShareAuthorizationResult, type ShareAuthorizedContent, type ShareCache, type ShareCacheEntry, type ShareCacheKey, type ShareCacheMetadata, type ShareDeliveryAdapter, type ShareErrorCode, type ShareErrorInfo, type ShareFetchOptions, type ShareHistoryView, type ShareInspection, type ShareMetadata, type ShareNodeTrust, ShareNotifyError, type ShareNotifyInput, type ShareNotifyResult, type ShareNotifyState, type SharePolicyChallenge, type SharePolicyPublishAdapter, type SharePolicyRootRevocation, type SharePolicySession, type SharePresentationMaterial, SharePublishError, type SharePublishErrorCode, type SharePublishOptions, type SharePublishTarget, type ShareReceiveAuthorizationRequired, ShareReceiveError, type ShareReceiveOutcome, type ShareReceiveResult, ShareRecipientClient, type ShareRecipientClientOptions, type ShareRevocationAdapter, type ShareRevocationResult, type ShareTarget, type SignedDelegation, type TargetPublishAdapter, type TargetPublishInput, type TargetPublishOutcome, authorizationMethodForTarget, authorizeShare, canonicalOwnerSharePolicy, claimShare, computeOwnerShareRegistrationCid, createAddressedAuthorization, createDelegatedShareKey, createNativeShare, createPolicyEnforcementDelegation, createShareV2HolderBindingArtifact, deliverCredentialInvitation, historyRecordForPublishedShare, inspectShare, listShares, normalizeShareTarget, notifyShare, openNativeShare, parseAddressedEnvelope, parseNativeShareUrl, publishAddressedShare, publishPolicyShare, publishTargetShare, receiveShare, recipientMatchesShareRecord, redactPublishedShare, restoreDelegatedShareKey, resumeShareAuthorization, revokeShare, showShare, targetAuthorizationMethod, toShareErrorInfo, validateOwnerSharePolicyRegistration, validateOwnerSharePolicyRegistrationBytes };
1127
+ export { type AddressedOwnerRootInput, type AddressedOwnerRootReceipt, type AddressedPolicyRegistrationInput, type AddressedPolicyRegistrationReceipt, type AddressedPublishAuthority, type AddressedSharePublishOptions, type AddressedShareRequest, type CreateOwnerDelegationParams, CredentialInvitationError, type CredentialInvitationErrorCode, type CredentialInvitationReceipt, type CredentialInvitationResult, DEFAULT_MAX_CONTENT_BLOB_BYTES, DEFAULT_MAX_SEALED_BLOB_BYTES, DEFAULT_SHARE_LIFETIME_MS, type DelegatedShareKey, EncryptedSenderShareHistory, type EncryptedShareHistoryStorage, MAX_CONTENT_BYTES, MemoryEncryptedShareHistoryStorage, MemorySenderShareKeyStorage, MemorySenderShareRecordStorage, MemoryShareCache, NATIVE_SHARE_FRAGMENT_PARAMETER, type NativeShareResult, type NativeSharingService, OWNER_SHARE_REGISTRATION_DOMAIN, type OwnerDelegationPermission, type OwnerDelegationReceipt, type OwnerShareAction, type OwnerShareDecryption, type OwnerShareMatcher, type OwnerSharePolicyRegistration, type OwnerSharePolicyRegistrationReceipt, type OwnerSharePolicyV2, type ParsedAddressedEnvelope, type PreparedAddressedShare, type PublishedShare, type PublishedShareDeliveryMaterial, type PublishedShareMetadata, type RegisterOwnerSharePolicyParams, SHARE_CONTENT_LIMIT, SHARE_PUBLISH_RESULT_VERSION, SHARE_RESULT_VERSION, SHARE_V2_PROTOCOL, type SenderShareKeyStorage, type SenderShareRecord, type SenderShareRecordStorage, type SenderShareRevocationScope, SenderShareStore, type ShareAuthorizationAdapter, type ShareAuthorizationDenied, type ShareAuthorizationMethod, type ShareAuthorizationReady, type ShareAuthorizationRequired, type ShareAuthorizationResult, type ShareAuthorizedContent, type ShareCache, type ShareCacheEntry, type ShareCacheKey, type ShareCacheMetadata, type ShareDeliveryAdapter, type ShareErrorCode, type ShareErrorInfo, type ShareFetchOptions, type ShareHistoryView, type ShareInspection, type ShareMetadata, type ShareNodeTrust, ShareNotifyError, type ShareNotifyInput, type ShareNotifyResult, type ShareNotifyState, type SharePolicyChallenge, type SharePolicyPublishAdapter, type SharePolicyRootRevocation, type SharePolicySession, type SharePresentationMaterial, SharePublishError, type SharePublishErrorCode, type SharePublishOptions, type SharePublishTarget, type ShareReceiveAuthorizationRequired, ShareReceiveError, type ShareReceiveOutcome, type ShareReceiveResult, ShareRecipientClient, type ShareRecipientClientOptions, type ShareRevocationAdapter, type ShareRevocationResult, type ShareTarget, type SignedDelegation, type TargetPublishAdapter, type TargetPublishInput, type TargetPublishOutcome, addressedCredentialRequirement, authorizationMethodForTarget, authorizeShare, canonicalOwnerSharePolicy, canonicalShareFilename, claimShare, computeOwnerShareRegistrationCid, createAddressedAuthorization, createDelegatedShareKey, createNativeShare, createPolicyEnforcementDelegation, createShareV2HolderBindingArtifact, deliverCredentialInvitation, hasUnsafeFilenameCodePoint, historyRecordForPublishedShare, inspectShare, listShares, normalizeShareTarget, notifyShare, openNativeShare, parseAddressedEnvelope, parseNativeShareUrl, prepareAddressedShare, publishAddressedShare, publishPolicyShare, publishTargetShare, receiveShare, recipientMatchesShareRecord, redactPublishedShare, restoreDelegatedShareKey, resumeShareAuthorization, revokeShare, showShare, targetAuthorizationMethod, toShareErrorInfo, validateOwnerSharePolicyRegistration, validateOwnerSharePolicyRegistrationBytes };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,10 @@
1
1
  import { ShareEnvelopeV2, ShareEnvelopeV3, UnifiedPolicyCapability, ShareAction, UnifiedContentSource, PolicyCredentialRequirementV1 } from '@tinycloud/share-envelope';
2
2
  export { CheckBearerDelegationOptions, DelegationCheckResult, canonicalize, checkBearerDelegation, fromBase64Url, requiredResourceUri, resourceUriCovers, toBase64Url } from '@tinycloud/share-envelope';
3
3
 
4
+ declare function hasUnsafeFilenameCodePoint(value: string): boolean;
5
+ /** A single display/download filename, never a path. Returns the NFC form. */
6
+ declare function canonicalShareFilename(value: string): string;
7
+
4
8
  /** The only interactive boundary exposed by the headless SDK. */
5
9
  type ShareAuthorizationMethod = "openkey-device" | "email-claim" | "email-otp";
6
10
  interface ShareAuthorizationRequired {
@@ -590,7 +594,30 @@ interface AddressedSharePublishOptions {
590
594
  readonly onDeliveryMaterial?: (input: PublishedShareDeliveryMaterial) => void;
591
595
  readonly authority: AddressedPublishAuthority;
592
596
  }
593
- declare function publishAddressedShare(options: AddressedSharePublishOptions): Promise<PublishedShare>;
597
+ type MailboxTarget = Extract<ShareTarget, {
598
+ readonly kind: "email" | "emailDomain";
599
+ }>;
600
+ /** The Policy/v2 credential commitment a share addressed to `target` must carry. */
601
+ declare function addressedCredentialRequirement(target: MailboxTarget): PolicyCredentialRequirementV1;
602
+ /** Facts every addressed-share check needs, available before any side effect. */
603
+ type AddressedShareRequest = Pick<AddressedSharePublishOptions, "target" | "actions" | "policyActions" | "filename" | "deliveryEmail">;
604
+ interface PreparedAddressedShare {
605
+ /** The canonical target the envelope's recipient matcher will carry. */
606
+ readonly target: Exclude<ShareTarget, {
607
+ readonly kind: "bearer";
608
+ }>;
609
+ /** Present for mailbox targets: the Policy/v2 commitment to pass to `publishAddressedShare`. */
610
+ readonly credentialRequirement?: PolicyCredentialRequirementV1;
611
+ }
612
+ /**
613
+ * Runs every argument and target check of `publishAddressedShare` that does
614
+ * not depend on stored content, and derives the credential commitment. It has
615
+ * no side effects, so senders call it before uploading content or publishing
616
+ * a location record; a refused share then leaves nothing behind.
617
+ */
618
+ declare function prepareAddressedShare(request: AddressedShareRequest): PreparedAddressedShare;
619
+ /** Canonical application-neutral Policy/v3 addressed publisher shared by browser and CLI. */
620
+ declare function publishAddressedShare(input: AddressedSharePublishOptions): Promise<PublishedShare>;
594
621
 
595
622
  interface ShareCacheKey {
596
623
  readonly cid: string;
@@ -798,7 +825,11 @@ declare class ShareNotifyError extends Error {
798
825
  readonly code: "delivery-failed";
799
826
  constructor(message?: string);
800
827
  }
801
- /** Match a delivery address against the recipient constraint recorded when the share was created. */
828
+ /**
829
+ * Match a delivery address against the recipient constraint recorded when the
830
+ * share was created. Both sides compare in canonical mailbox form, which is
831
+ * the only form the credential issuer accepts.
832
+ */
802
833
  declare function recipientMatchesShareRecord(record: SenderShareRecord, recipient: string): boolean;
803
834
  /**
804
835
  * Delivery is deliberately separate from publication. A failed notification
@@ -1093,4 +1124,4 @@ declare function parseNativeShareUrl(value: string): string;
1093
1124
  /** Receive through the public SDK without creating another transport protocol. */
1094
1125
  declare function openNativeShare(sharing: NativeSharingService, link: string): Promise<unknown>;
1095
1126
 
1096
- export { type AddressedOwnerRootInput, type AddressedOwnerRootReceipt, type AddressedPolicyRegistrationInput, type AddressedPolicyRegistrationReceipt, type AddressedPublishAuthority, type AddressedSharePublishOptions, type CreateOwnerDelegationParams, CredentialInvitationError, type CredentialInvitationErrorCode, type CredentialInvitationReceipt, type CredentialInvitationResult, DEFAULT_MAX_CONTENT_BLOB_BYTES, DEFAULT_MAX_SEALED_BLOB_BYTES, DEFAULT_SHARE_LIFETIME_MS, type DelegatedShareKey, EncryptedSenderShareHistory, type EncryptedShareHistoryStorage, MAX_CONTENT_BYTES, MemoryEncryptedShareHistoryStorage, MemorySenderShareKeyStorage, MemorySenderShareRecordStorage, MemoryShareCache, NATIVE_SHARE_FRAGMENT_PARAMETER, type NativeShareResult, type NativeSharingService, OWNER_SHARE_REGISTRATION_DOMAIN, type OwnerDelegationPermission, type OwnerDelegationReceipt, type OwnerShareAction, type OwnerShareDecryption, type OwnerShareMatcher, type OwnerSharePolicyRegistration, type OwnerSharePolicyRegistrationReceipt, type OwnerSharePolicyV2, type ParsedAddressedEnvelope, type PublishedShare, type PublishedShareDeliveryMaterial, type PublishedShareMetadata, type RegisterOwnerSharePolicyParams, SHARE_CONTENT_LIMIT, SHARE_PUBLISH_RESULT_VERSION, SHARE_RESULT_VERSION, SHARE_V2_PROTOCOL, type SenderShareKeyStorage, type SenderShareRecord, type SenderShareRecordStorage, type SenderShareRevocationScope, SenderShareStore, type ShareAuthorizationAdapter, type ShareAuthorizationDenied, type ShareAuthorizationMethod, type ShareAuthorizationReady, type ShareAuthorizationRequired, type ShareAuthorizationResult, type ShareAuthorizedContent, type ShareCache, type ShareCacheEntry, type ShareCacheKey, type ShareCacheMetadata, type ShareDeliveryAdapter, type ShareErrorCode, type ShareErrorInfo, type ShareFetchOptions, type ShareHistoryView, type ShareInspection, type ShareMetadata, type ShareNodeTrust, ShareNotifyError, type ShareNotifyInput, type ShareNotifyResult, type ShareNotifyState, type SharePolicyChallenge, type SharePolicyPublishAdapter, type SharePolicyRootRevocation, type SharePolicySession, type SharePresentationMaterial, SharePublishError, type SharePublishErrorCode, type SharePublishOptions, type SharePublishTarget, type ShareReceiveAuthorizationRequired, ShareReceiveError, type ShareReceiveOutcome, type ShareReceiveResult, ShareRecipientClient, type ShareRecipientClientOptions, type ShareRevocationAdapter, type ShareRevocationResult, type ShareTarget, type SignedDelegation, type TargetPublishAdapter, type TargetPublishInput, type TargetPublishOutcome, authorizationMethodForTarget, authorizeShare, canonicalOwnerSharePolicy, claimShare, computeOwnerShareRegistrationCid, createAddressedAuthorization, createDelegatedShareKey, createNativeShare, createPolicyEnforcementDelegation, createShareV2HolderBindingArtifact, deliverCredentialInvitation, historyRecordForPublishedShare, inspectShare, listShares, normalizeShareTarget, notifyShare, openNativeShare, parseAddressedEnvelope, parseNativeShareUrl, publishAddressedShare, publishPolicyShare, publishTargetShare, receiveShare, recipientMatchesShareRecord, redactPublishedShare, restoreDelegatedShareKey, resumeShareAuthorization, revokeShare, showShare, targetAuthorizationMethod, toShareErrorInfo, validateOwnerSharePolicyRegistration, validateOwnerSharePolicyRegistrationBytes };
1127
+ export { type AddressedOwnerRootInput, type AddressedOwnerRootReceipt, type AddressedPolicyRegistrationInput, type AddressedPolicyRegistrationReceipt, type AddressedPublishAuthority, type AddressedSharePublishOptions, type AddressedShareRequest, type CreateOwnerDelegationParams, CredentialInvitationError, type CredentialInvitationErrorCode, type CredentialInvitationReceipt, type CredentialInvitationResult, DEFAULT_MAX_CONTENT_BLOB_BYTES, DEFAULT_MAX_SEALED_BLOB_BYTES, DEFAULT_SHARE_LIFETIME_MS, type DelegatedShareKey, EncryptedSenderShareHistory, type EncryptedShareHistoryStorage, MAX_CONTENT_BYTES, MemoryEncryptedShareHistoryStorage, MemorySenderShareKeyStorage, MemorySenderShareRecordStorage, MemoryShareCache, NATIVE_SHARE_FRAGMENT_PARAMETER, type NativeShareResult, type NativeSharingService, OWNER_SHARE_REGISTRATION_DOMAIN, type OwnerDelegationPermission, type OwnerDelegationReceipt, type OwnerShareAction, type OwnerShareDecryption, type OwnerShareMatcher, type OwnerSharePolicyRegistration, type OwnerSharePolicyRegistrationReceipt, type OwnerSharePolicyV2, type ParsedAddressedEnvelope, type PreparedAddressedShare, type PublishedShare, type PublishedShareDeliveryMaterial, type PublishedShareMetadata, type RegisterOwnerSharePolicyParams, SHARE_CONTENT_LIMIT, SHARE_PUBLISH_RESULT_VERSION, SHARE_RESULT_VERSION, SHARE_V2_PROTOCOL, type SenderShareKeyStorage, type SenderShareRecord, type SenderShareRecordStorage, type SenderShareRevocationScope, SenderShareStore, type ShareAuthorizationAdapter, type ShareAuthorizationDenied, type ShareAuthorizationMethod, type ShareAuthorizationReady, type ShareAuthorizationRequired, type ShareAuthorizationResult, type ShareAuthorizedContent, type ShareCache, type ShareCacheEntry, type ShareCacheKey, type ShareCacheMetadata, type ShareDeliveryAdapter, type ShareErrorCode, type ShareErrorInfo, type ShareFetchOptions, type ShareHistoryView, type ShareInspection, type ShareMetadata, type ShareNodeTrust, ShareNotifyError, type ShareNotifyInput, type ShareNotifyResult, type ShareNotifyState, type SharePolicyChallenge, type SharePolicyPublishAdapter, type SharePolicyRootRevocation, type SharePolicySession, type SharePresentationMaterial, SharePublishError, type SharePublishErrorCode, type SharePublishOptions, type SharePublishTarget, type ShareReceiveAuthorizationRequired, ShareReceiveError, type ShareReceiveOutcome, type ShareReceiveResult, ShareRecipientClient, type ShareRecipientClientOptions, type ShareRevocationAdapter, type ShareRevocationResult, type ShareTarget, type SignedDelegation, type TargetPublishAdapter, type TargetPublishInput, type TargetPublishOutcome, addressedCredentialRequirement, authorizationMethodForTarget, authorizeShare, canonicalOwnerSharePolicy, canonicalShareFilename, claimShare, computeOwnerShareRegistrationCid, createAddressedAuthorization, createDelegatedShareKey, createNativeShare, createPolicyEnforcementDelegation, createShareV2HolderBindingArtifact, deliverCredentialInvitation, hasUnsafeFilenameCodePoint, historyRecordForPublishedShare, inspectShare, listShares, normalizeShareTarget, notifyShare, openNativeShare, parseAddressedEnvelope, parseNativeShareUrl, prepareAddressedShare, publishAddressedShare, publishPolicyShare, publishTargetShare, receiveShare, recipientMatchesShareRecord, redactPublishedShare, restoreDelegatedShareKey, resumeShareAuthorization, revokeShare, showShare, targetAuthorizationMethod, toShareErrorInfo, validateOwnerSharePolicyRegistration, validateOwnerSharePolicyRegistrationBytes };
package/dist/index.js CHANGED
@@ -4,6 +4,19 @@ var __export = (target, all) => {
4
4
  __defProp(target, name, { get: all[name], enumerable: true });
5
5
  };
6
6
 
7
+ // src/filename-policy.ts
8
+ var UNSAFE_FILENAME_CODE_POINT = /[\p{Cc}\p{Cf}\p{Cs}\u2028\u2029]/u;
9
+ function hasUnsafeFilenameCodePoint(value) {
10
+ return UNSAFE_FILENAME_CODE_POINT.test(value);
11
+ }
12
+ function canonicalShareFilename(value) {
13
+ const canonical = value.normalize("NFC");
14
+ if (canonical.length === 0 || canonical === "." || canonical === ".." || canonical.includes("/") || canonical.includes("\\") || hasUnsafeFilenameCodePoint(canonical)) {
15
+ throw new TypeError("share filename is unsafe");
16
+ }
17
+ return canonical;
18
+ }
19
+
7
20
  // ../share-envelope/dist/index.js
8
21
  import { sha256 } from "@noble/hashes/sha2";
9
22
  import { ed25519 } from "@noble/curves/ed25519";
@@ -5685,6 +5698,21 @@ function serialize(value) {
5685
5698
  function canonicalize(value) {
5686
5699
  return serialize(value);
5687
5700
  }
5701
+ var LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
5702
+ function isCanonicalEmailDomain(value) {
5703
+ if (value.length === 0 || value.length > 253) return false;
5704
+ const labels = value.split(".");
5705
+ return labels.length >= 2 && labels.every((label) => LABEL.test(label)) && !/^[0-9]+$/.test(labels.at(-1));
5706
+ }
5707
+ function canonicalMailbox(value) {
5708
+ const email = value.trim().toLowerCase();
5709
+ const at = email.indexOf("@");
5710
+ if (at <= 0 || at !== email.lastIndexOf("@")) return void 0;
5711
+ const local = email.slice(0, at);
5712
+ const domain = email.slice(at + 1);
5713
+ if (local.length > 64 || !/^[a-z0-9#$&'*+/=?^_`{|}~-]+(?:\.[a-z0-9#$&'*+/=?^_`{|}~-]+)*$/.test(local) || !isCanonicalEmailDomain(domain)) return void 0;
5714
+ return Object.freeze({ email, domain });
5715
+ }
5688
5716
  async function signCompactUcanAuthorization(input) {
5689
5717
  if (input.notBefore >= input.expiresAt || input.expiresAt - input.notBefore > 60) {
5690
5718
  throw new TypeError("compact invocation lifetime must be between one and 60 seconds");
@@ -6430,12 +6458,6 @@ var base58flickr2 = baseX2({
6430
6458
  });
6431
6459
 
6432
6460
  // src/targets.ts
6433
- function validEmail(value) {
6434
- return /^[^@\s]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i.test(value);
6435
- }
6436
- function validDomain(value) {
6437
- return /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)(?:\.(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?))+$/i.test(value);
6438
- }
6439
6461
  function validRecipientDid(value) {
6440
6462
  if (value.length === 0 || value.length > 2048 || /[\u0000-\u0020\u007f]/.test(value)) return false;
6441
6463
  const parts = value.split(":");
@@ -6465,18 +6487,28 @@ function normalizeShareTarget(target) {
6465
6487
  return { kind: target.kind, did: target.did };
6466
6488
  }
6467
6489
  if (target.kind === "email") {
6468
- if (!validEmail(target.address)) throw new TypeError("recipient email is invalid");
6469
- const at = target.address.lastIndexOf("@");
6470
- return { kind: target.kind, address: `${target.address.slice(0, at)}@${target.address.slice(at + 1).toLowerCase()}` };
6490
+ const mailbox = canonicalMailbox(target.address);
6491
+ if (mailbox === void 0) throw new TypeError("recipient email is invalid");
6492
+ return { kind: target.kind, address: mailbox.email };
6471
6493
  }
6472
- if (!validDomain(target.domain)) throw new TypeError("recipient email domain is invalid");
6473
- return { kind: target.kind, domain: target.domain.toLowerCase() };
6494
+ const domain = target.domain.toLowerCase();
6495
+ if (!isCanonicalEmailDomain(domain)) throw new TypeError("recipient email domain is invalid");
6496
+ return { kind: target.kind, domain };
6474
6497
  }
6475
6498
  function targetAuthorizationMethod(target) {
6476
6499
  return authorizationMethodForTarget(target);
6477
6500
  }
6501
+ function publishFilename(value) {
6502
+ try {
6503
+ return canonicalShareFilename(value);
6504
+ } catch {
6505
+ throw new SharePublishError("invalid-argument", hasUnsafeFilenameCodePoint(value) ? "filename contains control or invisible characters" : "filename must be one safe path segment");
6506
+ }
6507
+ }
6478
6508
  async function publishTargetShare(input) {
6479
6509
  const target = normalizeShareTarget(input.target);
6510
+ const filename = publishFilename(input.filename);
6511
+ const files = input.files?.map((file) => ({ ...file, filename: publishFilename(file.filename) }));
6480
6512
  if (input.targetAdapter === void 0) {
6481
6513
  if (target.kind === "bearer") throw new SharePublishError("authority-required", "native bearer publication requires an authenticated TinyCloud node");
6482
6514
  return {
@@ -6502,9 +6534,6 @@ async function publishTargetShare(input) {
6502
6534
  return bytes3;
6503
6535
  })();
6504
6536
  if (source.byteLength === 0) throw new SharePublishError("invalid-argument", "share content is empty");
6505
- if (!input.filename || input.filename === "." || input.filename === ".." || /[/\\\u0000-\u001f\u007f]/.test(input.filename)) {
6506
- throw new SharePublishError("invalid-argument", "filename must be one safe path segment");
6507
- }
6508
6537
  if (target.kind === "bearer" && input.allowBinary !== true) {
6509
6538
  try {
6510
6539
  new TextDecoder("utf-8", { fatal: true }).decode(source);
@@ -6512,10 +6541,10 @@ async function publishTargetShare(input) {
6512
6541
  throw new SharePublishError("invalid-argument", "Markdown input must be valid UTF-8");
6513
6542
  }
6514
6543
  }
6515
- const files = input.files === void 0 || input.files.length === 0 ? [{ bytes: source }] : input.files;
6544
+ const publishFiles = files === void 0 || files.length === 0 ? [{ bytes: source }] : files;
6516
6545
  const limit = input.maxBytes ?? SHARE_CONTENT_LIMIT;
6517
6546
  let totalBytes = 0;
6518
- for (const file of files) {
6547
+ for (const file of publishFiles) {
6519
6548
  totalBytes += file.bytes.byteLength;
6520
6549
  if (!Number.isSafeInteger(totalBytes) || totalBytes > limit) {
6521
6550
  throw new SharePublishError("max-bytes-exceeded", "share publication exceeds the combined byte limit");
@@ -6526,8 +6555,8 @@ async function publishTargetShare(input) {
6526
6555
  if (!Number.isFinite(expiresAt.getTime()) || expiresAt.getTime() <= nowMs) throw new SharePublishError("invalid-argument", "expiresAt must be a valid future time");
6527
6556
  return input.targetAdapter.publish({
6528
6557
  source,
6529
- filename: input.filename,
6530
- ...input.files === void 0 ? {} : { files: input.files },
6558
+ filename,
6559
+ ...files === void 0 ? {} : { files },
6531
6560
  ...input.mediaType === void 0 ? {} : { mediaType: input.mediaType },
6532
6561
  ...input.resourceKind === void 0 ? {} : { resourceKind: input.resourceKind },
6533
6562
  ...input.actions === void 0 ? {} : { actions: input.actions },
@@ -6557,9 +6586,7 @@ function targetKind(target) {
6557
6586
  }
6558
6587
  function assertSafeInput(input) {
6559
6588
  if (!/^[A-Za-z0-9_-]{16,128}$/.test(input.shareId)) throw new TypeError("addressed share id is invalid");
6560
- if (input.filename.length === 0 || input.filename === "." || input.filename === ".." || /[/\\\u0000-\u001f\u007f]/.test(input.filename)) throw new TypeError("addressed filename is invalid");
6561
6589
  if (!Number.isSafeInteger(input.byteLength) || input.byteLength < 0 || input.byteLength > SHARE_CONTENT_LIMIT) throw new TypeError("addressed content length is invalid");
6562
- if (input.actions.length === 0 || input.policyActions.length === 0) throw new TypeError("addressed share actions are empty");
6563
6590
  if (!Number.isFinite(input.expiresAt.getTime()) || input.expiresAt.getTime() <= Date.now()) throw new TypeError("addressed share expiry must be in the future");
6564
6591
  if (input.artifact === "html" && (input.resource.kind !== "prefix" || !input.actions.includes("read") || !input.actions.includes("list"))) throw new TypeError("html artifacts require a readable prefix");
6565
6592
  if (input.contentSource.shareId !== input.shareId || input.contentSource.selector !== input.resource.kind) throw new TypeError("addressed content source is not bound to the share");
@@ -6652,23 +6679,60 @@ function publicationResult(input) {
6652
6679
  Object.defineProperty(result, "deliveryMaterial", { enumerable: false, value: input.deliveryMaterial });
6653
6680
  return result;
6654
6681
  }
6682
+ var EMAIL_PROFILE = { id: "tinycloud.email-proof/v1", version: 1 };
6655
6683
  var EMAIL_DOMAIN_PROFILE = { id: "tinycloud.email-domain-proof/v1", version: 1 };
6656
6684
  var EMAIL_CREDENTIAL_TYPE = { id: "opencredentials.email/v1", version: 1 };
6685
+ var EMAIL_DESCRIPTOR_DIGEST = "1tg-qphmKBVtNwzVg9xyz-xxqt_xtMXAsQyXw46m8S0";
6657
6686
  var EMAIL_DOMAIN_DESCRIPTOR_DIGEST = "33X5mAkZZgApdD3xh_T-3KS5moop0J2Nloi2nqWsdWY";
6658
- function assertEmailDomainPolicy(options, domain) {
6659
- if (!options.actions.every((action) => action === "read" || action === "list") || !options.policyActions.every((action) => action === "tinycloud.kv/get" || action === "tinycloud.kv/list" || action === "tinycloud.kv/metadata")) throw new TypeError("email-domain shares are view-only");
6660
- if (options.deliveryEmail !== void 0) throw new TypeError("email-domain shares are not emailed");
6661
- const commitment = options.credentialRequirement;
6662
- if (commitment === void 0) throw new TypeError("email-domain shares require a credential requirement");
6663
- if (canonicalize(commitment.profile) !== canonicalize(EMAIL_DOMAIN_PROFILE) || canonicalize(commitment.credentialType) !== canonicalize(EMAIL_CREDENTIAL_TYPE) || commitment.descriptorDigest !== EMAIL_DOMAIN_DESCRIPTOR_DIGEST) throw new TypeError("email-domain shares require the email-domain credential profile");
6664
- const expected = { type: "TinyCloudCredentialRequirement", version: 1, profile: EMAIL_DOMAIN_PROFILE, credentialType: EMAIL_CREDENTIAL_TYPE, claims: { emailDomain: domain }, maxAgeSeconds: 300 };
6665
- if (toBase64Url(sha2563(textEncoder.encode(canonicalize(expected)))) !== commitment.requirementDigest) throw new TypeError("credential requirement is not bound to the email domain");
6666
- }
6667
- async function publishAddressedShare(options) {
6668
- assertSafeInput(options);
6669
- const target = normalizeShareTarget(options.target);
6687
+ var EMAIL_CREDENTIAL_ISSUER_DID = "did:web:issuer.credentials.org";
6688
+ var EMAIL_CREDENTIAL_ISSUER_KID = `${EMAIL_CREDENTIAL_ISSUER_DID}#controller`;
6689
+ function mailboxCredentialCommitment(target) {
6690
+ const profile = target.kind === "email" ? EMAIL_PROFILE : EMAIL_DOMAIN_PROFILE;
6691
+ const requirement = target.kind === "email" ? { type: "TinyCloudCredentialRequirement", version: 1, profile, credentialType: EMAIL_CREDENTIAL_TYPE, claims: { email: target.address }, maxAgeSeconds: 3600 } : { type: "TinyCloudCredentialRequirement", version: 1, profile, credentialType: EMAIL_CREDENTIAL_TYPE, claims: { emailDomain: target.domain }, maxAgeSeconds: 300 };
6692
+ return {
6693
+ type: "TinyCloudPolicyCredentialRequirement",
6694
+ version: 1,
6695
+ requirementDigest: toBase64Url(sha2563(textEncoder.encode(canonicalize(requirement)))),
6696
+ descriptorDigest: target.kind === "email" ? EMAIL_DESCRIPTOR_DIGEST : EMAIL_DOMAIN_DESCRIPTOR_DIGEST,
6697
+ issuerDid: EMAIL_CREDENTIAL_ISSUER_DID,
6698
+ issuerKid: EMAIL_CREDENTIAL_ISSUER_KID,
6699
+ profile,
6700
+ credentialType: EMAIL_CREDENTIAL_TYPE
6701
+ };
6702
+ }
6703
+ function addressedCredentialRequirement(target) {
6704
+ const normalized = normalizeShareTarget(target);
6705
+ if (normalized.kind !== "email" && normalized.kind !== "emailDomain") throw new TypeError("a mailbox target is required");
6706
+ return mailboxCredentialCommitment(normalized);
6707
+ }
6708
+ function prepareAddressedShare(request) {
6709
+ try {
6710
+ canonicalShareFilename(request.filename);
6711
+ } catch {
6712
+ throw new TypeError("addressed filename is invalid");
6713
+ }
6714
+ if (request.actions.length === 0 || request.policyActions.length === 0) throw new TypeError("addressed share actions are empty");
6715
+ const target = normalizeShareTarget(request.target);
6670
6716
  if (target.kind === "bearer") throw new TypeError("addressed target is required");
6671
- if (target.kind === "emailDomain") assertEmailDomainPolicy(options, target.domain);
6717
+ if (target.kind === "recipientDid") return { target };
6718
+ if (target.kind === "emailDomain") {
6719
+ if (!request.actions.every((action) => action === "read" || action === "list") || !request.policyActions.every((action) => action === "tinycloud.kv/get" || action === "tinycloud.kv/list" || action === "tinycloud.kv/metadata")) throw new TypeError("email-domain shares are view-only");
6720
+ if (request.deliveryEmail !== void 0) throw new TypeError("email-domain shares are not emailed");
6721
+ }
6722
+ return { target, credentialRequirement: mailboxCredentialCommitment(target) };
6723
+ }
6724
+ function assertMailboxCommitment(commitment, target) {
6725
+ const label = target.kind === "email" ? "email" : "email-domain";
6726
+ if (commitment === void 0) throw new TypeError(`${label} shares require a credential requirement`);
6727
+ const expected = mailboxCredentialCommitment(target);
6728
+ if (canonicalize(commitment.profile) !== canonicalize(expected.profile) || canonicalize(commitment.credentialType) !== canonicalize(expected.credentialType) || commitment.descriptorDigest !== expected.descriptorDigest || commitment.issuerDid !== expected.issuerDid || commitment.issuerKid !== expected.issuerKid) throw new TypeError(`${label} shares require the ${label} credential profile`);
6729
+ if (commitment.requirementDigest !== expected.requirementDigest) throw new TypeError(`credential requirement is not bound to the email ${target.kind === "email" ? "address" : "domain"}`);
6730
+ }
6731
+ async function publishAddressedShare(input) {
6732
+ assertSafeInput(input);
6733
+ const { target } = prepareAddressedShare(input);
6734
+ const options = { ...input, filename: canonicalShareFilename(input.filename) };
6735
+ if (target.kind !== "recipientDid") assertMailboxCommitment(options.credentialRequirement, target);
6672
6736
  const expiry = rfc3339Seconds(options.expiresAt);
6673
6737
  const matcher = targetMatcher(target);
6674
6738
  const capabilities = sortCanonical([
@@ -7779,17 +7843,20 @@ var ShareNotifyError = class extends Error {
7779
7843
  };
7780
7844
  function recipientMatchesShareRecord(record, recipient) {
7781
7845
  const matcher = record.recipientMatcher;
7782
- if (matcher.kind === "bearer" || matcher.kind === "recipientDid") return false;
7783
- if (matcher.kind === "exactEmail") return matcher.value === recipient;
7784
- const at = recipient.lastIndexOf("@");
7785
- return at > 0 && recipient.slice(at + 1).toLowerCase() === matcher.value;
7846
+ const mailbox = canonicalMailbox(recipient);
7847
+ if (mailbox === void 0) return false;
7848
+ if (matcher.kind === "exactEmail") return canonicalMailbox(matcher.value)?.email === mailbox.email;
7849
+ if (matcher.kind === "emailDomain") return mailbox.domain === matcher.value;
7850
+ return false;
7786
7851
  }
7787
7852
  async function notifyShare(input) {
7788
- if (!input.shareId || !input.recipient || !input.recipient.includes("@")) throw new ShareNotifyError("recipient is invalid");
7789
- if (input.record !== void 0 && !recipientMatchesShareRecord(input.record, input.recipient)) {
7853
+ const mailbox = canonicalMailbox(input.recipient);
7854
+ if (!input.shareId || mailbox === void 0) throw new ShareNotifyError("recipient is invalid");
7855
+ const recipient = mailbox.email;
7856
+ if (input.record !== void 0 && !recipientMatchesShareRecord(input.record, recipient)) {
7790
7857
  throw new ShareNotifyError("recipient does not match the stored share target");
7791
7858
  }
7792
- const idempotencyKey = input.idempotencyKey ?? await defaultIdempotencyKey(input.shareId, input.recipient);
7859
+ const idempotencyKey = input.idempotencyKey ?? await defaultIdempotencyKey(input.shareId, recipient);
7793
7860
  const attemptsLimit = input.maxAttempts ?? 3;
7794
7861
  if (!Number.isSafeInteger(attemptsLimit) || attemptsLimit < 1 || attemptsLimit > 8) throw new ShareNotifyError("maxAttempts is invalid");
7795
7862
  let attempts = 0;
@@ -7798,7 +7865,7 @@ async function notifyShare(input) {
7798
7865
  if (input.signal?.aborted) throw new ShareNotifyError("share delivery was cancelled");
7799
7866
  attempts += 1;
7800
7867
  try {
7801
- const state = await input.adapter.deliver({ shareId: input.shareId, recipient: input.recipient, idempotencyKey, ...input.record === void 0 ? {} : { record: input.record }, ...input.signal === void 0 ? {} : { signal: input.signal } });
7868
+ const state = await input.adapter.deliver({ shareId: input.shareId, recipient, idempotencyKey, ...input.record === void 0 ? {} : { record: input.record }, ...input.signal === void 0 ? {} : { signal: input.signal } });
7802
7869
  return { protocol: "tinycloud-share", version: 1, shareId: input.shareId, state, idempotencyKey, attempts };
7803
7870
  } catch (error) {
7804
7871
  lastError = error;
@@ -8526,9 +8593,11 @@ export {
8526
8593
  SharePublishError,
8527
8594
  ShareReceiveError,
8528
8595
  ShareRecipientClient,
8596
+ addressedCredentialRequirement,
8529
8597
  authorizationMethodForTarget,
8530
8598
  authorizeShare,
8531
8599
  canonicalOwnerSharePolicy,
8600
+ canonicalShareFilename,
8532
8601
  canonicalize,
8533
8602
  checkBearerDelegation,
8534
8603
  claimShare,
@@ -8540,6 +8609,7 @@ export {
8540
8609
  createShareV2HolderBindingArtifact,
8541
8610
  deliverCredentialInvitation,
8542
8611
  fromBase64Url,
8612
+ hasUnsafeFilenameCodePoint,
8543
8613
  historyRecordForPublishedShare,
8544
8614
  inspectShare,
8545
8615
  listShares,
@@ -8548,6 +8618,7 @@ export {
8548
8618
  openNativeShare,
8549
8619
  parseAddressedEnvelope,
8550
8620
  parseNativeShareUrl,
8621
+ prepareAddressedShare,
8551
8622
  publishAddressedShare,
8552
8623
  publishPolicyShare,
8553
8624
  publishTargetShare,