@tinycloud/node-sdk 2.4.0-beta.15 → 2.4.0-beta.17

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.
@@ -1047,15 +1047,44 @@ declare class TinyCloudNode {
1047
1047
  * registered on the node.
1048
1048
  *
1049
1049
  * Calling this resolves `name` to the owner's owned-space URI
1050
- * (`tinycloud:pkh:eip155:<chain>:<addr>:<name>`) and hosts it via the
1051
- * host-SIWE delegation flow. The host SIWE is idempotent server-side, so it
1052
- * is safe to call whether or not the space already exists; do not gate it on
1053
- * a prior existence check. Must be called after {@link signIn}.
1050
+ * (`tinycloud:pkh:eip155:<chain>:<addr>:<name>`). It first consults the
1051
+ * account-space spaces registry (`account/spaces/{space_id}`, the canonical
1052
+ * KV source of truth, fronted by a best-effort SQLite index): if the space is
1053
+ * already registered/hosted it returns the URI WITHOUT submitting a host
1054
+ * delegation, avoiding a redundant host-SIWE signature prompt for owners who
1055
+ * already use the space. Only when the space is absent — or the registry
1056
+ * check fails for any reason (e.g. a cold SQLite index reporting
1057
+ * `no such table: spaces`) — does it fall through to {@link hostOwnedSpace}.
1058
+ *
1059
+ * The registry check is purely an optimization: any failure falls back to
1060
+ * hosting, and the host SIWE is idempotent server-side, so re-hosting an
1061
+ * existing space remains a safe no-op. Must be called after {@link signIn}.
1054
1062
  *
1055
1063
  * @param name - The owned space name (e.g. `"secrets"`).
1056
1064
  * @returns The hosted owned-space URI.
1057
1065
  */
1058
1066
  ensureOwnedSpaceHosted(name: string): Promise<string>;
1067
+ /**
1068
+ * Check whether an owned space is already registered/hosted by consulting the
1069
+ * account spaces registry.
1070
+ *
1071
+ * Source of truth is the canonical KV registry record
1072
+ * `account/spaces/{space_id}`, read here via `account.spaces.get(spaceId)`.
1073
+ * The KV path is used (rather than `syncAccessible()`) because it works under
1074
+ * a manifest/recap session with NO extra prompt: the composed manifest recap
1075
+ * already grants `tinycloud.kv get/list` on the account space `spaces/`
1076
+ * prefix, whereas `syncAccessible()` depends on `tinycloud.space/list`, which
1077
+ * a recap session does not hold. Before reading, it consults the fast SQLite
1078
+ * index (`account.index.spaces.list()`) as a best-effort short-circuit; on a
1079
+ * cold index (`no such table: spaces`) or any other index failure it falls
1080
+ * back to the canonical KV read.
1081
+ *
1082
+ * This is a best-effort optimization. ANY failure of the check path (missing
1083
+ * table, KV error, missing record, thrown exception) resolves to `false` so
1084
+ * the caller falls through to hosting — per the directive, "if it fails in any
1085
+ * way then create the space".
1086
+ */
1087
+ private isOwnedSpaceRegistered;
1059
1088
  /**
1060
1089
  * Restore a previously established session from stored delegation data.
1061
1090
  *
@@ -1047,15 +1047,44 @@ declare class TinyCloudNode {
1047
1047
  * registered on the node.
1048
1048
  *
1049
1049
  * Calling this resolves `name` to the owner's owned-space URI
1050
- * (`tinycloud:pkh:eip155:<chain>:<addr>:<name>`) and hosts it via the
1051
- * host-SIWE delegation flow. The host SIWE is idempotent server-side, so it
1052
- * is safe to call whether or not the space already exists; do not gate it on
1053
- * a prior existence check. Must be called after {@link signIn}.
1050
+ * (`tinycloud:pkh:eip155:<chain>:<addr>:<name>`). It first consults the
1051
+ * account-space spaces registry (`account/spaces/{space_id}`, the canonical
1052
+ * KV source of truth, fronted by a best-effort SQLite index): if the space is
1053
+ * already registered/hosted it returns the URI WITHOUT submitting a host
1054
+ * delegation, avoiding a redundant host-SIWE signature prompt for owners who
1055
+ * already use the space. Only when the space is absent — or the registry
1056
+ * check fails for any reason (e.g. a cold SQLite index reporting
1057
+ * `no such table: spaces`) — does it fall through to {@link hostOwnedSpace}.
1058
+ *
1059
+ * The registry check is purely an optimization: any failure falls back to
1060
+ * hosting, and the host SIWE is idempotent server-side, so re-hosting an
1061
+ * existing space remains a safe no-op. Must be called after {@link signIn}.
1054
1062
  *
1055
1063
  * @param name - The owned space name (e.g. `"secrets"`).
1056
1064
  * @returns The hosted owned-space URI.
1057
1065
  */
1058
1066
  ensureOwnedSpaceHosted(name: string): Promise<string>;
1067
+ /**
1068
+ * Check whether an owned space is already registered/hosted by consulting the
1069
+ * account spaces registry.
1070
+ *
1071
+ * Source of truth is the canonical KV registry record
1072
+ * `account/spaces/{space_id}`, read here via `account.spaces.get(spaceId)`.
1073
+ * The KV path is used (rather than `syncAccessible()`) because it works under
1074
+ * a manifest/recap session with NO extra prompt: the composed manifest recap
1075
+ * already grants `tinycloud.kv get/list` on the account space `spaces/`
1076
+ * prefix, whereas `syncAccessible()` depends on `tinycloud.space/list`, which
1077
+ * a recap session does not hold. Before reading, it consults the fast SQLite
1078
+ * index (`account.index.spaces.list()`) as a best-effort short-circuit; on a
1079
+ * cold index (`no such table: spaces`) or any other index failure it falls
1080
+ * back to the canonical KV read.
1081
+ *
1082
+ * This is a best-effort optimization. ANY failure of the check path (missing
1083
+ * table, KV error, missing record, thrown exception) resolves to `false` so
1084
+ * the caller falls through to hosting — per the directive, "if it fails in any
1085
+ * way then create the space".
1086
+ */
1087
+ private isOwnedSpaceRegistered;
1059
1088
  /**
1060
1089
  * Restore a previously established session from stored delegation data.
1061
1090
  *
package/dist/core.cjs CHANGED
@@ -2027,6 +2027,7 @@ var _TinyCloudNode = class _TinyCloudNode {
2027
2027
  }
2028
2028
  scheduleAccountRegistrySync() {
2029
2029
  void this.withAccountRegistryRetry(async () => {
2030
+ void this.account.index.ensure();
2030
2031
  await this.writeManifestRegistryRecords();
2031
2032
  const spaces = await this.account.spaces.syncAccessible();
2032
2033
  if (!spaces.ok) {
@@ -2178,16 +2179,78 @@ var _TinyCloudNode = class _TinyCloudNode {
2178
2179
  * registered on the node.
2179
2180
  *
2180
2181
  * Calling this resolves `name` to the owner's owned-space URI
2181
- * (`tinycloud:pkh:eip155:<chain>:<addr>:<name>`) and hosts it via the
2182
- * host-SIWE delegation flow. The host SIWE is idempotent server-side, so it
2183
- * is safe to call whether or not the space already exists; do not gate it on
2184
- * a prior existence check. Must be called after {@link signIn}.
2182
+ * (`tinycloud:pkh:eip155:<chain>:<addr>:<name>`). It first consults the
2183
+ * account-space spaces registry (`account/spaces/{space_id}`, the canonical
2184
+ * KV source of truth, fronted by a best-effort SQLite index): if the space is
2185
+ * already registered/hosted it returns the URI WITHOUT submitting a host
2186
+ * delegation, avoiding a redundant host-SIWE signature prompt for owners who
2187
+ * already use the space. Only when the space is absent — or the registry
2188
+ * check fails for any reason (e.g. a cold SQLite index reporting
2189
+ * `no such table: spaces`) — does it fall through to {@link hostOwnedSpace}.
2190
+ *
2191
+ * The registry check is purely an optimization: any failure falls back to
2192
+ * hosting, and the host SIWE is idempotent server-side, so re-hosting an
2193
+ * existing space remains a safe no-op. Must be called after {@link signIn}.
2185
2194
  *
2186
2195
  * @param name - The owned space name (e.g. `"secrets"`).
2187
2196
  * @returns The hosted owned-space URI.
2188
2197
  */
2189
2198
  async ensureOwnedSpaceHosted(name) {
2190
- return this.hostOwnedSpace(name);
2199
+ if (!this.auth || !this.auth.tinyCloudSession) {
2200
+ throw new Error("Not signed in. Call signIn() first.");
2201
+ }
2202
+ const spaceId = this.ownedSpaceId(name);
2203
+ if (await this.isOwnedSpaceRegistered(spaceId)) {
2204
+ return spaceId;
2205
+ }
2206
+ const hosted = await this.hostOwnedSpace(name);
2207
+ try {
2208
+ await this.account.spaces.register({
2209
+ spaceId,
2210
+ name,
2211
+ ownerDid: this.did,
2212
+ type: "owned",
2213
+ permissions: ["*"],
2214
+ status: "active"
2215
+ });
2216
+ } catch {
2217
+ }
2218
+ return hosted;
2219
+ }
2220
+ /**
2221
+ * Check whether an owned space is already registered/hosted by consulting the
2222
+ * account spaces registry.
2223
+ *
2224
+ * Source of truth is the canonical KV registry record
2225
+ * `account/spaces/{space_id}`, read here via `account.spaces.get(spaceId)`.
2226
+ * The KV path is used (rather than `syncAccessible()`) because it works under
2227
+ * a manifest/recap session with NO extra prompt: the composed manifest recap
2228
+ * already grants `tinycloud.kv get/list` on the account space `spaces/`
2229
+ * prefix, whereas `syncAccessible()` depends on `tinycloud.space/list`, which
2230
+ * a recap session does not hold. Before reading, it consults the fast SQLite
2231
+ * index (`account.index.spaces.list()`) as a best-effort short-circuit; on a
2232
+ * cold index (`no such table: spaces`) or any other index failure it falls
2233
+ * back to the canonical KV read.
2234
+ *
2235
+ * This is a best-effort optimization. ANY failure of the check path (missing
2236
+ * table, KV error, missing record, thrown exception) resolves to `false` so
2237
+ * the caller falls through to hosting — per the directive, "if it fails in any
2238
+ * way then create the space".
2239
+ */
2240
+ async isOwnedSpaceRegistered(spaceId) {
2241
+ try {
2242
+ const indexed = await this.account.index.spaces.list();
2243
+ if (indexed.ok && indexed.data.some((space) => space.spaceId === spaceId)) {
2244
+ return true;
2245
+ }
2246
+ } catch {
2247
+ }
2248
+ try {
2249
+ const record = await this.account.spaces.get(spaceId);
2250
+ return record.ok;
2251
+ } catch {
2252
+ return false;
2253
+ }
2191
2254
  }
2192
2255
  /**
2193
2256
  * Restore a previously established session from stored delegation data.