@tinycloud/sdk-core 2.0.0 → 2.0.2-beta.0

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.
Files changed (188) hide show
  1. package/dist/index.cjs +3816 -0
  2. package/dist/index.cjs.map +1 -0
  3. package/dist/index.d.cts +3867 -0
  4. package/dist/index.d.ts +3861 -21
  5. package/dist/index.js +3767 -61
  6. package/dist/index.js.map +1 -1
  7. package/package.json +6 -5
  8. package/dist/TinyCloud.d.ts +0 -271
  9. package/dist/TinyCloud.d.ts.map +0 -1
  10. package/dist/TinyCloud.js +0 -458
  11. package/dist/TinyCloud.js.map +0 -1
  12. package/dist/TinyCloud.schema.d.ts +0 -173
  13. package/dist/TinyCloud.schema.d.ts.map +0 -1
  14. package/dist/TinyCloud.schema.js +0 -136
  15. package/dist/TinyCloud.schema.js.map +0 -1
  16. package/dist/TinyCloud.schema.test.d.ts +0 -5
  17. package/dist/TinyCloud.schema.test.d.ts.map +0 -1
  18. package/dist/TinyCloud.schema.test.js +0 -286
  19. package/dist/TinyCloud.schema.test.js.map +0 -1
  20. package/dist/authorization/CapabilityKeyRegistry.d.ts +0 -317
  21. package/dist/authorization/CapabilityKeyRegistry.d.ts.map +0 -1
  22. package/dist/authorization/CapabilityKeyRegistry.js +0 -509
  23. package/dist/authorization/CapabilityKeyRegistry.js.map +0 -1
  24. package/dist/authorization/authorization.schema.d.ts +0 -233
  25. package/dist/authorization/authorization.schema.d.ts.map +0 -1
  26. package/dist/authorization/authorization.schema.js +0 -220
  27. package/dist/authorization/authorization.schema.js.map +0 -1
  28. package/dist/authorization/authorization.schema.test.d.ts +0 -5
  29. package/dist/authorization/authorization.schema.test.d.ts.map +0 -1
  30. package/dist/authorization/authorization.schema.test.js +0 -618
  31. package/dist/authorization/authorization.schema.test.js.map +0 -1
  32. package/dist/authorization/index.d.ts +0 -38
  33. package/dist/authorization/index.d.ts.map +0 -1
  34. package/dist/authorization/index.js +0 -52
  35. package/dist/authorization/index.js.map +0 -1
  36. package/dist/authorization/spaceCreation.d.ts +0 -96
  37. package/dist/authorization/spaceCreation.d.ts.map +0 -1
  38. package/dist/authorization/spaceCreation.js +0 -35
  39. package/dist/authorization/spaceCreation.js.map +0 -1
  40. package/dist/authorization/spaceCreation.schema.d.ts +0 -67
  41. package/dist/authorization/spaceCreation.schema.d.ts.map +0 -1
  42. package/dist/authorization/spaceCreation.schema.js +0 -95
  43. package/dist/authorization/spaceCreation.schema.js.map +0 -1
  44. package/dist/authorization/spaceCreation.schema.test.d.ts +0 -5
  45. package/dist/authorization/spaceCreation.schema.test.d.ts.map +0 -1
  46. package/dist/authorization/spaceCreation.schema.test.js +0 -168
  47. package/dist/authorization/spaceCreation.schema.test.js.map +0 -1
  48. package/dist/authorization/strategies.d.ts +0 -134
  49. package/dist/authorization/strategies.d.ts.map +0 -1
  50. package/dist/authorization/strategies.js +0 -15
  51. package/dist/authorization/strategies.js.map +0 -1
  52. package/dist/authorization/strategies.schema.d.ts +0 -185
  53. package/dist/authorization/strategies.schema.d.ts.map +0 -1
  54. package/dist/authorization/strategies.schema.js +0 -147
  55. package/dist/authorization/strategies.schema.js.map +0 -1
  56. package/dist/authorization/strategies.schema.test.d.ts +0 -5
  57. package/dist/authorization/strategies.schema.test.d.ts.map +0 -1
  58. package/dist/authorization/strategies.schema.test.js +0 -253
  59. package/dist/authorization/strategies.schema.test.js.map +0 -1
  60. package/dist/client-types.d.ts +0 -128
  61. package/dist/client-types.d.ts.map +0 -1
  62. package/dist/client-types.js +0 -40
  63. package/dist/client-types.js.map +0 -1
  64. package/dist/delegations/DelegationManager.d.ts +0 -164
  65. package/dist/delegations/DelegationManager.d.ts.map +0 -1
  66. package/dist/delegations/DelegationManager.js +0 -428
  67. package/dist/delegations/DelegationManager.js.map +0 -1
  68. package/dist/delegations/SharingService.d.ts +0 -341
  69. package/dist/delegations/SharingService.d.ts.map +0 -1
  70. package/dist/delegations/SharingService.js +0 -722
  71. package/dist/delegations/SharingService.js.map +0 -1
  72. package/dist/delegations/SharingService.schema.d.ts +0 -409
  73. package/dist/delegations/SharingService.schema.d.ts.map +0 -1
  74. package/dist/delegations/SharingService.schema.js +0 -222
  75. package/dist/delegations/SharingService.schema.js.map +0 -1
  76. package/dist/delegations/index.d.ts +0 -38
  77. package/dist/delegations/index.d.ts.map +0 -1
  78. package/dist/delegations/index.js +0 -42
  79. package/dist/delegations/index.js.map +0 -1
  80. package/dist/delegations/types.d.ts +0 -13
  81. package/dist/delegations/types.d.ts.map +0 -1
  82. package/dist/delegations/types.js +0 -42
  83. package/dist/delegations/types.js.map +0 -1
  84. package/dist/delegations/types.schema.d.ts +0 -1773
  85. package/dist/delegations/types.schema.d.ts.map +0 -1
  86. package/dist/delegations/types.schema.js +0 -535
  87. package/dist/delegations/types.schema.js.map +0 -1
  88. package/dist/delegations/types.schema.test.d.ts +0 -5
  89. package/dist/delegations/types.schema.test.d.ts.map +0 -1
  90. package/dist/delegations/types.schema.test.js +0 -627
  91. package/dist/delegations/types.schema.test.js.map +0 -1
  92. package/dist/ens.d.ts +0 -17
  93. package/dist/ens.d.ts.map +0 -1
  94. package/dist/ens.js +0 -10
  95. package/dist/ens.js.map +0 -1
  96. package/dist/index.d.ts.map +0 -1
  97. package/dist/json-schema.d.ts +0 -327
  98. package/dist/json-schema.d.ts.map +0 -1
  99. package/dist/json-schema.js +0 -703
  100. package/dist/json-schema.js.map +0 -1
  101. package/dist/json-schema.test.d.ts +0 -7
  102. package/dist/json-schema.test.d.ts.map +0 -1
  103. package/dist/json-schema.test.js +0 -365
  104. package/dist/json-schema.test.js.map +0 -1
  105. package/dist/notifications.d.ts +0 -33
  106. package/dist/notifications.d.ts.map +0 -1
  107. package/dist/notifications.js +0 -15
  108. package/dist/notifications.js.map +0 -1
  109. package/dist/signer.d.ts +0 -28
  110. package/dist/signer.d.ts.map +0 -1
  111. package/dist/signer.js +0 -2
  112. package/dist/signer.js.map +0 -1
  113. package/dist/space.d.ts +0 -57
  114. package/dist/space.d.ts.map +0 -1
  115. package/dist/space.js +0 -87
  116. package/dist/space.js.map +0 -1
  117. package/dist/space.schema.d.ts +0 -65
  118. package/dist/space.schema.d.ts.map +0 -1
  119. package/dist/space.schema.js +0 -65
  120. package/dist/space.schema.js.map +0 -1
  121. package/dist/space.schema.test.d.ts +0 -5
  122. package/dist/space.schema.test.d.ts.map +0 -1
  123. package/dist/space.schema.test.js +0 -148
  124. package/dist/space.schema.test.js.map +0 -1
  125. package/dist/space.test.d.ts +0 -5
  126. package/dist/space.test.d.ts.map +0 -1
  127. package/dist/space.test.js +0 -87
  128. package/dist/space.test.js.map +0 -1
  129. package/dist/spaces/Space.d.ts +0 -175
  130. package/dist/spaces/Space.d.ts.map +0 -1
  131. package/dist/spaces/Space.js +0 -84
  132. package/dist/spaces/Space.js.map +0 -1
  133. package/dist/spaces/SpaceService.d.ts +0 -291
  134. package/dist/spaces/SpaceService.d.ts.map +0 -1
  135. package/dist/spaces/SpaceService.js +0 -740
  136. package/dist/spaces/SpaceService.js.map +0 -1
  137. package/dist/spaces/index.d.ts +0 -11
  138. package/dist/spaces/index.d.ts.map +0 -1
  139. package/dist/spaces/index.js +0 -22
  140. package/dist/spaces/index.js.map +0 -1
  141. package/dist/spaces/spaces.schema.d.ts +0 -421
  142. package/dist/spaces/spaces.schema.d.ts.map +0 -1
  143. package/dist/spaces/spaces.schema.js +0 -342
  144. package/dist/spaces/spaces.schema.js.map +0 -1
  145. package/dist/spaces/spaces.schema.test.d.ts +0 -5
  146. package/dist/spaces/spaces.schema.test.d.ts.map +0 -1
  147. package/dist/spaces/spaces.schema.test.js +0 -471
  148. package/dist/spaces/spaces.schema.test.js.map +0 -1
  149. package/dist/storage.d.ts +0 -47
  150. package/dist/storage.d.ts.map +0 -1
  151. package/dist/storage.js +0 -14
  152. package/dist/storage.js.map +0 -1
  153. package/dist/storage.schema.d.ts +0 -291
  154. package/dist/storage.schema.d.ts.map +0 -1
  155. package/dist/storage.schema.js +0 -189
  156. package/dist/storage.schema.js.map +0 -1
  157. package/dist/storage.schema.test.d.ts +0 -5
  158. package/dist/storage.schema.test.d.ts.map +0 -1
  159. package/dist/storage.schema.test.js +0 -346
  160. package/dist/storage.schema.test.js.map +0 -1
  161. package/dist/userAuthorization.d.ts +0 -117
  162. package/dist/userAuthorization.d.ts.map +0 -1
  163. package/dist/userAuthorization.js +0 -3
  164. package/dist/userAuthorization.js.map +0 -1
  165. package/dist/userAuthorization.schema.d.ts +0 -260
  166. package/dist/userAuthorization.schema.d.ts.map +0 -1
  167. package/dist/userAuthorization.schema.js +0 -169
  168. package/dist/userAuthorization.schema.js.map +0 -1
  169. package/dist/userAuthorization.schema.test.d.ts +0 -5
  170. package/dist/userAuthorization.schema.test.d.ts.map +0 -1
  171. package/dist/userAuthorization.schema.test.js +0 -356
  172. package/dist/userAuthorization.schema.test.js.map +0 -1
  173. package/dist/version.d.ts +0 -32
  174. package/dist/version.d.ts.map +0 -1
  175. package/dist/version.js +0 -59
  176. package/dist/version.js.map +0 -1
  177. package/dist/wasm-validation.d.ts +0 -291
  178. package/dist/wasm-validation.d.ts.map +0 -1
  179. package/dist/wasm-validation.js +0 -221
  180. package/dist/wasm-validation.js.map +0 -1
  181. package/dist/wasm-validation.test.d.ts +0 -5
  182. package/dist/wasm-validation.test.d.ts.map +0 -1
  183. package/dist/wasm-validation.test.js +0 -233
  184. package/dist/wasm-validation.test.js.map +0 -1
  185. package/dist/wasm.d.ts +0 -66
  186. package/dist/wasm.d.ts.map +0 -1
  187. package/dist/wasm.js +0 -10
  188. package/dist/wasm.js.map +0 -1
@@ -1,740 +0,0 @@
1
- /**
2
- * SpaceService - Global singleton for managing spaces (owned and delegated).
3
- *
4
- * SpaceService provides a unified interface for discovering, creating,
5
- * and accessing spaces. It handles both owned spaces (created by the user)
6
- * and delegated spaces (shared by other users).
7
- *
8
- * @packageDocumentation
9
- */
10
- import { ok, err, serviceError } from "@tinycloud/sdk-services";
11
- import { Space, } from "./Space";
12
- import { validateServerDelegationsResponse, validateServerOwnedSpacesResponse, validateServerCreateSpaceResponse, validateServerSpaceInfoResponse, } from "./spaces.schema.js";
13
- // =============================================================================
14
- // Service Name and Error Codes
15
- // =============================================================================
16
- const SERVICE_NAME = "space";
17
- /**
18
- * Error codes for SpaceService operations.
19
- */
20
- export const SpaceErrorCodes = {
21
- /** Space not found */
22
- NOT_FOUND: "SPACE_NOT_FOUND",
23
- /** Space already exists */
24
- ALREADY_EXISTS: "SPACE_ALREADY_EXISTS",
25
- /** Creation failed */
26
- CREATION_FAILED: "SPACE_CREATION_FAILED",
27
- /** Authentication required */
28
- AUTH_REQUIRED: "AUTH_REQUIRED",
29
- /** Invalid space name or URI */
30
- INVALID_NAME: "INVALID_SPACE_NAME",
31
- /** Network error */
32
- NETWORK_ERROR: "NETWORK_ERROR",
33
- /** Not initialized */
34
- NOT_INITIALIZED: "NOT_INITIALIZED",
35
- };
36
- // =============================================================================
37
- // Public Space Utilities
38
- // =============================================================================
39
- /**
40
- * Construct the deterministic public space ID for a given address and chain ID.
41
- *
42
- * Public space IDs follow the format:
43
- * `tinycloud:pkh:eip155:{chainId}:{address}:public`
44
- *
45
- * Given an address and chain ID, any client can construct this ID
46
- * to discover and read a user's public data without prior interaction.
47
- *
48
- * @param address - Ethereum address (0x-prefixed)
49
- * @param chainId - Chain ID (e.g., 1 for mainnet)
50
- * @returns The full public space ID URI
51
- *
52
- * @example
53
- * ```typescript
54
- * const spaceId = makePublicSpaceId('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', 1);
55
- * // => "tinycloud:pkh:eip155:1:0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045:public"
56
- * ```
57
- */
58
- export function makePublicSpaceId(address, chainId) {
59
- return `tinycloud:pkh:eip155:${chainId}:${address}:public`;
60
- }
61
- // =============================================================================
62
- // Space URI Utilities
63
- // =============================================================================
64
- /**
65
- * Parse a space URI to extract components.
66
- *
67
- * Full URI format: `tinycloud:pkh:eip155:{chainId}:{address}:{name}`
68
- * Short name format: `{name}`
69
- *
70
- * @param uri - The space URI or short name
71
- * @returns Parsed components or null if invalid
72
- */
73
- export function parseSpaceUri(uri) {
74
- // Full URI format: tinycloud:pkh:eip155:{chainId}:{address}:{name}
75
- const fullUriMatch = uri.match(/^tinycloud:pkh:eip155:(\d+):(0x[a-fA-F0-9]{40}):(.+)$/);
76
- if (fullUriMatch) {
77
- const [, chainId, address, name] = fullUriMatch;
78
- return {
79
- owner: `did:pkh:eip155:${chainId}:${address}`,
80
- name,
81
- chainId,
82
- address,
83
- };
84
- }
85
- // Short name format - just return the name, owner will be inferred
86
- if (/^[a-zA-Z0-9_-]+$/.test(uri)) {
87
- return {
88
- owner: "", // Will be filled in from session
89
- name: uri,
90
- };
91
- }
92
- return null;
93
- }
94
- /**
95
- * Build a full space URI from components.
96
- *
97
- * @param owner - Owner DID (did:pkh:eip155:{chainId}:{address})
98
- * @param name - Space name
99
- * @returns Full space URI
100
- */
101
- export function buildSpaceUri(owner, name) {
102
- // Extract chain ID and address from PKH DID
103
- const pkhMatch = owner.match(/^did:pkh:eip155:(\d+):(0x[a-fA-F0-9]{40})$/);
104
- if (pkhMatch) {
105
- const [, chainId, address] = pkhMatch;
106
- return `tinycloud:pkh:eip155:${chainId}:${address}:${name}`;
107
- }
108
- // Fallback - shouldn't happen with valid PKH DIDs
109
- return `tinycloud:${owner}:${name}`;
110
- }
111
- // =============================================================================
112
- // Server Response Transformation
113
- // =============================================================================
114
- /**
115
- * Transform validated server delegation response to SDK's Delegation[] format.
116
- *
117
- * Server returns { [cid: string]: DelegationInfo } where:
118
- * - Key is the delegation CID
119
- * - Value is DelegationInfo with delegator, delegate, capabilities, etc.
120
- *
121
- * SDK expects Delegation[] with cid field included.
122
- *
123
- * @param validatedData - Pre-validated server response from validateServerDelegationsResponse()
124
- * @param defaultSpaceId - Default space ID to use if not extractable from resource
125
- */
126
- function transformServerDelegations(validatedData, defaultSpaceId) {
127
- const result = [];
128
- for (const [cid, info] of Object.entries(validatedData)) {
129
- // Extract path from capabilities (use first capability's resource path)
130
- const capabilities = info.capabilities;
131
- let path = "";
132
- let spaceId = defaultSpaceId;
133
- const actions = [];
134
- for (const cap of capabilities) {
135
- actions.push(cap.ability);
136
- // Parse resource to extract space and path
137
- // Resource format: tinycloud:pkh:eip155:{chainId}:{address}:{spaceName}/{service}/{path}
138
- const resourceMatch = cap.resource.match(/^(tinycloud:pkh:eip155:\d+:0x[a-fA-F0-9]+:[^/]+)\/[^/]+\/(.*)$/);
139
- if (resourceMatch) {
140
- spaceId = resourceMatch[1];
141
- path = resourceMatch[2] || "";
142
- }
143
- }
144
- // Extract first string parent CID (server may return byte arrays for some CIDs)
145
- const firstStringParent = info.parents?.find((p) => typeof p === 'string');
146
- result.push({
147
- cid,
148
- delegateDID: info.delegate,
149
- delegatorDID: info.delegator,
150
- spaceId,
151
- path,
152
- actions,
153
- expiry: info.expiry ? new Date(info.expiry) : new Date(Date.now() + 24 * 60 * 60 * 1000),
154
- isRevoked: false,
155
- createdAt: info.issued_at ? new Date(info.issued_at) : undefined,
156
- parentCid: firstStringParent,
157
- });
158
- }
159
- return result;
160
- }
161
- // =============================================================================
162
- // Implementation
163
- // =============================================================================
164
- /**
165
- * SpaceService - Global singleton for managing spaces.
166
- *
167
- * @example
168
- * ```typescript
169
- * const spaceService = new SpaceService({
170
- * hosts: ['https://node.tinycloud.xyz'],
171
- * session,
172
- * invoke,
173
- * });
174
- *
175
- * // List all accessible spaces
176
- * const result = await spaceService.list();
177
- * if (result.ok) {
178
- * for (const space of result.data) {
179
- * console.log(`${space.name} (${space.type})`);
180
- * }
181
- * }
182
- *
183
- * // Create a new space
184
- * const createResult = await spaceService.create('photos');
185
- *
186
- * // Get a space object for operations
187
- * const space = spaceService.get('photos');
188
- * await space.kv.put('album/vacation', { photos: [...] });
189
- * ```
190
- */
191
- export class SpaceService {
192
- /**
193
- * Create a new SpaceService instance.
194
- *
195
- * @param config - Service configuration
196
- */
197
- constructor(config) {
198
- /** Cache of created Space objects */
199
- this.spaceCache = new Map();
200
- /** Cache of space info */
201
- this.infoCache = new Map();
202
- /** Cache TTL in milliseconds (5 minutes) */
203
- this.cacheTTL = 5 * 60 * 1000;
204
- this.hosts = config.hosts;
205
- this.session = config.session;
206
- this.invoke = config.invoke;
207
- this.fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);
208
- this.capabilityRegistry = config.capabilityRegistry;
209
- this.createKVServiceFn = config.createKVService;
210
- this._userDid = config.userDid;
211
- this.sharingService = config.sharingService;
212
- this.createDelegationFn = config.createDelegation;
213
- }
214
- /**
215
- * Update the service configuration.
216
- */
217
- updateConfig(config) {
218
- if (config.hosts)
219
- this.hosts = config.hosts;
220
- if (config.session)
221
- this.session = config.session;
222
- if (config.invoke)
223
- this.invoke = config.invoke;
224
- if (config.fetch)
225
- this.fetchFn = config.fetch;
226
- if (config.capabilityRegistry)
227
- this.capabilityRegistry = config.capabilityRegistry;
228
- if (config.createKVService)
229
- this.createKVServiceFn = config.createKVService;
230
- if (config.userDid !== undefined)
231
- this._userDid = config.userDid;
232
- if (config.sharingService)
233
- this.sharingService = config.sharingService;
234
- if (config.createDelegation)
235
- this.createDelegationFn = config.createDelegation;
236
- // Clear caches when config changes
237
- this.spaceCache.clear();
238
- this.infoCache.clear();
239
- }
240
- /**
241
- * Get the current user's primary space ID.
242
- */
243
- getCurrentSpaceId() {
244
- return this.session?.spaceId;
245
- }
246
- /**
247
- * Get the primary host URL.
248
- */
249
- get host() {
250
- return this.hosts[0];
251
- }
252
- /**
253
- * Get the current user's PKH DID.
254
- */
255
- get userDid() {
256
- // Return explicitly set user DID, or try to derive from verificationMethod
257
- if (this._userDid) {
258
- return this._userDid;
259
- }
260
- // verificationMethod might be did:key format - cannot derive PKH from it
261
- // The caller should provide userDid explicitly for full functionality
262
- return undefined;
263
- }
264
- // ===========================================================================
265
- // List Spaces
266
- // ===========================================================================
267
- /**
268
- * List all spaces the user has access to.
269
- *
270
- * Combines owned spaces (from the server) with delegated spaces
271
- * (from the capability registry).
272
- */
273
- async list() {
274
- if (!this.session) {
275
- return err(serviceError(SpaceErrorCodes.AUTH_REQUIRED, "Authentication required", SERVICE_NAME));
276
- }
277
- try {
278
- const spaces = [];
279
- // 1. Get owned spaces from the server
280
- const ownedResult = await this.listOwnedSpaces();
281
- if (ownedResult.ok) {
282
- spaces.push(...ownedResult.data);
283
- }
284
- // 2. Get delegated spaces from capability registry
285
- if (this.capabilityRegistry) {
286
- const delegatedSpaces = this.discoverDelegatedSpaces();
287
- spaces.push(...delegatedSpaces);
288
- }
289
- // Remove duplicates (prefer owned over delegated)
290
- const uniqueSpaces = this.deduplicateSpaces(spaces);
291
- return ok(uniqueSpaces);
292
- }
293
- catch (error) {
294
- return err(serviceError(SpaceErrorCodes.NETWORK_ERROR, `Failed to list spaces: ${String(error)}`, SERVICE_NAME, { cause: error instanceof Error ? error : undefined }));
295
- }
296
- }
297
- /**
298
- * List owned spaces from the server.
299
- */
300
- async listOwnedSpaces() {
301
- try {
302
- const headers = this.invoke(this.session, "space", "", "tinycloud.space/list");
303
- const response = await this.fetchFn(`${this.host}/invoke`, {
304
- method: "POST",
305
- headers,
306
- });
307
- if (!response.ok) {
308
- const errorText = await response.text();
309
- return err(serviceError(SpaceErrorCodes.NETWORK_ERROR, `Failed to list owned spaces: ${response.status} - ${errorText}`, SERVICE_NAME, { meta: { status: response.status } }));
310
- }
311
- const rawData = await response.json();
312
- // Validate server response
313
- const validationResult = validateServerOwnedSpacesResponse(rawData);
314
- if (!validationResult.ok) {
315
- return err(serviceError(SpaceErrorCodes.NETWORK_ERROR, validationResult.error.message, SERVICE_NAME, { meta: validationResult.error.meta }));
316
- }
317
- const spaces = validationResult.data.map((item) => ({
318
- id: item.id,
319
- name: item.name ?? this.extractNameFromId(item.id),
320
- owner: item.owner,
321
- type: "owned",
322
- permissions: ["*"], // Full permissions for owned spaces
323
- }));
324
- return ok(spaces);
325
- }
326
- catch (error) {
327
- return err(serviceError(SpaceErrorCodes.NETWORK_ERROR, `Network error listing owned spaces: ${String(error)}`, SERVICE_NAME, { cause: error instanceof Error ? error : undefined }));
328
- }
329
- }
330
- /**
331
- * Discover delegated spaces from the capability registry.
332
- */
333
- discoverDelegatedSpaces() {
334
- if (!this.capabilityRegistry) {
335
- return [];
336
- }
337
- const spaces = new Map();
338
- // Get all capabilities and extract unique space IDs
339
- const capabilities = this.capabilityRegistry.getAllCapabilities();
340
- for (const capability of capabilities) {
341
- const spaceId = capability.delegation.spaceId;
342
- // Skip if we already have this space
343
- if (spaces.has(spaceId)) {
344
- const existing = spaces.get(spaceId);
345
- // Merge permissions
346
- if (existing.permissions) {
347
- const actions = capability.delegation.actions;
348
- for (const action of actions) {
349
- if (!existing.permissions.includes(action)) {
350
- existing.permissions.push(action);
351
- }
352
- }
353
- }
354
- continue;
355
- }
356
- // Skip if this is the user's own space
357
- if (spaceId === this.session?.spaceId) {
358
- continue;
359
- }
360
- const parsed = parseSpaceUri(spaceId);
361
- spaces.set(spaceId, {
362
- id: spaceId,
363
- name: parsed?.name ?? this.extractNameFromId(spaceId),
364
- owner: capability.delegation.delegatorDID ?? parsed?.owner ?? "",
365
- type: "delegated",
366
- permissions: [...capability.delegation.actions],
367
- expiresAt: capability.expiresAt,
368
- });
369
- }
370
- return Array.from(spaces.values());
371
- }
372
- /**
373
- * Extract space name from a full space ID.
374
- */
375
- extractNameFromId(id) {
376
- const parsed = parseSpaceUri(id);
377
- if (parsed) {
378
- return parsed.name;
379
- }
380
- // Fallback: take last segment
381
- const parts = id.split(":");
382
- return parts[parts.length - 1] || id;
383
- }
384
- /**
385
- * Deduplicate spaces, preferring owned over delegated.
386
- */
387
- deduplicateSpaces(spaces) {
388
- const seen = new Map();
389
- for (const space of spaces) {
390
- const existing = seen.get(space.id);
391
- if (!existing || (existing.type === "delegated" && space.type === "owned")) {
392
- seen.set(space.id, space);
393
- }
394
- }
395
- return Array.from(seen.values());
396
- }
397
- // ===========================================================================
398
- // Create Space
399
- // ===========================================================================
400
- /**
401
- * Create a new space.
402
- *
403
- * @param name - The name for the new space
404
- */
405
- async create(name) {
406
- if (!this.session) {
407
- return err(serviceError(SpaceErrorCodes.AUTH_REQUIRED, "Authentication required", SERVICE_NAME));
408
- }
409
- // Validate name
410
- if (!name || !/^[a-zA-Z0-9_-]+$/.test(name)) {
411
- return err(serviceError(SpaceErrorCodes.INVALID_NAME, "Space name must contain only alphanumeric characters, underscores, and hyphens", SERVICE_NAME));
412
- }
413
- try {
414
- const headers = this.invoke(this.session, "space", name, "tinycloud.space/create");
415
- const response = await this.fetchFn(`${this.host}/invoke`, {
416
- method: "POST",
417
- headers,
418
- body: JSON.stringify({ name }),
419
- });
420
- if (!response.ok) {
421
- const errorText = await response.text();
422
- if (response.status === 409) {
423
- return err(serviceError(SpaceErrorCodes.ALREADY_EXISTS, `Space "${name}" already exists`, SERVICE_NAME));
424
- }
425
- return err(serviceError(SpaceErrorCodes.CREATION_FAILED, `Failed to create space: ${response.status} - ${errorText}`, SERVICE_NAME, { meta: { status: response.status } }));
426
- }
427
- const rawData = await response.json();
428
- // Validate server response
429
- const validationResult = validateServerCreateSpaceResponse(rawData);
430
- if (!validationResult.ok) {
431
- return err(serviceError(SpaceErrorCodes.CREATION_FAILED, validationResult.error.message, SERVICE_NAME, { meta: validationResult.error.meta }));
432
- }
433
- const spaceInfo = {
434
- id: validationResult.data.id,
435
- name: validationResult.data.name || name,
436
- owner: validationResult.data.owner || this.userDid || "",
437
- type: "owned",
438
- permissions: ["*"],
439
- };
440
- // Cache the info
441
- this.infoCache.set(spaceInfo.id, { info: spaceInfo, cachedAt: Date.now() });
442
- return ok(spaceInfo);
443
- }
444
- catch (error) {
445
- return err(serviceError(SpaceErrorCodes.NETWORK_ERROR, `Network error creating space: ${String(error)}`, SERVICE_NAME, { cause: error instanceof Error ? error : undefined }));
446
- }
447
- }
448
- // ===========================================================================
449
- // Get Space
450
- // ===========================================================================
451
- /**
452
- * Get a Space object by name or full URI.
453
- *
454
- * @param nameOrUri - Short name or full URI
455
- */
456
- get(nameOrUri) {
457
- // Resolve the full space ID
458
- const spaceId = this.resolveSpaceId(nameOrUri);
459
- // Check cache
460
- const cached = this.spaceCache.get(spaceId);
461
- if (cached) {
462
- return cached;
463
- }
464
- // Create new Space object
465
- const parsed = parseSpaceUri(spaceId);
466
- const name = parsed?.name ?? this.extractNameFromId(spaceId);
467
- const config = {
468
- id: spaceId,
469
- name,
470
- createKV: this.createSpaceScopedKV.bind(this),
471
- createDelegations: this.createSpaceScopedDelegations.bind(this),
472
- createSharing: this.createSpaceScopedSharing.bind(this),
473
- getInfo: this.getSpaceInfo.bind(this),
474
- };
475
- const space = new Space(config);
476
- this.spaceCache.set(spaceId, space);
477
- return space;
478
- }
479
- /**
480
- * Resolve a name or URI to a full space ID.
481
- */
482
- resolveSpaceId(nameOrUri) {
483
- const parsed = parseSpaceUri(nameOrUri);
484
- if (!parsed) {
485
- // Invalid format, return as-is
486
- return nameOrUri;
487
- }
488
- if (parsed.owner) {
489
- // Full URI - return as-is
490
- return nameOrUri;
491
- }
492
- // Short name - build full URI from user's DID
493
- if (this.userDid) {
494
- return buildSpaceUri(this.userDid, parsed.name);
495
- }
496
- // No user DID available, return as-is
497
- return nameOrUri;
498
- }
499
- // ===========================================================================
500
- // Exists Check
501
- // ===========================================================================
502
- /**
503
- * Check if a space exists and the user has access.
504
- */
505
- async exists(nameOrUri) {
506
- const spaceId = this.resolveSpaceId(nameOrUri);
507
- // Check info cache first
508
- const cached = this.infoCache.get(spaceId);
509
- if (cached && Date.now() - cached.cachedAt < this.cacheTTL) {
510
- return ok(true);
511
- }
512
- // Query the server
513
- const infoResult = await this.getSpaceInfo(spaceId);
514
- return ok(infoResult.ok);
515
- }
516
- // ===========================================================================
517
- // Space Info
518
- // ===========================================================================
519
- /**
520
- * Get space info from server or cache.
521
- */
522
- async getSpaceInfo(spaceId) {
523
- // Check cache
524
- const cached = this.infoCache.get(spaceId);
525
- if (cached && Date.now() - cached.cachedAt < this.cacheTTL) {
526
- return ok(cached.info);
527
- }
528
- if (!this.session) {
529
- return err(serviceError(SpaceErrorCodes.AUTH_REQUIRED, "Authentication required", SERVICE_NAME));
530
- }
531
- try {
532
- const headers = this.invoke(this.session, "space", spaceId, "tinycloud.space/info");
533
- const response = await this.fetchFn(`${this.host}/invoke`, {
534
- method: "POST",
535
- headers,
536
- body: JSON.stringify({ spaceId }),
537
- });
538
- if (!response.ok) {
539
- if (response.status === 404) {
540
- return err(serviceError(SpaceErrorCodes.NOT_FOUND, `Space not found: ${spaceId}`, SERVICE_NAME));
541
- }
542
- const errorText = await response.text();
543
- return err(serviceError(SpaceErrorCodes.NETWORK_ERROR, `Failed to get space info: ${response.status} - ${errorText}`, SERVICE_NAME));
544
- }
545
- const rawData = await response.json();
546
- // Validate server response
547
- const validationResult = validateServerSpaceInfoResponse(rawData);
548
- if (!validationResult.ok) {
549
- return err(serviceError(SpaceErrorCodes.NETWORK_ERROR, validationResult.error.message, SERVICE_NAME, { meta: validationResult.error.meta }));
550
- }
551
- const data = validationResult.data;
552
- const spaceInfo = {
553
- id: data.id,
554
- name: data.name ?? this.extractNameFromId(data.id),
555
- owner: data.owner,
556
- type: data.type ?? (data.owner === this.userDid ? "owned" : "delegated"),
557
- permissions: data.permissions,
558
- expiresAt: data.expiresAt ? new Date(data.expiresAt) : undefined,
559
- };
560
- // Cache the info
561
- this.infoCache.set(spaceId, { info: spaceInfo, cachedAt: Date.now() });
562
- return ok(spaceInfo);
563
- }
564
- catch (error) {
565
- return err(serviceError(SpaceErrorCodes.NETWORK_ERROR, `Network error getting space info: ${String(error)}`, SERVICE_NAME, { cause: error instanceof Error ? error : undefined }));
566
- }
567
- }
568
- // ===========================================================================
569
- // Space-Scoped Service Factories
570
- // ===========================================================================
571
- /**
572
- * Create a space-scoped KV service.
573
- */
574
- createSpaceScopedKV(spaceId) {
575
- if (this.createKVServiceFn) {
576
- return this.createKVServiceFn(spaceId);
577
- }
578
- // Return a proxy that throws a helpful error
579
- return new Proxy({}, {
580
- get: () => {
581
- throw new Error("KV service factory not configured. Provide createKVService in SpaceServiceConfig.");
582
- },
583
- });
584
- }
585
- /**
586
- * Create space-scoped delegation operations.
587
- */
588
- createSpaceScopedDelegations(spaceId) {
589
- const self = this;
590
- return {
591
- async list() {
592
- // List outgoing delegations (created by user) using tinycloud.capabilities/read
593
- try {
594
- // Facts contain the query params for the capabilities/read capability
595
- const facts = [
596
- {
597
- capabilitiesReadParams: {
598
- type: "list",
599
- filters: { direction: "created" },
600
- },
601
- },
602
- ];
603
- // The capabilities/read endpoint requires path="all" to match server routing
604
- const headers = self.invoke(self.session, "capabilities", "all", "tinycloud.capabilities/read", facts);
605
- const response = await self.fetchFn(`${self.host}/invoke`, {
606
- method: "POST",
607
- headers,
608
- });
609
- if (!response.ok) {
610
- const errorText = await response.text();
611
- return err(serviceError(SpaceErrorCodes.NETWORK_ERROR, `Failed to list delegations: ${response.status} - ${errorText}`, SERVICE_NAME));
612
- }
613
- // Server returns { [cid: string]: DelegationInfo } - validate and transform to Delegation[]
614
- const rawData = await response.json();
615
- // Validate server response
616
- const validationResult = validateServerDelegationsResponse(rawData);
617
- if (!validationResult.ok) {
618
- return err(serviceError(SpaceErrorCodes.NETWORK_ERROR, validationResult.error.message, SERVICE_NAME, { meta: validationResult.error.meta }));
619
- }
620
- const delegations = transformServerDelegations(validationResult.data, spaceId);
621
- return ok(delegations);
622
- }
623
- catch (error) {
624
- return err(serviceError(SpaceErrorCodes.NETWORK_ERROR, `Network error listing delegations: ${String(error)}`, SERVICE_NAME));
625
- }
626
- },
627
- async listReceived() {
628
- // List incoming delegations (received by user) using tinycloud.capabilities/read
629
- try {
630
- // Facts contain the query params for the capabilities/read capability
631
- const facts = [
632
- {
633
- capabilitiesReadParams: {
634
- type: "list",
635
- filters: { direction: "received" },
636
- },
637
- },
638
- ];
639
- // The capabilities/read endpoint requires path="all" to match server routing
640
- const headers = self.invoke(self.session, "capabilities", "all", "tinycloud.capabilities/read", facts);
641
- const response = await self.fetchFn(`${self.host}/invoke`, {
642
- method: "POST",
643
- headers,
644
- });
645
- if (!response.ok) {
646
- const errorText = await response.text();
647
- return err(serviceError(SpaceErrorCodes.NETWORK_ERROR, `Failed to list received delegations: ${response.status} - ${errorText}`, SERVICE_NAME));
648
- }
649
- // Server returns { [cid: string]: DelegationInfo } - validate and transform to Delegation[]
650
- const rawData = await response.json();
651
- // Validate server response
652
- const validationResult = validateServerDelegationsResponse(rawData);
653
- if (!validationResult.ok) {
654
- return err(serviceError(SpaceErrorCodes.NETWORK_ERROR, validationResult.error.message, SERVICE_NAME, { meta: validationResult.error.meta }));
655
- }
656
- const delegations = transformServerDelegations(validationResult.data, spaceId);
657
- return ok(delegations);
658
- }
659
- catch (error) {
660
- return err(serviceError(SpaceErrorCodes.NETWORK_ERROR, `Network error listing received delegations: ${String(error)}`, SERVICE_NAME));
661
- }
662
- },
663
- async create(params) {
664
- // Use the platform-provided createDelegation function (SIWE-based flow)
665
- if (self.createDelegationFn) {
666
- return self.createDelegationFn({ ...params, spaceId });
667
- }
668
- // Fallback: return error if no createDelegation function provided
669
- // The old invoke-based approach doesn't work because /delegate expects
670
- // SIWE delegation headers, not invocation headers
671
- return err(serviceError(SpaceErrorCodes.NOT_INITIALIZED, "Delegation creation requires a createDelegation function. " +
672
- "This should be provided by the platform SDK (web-sdk or node-sdk).", SERVICE_NAME));
673
- },
674
- async revoke(cid) {
675
- try {
676
- const headers = self.invoke(self.session, "delegation", cid, "tinycloud.delegation/revoke");
677
- const response = await self.fetchFn(`${self.host}/revoke`, {
678
- method: "POST",
679
- headers,
680
- body: JSON.stringify({ cid, spaceId }),
681
- });
682
- if (!response.ok) {
683
- const errorText = await response.text();
684
- return err(serviceError(SpaceErrorCodes.NETWORK_ERROR, `Failed to revoke delegation: ${response.status} - ${errorText}`, SERVICE_NAME));
685
- }
686
- return ok(undefined);
687
- }
688
- catch (error) {
689
- return err(serviceError(SpaceErrorCodes.NETWORK_ERROR, `Network error revoking delegation: ${String(error)}`, SERVICE_NAME));
690
- }
691
- },
692
- };
693
- }
694
- /**
695
- * Create space-scoped sharing operations.
696
- *
697
- * When a SharingService is configured, delegates to client-side v2 sharing.
698
- * V2 sharing links are self-contained with embedded private keys - no server tracking.
699
- */
700
- createSpaceScopedSharing(spaceId) {
701
- const self = this;
702
- return {
703
- async generate(params) {
704
- // Use v2 SharingService when available (client-side sharing links)
705
- if (self.sharingService) {
706
- // Note: SharingService uses session.spaceId for the space.
707
- // For space-scoped sharing on delegated spaces, ensure the session
708
- // is configured for the correct space before calling generate().
709
- const result = await self.sharingService.generate(params);
710
- if (!result.ok) {
711
- return err(serviceError(SpaceErrorCodes.NETWORK_ERROR, result.error.message || "Failed to generate share link", SERVICE_NAME));
712
- }
713
- return ok(result.data);
714
- }
715
- // Fallback: return error since server endpoint doesn't exist
716
- return err(serviceError(SpaceErrorCodes.NOT_INITIALIZED, "SharingService not configured. V2 sharing requires a SharingService instance.", SERVICE_NAME));
717
- },
718
- async list() {
719
- // V2 sharing links are self-contained (not server-tracked)
720
- // This operation is not supported in the v2 spec
721
- return err(serviceError(SpaceErrorCodes.NOT_INITIALIZED, "Listing share links is not supported in v2. Share links are self-contained tokens that are not tracked on the server.", SERVICE_NAME));
722
- },
723
- async revoke(token) {
724
- // V2 sharing links are revoked by revoking the underlying delegation
725
- // This requires the delegation CID, not the share token
726
- return err(serviceError(SpaceErrorCodes.NOT_INITIALIZED, "Revoking share links by token is not supported in v2. To revoke access, revoke the underlying delegation using space.delegations.revoke(cid).", SERVICE_NAME));
727
- },
728
- };
729
- }
730
- }
731
- /**
732
- * Create a new SpaceService instance.
733
- *
734
- * @param config - Service configuration
735
- * @returns A new SpaceService instance
736
- */
737
- export function createSpaceService(config) {
738
- return new SpaceService(config);
739
- }
740
- //# sourceMappingURL=SpaceService.js.map