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

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.cjs CHANGED
@@ -78,6 +78,7 @@ __export(index_exports, {
78
78
  restoreDelegatedShareKey: () => restoreDelegatedShareKey,
79
79
  resumeShareAuthorization: () => resumeShareAuthorization,
80
80
  revokeShare: () => revokeShare,
81
+ shareDeliveryWindowExpiresAt: () => shareDeliveryWindowExpiresAt,
81
82
  showShare: () => showShare,
82
83
  targetAuthorizationMethod: () => targetAuthorizationMethod,
83
84
  toBase64Url: () => toBase64Url,
@@ -7783,8 +7784,25 @@ function historyRecordForPublishedShare(result, now = /* @__PURE__ */ new Date()
7783
7784
  }
7784
7785
  var MemorySenderShareRecordStorage = class {
7785
7786
  records = /* @__PURE__ */ new Map();
7787
+ operation = Promise.resolve();
7788
+ serialize(action) {
7789
+ const next = this.operation.then(action, action);
7790
+ this.operation = next.then(() => void 0, () => void 0);
7791
+ return next;
7792
+ }
7786
7793
  async put(record) {
7787
- this.records.set(record.shareId, record);
7794
+ return this.serialize(async () => {
7795
+ this.records.set(record.shareId, record);
7796
+ });
7797
+ }
7798
+ async update(shareId, change) {
7799
+ return this.serialize(async () => {
7800
+ const current = this.records.get(shareId);
7801
+ if (current === void 0) return void 0;
7802
+ const updated = await change(current);
7803
+ this.records.set(shareId, updated);
7804
+ return updated;
7805
+ });
7788
7806
  }
7789
7807
  async list() {
7790
7808
  return [...this.records.values()];
@@ -7793,7 +7811,9 @@ var MemorySenderShareRecordStorage = class {
7793
7811
  return this.records.get(shareId);
7794
7812
  }
7795
7813
  async delete(shareId) {
7796
- this.records.delete(shareId);
7814
+ return this.serialize(async () => {
7815
+ this.records.delete(shareId);
7816
+ });
7797
7817
  }
7798
7818
  };
7799
7819
  var MemorySenderShareKeyStorage = class {
@@ -7877,6 +7897,11 @@ var SenderShareStore = class {
7877
7897
  }
7878
7898
  /** Mark a share revoked locally after the caller's node-side revoke call succeeds. */
7879
7899
  async markRevoked(shareId, revokedAt = this.now().toISOString()) {
7900
+ if (this.records.update !== void 0) {
7901
+ const updated = await this.records.update(shareId, (record2) => ({ ...record2, revokedAt }));
7902
+ if (updated === void 0) throw new Error(`Unknown share: ${shareId}`);
7903
+ return updated;
7904
+ }
7880
7905
  const record = await this.records.get(shareId);
7881
7906
  if (record === void 0) throw new Error(`Unknown share: ${shareId}`);
7882
7907
  const revoked = { ...record, revokedAt };
@@ -7924,11 +7949,12 @@ var EncryptedSenderShareHistory = class {
7924
7949
 
7925
7950
  // src/notify.ts
7926
7951
  var ShareNotifyError = class extends Error {
7927
- code = "delivery-failed";
7928
- constructor(message = "share delivery did not complete") {
7952
+ constructor(message = "share delivery did not complete", reason) {
7929
7953
  super(message);
7954
+ this.reason = reason;
7930
7955
  this.name = "ShareNotifyError";
7931
7956
  }
7957
+ code = "delivery-failed";
7932
7958
  };
7933
7959
  function recipientMatchesShareRecord(record, recipient) {
7934
7960
  const matcher = record.recipientMatcher;
@@ -7938,6 +7964,11 @@ function recipientMatchesShareRecord(record, recipient) {
7938
7964
  if (matcher.kind === "emailDomain") return mailbox.domain === matcher.value;
7939
7965
  return false;
7940
7966
  }
7967
+ function shareDeliveryWindowExpiresAt(record) {
7968
+ const expiresAt = Math.min(Date.parse(record.expiresAt), Date.parse(record.registeredAt) + 5 * 60 * 1e3);
7969
+ if (!Number.isFinite(expiresAt)) throw new ShareNotifyError("share delivery history has invalid timestamps");
7970
+ return expiresAt;
7971
+ }
7941
7972
  async function notifyShare(input) {
7942
7973
  const mailbox = canonicalMailbox(input.recipient);
7943
7974
  if (!input.shareId || mailbox === void 0) throw new ShareNotifyError("recipient is invalid");
@@ -7948,6 +7979,19 @@ async function notifyShare(input) {
7948
7979
  const idempotencyKey = input.idempotencyKey ?? await defaultIdempotencyKey(input.shareId, recipient);
7949
7980
  const attemptsLimit = input.maxAttempts ?? 3;
7950
7981
  if (!Number.isSafeInteger(attemptsLimit) || attemptsLimit < 1 || attemptsLimit > 8) throw new ShareNotifyError("maxAttempts is invalid");
7982
+ if (input.signal?.aborted) throw new ShareNotifyError("share delivery was cancelled");
7983
+ if (input.checkDeliveryWindow && input.record !== void 0 && shareDeliveryWindowExpiresAt(input.record) <= Date.now()) {
7984
+ return {
7985
+ protocol: "tinycloud-share",
7986
+ version: 1,
7987
+ shareId: input.shareId,
7988
+ state: "partial-failure",
7989
+ idempotencyKey,
7990
+ attempts: 1,
7991
+ retryable: false,
7992
+ reason: "delivery-window-expired"
7993
+ };
7994
+ }
7951
7995
  let attempts = 0;
7952
7996
  let lastError;
7953
7997
  while (attempts < attemptsLimit) {
@@ -7955,8 +7999,27 @@ async function notifyShare(input) {
7955
7999
  attempts += 1;
7956
8000
  try {
7957
8001
  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 } });
7958
- return { protocol: "tinycloud-share", version: 1, shareId: input.shareId, state, idempotencyKey, attempts };
8002
+ return {
8003
+ protocol: "tinycloud-share",
8004
+ version: 1,
8005
+ shareId: input.shareId,
8006
+ state: state === "delivered" && input.record?.deliveredRecipients?.includes(recipient) ? "already-delivered" : state,
8007
+ idempotencyKey,
8008
+ attempts
8009
+ };
7959
8010
  } catch (error) {
8011
+ if (error instanceof ShareNotifyError && error.reason === "delivery-window-expired") {
8012
+ return {
8013
+ protocol: "tinycloud-share",
8014
+ version: 1,
8015
+ shareId: input.shareId,
8016
+ state: "partial-failure",
8017
+ idempotencyKey,
8018
+ attempts,
8019
+ retryable: false,
8020
+ reason: "delivery-window-expired"
8021
+ };
8022
+ }
7960
8023
  lastError = error;
7961
8024
  }
7962
8025
  }
@@ -8119,7 +8182,11 @@ async function revokeShare(input) {
8119
8182
  });
8120
8183
  }
8121
8184
  const revokedAt = (input.now?.() ?? /* @__PURE__ */ new Date()).toISOString();
8122
- if (input.records !== void 0) await input.records.put({ ...input.record, revokedAt });
8185
+ if (input.records?.update !== void 0) {
8186
+ await input.records.update(input.record.shareId, (record) => ({ ...record, revokedAt }));
8187
+ } else if (input.records !== void 0) {
8188
+ await input.records.put({ ...input.record, revokedAt });
8189
+ }
8123
8190
  return { state: "revoked", target, delegationCid, revokedAt };
8124
8191
  }
8125
8192
  function redactRecord(record, revealLink, link) {
@@ -8746,6 +8813,7 @@ async function openNativeShare(sharing, link) {
8746
8813
  restoreDelegatedShareKey,
8747
8814
  resumeShareAuthorization,
8748
8815
  revokeShare,
8816
+ shareDeliveryWindowExpiresAt,
8749
8817
  showShare,
8750
8818
  targetAuthorizationMethod,
8751
8819
  toBase64Url,