@tinycloud/share-sdk 1.0.1-beta.0 → 1.1.0-beta.2

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
@@ -707,6 +707,8 @@ interface SenderShareRecord {
707
707
  readonly filename?: string;
708
708
  /** Policy/v3 delivery material. The containing history record must remain encrypted at rest. */
709
709
  readonly deliveryMaterial?: PublishedShareDeliveryMaterial;
710
+ /** Canonical mailboxes whose invitation delivery was confirmed; history is encrypted at rest. */
711
+ readonly deliveredRecipients?: readonly string[];
710
712
  }
711
713
  /** Create the durable encrypted-history shape from canonical publication receipts. */
712
714
  declare function historyRecordForPublishedShare(result: PublishedShare, now?: Date): SenderShareRecord;
@@ -720,6 +722,9 @@ declare function historyRecordForPublishedShare(result: PublishedShare, now?: Da
720
722
  type SenderShareRevocationScope = "direct" | "ancestor";
721
723
  interface SenderShareRecordStorage {
722
724
  put(record: SenderShareRecord): Promise<void>;
725
+ /** Atomically transform the latest record, if present. Implementations that
726
+ * provide this must serialize the read and write with every other writer. */
727
+ update?(shareId: string, change: (record: SenderShareRecord) => SenderShareRecord | Promise<SenderShareRecord>): Promise<SenderShareRecord | undefined>;
723
728
  list(): Promise<readonly SenderShareRecord[]>;
724
729
  get(shareId: string): Promise<SenderShareRecord | undefined>;
725
730
  delete(shareId: string): Promise<void>;
@@ -737,7 +742,10 @@ interface SenderShareKeyStorage {
737
742
  /** In-memory reference implementation. Real deployments back these with IndexedDB so the non-extractable `CryptoKey` survives a reload via structured clone. */
738
743
  declare class MemorySenderShareRecordStorage implements SenderShareRecordStorage {
739
744
  private readonly records;
745
+ private operation;
746
+ private serialize;
740
747
  put(record: SenderShareRecord): Promise<void>;
748
+ update(shareId: string, change: (record: SenderShareRecord) => SenderShareRecord | Promise<SenderShareRecord>): Promise<SenderShareRecord | undefined>;
741
749
  list(): Promise<readonly SenderShareRecord[]>;
742
750
  get(shareId: string): Promise<SenderShareRecord | undefined>;
743
751
  delete(shareId: string): Promise<void>;
@@ -809,6 +817,7 @@ interface ShareNotifyResult {
809
817
  readonly idempotencyKey: string;
810
818
  readonly attempts: number;
811
819
  readonly retryable?: boolean;
820
+ readonly reason?: "delivery-window-expired";
812
821
  }
813
822
  interface ShareNotifyInput {
814
823
  readonly shareId: string;
@@ -822,8 +831,9 @@ interface ShareDeliveryAdapter {
822
831
  deliver(input: ShareNotifyInput): Promise<"delivered" | "already-delivered">;
823
832
  }
824
833
  declare class ShareNotifyError extends Error {
834
+ readonly reason?: "delivery-window-expired" | undefined;
825
835
  readonly code: "delivery-failed";
826
- constructor(message?: string);
836
+ constructor(message?: string, reason?: "delivery-window-expired" | undefined);
827
837
  }
828
838
  /**
829
839
  * Match a delivery address against the recipient constraint recorded when the
@@ -831,6 +841,8 @@ declare class ShareNotifyError extends Error {
831
841
  * the only form the credential issuer accepts.
832
842
  */
833
843
  declare function recipientMatchesShareRecord(record: SenderShareRecord, recipient: string): boolean;
844
+ /** The sender can authorize a fresh invitation only until this persisted deadline. */
845
+ declare function shareDeliveryWindowExpiresAt(record: SenderShareRecord): number;
834
846
  /**
835
847
  * Delivery is deliberately separate from publication. A failed notification
836
848
  * never causes a second envelope or link to be minted.
@@ -843,6 +855,8 @@ declare function notifyShare(input: {
843
855
  readonly idempotencyKey?: string;
844
856
  readonly maxAttempts?: number;
845
857
  readonly signal?: AbortSignal;
858
+ /** Check the persisted delivery window locally before calling the adapter. */
859
+ readonly checkDeliveryWindow?: boolean;
846
860
  }): Promise<ShareNotifyResult>;
847
861
 
848
862
  /** The signed, app-neutral receipt accepted by OpenCredentials' invitation endpoint. */
@@ -1133,4 +1147,4 @@ declare function parseNativeShareUrl(value: string): string;
1133
1147
  /** Receive through the public SDK without creating another transport protocol. */
1134
1148
  declare function openNativeShare(sharing: NativeSharingService, link: string): Promise<unknown>;
1135
1149
 
1136
- 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 SharePolicyDelegationLink, 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 };
1150
+ 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 SharePolicyDelegationLink, 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, shareDeliveryWindowExpiresAt, showShare, targetAuthorizationMethod, toShareErrorInfo, validateOwnerSharePolicyRegistration, validateOwnerSharePolicyRegistrationBytes };
package/dist/index.d.ts CHANGED
@@ -707,6 +707,8 @@ interface SenderShareRecord {
707
707
  readonly filename?: string;
708
708
  /** Policy/v3 delivery material. The containing history record must remain encrypted at rest. */
709
709
  readonly deliveryMaterial?: PublishedShareDeliveryMaterial;
710
+ /** Canonical mailboxes whose invitation delivery was confirmed; history is encrypted at rest. */
711
+ readonly deliveredRecipients?: readonly string[];
710
712
  }
711
713
  /** Create the durable encrypted-history shape from canonical publication receipts. */
712
714
  declare function historyRecordForPublishedShare(result: PublishedShare, now?: Date): SenderShareRecord;
@@ -720,6 +722,9 @@ declare function historyRecordForPublishedShare(result: PublishedShare, now?: Da
720
722
  type SenderShareRevocationScope = "direct" | "ancestor";
721
723
  interface SenderShareRecordStorage {
722
724
  put(record: SenderShareRecord): Promise<void>;
725
+ /** Atomically transform the latest record, if present. Implementations that
726
+ * provide this must serialize the read and write with every other writer. */
727
+ update?(shareId: string, change: (record: SenderShareRecord) => SenderShareRecord | Promise<SenderShareRecord>): Promise<SenderShareRecord | undefined>;
723
728
  list(): Promise<readonly SenderShareRecord[]>;
724
729
  get(shareId: string): Promise<SenderShareRecord | undefined>;
725
730
  delete(shareId: string): Promise<void>;
@@ -737,7 +742,10 @@ interface SenderShareKeyStorage {
737
742
  /** In-memory reference implementation. Real deployments back these with IndexedDB so the non-extractable `CryptoKey` survives a reload via structured clone. */
738
743
  declare class MemorySenderShareRecordStorage implements SenderShareRecordStorage {
739
744
  private readonly records;
745
+ private operation;
746
+ private serialize;
740
747
  put(record: SenderShareRecord): Promise<void>;
748
+ update(shareId: string, change: (record: SenderShareRecord) => SenderShareRecord | Promise<SenderShareRecord>): Promise<SenderShareRecord | undefined>;
741
749
  list(): Promise<readonly SenderShareRecord[]>;
742
750
  get(shareId: string): Promise<SenderShareRecord | undefined>;
743
751
  delete(shareId: string): Promise<void>;
@@ -809,6 +817,7 @@ interface ShareNotifyResult {
809
817
  readonly idempotencyKey: string;
810
818
  readonly attempts: number;
811
819
  readonly retryable?: boolean;
820
+ readonly reason?: "delivery-window-expired";
812
821
  }
813
822
  interface ShareNotifyInput {
814
823
  readonly shareId: string;
@@ -822,8 +831,9 @@ interface ShareDeliveryAdapter {
822
831
  deliver(input: ShareNotifyInput): Promise<"delivered" | "already-delivered">;
823
832
  }
824
833
  declare class ShareNotifyError extends Error {
834
+ readonly reason?: "delivery-window-expired" | undefined;
825
835
  readonly code: "delivery-failed";
826
- constructor(message?: string);
836
+ constructor(message?: string, reason?: "delivery-window-expired" | undefined);
827
837
  }
828
838
  /**
829
839
  * Match a delivery address against the recipient constraint recorded when the
@@ -831,6 +841,8 @@ declare class ShareNotifyError extends Error {
831
841
  * the only form the credential issuer accepts.
832
842
  */
833
843
  declare function recipientMatchesShareRecord(record: SenderShareRecord, recipient: string): boolean;
844
+ /** The sender can authorize a fresh invitation only until this persisted deadline. */
845
+ declare function shareDeliveryWindowExpiresAt(record: SenderShareRecord): number;
834
846
  /**
835
847
  * Delivery is deliberately separate from publication. A failed notification
836
848
  * never causes a second envelope or link to be minted.
@@ -843,6 +855,8 @@ declare function notifyShare(input: {
843
855
  readonly idempotencyKey?: string;
844
856
  readonly maxAttempts?: number;
845
857
  readonly signal?: AbortSignal;
858
+ /** Check the persisted delivery window locally before calling the adapter. */
859
+ readonly checkDeliveryWindow?: boolean;
846
860
  }): Promise<ShareNotifyResult>;
847
861
 
848
862
  /** The signed, app-neutral receipt accepted by OpenCredentials' invitation endpoint. */
@@ -1133,4 +1147,4 @@ declare function parseNativeShareUrl(value: string): string;
1133
1147
  /** Receive through the public SDK without creating another transport protocol. */
1134
1148
  declare function openNativeShare(sharing: NativeSharingService, link: string): Promise<unknown>;
1135
1149
 
1136
- 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 SharePolicyDelegationLink, 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 };
1150
+ 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 SharePolicyDelegationLink, 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, shareDeliveryWindowExpiresAt, showShare, targetAuthorizationMethod, toShareErrorInfo, validateOwnerSharePolicyRegistration, validateOwnerSharePolicyRegistrationBytes };
package/dist/index.js CHANGED
@@ -7700,8 +7700,25 @@ function historyRecordForPublishedShare(result, now = /* @__PURE__ */ new Date()
7700
7700
  }
7701
7701
  var MemorySenderShareRecordStorage = class {
7702
7702
  records = /* @__PURE__ */ new Map();
7703
+ operation = Promise.resolve();
7704
+ serialize(action) {
7705
+ const next = this.operation.then(action, action);
7706
+ this.operation = next.then(() => void 0, () => void 0);
7707
+ return next;
7708
+ }
7703
7709
  async put(record) {
7704
- this.records.set(record.shareId, record);
7710
+ return this.serialize(async () => {
7711
+ this.records.set(record.shareId, record);
7712
+ });
7713
+ }
7714
+ async update(shareId, change) {
7715
+ return this.serialize(async () => {
7716
+ const current = this.records.get(shareId);
7717
+ if (current === void 0) return void 0;
7718
+ const updated = await change(current);
7719
+ this.records.set(shareId, updated);
7720
+ return updated;
7721
+ });
7705
7722
  }
7706
7723
  async list() {
7707
7724
  return [...this.records.values()];
@@ -7710,7 +7727,9 @@ var MemorySenderShareRecordStorage = class {
7710
7727
  return this.records.get(shareId);
7711
7728
  }
7712
7729
  async delete(shareId) {
7713
- this.records.delete(shareId);
7730
+ return this.serialize(async () => {
7731
+ this.records.delete(shareId);
7732
+ });
7714
7733
  }
7715
7734
  };
7716
7735
  var MemorySenderShareKeyStorage = class {
@@ -7794,6 +7813,11 @@ var SenderShareStore = class {
7794
7813
  }
7795
7814
  /** Mark a share revoked locally after the caller's node-side revoke call succeeds. */
7796
7815
  async markRevoked(shareId, revokedAt = this.now().toISOString()) {
7816
+ if (this.records.update !== void 0) {
7817
+ const updated = await this.records.update(shareId, (record2) => ({ ...record2, revokedAt }));
7818
+ if (updated === void 0) throw new Error(`Unknown share: ${shareId}`);
7819
+ return updated;
7820
+ }
7797
7821
  const record = await this.records.get(shareId);
7798
7822
  if (record === void 0) throw new Error(`Unknown share: ${shareId}`);
7799
7823
  const revoked = { ...record, revokedAt };
@@ -7841,11 +7865,12 @@ var EncryptedSenderShareHistory = class {
7841
7865
 
7842
7866
  // src/notify.ts
7843
7867
  var ShareNotifyError = class extends Error {
7844
- code = "delivery-failed";
7845
- constructor(message = "share delivery did not complete") {
7868
+ constructor(message = "share delivery did not complete", reason) {
7846
7869
  super(message);
7870
+ this.reason = reason;
7847
7871
  this.name = "ShareNotifyError";
7848
7872
  }
7873
+ code = "delivery-failed";
7849
7874
  };
7850
7875
  function recipientMatchesShareRecord(record, recipient) {
7851
7876
  const matcher = record.recipientMatcher;
@@ -7855,6 +7880,11 @@ function recipientMatchesShareRecord(record, recipient) {
7855
7880
  if (matcher.kind === "emailDomain") return mailbox.domain === matcher.value;
7856
7881
  return false;
7857
7882
  }
7883
+ function shareDeliveryWindowExpiresAt(record) {
7884
+ const expiresAt = Math.min(Date.parse(record.expiresAt), Date.parse(record.registeredAt) + 5 * 60 * 1e3);
7885
+ if (!Number.isFinite(expiresAt)) throw new ShareNotifyError("share delivery history has invalid timestamps");
7886
+ return expiresAt;
7887
+ }
7858
7888
  async function notifyShare(input) {
7859
7889
  const mailbox = canonicalMailbox(input.recipient);
7860
7890
  if (!input.shareId || mailbox === void 0) throw new ShareNotifyError("recipient is invalid");
@@ -7865,19 +7895,59 @@ async function notifyShare(input) {
7865
7895
  const idempotencyKey = input.idempotencyKey ?? await defaultIdempotencyKey(input.shareId, recipient);
7866
7896
  const attemptsLimit = input.maxAttempts ?? 3;
7867
7897
  if (!Number.isSafeInteger(attemptsLimit) || attemptsLimit < 1 || attemptsLimit > 8) throw new ShareNotifyError("maxAttempts is invalid");
7898
+ if (input.signal?.aborted) throw new ShareNotifyError("share delivery was cancelled");
7899
+ if (input.checkDeliveryWindow && input.record !== void 0 && shareDeliveryWindowExpiresAt(input.record) <= Date.now()) {
7900
+ return {
7901
+ protocol: "tinycloud-share",
7902
+ version: 1,
7903
+ shareId: input.shareId,
7904
+ state: "partial-failure",
7905
+ idempotencyKey,
7906
+ attempts: 1,
7907
+ retryable: false,
7908
+ reason: "delivery-window-expired"
7909
+ };
7910
+ }
7868
7911
  let attempts = 0;
7869
- let lastError;
7870
7912
  while (attempts < attemptsLimit) {
7871
7913
  if (input.signal?.aborted) throw new ShareNotifyError("share delivery was cancelled");
7872
7914
  attempts += 1;
7873
7915
  try {
7874
7916
  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 } });
7875
- return { protocol: "tinycloud-share", version: 1, shareId: input.shareId, state, idempotencyKey, attempts };
7917
+ return {
7918
+ protocol: "tinycloud-share",
7919
+ version: 1,
7920
+ shareId: input.shareId,
7921
+ state: state === "delivered" && input.record?.deliveredRecipients?.includes(recipient) ? "already-delivered" : state,
7922
+ idempotencyKey,
7923
+ attempts
7924
+ };
7876
7925
  } catch (error) {
7877
- lastError = error;
7926
+ if (error instanceof ShareNotifyError && error.reason === "delivery-window-expired") {
7927
+ return {
7928
+ protocol: "tinycloud-share",
7929
+ version: 1,
7930
+ shareId: input.shareId,
7931
+ state: "partial-failure",
7932
+ idempotencyKey,
7933
+ attempts,
7934
+ retryable: false,
7935
+ reason: "delivery-window-expired"
7936
+ };
7937
+ }
7938
+ if (error !== null && typeof error === "object" && "status" in error && (error.status === 401 || error.status === 403)) {
7939
+ return {
7940
+ protocol: "tinycloud-share",
7941
+ version: 1,
7942
+ shareId: input.shareId,
7943
+ state: "partial-failure",
7944
+ idempotencyKey,
7945
+ attempts,
7946
+ retryable: false
7947
+ };
7948
+ }
7878
7949
  }
7879
7950
  }
7880
- void lastError;
7881
7951
  return { protocol: "tinycloud-share", version: 1, shareId: input.shareId, state: "partial-failure", idempotencyKey, attempts, retryable: true };
7882
7952
  }
7883
7953
  async function defaultIdempotencyKey(shareId, recipient) {
@@ -8036,7 +8106,11 @@ async function revokeShare(input) {
8036
8106
  });
8037
8107
  }
8038
8108
  const revokedAt = (input.now?.() ?? /* @__PURE__ */ new Date()).toISOString();
8039
- if (input.records !== void 0) await input.records.put({ ...input.record, revokedAt });
8109
+ if (input.records?.update !== void 0) {
8110
+ await input.records.update(input.record.shareId, (record) => ({ ...record, revokedAt }));
8111
+ } else if (input.records !== void 0) {
8112
+ await input.records.put({ ...input.record, revokedAt });
8113
+ }
8040
8114
  return { state: "revoked", target, delegationCid, revokedAt };
8041
8115
  }
8042
8116
  function redactRecord(record, revealLink, link) {
@@ -8299,6 +8373,10 @@ function parseV3InlineEncryptedEnvelope(bytes3, expected) {
8299
8373
  }
8300
8374
  return record;
8301
8375
  }
8376
+ async function recipientHttpError(response, context) {
8377
+ const body = (await response.text().catch(() => "")).trim().slice(0, 512);
8378
+ return Object.assign(new Error(`${context} (${response.status})${body ? `: ${body}` : ""}`), { status: response.status });
8379
+ }
8302
8380
  var ShareRecipientClient = class {
8303
8381
  constructor(options) {
8304
8382
  this.options = options;
@@ -8372,7 +8450,7 @@ var ShareRecipientClient = class {
8372
8450
  body: JSON.stringify({ policyCid: envelope.policyCid, recipientDid: this.options.holderDid, requestedCapabilities: envelope.policy.capabilityCeiling }),
8373
8451
  ...this.options.signal === void 0 ? {} : { signal: this.options.signal }
8374
8452
  });
8375
- if (!challengeResponse.ok) throw new Error(`v3 policy challenge rejected (${challengeResponse.status})`);
8453
+ if (!challengeResponse.ok) throw await recipientHttpError(challengeResponse, "v3 policy challenge rejected");
8376
8454
  const challenge = object3(await challengeResponse.json(), "v3 policy challenge");
8377
8455
  if (typeof challenge.challengeId !== "string" || typeof challenge.nonce !== "string" || challenge.policyCid !== envelope.policyCid || challenge.recipientDid !== this.options.holderDid || challenge.nodeAudience !== attestedEnforcer.nodeAudience || challenge.enforcerDid !== void 0 && challenge.enforcerDid !== attestedEnforcer.enforcerDid || typeof challenge.expiresAt !== "string" || !Number.isFinite(Date.parse(challenge.expiresAt)) || Date.parse(challenge.expiresAt) <= Date.now()) throw new Error("v3 policy challenge binding mismatch");
8378
8456
  const material = await this.options.buildPresentation({
@@ -8397,14 +8475,14 @@ var ShareRecipientClient = class {
8397
8475
  const requestedExpiresAt = requestedSessionExpiry(envelope.expiry);
8398
8476
  let delegationResponse = await mint(requestedExpiresAt === void 0 ? mintBody : { ...mintBody, requestedExpiresAt });
8399
8477
  if (requestedExpiresAt !== void 0 && delegationResponse.status === 422) delegationResponse = await mint(mintBody);
8400
- if (!delegationResponse.ok) throw new Error(`v3 policy delegation rejected (${delegationResponse.status})`);
8478
+ if (!delegationResponse.ok) throw await recipientHttpError(delegationResponse, "v3 policy delegation rejected");
8401
8479
  const delegation = object3(await delegationResponse.json(), "v3 policy delegation");
8402
8480
  if (delegation.admitted !== true || typeof delegation.sessionCid !== "string" || typeof delegation.authorization !== "string") throw new Error("v3 policy delegation response is not admitted");
8403
8481
  const compact = verifyV3PolicyAuthorization({ authorization: delegation.authorization, cid: delegation.sessionCid, envelope, holderDid: this.options.holderDid });
8404
8482
  const fact = compact.payload.fct[0];
8405
8483
  this.options.onStage?.("delegation-import");
8406
8484
  const imported = await this.fetchFn(new URL("/delegate", this.options.nodeOrigin), { method: "POST", redirect: "error", headers: { Authorization: delegation.authorization }, ...this.options.signal === void 0 ? {} : { signal: this.options.signal } });
8407
- if (!imported.ok) throw new Error(`ordinary delegation import rejected (${imported.status})`);
8485
+ if (!imported.ok) throw await recipientHttpError(imported, "ordinary delegation import rejected");
8408
8486
  this.signer = material.sign;
8409
8487
  this.v3Authorization = delegation.authorization;
8410
8488
  this.v3NodeAudience = fact.nodeAudience;
@@ -8439,7 +8517,7 @@ var ShareRecipientClient = class {
8439
8517
  try {
8440
8518
  this.options.onStage?.("decryption");
8441
8519
  const response = await this.fetchFn(new URL("/invoke", this.options.nodeOrigin), { method: "POST", redirect: "error", headers: { accept: "application/json", "content-type": "application/json", Authorization: invocation.authorization }, body: JSON.stringify(body), ...this.options.signal === void 0 ? {} : { signal: this.options.signal } });
8442
- if (!response.ok) throw new Error(`v3 decrypt invocation rejected (${response.status})`);
8520
+ if (!response.ok) throw await recipientHttpError(response, "v3 decrypt invocation rejected");
8443
8521
  const value = object3(await response.json(), "v3 decrypt response");
8444
8522
  const allowed = ["type", "targetNode", "networkId", "invocationCid", "encryptedSymmetricKeyHash", "receiverPublicKeyHash", "wrappedKey", "alg", "keyVersion", "requestHash", "nodeId", "nodeSignature"];
8445
8523
  if (Object.keys(value).length !== allowed.length || Object.keys(value).some((key) => !allowed.includes(key)) || value.type !== "tinycloud.encryption.decrypt-result/v1" || value.targetNode !== body.targetNode || value.nodeId !== body.targetNode || value.networkId !== body.networkId || value.invocationCid !== invocation.cid || value.encryptedSymmetricKeyHash !== body.encryptedSymmetricKeyHash || value.receiverPublicKeyHash !== receiverPublicKeyHash || value.alg !== body.alg || value.keyVersion !== body.keyVersion || value.requestHash !== hex3(sha2566(new TextEncoder().encode(`${invocation.cid}${bodyHash}`))) || typeof value.wrappedKey !== "string" || typeof value.nodeSignature !== "string") throw new Error("v3 decrypt response binding is invalid");
@@ -8662,6 +8740,7 @@ export {
8662
8740
  restoreDelegatedShareKey,
8663
8741
  resumeShareAuthorization,
8664
8742
  revokeShare,
8743
+ shareDeliveryWindowExpiresAt,
8665
8744
  showShare,
8666
8745
  targetAuthorizationMethod,
8667
8746
  toBase64Url,