@tinycloud/share-sdk 1.4.4-beta.0 → 1.4.4

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
@@ -141,10 +141,29 @@ declare function deliverCredentialInvitation(input: {
141
141
  readonly signal?: AbortSignal;
142
142
  }): Promise<CredentialInvitationResult>;
143
143
 
144
+ /**
145
+ * One root of an addressed share after `revokeShare`. A `left-live` root is
146
+ * only ever the policy-authority root, whose audience is a policy digest DID
147
+ * with no key: it cannot authorize anything once the enforcement root is
148
+ * revoked, and it lapses at `expiresAt`.
149
+ */
150
+ type ShareRootRevocationState = {
151
+ readonly rootCid: string;
152
+ readonly targetRole: "policy-authority" | "policy-enforcement";
153
+ readonly state: "revoked";
154
+ } | {
155
+ readonly rootCid: string;
156
+ readonly targetRole: "policy-authority";
157
+ readonly state: "left-live";
158
+ readonly expiresAt: string;
159
+ };
144
160
  type ShareRevocationResult = {
145
161
  readonly state: "revoked";
146
162
  readonly target: "bearer" | "recipientDid" | "email" | "emailDomain";
163
+ /** The bearer delegation; for an addressed share its enforcement root (its policy root when the record has no enforcement root). */
147
164
  readonly delegationCid: string;
165
+ /** Addressed shares: every root the share created, with what this revocation did to it. Recipient access has ended. */
166
+ readonly roots?: readonly ShareRootRevocationState[];
148
167
  readonly revokedAt: string;
149
168
  } | {
150
169
  readonly state: "unsupported";
@@ -172,16 +191,47 @@ interface ShareRevocationAdapter {
172
191
  /**
173
192
  * Addressed Policy/v3 authority uses the Node's signed root-revocation
174
193
  * endpoint. The Node checks that root for active sessions, new admission,
175
- * and delivery; this is not a separate share authority plane.
194
+ * and delivery; this is not a separate share authority plane. It resolves
195
+ * when the root is revoked, including when the Node already holds a
196
+ * revocation for it, and rejects otherwise. For the policy-authority root
197
+ * only, it may resolve `"left-live"` instead of revoking it (for example
198
+ * when revoking it would cost the owner another wallet approval).
176
199
  */
177
- revokePolicyRoot?(input: SharePolicyRootRevocation): Promise<void>;
200
+ revokePolicyRoot?(input: SharePolicyRootRevocation): Promise<void | "left-live">;
201
+ }
202
+ /** A root an addressed revocation did not end. */
203
+ interface ShareRootRevocationFailure {
204
+ readonly rootCid: string;
205
+ readonly targetRole: SharePolicyRootRevocation["targetRole"];
206
+ readonly error: unknown;
207
+ }
208
+ /**
209
+ * An addressed revocation did not finish: a root failed, or only the
210
+ * policy-authority root was dealt with. `revoked` names the roots that were
211
+ * revoked; if it is not empty, recipient access has ended, since every
212
+ * recipient request needs both roots. The share record is not marked
213
+ * revoked, so the caller can retry.
214
+ */
215
+ declare class ShareRevocationIncompleteError extends Error {
216
+ readonly revoked: readonly string[];
217
+ readonly failed: readonly ShareRootRevocationFailure[];
218
+ constructor(revoked: readonly string[], failed: readonly ShareRootRevocationFailure[]);
178
219
  }
179
- /** Revoke the native bearer delegation or the selected signed Policy/v3 root. */
220
+ /**
221
+ * Revoke a share. A bearer share revokes the delegation `scope` selects. An
222
+ * addressed share asks the adapter for every root it created, the
223
+ * enforcement root and then the policy root, whatever `scope` says; the
224
+ * adapter may leave the keyless policy-authority root live. It succeeds only
225
+ * when no root failed and at least one was revoked (so recipient access has
226
+ * ended); otherwise it throws {@link ShareRevocationIncompleteError} after
227
+ * trying every root.
228
+ */
180
229
  declare function revokeShare(input: {
181
230
  readonly record: SenderShareRecord;
182
231
  /** Optional durable store; successful revocation is persisted before return. */
183
232
  readonly records?: SenderShareRecordStorage;
184
233
  readonly adapter?: ShareRevocationAdapter;
234
+ /** Bearer shares only. */
185
235
  readonly scope?: "direct" | "ancestor";
186
236
  readonly now?: () => Date;
187
237
  }): Promise<ShareRevocationResult>;
@@ -372,4 +422,4 @@ declare function assertArtifactBinding(view: ArtifactBindingView, expected?: Exp
372
422
  */
373
423
  declare function verifyArtifactShare(envelope: ShareEnvelopeV3, options: VerifyArtifactShareOptions): ArtifactPin | undefined;
374
424
 
375
- export { type ArtifactBindingView, ArtifactPinError, type ArtifactPinErrorCode, CredentialInvitationError, type CredentialInvitationErrorCode, type CredentialInvitationReceipt, type CredentialInvitationResult, type ExpectedArtifact, MemoryShareCache, NATIVE_SHARE_FRAGMENT_PARAMETER, type NativeShareResult, type NativeSharingService, type ParsedAddressedEnvelope, SHARE_V2_PROTOCOL, SenderShareRecord, SenderShareRecordStorage, ShareAuthorizationAdapter, ShareAuthorizationResult, type ShareCache, type ShareCacheEntry, type ShareCacheKey, type ShareCacheMetadata, type ShareDeliveryAdapter, type ShareHistoryView, ShareNotifyError, type ShareNotifyInput, type ShareNotifyResult, type ShareNotifyState, type SharePolicyPublishAdapter, type SharePolicyRootRevocation, type ShareRevocationAdapter, type ShareRevocationResult, ShareTarget, TargetPublishInput, TargetPublishOutcome, type VerifyArtifactShareOptions, artifactBindingView, assertArtifactBinding, canonicalShareFilename, claimShare, createNativeShare, createShareV2HolderBindingArtifact, deliverCredentialInvitation, hasUnsafeFilenameCodePoint, listShares, notifyShare, openNativeShare, parseAddressedEnvelope, parseNativeShareUrl, publishPolicyShare, recipientMatchesShareRecord, resumeShareAuthorization, revokeShare, shareDeliveryWindowExpiresAt, showShare, verifyArtifactShare };
425
+ export { type ArtifactBindingView, ArtifactPinError, type ArtifactPinErrorCode, CredentialInvitationError, type CredentialInvitationErrorCode, type CredentialInvitationReceipt, type CredentialInvitationResult, type ExpectedArtifact, MemoryShareCache, NATIVE_SHARE_FRAGMENT_PARAMETER, type NativeShareResult, type NativeSharingService, type ParsedAddressedEnvelope, SHARE_V2_PROTOCOL, SenderShareRecord, SenderShareRecordStorage, ShareAuthorizationAdapter, ShareAuthorizationResult, type ShareCache, type ShareCacheEntry, type ShareCacheKey, type ShareCacheMetadata, type ShareDeliveryAdapter, type ShareHistoryView, ShareNotifyError, type ShareNotifyInput, type ShareNotifyResult, type ShareNotifyState, type SharePolicyPublishAdapter, type SharePolicyRootRevocation, type ShareRevocationAdapter, ShareRevocationIncompleteError, type ShareRevocationResult, type ShareRootRevocationFailure, type ShareRootRevocationState, ShareTarget, TargetPublishInput, TargetPublishOutcome, type VerifyArtifactShareOptions, artifactBindingView, assertArtifactBinding, canonicalShareFilename, claimShare, createNativeShare, createShareV2HolderBindingArtifact, deliverCredentialInvitation, hasUnsafeFilenameCodePoint, listShares, notifyShare, openNativeShare, parseAddressedEnvelope, parseNativeShareUrl, publishPolicyShare, recipientMatchesShareRecord, resumeShareAuthorization, revokeShare, shareDeliveryWindowExpiresAt, showShare, verifyArtifactShare };
package/dist/index.d.ts CHANGED
@@ -141,10 +141,29 @@ declare function deliverCredentialInvitation(input: {
141
141
  readonly signal?: AbortSignal;
142
142
  }): Promise<CredentialInvitationResult>;
143
143
 
144
+ /**
145
+ * One root of an addressed share after `revokeShare`. A `left-live` root is
146
+ * only ever the policy-authority root, whose audience is a policy digest DID
147
+ * with no key: it cannot authorize anything once the enforcement root is
148
+ * revoked, and it lapses at `expiresAt`.
149
+ */
150
+ type ShareRootRevocationState = {
151
+ readonly rootCid: string;
152
+ readonly targetRole: "policy-authority" | "policy-enforcement";
153
+ readonly state: "revoked";
154
+ } | {
155
+ readonly rootCid: string;
156
+ readonly targetRole: "policy-authority";
157
+ readonly state: "left-live";
158
+ readonly expiresAt: string;
159
+ };
144
160
  type ShareRevocationResult = {
145
161
  readonly state: "revoked";
146
162
  readonly target: "bearer" | "recipientDid" | "email" | "emailDomain";
163
+ /** The bearer delegation; for an addressed share its enforcement root (its policy root when the record has no enforcement root). */
147
164
  readonly delegationCid: string;
165
+ /** Addressed shares: every root the share created, with what this revocation did to it. Recipient access has ended. */
166
+ readonly roots?: readonly ShareRootRevocationState[];
148
167
  readonly revokedAt: string;
149
168
  } | {
150
169
  readonly state: "unsupported";
@@ -172,16 +191,47 @@ interface ShareRevocationAdapter {
172
191
  /**
173
192
  * Addressed Policy/v3 authority uses the Node's signed root-revocation
174
193
  * endpoint. The Node checks that root for active sessions, new admission,
175
- * and delivery; this is not a separate share authority plane.
194
+ * and delivery; this is not a separate share authority plane. It resolves
195
+ * when the root is revoked, including when the Node already holds a
196
+ * revocation for it, and rejects otherwise. For the policy-authority root
197
+ * only, it may resolve `"left-live"` instead of revoking it (for example
198
+ * when revoking it would cost the owner another wallet approval).
176
199
  */
177
- revokePolicyRoot?(input: SharePolicyRootRevocation): Promise<void>;
200
+ revokePolicyRoot?(input: SharePolicyRootRevocation): Promise<void | "left-live">;
201
+ }
202
+ /** A root an addressed revocation did not end. */
203
+ interface ShareRootRevocationFailure {
204
+ readonly rootCid: string;
205
+ readonly targetRole: SharePolicyRootRevocation["targetRole"];
206
+ readonly error: unknown;
207
+ }
208
+ /**
209
+ * An addressed revocation did not finish: a root failed, or only the
210
+ * policy-authority root was dealt with. `revoked` names the roots that were
211
+ * revoked; if it is not empty, recipient access has ended, since every
212
+ * recipient request needs both roots. The share record is not marked
213
+ * revoked, so the caller can retry.
214
+ */
215
+ declare class ShareRevocationIncompleteError extends Error {
216
+ readonly revoked: readonly string[];
217
+ readonly failed: readonly ShareRootRevocationFailure[];
218
+ constructor(revoked: readonly string[], failed: readonly ShareRootRevocationFailure[]);
178
219
  }
179
- /** Revoke the native bearer delegation or the selected signed Policy/v3 root. */
220
+ /**
221
+ * Revoke a share. A bearer share revokes the delegation `scope` selects. An
222
+ * addressed share asks the adapter for every root it created, the
223
+ * enforcement root and then the policy root, whatever `scope` says; the
224
+ * adapter may leave the keyless policy-authority root live. It succeeds only
225
+ * when no root failed and at least one was revoked (so recipient access has
226
+ * ended); otherwise it throws {@link ShareRevocationIncompleteError} after
227
+ * trying every root.
228
+ */
180
229
  declare function revokeShare(input: {
181
230
  readonly record: SenderShareRecord;
182
231
  /** Optional durable store; successful revocation is persisted before return. */
183
232
  readonly records?: SenderShareRecordStorage;
184
233
  readonly adapter?: ShareRevocationAdapter;
234
+ /** Bearer shares only. */
185
235
  readonly scope?: "direct" | "ancestor";
186
236
  readonly now?: () => Date;
187
237
  }): Promise<ShareRevocationResult>;
@@ -372,4 +422,4 @@ declare function assertArtifactBinding(view: ArtifactBindingView, expected?: Exp
372
422
  */
373
423
  declare function verifyArtifactShare(envelope: ShareEnvelopeV3, options: VerifyArtifactShareOptions): ArtifactPin | undefined;
374
424
 
375
- export { type ArtifactBindingView, ArtifactPinError, type ArtifactPinErrorCode, CredentialInvitationError, type CredentialInvitationErrorCode, type CredentialInvitationReceipt, type CredentialInvitationResult, type ExpectedArtifact, MemoryShareCache, NATIVE_SHARE_FRAGMENT_PARAMETER, type NativeShareResult, type NativeSharingService, type ParsedAddressedEnvelope, SHARE_V2_PROTOCOL, SenderShareRecord, SenderShareRecordStorage, ShareAuthorizationAdapter, ShareAuthorizationResult, type ShareCache, type ShareCacheEntry, type ShareCacheKey, type ShareCacheMetadata, type ShareDeliveryAdapter, type ShareHistoryView, ShareNotifyError, type ShareNotifyInput, type ShareNotifyResult, type ShareNotifyState, type SharePolicyPublishAdapter, type SharePolicyRootRevocation, type ShareRevocationAdapter, type ShareRevocationResult, ShareTarget, TargetPublishInput, TargetPublishOutcome, type VerifyArtifactShareOptions, artifactBindingView, assertArtifactBinding, canonicalShareFilename, claimShare, createNativeShare, createShareV2HolderBindingArtifact, deliverCredentialInvitation, hasUnsafeFilenameCodePoint, listShares, notifyShare, openNativeShare, parseAddressedEnvelope, parseNativeShareUrl, publishPolicyShare, recipientMatchesShareRecord, resumeShareAuthorization, revokeShare, shareDeliveryWindowExpiresAt, showShare, verifyArtifactShare };
425
+ export { type ArtifactBindingView, ArtifactPinError, type ArtifactPinErrorCode, CredentialInvitationError, type CredentialInvitationErrorCode, type CredentialInvitationReceipt, type CredentialInvitationResult, type ExpectedArtifact, MemoryShareCache, NATIVE_SHARE_FRAGMENT_PARAMETER, type NativeShareResult, type NativeSharingService, type ParsedAddressedEnvelope, SHARE_V2_PROTOCOL, SenderShareRecord, SenderShareRecordStorage, ShareAuthorizationAdapter, ShareAuthorizationResult, type ShareCache, type ShareCacheEntry, type ShareCacheKey, type ShareCacheMetadata, type ShareDeliveryAdapter, type ShareHistoryView, ShareNotifyError, type ShareNotifyInput, type ShareNotifyResult, type ShareNotifyState, type SharePolicyPublishAdapter, type SharePolicyRootRevocation, type ShareRevocationAdapter, ShareRevocationIncompleteError, type ShareRevocationResult, type ShareRootRevocationFailure, type ShareRootRevocationState, ShareTarget, TargetPublishInput, TargetPublishOutcome, type VerifyArtifactShareOptions, artifactBindingView, assertArtifactBinding, canonicalShareFilename, claimShare, createNativeShare, createShareV2HolderBindingArtifact, deliverCredentialInvitation, hasUnsafeFilenameCodePoint, listShares, notifyShare, openNativeShare, parseAddressedEnvelope, parseNativeShareUrl, publishPolicyShare, recipientMatchesShareRecord, resumeShareAuthorization, revokeShare, shareDeliveryWindowExpiresAt, showShare, verifyArtifactShare };
package/dist/index.js CHANGED
@@ -8806,6 +8806,14 @@ async function deliverCredentialInvitation(input) {
8806
8806
  }
8807
8807
 
8808
8808
  // src/lifecycle.ts
8809
+ var ShareRevocationIncompleteError = class extends Error {
8810
+ constructor(revoked, failed) {
8811
+ super(`share revocation incomplete: ${failed.map((failure) => failure.targetRole).join(", ")} not revoked`);
8812
+ this.revoked = revoked;
8813
+ this.failed = failed;
8814
+ this.name = "ShareRevocationIncompleteError";
8815
+ }
8816
+ };
8809
8817
  function targetKind2(record) {
8810
8818
  if (record.targetKind !== void 0) return record.targetKind;
8811
8819
  return record.recipientMatcher.kind === "exactEmail" ? "email" : record.recipientMatcher.kind === "emailDomain" ? "emailDomain" : record.recipientMatcher.kind === "recipientDid" ? "recipientDid" : "bearer";
@@ -8821,22 +8829,49 @@ function policyNodeAudience(record) {
8821
8829
  async function revokeShare(input) {
8822
8830
  const target = targetKind2(input.record);
8823
8831
  if (input.adapter === void 0) return { state: "unsupported", target, reason: "node revocation authority is required", code: "unsupported-target" };
8824
- const scope = input.scope ?? "direct";
8825
- const delegationCid = scope === "ancestor" ? input.record.ownerDelegationCid : input.record.enforcementDelegationCid;
8826
- if (delegationCid === void 0) return { state: "unsupported", target, reason: "share has no node-enforced delegation receipt", code: "unsupported-target" };
8832
+ let delegationCid;
8833
+ let roots;
8827
8834
  if (target === "bearer") {
8835
+ const scope = input.scope ?? "direct";
8836
+ const selected = scope === "ancestor" ? input.record.ownerDelegationCid : input.record.enforcementDelegationCid;
8837
+ if (selected === void 0) return { state: "unsupported", target, reason: "share has no node-enforced delegation receipt", code: "unsupported-target" };
8828
8838
  if (input.adapter.revokeDelegation === void 0) return { state: "unsupported", target, reason: "native delegation revocation authority is required", code: "unsupported-target" };
8829
- await input.adapter.revokeDelegation({ delegationCid, scope, record: input.record });
8839
+ await input.adapter.revokeDelegation({ delegationCid: selected, scope, record: input.record });
8840
+ delegationCid = selected;
8830
8841
  } else {
8831
- if (input.record.ownerDid === void 0) return { state: "unsupported", target, reason: "share has no Policy/v3 owner receipt", code: "unsupported-target" };
8832
- if (input.adapter.revokePolicyRoot === void 0) return { state: "unsupported", target, reason: "Policy/v3 root revocation authority is required", code: "unsupported-target" };
8833
- await input.adapter.revokePolicyRoot({
8834
- rootCid: delegationCid,
8835
- targetRole: scope === "ancestor" ? "policy-authority" : "policy-enforcement",
8836
- ownerDid: input.record.ownerDid,
8837
- nodeOrigin: input.record.target.origin,
8838
- nodeAudience: policyNodeAudience(input.record)
8839
- });
8842
+ const requests = [
8843
+ [input.record.enforcementDelegationCid, "policy-enforcement"],
8844
+ [input.record.ownerDelegationCid, "policy-authority"]
8845
+ ].flatMap(([rootCid, targetRole]) => rootCid === void 0 ? [] : [{ rootCid, targetRole }]);
8846
+ const first = requests[0];
8847
+ if (first === void 0) return { state: "unsupported", target, reason: "share has no node-enforced delegation receipt", code: "unsupported-target" };
8848
+ const ownerDid = input.record.ownerDid;
8849
+ if (ownerDid === void 0) return { state: "unsupported", target, reason: "share has no Policy/v3 owner receipt", code: "unsupported-target" };
8850
+ const revokePolicyRoot = input.adapter.revokePolicyRoot;
8851
+ if (revokePolicyRoot === void 0) return { state: "unsupported", target, reason: "Policy/v3 root revocation authority is required", code: "unsupported-target" };
8852
+ const nodeAudience = policyNodeAudience(input.record);
8853
+ roots = [];
8854
+ const failed = [];
8855
+ for (const request of requests) {
8856
+ try {
8857
+ const outcome = await revokePolicyRoot({ ...request, ownerDid, nodeOrigin: input.record.target.origin, nodeAudience });
8858
+ if (outcome === "left-live") {
8859
+ if (request.targetRole !== "policy-authority") throw new Error("the enforcement root cannot be left live");
8860
+ roots.push({ rootCid: request.rootCid, targetRole: "policy-authority", state: "left-live", expiresAt: input.record.expiresAt });
8861
+ } else if (outcome === void 0) {
8862
+ roots.push({ ...request, state: "revoked" });
8863
+ } else {
8864
+ throw new Error("root revocation returned an unknown outcome");
8865
+ }
8866
+ } catch (error) {
8867
+ failed.push({ ...request, error });
8868
+ }
8869
+ }
8870
+ const revoked = roots.flatMap((root) => root.state === "revoked" ? [root.rootCid] : []);
8871
+ if (failed.length > 0 || revoked.length === 0) {
8872
+ throw new ShareRevocationIncompleteError(revoked, failed.length > 0 ? failed : roots.map((root) => ({ rootCid: root.rootCid, targetRole: root.targetRole, error: new Error("no root was revoked") })));
8873
+ }
8874
+ delegationCid = first.rootCid;
8840
8875
  }
8841
8876
  let revokedAt = input.record.revokedAt ?? (input.now?.() ?? /* @__PURE__ */ new Date()).toISOString();
8842
8877
  if (input.records?.update !== void 0) {
@@ -8847,7 +8882,7 @@ async function revokeShare(input) {
8847
8882
  revokedAt = latest?.revokedAt ?? revokedAt;
8848
8883
  await input.records.put({ ...latest ?? input.record, revokedAt });
8849
8884
  }
8850
- return { state: "revoked", target, delegationCid, revokedAt };
8885
+ return { state: "revoked", target, delegationCid, ...roots === void 0 ? {} : { roots }, revokedAt };
8851
8886
  }
8852
8887
  function redactRecord(record, revealLink, link) {
8853
8888
  const matcher = record.recipientMatcher;
@@ -9436,6 +9471,7 @@ export {
9436
9471
  SharePublishError,
9437
9472
  ShareReceiveError,
9438
9473
  ShareRecipientClient,
9474
+ ShareRevocationIncompleteError,
9439
9475
  addressedCredentialRequirement,
9440
9476
  addressedShareBodyUpperBound,
9441
9477
  artifactBindingView,