@tinycloud/node-sdk 3.7.0-beta.1 → 3.7.0-beta.3

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
@@ -21681,7 +21681,10 @@ var _TinyCloudNode = class _TinyCloudNode {
21681
21681
  * and remembers whose storage is full so sign-in and listing skip their
21682
21682
  * best-effort writes. Lives as long as this instance, across sign-ins.
21683
21683
  */
21684
- this.storageFull = new import_sdk_core10.StorageFullMonitor((spaceId) => spaceOwnerKey(spaceId) ?? spaceId);
21684
+ this.storageFull = new import_sdk_core10.StorageFullMonitor(
21685
+ (spaceId) => spaceOwnerKey(spaceId) ?? spaceId,
21686
+ () => this.currentStorageOwner()
21687
+ );
21685
21688
  this.invokeWithRuntimePermissions = (session, service, path, action, facts) => {
21686
21689
  const operation = {
21687
21690
  spaceId: session.spaceId,
@@ -21713,17 +21716,7 @@ var _TinyCloudNode = class _TinyCloudNode {
21713
21716
  throw new Error("WASM binding does not support invokeAny");
21714
21717
  }
21715
21718
  const routedEntries = entries;
21716
- const operations = routedEntries.flatMap((entry) => {
21717
- const operation = this.operationFromInvokeAnyEntry(entry);
21718
- return operation ? [operation] : [];
21719
- });
21720
- const authorityCid = routedEntries.length === 1 ? routedEntries[0]?.revokeAuthorityCid : void 0;
21721
- const explicitGrant = authorityCid === void 0 ? void 0 : this.runtimePermissionGrants.find((grant2) => grant2.delegation.cid === authorityCid);
21722
- if (authorityCid !== void 0 && !explicitGrant) {
21723
- throw new Error(`Explicit revocation authority ${authorityCid} is not installed.`);
21724
- }
21725
- const grant = explicitGrant ?? this.findGrantForOperations(operations);
21726
- const invocationSession = explicitGrant ? explicitGrant.session : !grant || grant.provenance === "primary" ? session : grant.session;
21719
+ const { session: invocationSession, grant } = this.selectInvokeAnySession(session, entries);
21727
21720
  const caveatPreservingEntries = routedEntries.map(({ revokeAuthorityCid: _cid, ...entry }) => {
21728
21721
  const requested = this.operationFromInvokeAnyEntry(entry);
21729
21722
  const granted = grant && requested && grant.operations.find(
@@ -21847,12 +21840,15 @@ var _TinyCloudNode = class _TinyCloudNode {
21847
21840
  return this._bootstrapStatus;
21848
21841
  }
21849
21842
  /**
21850
- * Subscribe to `storage.full`. It fires once, on the first write any
21851
- * service had rejected because the owner's TinyCloud storage is full
21843
+ * Subscribe to `storage.full`. It fires on the first write any service had
21844
+ * rejected because the owner's TinyCloud storage is full
21852
21845
  * (`STORAGE_QUOTA_EXCEEDED` or `STORAGE_LIMIT_REACHED`), so an app can show
21853
- * one read-only banner. Reads keep working. It fires again only after
21854
- * `storage.status()` has reported space again for every owner whose write
21855
- * was rejected. Returns the unsubscribe function.
21846
+ * one read-only banner. Reads keep working. For the signed-in account's own
21847
+ * storage it fires once per episode: the episode ends only when a write to
21848
+ * that storage succeeds, or on sign-out. A `storage.status()` read does not
21849
+ * end it. A rejection on another owner's delegated space fires once per
21850
+ * owner, until that owner's `storage.status()` reports space again. Returns
21851
+ * the unsubscribe function.
21856
21852
  */
21857
21853
  on(event, handler) {
21858
21854
  if (event !== "storage.full") {
@@ -21860,6 +21856,21 @@ var _TinyCloudNode = class _TinyCloudNode {
21860
21856
  }
21861
21857
  return this.storageFull.on(handler);
21862
21858
  }
21859
+ /**
21860
+ * Storage episode state for the UI mount. @internal
21861
+ *
21862
+ * `current()` returns the own-storage rejection that started the episode, or
21863
+ * undefined. `subscribe` calls its listener when the own episode starts or
21864
+ * ends; read `current()` inside it. `subscribeDelegated` calls its listener
21865
+ * once per delegated owner found full, until that owner is cleared.
21866
+ */
21867
+ get storageEpisode() {
21868
+ return {
21869
+ current: () => this.storageFull.rejection,
21870
+ subscribe: (listener) => this.storageFull.onEpisode(listener),
21871
+ subscribeDelegated: (listener) => this.storageFull.onDelegated(listener)
21872
+ };
21873
+ }
21863
21874
  /** Owner key of the account the current session's own storage belongs to. */
21864
21875
  currentStorageOwner() {
21865
21876
  return spaceOwnerKey(this._serviceContext?.session?.spaceId ?? this.currentTinyCloudSession()?.spaceId);
@@ -21961,6 +21972,27 @@ var _TinyCloudNode = class _TinyCloudNode {
21961
21972
  get siweDomain() {
21962
21973
  return this.config.domain ?? "app.tinycloud.xyz";
21963
21974
  }
21975
+ /**
21976
+ * The session (key, issuer, proof) that signs an invokeAny call for `entries`:
21977
+ * the explicit revocation authority, else the covering non-primary runtime
21978
+ * grant, else the caller's session. Everything that re-signs the resulting
21979
+ * invocation must use this session's key.
21980
+ */
21981
+ selectInvokeAnySession(session, entries) {
21982
+ const routedEntries = entries;
21983
+ const operations = routedEntries.flatMap((entry) => {
21984
+ const operation = this.operationFromInvokeAnyEntry(entry);
21985
+ return operation ? [operation] : [];
21986
+ });
21987
+ const authorityCid = routedEntries.length === 1 ? routedEntries[0]?.revokeAuthorityCid : void 0;
21988
+ const explicitGrant = authorityCid === void 0 ? void 0 : this.runtimePermissionGrants.find((grant2) => grant2.delegation.cid === authorityCid);
21989
+ if (authorityCid !== void 0 && !explicitGrant) {
21990
+ throw new Error(`Explicit revocation authority ${authorityCid} is not installed.`);
21991
+ }
21992
+ const grant = explicitGrant ?? this.findGrantForOperations(operations);
21993
+ const invocationSession = explicitGrant ? explicitGrant.session : !grant || grant.provenance === "primary" ? session : grant.session;
21994
+ return { session: invocationSession, grant };
21995
+ }
21964
21996
  /**
21965
21997
  * SharingService for receive-only access (no session required), bound to the
21966
21998
  * current service graph. This lets session-only users receive sharing links
@@ -24380,7 +24412,13 @@ var _TinyCloudNode = class _TinyCloudNode {
24380
24412
  async bindInvocationAudience(authorization, audience) {
24381
24413
  const session = this.currentTinyCloudSession() ?? this._activeServiceSession;
24382
24414
  if (session === void 0) throw new Error("Not signed in. Call signIn() first.");
24383
- return rewriteInvocationAudience(authorization, audience, session.jwk);
24415
+ const payload = JSON.parse(
24416
+ new TextDecoder().decode(base64UrlDecode(authorization.split(".")[1] ?? ""))
24417
+ );
24418
+ const did = (value) => String(value).split("#", 1)[0];
24419
+ const signer = [session, ...this.runtimePermissionGrants.map((grant) => grant.session)].find((candidate) => did(candidate.verificationMethod) === did(payload.iss));
24420
+ if (!signer) throw new Error("invocation issuer does not match a held session");
24421
+ return rewriteInvocationAudience(authorization, audience, signer.jwk);
24384
24422
  }
24385
24423
  /**
24386
24424
  * Connect a wallet to upgrade from session-only mode to wallet mode.
@@ -24715,23 +24753,25 @@ var _TinyCloudNode = class _TinyCloudNode {
24715
24753
  throw new Error("WASM binding does not support invocation CID computation");
24716
24754
  }
24717
24755
  const session = binding?.session ?? this.requireEncryptionSession();
24756
+ const entries = [
24757
+ {
24758
+ resource: input.networkId,
24759
+ service: "encryption",
24760
+ path: input.networkId,
24761
+ action: input.action
24762
+ }
24763
+ ];
24718
24764
  const headers = this.invokeAnyWithRuntimePermissions(
24719
24765
  session,
24720
- [
24721
- {
24722
- resource: input.networkId,
24723
- service: "encryption",
24724
- path: input.networkId,
24725
- action: input.action
24726
- }
24727
- ],
24766
+ entries,
24728
24767
  [input.facts]
24729
24768
  );
24769
+ const signingSession = this.selectInvokeAnySession(session, entries).session;
24730
24770
  const authorization = authorizationHeader(headers);
24731
24771
  const audienceBound = await rewriteInvocationAudience(
24732
24772
  authorization,
24733
24773
  input.targetNode,
24734
- session.jwk
24774
+ signingSession.jwk
24735
24775
  );
24736
24776
  binding?.graph.assertActive();
24737
24777
  return {
@@ -24784,7 +24824,11 @@ var _TinyCloudNode = class _TinyCloudNode {
24784
24824
  path: networkId,
24785
24825
  actions: ["tinycloud.encryption/decrypt"]
24786
24826
  }) : void 0;
24787
- throw new import_sdk_core10.DecryptTransportResponseError(response.status, permissionHint);
24827
+ throw new import_sdk_core10.DecryptTransportResponseError(
24828
+ response.status,
24829
+ permissionHint,
24830
+ response.status === 404 && networkId === this.getDefaultEncryptionNetworkId() && await response.text().catch(() => "") === "network not found" ? `Network not found: ${networkId}. Sign in again with a current SDK to create the account's encryption network.` : void 0
24831
+ );
24788
24832
  }
24789
24833
  return await response.json();
24790
24834
  }