@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
package/dist/index.js CHANGED
@@ -1,62 +1,3768 @@
1
- /**
2
- * @tinycloud/sdk-core
3
- *
4
- * Core TinyCloud SDK package providing shared interfaces and the TinyCloud class.
5
- *
6
- * This package defines the platform-agnostic interfaces that both web-sdk and node-sdk
7
- * implement. The main TinyCloud class accepts an IUserAuthorization implementation,
8
- * allowing it to work in both browser and Node.js environments.
9
- *
10
- * @packageDocumentation
11
- */
12
- // Platform-agnostic client types (canonical definitions)
13
- export { ClientSessionSchema, EnsDataSchema, SiweConfigSchema, validateClientSession, SiweMessage, } from "./client-types";
14
- // Notification handler
15
- export { SilentNotificationHandler, } from "./notifications";
16
- // Session storage interface and types
17
- export {
18
- // Validation
19
- validatePersistedSessionData, } from "./storage";
20
- // Main TinyCloud class
21
- export { TinyCloud, } from "./TinyCloud";
22
- // Re-export service types from sdk-services for convenience
23
- export {
24
- // Context
25
- ServiceContext,
26
- // KV Service
27
- KVService, PrefixedKVService, ok, err, serviceError, ErrorCodes, defaultRetryPolicy,
28
- // SQL Service
29
- SQLService, DatabaseHandle, SQLAction,
30
- // DuckDB Service
31
- DuckDbService, DuckDbDatabaseHandle, DuckDbAction,
32
- // Vault Service
33
- DataVaultService, VaultHeaders, VaultPublicSpaceKVActions, createVaultCrypto, } from "@tinycloud/sdk-services";
34
- // Space utilities
35
- export { fetchPeerId, submitHostDelegation, activateSessionWithHost, } from "./space";
36
- // Delegations
37
- export { DelegationErrorCodes,
38
- // Classes
39
- DelegationManager,
40
- // v2 SharingService
41
- SharingService, createSharingService, } from "./delegations";
42
- // Authorization (v2 spec)
43
- export {
44
- // Class
45
- CapabilityKeyRegistry,
46
- // Factory
47
- createCapabilityKeyRegistry,
48
- // Error codes
49
- CapabilityKeyRegistryErrorCodes, defaultSignStrategy, AutoApproveSpaceCreationHandler, defaultSpaceCreationHandler, } from "./authorization";
50
- // Spaces (v2 spec)
51
- export {
52
- // Space object
53
- Space,
54
- // SpaceService
55
- SpaceService, SpaceErrorCodes, createSpaceService,
56
- // URI utilities
57
- parseSpaceUri, buildSpaceUri,
58
- // Public space utility
59
- makePublicSpaceId, } from "./spaces";
60
- // Protocol version checking
61
- export { ProtocolMismatchError, VersionCheckError, UnsupportedFeatureError, checkNodeInfo, } from "./version";
1
+ // src/client-types.ts
2
+ import { z } from "zod";
3
+ import { SiweMessage } from "siwe";
4
+ var EnsDataSchema = z.object({
5
+ domain: z.string().nullable().optional(),
6
+ avatarUrl: z.string().nullable().optional()
7
+ });
8
+ var SiweConfigSchema = z.object({
9
+ domain: z.string().optional(),
10
+ uri: z.string().optional(),
11
+ chainId: z.number().optional(),
12
+ statement: z.string().optional(),
13
+ nonce: z.string().optional(),
14
+ expirationTime: z.string().optional(),
15
+ notBefore: z.string().optional(),
16
+ requestId: z.string().optional(),
17
+ resources: z.array(z.string()).optional()
18
+ }).passthrough();
19
+ var ClientSessionSchema = z.object({
20
+ address: z.string(),
21
+ walletAddress: z.string(),
22
+ chainId: z.number(),
23
+ sessionKey: z.string(),
24
+ siwe: z.string(),
25
+ signature: z.string(),
26
+ ens: EnsDataSchema.optional()
27
+ });
28
+ function validateClientSession(data) {
29
+ const result = ClientSessionSchema.safeParse(data);
30
+ return result.success ? result.data : null;
31
+ }
32
+
33
+ // src/notifications.ts
34
+ var SilentNotificationHandler = class {
35
+ success() {
36
+ }
37
+ warning() {
38
+ }
39
+ error() {
40
+ }
41
+ };
42
+
43
+ // src/storage.schema.ts
44
+ import { z as z2 } from "zod";
45
+ var ethereumAddressPattern = /^0x[a-fA-F0-9]{40}$/;
46
+ var EnsDataSchema2 = z2.object({
47
+ /** ENS name/domain. */
48
+ domain: z2.string().nullable().optional(),
49
+ /** ENS avatar URL. */
50
+ avatarUrl: z2.string().nullable().optional()
51
+ });
52
+ var PersistedTinyCloudSessionSchema = z2.object({
53
+ /** The delegation header containing the UCAN */
54
+ delegationHeader: z2.object({
55
+ Authorization: z2.string()
56
+ }),
57
+ /** The delegation CID */
58
+ delegationCid: z2.string(),
59
+ /** The space ID for this session */
60
+ spaceId: z2.string(),
61
+ /** Additional spaces included in this session's capabilities. Key is logical name, value is full spaceId URI */
62
+ spaces: z2.record(z2.string(), z2.string()).optional(),
63
+ /** The verification method DID */
64
+ verificationMethod: z2.string()
65
+ });
66
+ var PersistedSessionDataSchema = z2.object({
67
+ /** User's Ethereum address */
68
+ address: z2.string().regex(ethereumAddressPattern, "Invalid Ethereum address"),
69
+ /** EIP-155 Chain ID */
70
+ chainId: z2.number().int().positive(),
71
+ /** Session key in JWK format (stringified) */
72
+ sessionKey: z2.string(),
73
+ /** The signed SIWE message */
74
+ siwe: z2.string(),
75
+ /** User's signature of the SIWE message */
76
+ signature: z2.string(),
77
+ /** TinyCloud delegation data if available */
78
+ tinycloudSession: PersistedTinyCloudSessionSchema.optional(),
79
+ /** Session expiration timestamp (ISO 8601 with timezone offset) */
80
+ expiresAt: z2.string().datetime({ offset: true }),
81
+ /** Session creation timestamp (ISO 8601 with timezone offset) */
82
+ createdAt: z2.string().datetime({ offset: true }),
83
+ /** Schema version for migrations */
84
+ version: z2.string(),
85
+ /** Optional ENS data */
86
+ ens: EnsDataSchema2.optional()
87
+ });
88
+ var TinyCloudSessionSchema = z2.object({
89
+ /** User's Ethereum address */
90
+ address: z2.string().regex(ethereumAddressPattern, "Invalid Ethereum address"),
91
+ /** EIP-155 Chain ID */
92
+ chainId: z2.number().int().positive(),
93
+ /** Session key ID */
94
+ sessionKey: z2.string(),
95
+ /** The space ID for this session */
96
+ spaceId: z2.string(),
97
+ /** Additional spaces included in this session's capabilities. Key is logical name, value is full spaceId URI */
98
+ spaces: z2.record(z2.string(), z2.string()).optional(),
99
+ /** The delegation CID */
100
+ delegationCid: z2.string(),
101
+ /** The delegation header for API calls */
102
+ delegationHeader: z2.object({
103
+ Authorization: z2.string()
104
+ }),
105
+ /** The verification method DID */
106
+ verificationMethod: z2.string(),
107
+ /** The session key JWK (required for invoke operations) */
108
+ jwk: z2.object({}).passthrough(),
109
+ /** The signed SIWE message */
110
+ siwe: z2.string(),
111
+ /** User's signature of the SIWE message */
112
+ signature: z2.string()
113
+ });
114
+ function validatePersistedSessionData(data) {
115
+ const result = PersistedSessionDataSchema.safeParse(data);
116
+ if (!result.success) {
117
+ return {
118
+ ok: false,
119
+ error: {
120
+ code: "VALIDATION_ERROR",
121
+ message: result.error.message,
122
+ service: "session",
123
+ meta: { issues: result.error.issues }
124
+ }
125
+ };
126
+ }
127
+ return { ok: true, data: result.data };
128
+ }
129
+
130
+ // src/TinyCloud.ts
131
+ import {
132
+ ServiceContext,
133
+ KVService,
134
+ SQLService,
135
+ DuckDbService,
136
+ ok as ok2,
137
+ err as err2,
138
+ serviceError as serviceError2,
139
+ ErrorCodes
140
+ } from "@tinycloud/sdk-services";
141
+
142
+ // src/spaces/SpaceService.ts
143
+ import { ok, err, serviceError } from "@tinycloud/sdk-services";
144
+
145
+ // src/spaces/Space.ts
146
+ var Space = class {
147
+ /**
148
+ * Create a new Space instance.
149
+ *
150
+ * @param config - Space configuration
151
+ */
152
+ constructor(config) {
153
+ this._id = config.id;
154
+ this._name = config.name;
155
+ this._kv = config.createKV(config.id);
156
+ this._delegations = config.createDelegations(config.id);
157
+ this._sharing = config.createSharing(config.id);
158
+ this._getInfo = config.getInfo;
159
+ }
160
+ /**
161
+ * The space identifier (full URI).
162
+ */
163
+ get id() {
164
+ return this._id;
165
+ }
166
+ /**
167
+ * The short name of the space.
168
+ */
169
+ get name() {
170
+ return this._name;
171
+ }
172
+ /**
173
+ * KV operations scoped to this space.
174
+ */
175
+ get kv() {
176
+ return this._kv;
177
+ }
178
+ /**
179
+ * Delegation operations scoped to this space.
180
+ */
181
+ get delegations() {
182
+ return this._delegations;
183
+ }
184
+ /**
185
+ * Sharing operations scoped to this space.
186
+ */
187
+ get sharing() {
188
+ return this._sharing;
189
+ }
190
+ /**
191
+ * Get space metadata.
192
+ *
193
+ * @returns Result containing space information
194
+ */
195
+ async info() {
196
+ return this._getInfo(this._id);
197
+ }
198
+ };
199
+
200
+ // src/spaces/spaces.schema.ts
201
+ import { z as z4 } from "zod";
202
+
203
+ // src/delegations/types.schema.ts
204
+ import { z as z3 } from "zod";
205
+ var JWKSchema = z3.object({
206
+ /** Key type (e.g., "EC", "RSA", "OKP") */
207
+ kty: z3.string(),
208
+ /** Curve for EC/OKP keys (e.g., "P-256", "Ed25519") */
209
+ crv: z3.string().optional(),
210
+ /** X coordinate for EC keys, public key for OKP */
211
+ x: z3.string().optional(),
212
+ /** Y coordinate for EC keys */
213
+ y: z3.string().optional(),
214
+ /** Private key value (d parameter) */
215
+ d: z3.string().optional(),
216
+ /** Public exponent for RSA keys */
217
+ e: z3.string().optional(),
218
+ /** Modulus for RSA keys */
219
+ n: z3.string().optional(),
220
+ /** Key ID */
221
+ kid: z3.string().optional(),
222
+ /** Algorithm */
223
+ alg: z3.string().optional(),
224
+ /** Key use (e.g., "sig", "enc") */
225
+ use: z3.string().optional(),
226
+ /** Key operations (e.g., ["sign", "verify"]) */
227
+ key_ops: z3.array(z3.string()).optional()
228
+ });
229
+ var KeyTypeSchema = z3.enum(["main", "session", "ingested"]);
230
+ var KeyInfoSchema = z3.object({
231
+ /** Unique identifier for this key */
232
+ id: z3.string(),
233
+ /** DID associated with this key */
234
+ did: z3.string(),
235
+ /** Type of key determining its authority level */
236
+ type: KeyTypeSchema,
237
+ /** Private key in JWK format */
238
+ jwk: JWKSchema.optional(),
239
+ /** Priority for key selection (lower = higher priority) */
240
+ priority: z3.number()
241
+ });
242
+ var DelegationErrorSchema = z3.object({
243
+ /** Error code for programmatic handling */
244
+ code: z3.string(),
245
+ /** Human-readable error message */
246
+ message: z3.string(),
247
+ /** The service that produced the error */
248
+ service: z3.literal("delegation"),
249
+ /** Original error if wrapping another error */
250
+ cause: z3.instanceof(Error).optional(),
251
+ /** Additional metadata about the error */
252
+ meta: z3.record(z3.string(), z3.unknown()).optional()
253
+ });
254
+ var DelegationErrorCodes = {
255
+ AUTH_REQUIRED: "AUTH_REQUIRED",
256
+ AUTH_EXPIRED: "AUTH_EXPIRED",
257
+ NOT_INITIALIZED: "NOT_INITIALIZED",
258
+ NOT_FOUND: "NOT_FOUND",
259
+ REVOKED: "REVOKED",
260
+ NETWORK_ERROR: "NETWORK_ERROR",
261
+ TIMEOUT: "TIMEOUT",
262
+ ABORTED: "ABORTED",
263
+ INVALID_INPUT: "INVALID_INPUT",
264
+ PERMISSION_DENIED: "PERMISSION_DENIED",
265
+ CREATION_FAILED: "CREATION_FAILED",
266
+ REVOCATION_FAILED: "REVOCATION_FAILED",
267
+ INVALID_TOKEN: "INVALID_TOKEN",
268
+ KV_SERVICE_UNAVAILABLE: "KV_SERVICE_UNAVAILABLE",
269
+ DATA_FETCH_FAILED: "DATA_FETCH_FAILED",
270
+ VALIDATION_ERROR: "VALIDATION_ERROR"
271
+ };
272
+ var DelegationSchema = z3.object({
273
+ /** Content identifier (CID) of the delegation */
274
+ cid: z3.string(),
275
+ /** DID of the delegate (the party receiving the delegation) */
276
+ delegateDID: z3.string(),
277
+ /** Space ID this delegation applies to */
278
+ spaceId: z3.string(),
279
+ /** Resource path this delegation grants access to */
280
+ path: z3.string(),
281
+ /** Actions this delegation authorizes */
282
+ actions: z3.array(z3.string()),
283
+ /** When this delegation expires (accepts Date or ISO string from JSON) */
284
+ expiry: z3.coerce.date(),
285
+ /** Whether this delegation has been revoked */
286
+ isRevoked: z3.boolean(),
287
+ /** DID of the delegator (the party granting the delegation) */
288
+ delegatorDID: z3.string().optional(),
289
+ /** When this delegation was created (accepts Date or ISO string from JSON) */
290
+ createdAt: z3.coerce.date().optional(),
291
+ /** Parent delegation CID if this is a sub-delegation */
292
+ parentCid: z3.string().optional(),
293
+ /** Whether sub-delegation is allowed */
294
+ allowSubDelegation: z3.boolean().optional(),
295
+ /** Authorization header (UCAN bearer token) */
296
+ authHeader: z3.string().optional()
297
+ });
298
+ var CapabilityEntrySchema = z3.object({
299
+ /** Resource URI this capability applies to */
300
+ resource: z3.string(),
301
+ /** Action this capability authorizes */
302
+ action: z3.string(),
303
+ /** Keys that can exercise this capability, ordered by priority */
304
+ keys: z3.array(KeyInfoSchema),
305
+ /** The delegation that grants this capability */
306
+ delegation: DelegationSchema,
307
+ /** When this capability expires (accepts Date or ISO string from JSON) */
308
+ expiresAt: z3.coerce.date().optional()
309
+ });
310
+ var DelegationRecordSchema = z3.object({
311
+ /** Content identifier (CID) of the delegation */
312
+ cid: z3.string(),
313
+ /** Space ID this delegation applies to */
314
+ spaceId: z3.string(),
315
+ /** DID of the delegator (grantor) */
316
+ delegator: z3.string(),
317
+ /** DID of the delegatee (recipient) */
318
+ delegatee: z3.string(),
319
+ /** Key ID used to sign/exercise this delegation */
320
+ keyId: z3.string().optional(),
321
+ /** Resource path pattern this delegation grants access to */
322
+ path: z3.string(),
323
+ /** Actions this delegation authorizes */
324
+ actions: z3.array(z3.string()),
325
+ /** When this delegation expires (accepts Date or ISO string from JSON) */
326
+ expiry: z3.coerce.date().optional(),
327
+ /** When this delegation becomes valid (not before) (accepts Date or ISO string) */
328
+ notBefore: z3.coerce.date().optional(),
329
+ /** Whether this delegation has been revoked */
330
+ isRevoked: z3.boolean(),
331
+ /** When this delegation was created (accepts Date or ISO string from JSON) */
332
+ createdAt: z3.coerce.date(),
333
+ /** Parent delegation CID if this is a sub-delegation */
334
+ parentCid: z3.string().optional()
335
+ });
336
+ var CreateDelegationParamsSchema = z3.object({
337
+ /** DID of the delegate (the party receiving the delegation) */
338
+ delegateDID: z3.string(),
339
+ /** Resource path this delegation grants access to */
340
+ path: z3.string(),
341
+ /** Actions to authorize */
342
+ actions: z3.array(z3.string()),
343
+ /** When this delegation expires (accepts Date or ISO string) */
344
+ expiry: z3.coerce.date().optional(),
345
+ /** Whether to disable sub-delegation */
346
+ disableSubDelegation: z3.boolean().optional(),
347
+ /** Optional statement for the SIWE message */
348
+ statement: z3.string().optional()
349
+ });
350
+ var DelegationChainSchema = z3.array(DelegationSchema);
351
+ var DelegationChainV2Schema = z3.object({
352
+ /** The root delegation from the original authority */
353
+ root: DelegationSchema,
354
+ /** Intermediate delegations in the chain (may be empty) */
355
+ chain: z3.array(DelegationSchema),
356
+ /** The final delegation to the current user */
357
+ leaf: DelegationSchema
358
+ });
359
+ var DelegationDirectionSchema = z3.enum(["granted", "received", "all"]);
360
+ var DelegationFiltersSchema = z3.object({
361
+ /** Filter by delegation direction */
362
+ direction: DelegationDirectionSchema.optional(),
363
+ /** Filter by resource path pattern */
364
+ path: z3.string().optional(),
365
+ /** Filter by required actions */
366
+ actions: z3.array(z3.string()).optional(),
367
+ /** Include revoked delegations */
368
+ includeRevoked: z3.boolean().optional(),
369
+ /** Filter by delegator DID */
370
+ delegator: z3.string().optional(),
371
+ /** Filter by delegatee DID */
372
+ delegatee: z3.string().optional(),
373
+ /** Only include delegations valid at this time */
374
+ validAt: z3.coerce.date().optional(),
375
+ /** Maximum number of results to return */
376
+ limit: z3.number().optional(),
377
+ /** Cursor for pagination */
378
+ cursor: z3.string().optional()
379
+ });
380
+ var SpaceOwnershipSchema = z3.enum(["owned", "delegated"]);
381
+ var SpaceInfoSchema = z3.object({
382
+ /** Space identifier */
383
+ id: z3.string(),
384
+ /** Human-readable name for the space */
385
+ name: z3.string().optional(),
386
+ /** DID of the space owner */
387
+ owner: z3.string(),
388
+ /** Whether user owns or has delegated access */
389
+ type: SpaceOwnershipSchema,
390
+ /** Permissions the user has in this space */
391
+ permissions: z3.array(z3.string()).optional(),
392
+ /** When the access expires (for delegated spaces) */
393
+ expiresAt: z3.coerce.date().optional()
394
+ });
395
+ var ShareSchemaSchema = z3.enum(["base64", "compact", "ipfs"]);
396
+ var ShareLinkSchema = z3.object({
397
+ /** Unique token identifying this share link */
398
+ token: z3.string(),
399
+ /** Full URL for sharing */
400
+ url: z3.string(),
401
+ /** The delegation this link grants access to */
402
+ delegation: DelegationSchema,
403
+ /** Encoding schema used for the link */
404
+ schema: ShareSchemaSchema,
405
+ /** When this share link expires */
406
+ expiresAt: z3.coerce.date().optional(),
407
+ /** Human-readable description of what is being shared */
408
+ description: z3.string().optional()
409
+ });
410
+ function createShareLinkDataSchema(dataSchema) {
411
+ return z3.object({
412
+ /** The retrieved data */
413
+ data: dataSchema,
414
+ /** The delegation that authorized this access */
415
+ delegation: DelegationSchema,
416
+ /** The space the data belongs to */
417
+ spaceId: z3.string(),
418
+ /** The resource path that was accessed */
419
+ path: z3.string()
420
+ });
421
+ }
422
+ var ShareLinkDataSchema = createShareLinkDataSchema(z3.unknown());
423
+ var IngestOptionsSchema = z3.object({
424
+ /** Whether to persist the delegation to storage */
425
+ persist: z3.boolean().optional(),
426
+ /** Whether to validate the full delegation chain */
427
+ validateChain: z3.boolean().optional(),
428
+ /** Name for the ingested key */
429
+ keyName: z3.string().optional(),
430
+ /** Whether to create a session key for this delegation */
431
+ createSessionKey: z3.boolean().optional(),
432
+ /** Override the priority for the ingested key */
433
+ priority: z3.number().optional()
434
+ });
435
+ var GenerateShareParamsSchema = z3.object({
436
+ /** Resource path to share */
437
+ path: z3.string(),
438
+ /** Actions to authorize */
439
+ actions: z3.array(z3.string()).optional(),
440
+ /** When the share link expires */
441
+ expiry: z3.coerce.date().optional(),
442
+ /** Encoding schema for the link */
443
+ schema: ShareSchemaSchema.optional(),
444
+ /** Human-readable description */
445
+ description: z3.string().optional(),
446
+ /** Base URL for the share link */
447
+ baseUrl: z3.string().optional()
448
+ });
449
+ var DelegationManagerConfigSchema = z3.object({
450
+ /** TinyCloud host URLs */
451
+ hosts: z3.array(z3.string()),
452
+ /** Active session for authentication */
453
+ session: z3.unknown().refine(
454
+ (val) => val !== null && typeof val === "object",
455
+ { message: "Expected a ServiceSession object" }
456
+ ),
457
+ /** Platform-specific invoke function */
458
+ invoke: z3.unknown().refine(
459
+ (val) => typeof val === "function",
460
+ { message: "Expected an invoke function" }
461
+ ),
462
+ /** Optional custom fetch implementation */
463
+ fetch: z3.unknown().refine(
464
+ (val) => val === void 0 || typeof val === "function",
465
+ { message: "Expected a fetch function or undefined" }
466
+ ).optional()
467
+ });
468
+ var KeyProviderSchema = z3.object({
469
+ /** Generate a new session key, returns key ID */
470
+ createSessionKey: z3.unknown().refine(
471
+ (val) => typeof val === "function",
472
+ { message: "Expected a function" }
473
+ ),
474
+ /** Get JWK for a key */
475
+ getJWK: z3.unknown().refine(
476
+ (val) => typeof val === "function",
477
+ { message: "Expected a function" }
478
+ ),
479
+ /** Get DID for a key */
480
+ getDID: z3.unknown().refine(
481
+ (val) => typeof val === "function",
482
+ { message: "Expected a function" }
483
+ )
484
+ });
485
+ var DelegationApiResponseSchema = z3.object({
486
+ /** SIWE message content */
487
+ siwe: z3.string(),
488
+ /** Signature of the SIWE message */
489
+ signature: z3.string(),
490
+ /** Delegation version */
491
+ version: z3.number(),
492
+ /** CID of the created delegation */
493
+ cid: z3.string().optional()
494
+ });
495
+ var CreateDelegationWasmParamsSchema = z3.object({
496
+ /** The session containing delegation credentials */
497
+ session: z3.unknown().refine(
498
+ (val) => val !== null && typeof val === "object",
499
+ { message: "Expected a ServiceSession object" }
500
+ ),
501
+ /** DID of the delegate */
502
+ delegateDID: z3.string(),
503
+ /** Space ID this delegation applies to */
504
+ spaceId: z3.string(),
505
+ /** Resource path this delegation grants access to */
506
+ path: z3.string(),
507
+ /** Actions to authorize */
508
+ actions: z3.array(z3.string()),
509
+ /** Expiration time in seconds since Unix epoch */
510
+ expirationSecs: z3.number(),
511
+ /** Optional not-before time in seconds since Unix epoch */
512
+ notBeforeSecs: z3.number().optional()
513
+ });
514
+ var CreateDelegationWasmResultSchema = z3.object({
515
+ /** Base64url-encoded UCAN delegation */
516
+ delegation: z3.string(),
517
+ /** CID of the delegation */
518
+ cid: z3.string(),
519
+ /** DID of the delegate */
520
+ delegateDID: z3.string(),
521
+ /** Resource path the delegation grants access to */
522
+ path: z3.string(),
523
+ /** Actions the delegation authorizes */
524
+ actions: z3.array(z3.string()),
525
+ /** Expiration time */
526
+ expiry: z3.coerce.date()
527
+ });
528
+
529
+ // src/spaces/spaces.schema.ts
530
+ var SpaceConfigSchema = z4.object({
531
+ /** The space identifier (full URI) */
532
+ id: z4.string(),
533
+ /** The short name of the space */
534
+ name: z4.string(),
535
+ /** Factory function to create a space-scoped KV service */
536
+ createKV: z4.function(),
537
+ /** Factory function to create space-scoped delegations */
538
+ createDelegations: z4.function(),
539
+ /** Factory function to create space-scoped sharing */
540
+ createSharing: z4.function(),
541
+ /** Function to get space info */
542
+ getInfo: z4.function()
543
+ });
544
+ var SpaceServiceConfigSchema = z4.object({
545
+ /** TinyCloud host URLs */
546
+ hosts: z4.array(z4.string()),
547
+ /** Active session for authentication */
548
+ session: z4.unknown(),
549
+ /** Platform-specific invoke function */
550
+ invoke: z4.function(),
551
+ /** Optional custom fetch implementation */
552
+ fetch: z4.function().optional(),
553
+ /** Optional capability key registry for delegated space discovery */
554
+ capabilityRegistry: z4.unknown().optional(),
555
+ /** Factory function to create a space-scoped KV service */
556
+ createKVService: z4.function().optional(),
557
+ /** User's PKH DID (derived from address or provided explicitly) */
558
+ userDid: z4.string().optional(),
559
+ /** Optional SharingService for v2 sharing links (client-side) */
560
+ sharingService: z4.unknown().optional(),
561
+ /** Factory function to create delegations using SIWE-based flow */
562
+ createDelegation: z4.function().optional()
563
+ });
564
+ var SpaceDelegationParamsSchema = CreateDelegationParamsSchema.extend({
565
+ /** The space ID to create the delegation for */
566
+ spaceId: z4.string()
567
+ });
568
+ var ServerDelegationInfoSchema = z4.object({
569
+ /** DID of the delegator */
570
+ delegator: z4.string(),
571
+ /** DID of the delegate */
572
+ delegate: z4.string(),
573
+ /** Parent delegation CIDs - accepts string or byte array format from server */
574
+ parents: z4.array(z4.union([z4.string(), z4.array(z4.number())])),
575
+ /** Expiration time (ISO8601 string) */
576
+ expiry: z4.string().optional(),
577
+ /** Not-before time (ISO8601 string) */
578
+ not_before: z4.string().optional(),
579
+ /** Issued-at time (ISO8601 string) */
580
+ issued_at: z4.string().optional(),
581
+ /** Capabilities granted by this delegation */
582
+ capabilities: z4.array(
583
+ z4.object({
584
+ resource: z4.string(),
585
+ ability: z4.string()
586
+ })
587
+ )
588
+ });
589
+ var ServerDelegationsResponseSchema = z4.record(
590
+ z4.string(),
591
+ ServerDelegationInfoSchema
592
+ );
593
+ var ServerOwnedSpaceSchema = z4.object({
594
+ /** Space identifier */
595
+ id: z4.string(),
596
+ /** Space name (optional, can be derived from id) */
597
+ name: z4.string().optional(),
598
+ /** Owner DID */
599
+ owner: z4.string(),
600
+ /** Creation timestamp */
601
+ createdAt: z4.string().optional()
602
+ });
603
+ var ServerOwnedSpacesResponseSchema = z4.array(ServerOwnedSpaceSchema);
604
+ var ServerCreateSpaceResponseSchema = z4.object({
605
+ /** Space identifier */
606
+ id: z4.string(),
607
+ /** Space name */
608
+ name: z4.string(),
609
+ /** Owner DID */
610
+ owner: z4.string(),
611
+ /** Creation timestamp */
612
+ createdAt: z4.string().optional()
613
+ });
614
+ var ServerSpaceInfoResponseSchema = z4.object({
615
+ /** Space identifier */
616
+ id: z4.string(),
617
+ /** Space name (optional) */
618
+ name: z4.string().optional(),
619
+ /** Owner DID */
620
+ owner: z4.string(),
621
+ /** Ownership type */
622
+ type: z4.enum(["owned", "delegated"]).optional(),
623
+ /** Permissions the user has in this space */
624
+ permissions: z4.array(z4.string()).optional(),
625
+ /** Expiration for delegated access */
626
+ expiresAt: z4.string().optional()
627
+ });
628
+ function validateServerDelegationsResponse(data) {
629
+ if (data === null || data === void 0) {
630
+ return { ok: true, data: {} };
631
+ }
632
+ if (Array.isArray(data)) {
633
+ return { ok: true, data: {} };
634
+ }
635
+ const result = ServerDelegationsResponseSchema.safeParse(data);
636
+ if (!result.success) {
637
+ return {
638
+ ok: false,
639
+ error: {
640
+ code: "VALIDATION_ERROR",
641
+ message: `Invalid server delegations response: ${result.error.message}`,
642
+ service: "space",
643
+ meta: { issues: result.error.issues }
644
+ }
645
+ };
646
+ }
647
+ return { ok: true, data: result.data };
648
+ }
649
+ function validateServerOwnedSpacesResponse(data) {
650
+ const result = ServerOwnedSpacesResponseSchema.safeParse(data);
651
+ if (!result.success) {
652
+ return {
653
+ ok: false,
654
+ error: {
655
+ code: "VALIDATION_ERROR",
656
+ message: `Invalid server owned spaces response: ${result.error.message}`,
657
+ service: "space",
658
+ meta: { issues: result.error.issues }
659
+ }
660
+ };
661
+ }
662
+ return { ok: true, data: result.data };
663
+ }
664
+ function validateServerCreateSpaceResponse(data) {
665
+ const result = ServerCreateSpaceResponseSchema.safeParse(data);
666
+ if (!result.success) {
667
+ return {
668
+ ok: false,
669
+ error: {
670
+ code: "VALIDATION_ERROR",
671
+ message: `Invalid server create space response: ${result.error.message}`,
672
+ service: "space",
673
+ meta: { issues: result.error.issues }
674
+ }
675
+ };
676
+ }
677
+ return { ok: true, data: result.data };
678
+ }
679
+ function validateServerSpaceInfoResponse(data) {
680
+ const result = ServerSpaceInfoResponseSchema.safeParse(data);
681
+ if (!result.success) {
682
+ return {
683
+ ok: false,
684
+ error: {
685
+ code: "VALIDATION_ERROR",
686
+ message: `Invalid server space info response: ${result.error.message}`,
687
+ service: "space",
688
+ meta: { issues: result.error.issues }
689
+ }
690
+ };
691
+ }
692
+ return { ok: true, data: result.data };
693
+ }
694
+
695
+ // src/spaces/SpaceService.ts
696
+ var SERVICE_NAME = "space";
697
+ var SpaceErrorCodes = {
698
+ /** Space not found */
699
+ NOT_FOUND: "SPACE_NOT_FOUND",
700
+ /** Space already exists */
701
+ ALREADY_EXISTS: "SPACE_ALREADY_EXISTS",
702
+ /** Creation failed */
703
+ CREATION_FAILED: "SPACE_CREATION_FAILED",
704
+ /** Authentication required */
705
+ AUTH_REQUIRED: "AUTH_REQUIRED",
706
+ /** Invalid space name or URI */
707
+ INVALID_NAME: "INVALID_SPACE_NAME",
708
+ /** Network error */
709
+ NETWORK_ERROR: "NETWORK_ERROR",
710
+ /** Not initialized */
711
+ NOT_INITIALIZED: "NOT_INITIALIZED"
712
+ };
713
+ function makePublicSpaceId(address, chainId) {
714
+ return `tinycloud:pkh:eip155:${chainId}:${address}:public`;
715
+ }
716
+ function parseSpaceUri(uri) {
717
+ const fullUriMatch = uri.match(
718
+ /^tinycloud:pkh:eip155:(\d+):(0x[a-fA-F0-9]{40}):(.+)$/
719
+ );
720
+ if (fullUriMatch) {
721
+ const [, chainId, address, name] = fullUriMatch;
722
+ return {
723
+ owner: `did:pkh:eip155:${chainId}:${address}`,
724
+ name,
725
+ chainId,
726
+ address
727
+ };
728
+ }
729
+ if (/^[a-zA-Z0-9_-]+$/.test(uri)) {
730
+ return {
731
+ owner: "",
732
+ // Will be filled in from session
733
+ name: uri
734
+ };
735
+ }
736
+ return null;
737
+ }
738
+ function buildSpaceUri(owner, name) {
739
+ const pkhMatch = owner.match(/^did:pkh:eip155:(\d+):(0x[a-fA-F0-9]{40})$/);
740
+ if (pkhMatch) {
741
+ const [, chainId, address] = pkhMatch;
742
+ return `tinycloud:pkh:eip155:${chainId}:${address}:${name}`;
743
+ }
744
+ return `tinycloud:${owner}:${name}`;
745
+ }
746
+ function transformServerDelegations(validatedData, defaultSpaceId) {
747
+ const result = [];
748
+ for (const [cid, info] of Object.entries(validatedData)) {
749
+ const capabilities = info.capabilities;
750
+ let path = "";
751
+ let spaceId = defaultSpaceId;
752
+ const actions = [];
753
+ for (const cap of capabilities) {
754
+ actions.push(cap.ability);
755
+ const resourceMatch = cap.resource.match(
756
+ /^(tinycloud:pkh:eip155:\d+:0x[a-fA-F0-9]+:[^/]+)\/[^/]+\/(.*)$/
757
+ );
758
+ if (resourceMatch) {
759
+ spaceId = resourceMatch[1];
760
+ path = resourceMatch[2] || "";
761
+ }
762
+ }
763
+ const firstStringParent = info.parents?.find((p) => typeof p === "string");
764
+ result.push({
765
+ cid,
766
+ delegateDID: info.delegate,
767
+ delegatorDID: info.delegator,
768
+ spaceId,
769
+ path,
770
+ actions,
771
+ expiry: info.expiry ? new Date(info.expiry) : new Date(Date.now() + 24 * 60 * 60 * 1e3),
772
+ isRevoked: false,
773
+ createdAt: info.issued_at ? new Date(info.issued_at) : void 0,
774
+ parentCid: firstStringParent
775
+ });
776
+ }
777
+ return result;
778
+ }
779
+ var SpaceService = class {
780
+ /**
781
+ * Create a new SpaceService instance.
782
+ *
783
+ * @param config - Service configuration
784
+ */
785
+ constructor(config) {
786
+ /** Cache of created Space objects */
787
+ this.spaceCache = /* @__PURE__ */ new Map();
788
+ /** Cache of space info */
789
+ this.infoCache = /* @__PURE__ */ new Map();
790
+ /** Cache TTL in milliseconds (5 minutes) */
791
+ this.cacheTTL = 5 * 60 * 1e3;
792
+ this.hosts = config.hosts;
793
+ this.session = config.session;
794
+ this.invoke = config.invoke;
795
+ this.fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);
796
+ this.capabilityRegistry = config.capabilityRegistry;
797
+ this.createKVServiceFn = config.createKVService;
798
+ this._userDid = config.userDid;
799
+ this.sharingService = config.sharingService;
800
+ this.createDelegationFn = config.createDelegation;
801
+ }
802
+ /**
803
+ * Update the service configuration.
804
+ */
805
+ updateConfig(config) {
806
+ if (config.hosts) this.hosts = config.hosts;
807
+ if (config.session) this.session = config.session;
808
+ if (config.invoke) this.invoke = config.invoke;
809
+ if (config.fetch) this.fetchFn = config.fetch;
810
+ if (config.capabilityRegistry) this.capabilityRegistry = config.capabilityRegistry;
811
+ if (config.createKVService) this.createKVServiceFn = config.createKVService;
812
+ if (config.userDid !== void 0) this._userDid = config.userDid;
813
+ if (config.sharingService) this.sharingService = config.sharingService;
814
+ if (config.createDelegation) this.createDelegationFn = config.createDelegation;
815
+ this.spaceCache.clear();
816
+ this.infoCache.clear();
817
+ }
818
+ /**
819
+ * Get the current user's primary space ID.
820
+ */
821
+ getCurrentSpaceId() {
822
+ return this.session?.spaceId;
823
+ }
824
+ /**
825
+ * Get the primary host URL.
826
+ */
827
+ get host() {
828
+ return this.hosts[0];
829
+ }
830
+ /**
831
+ * Get the current user's PKH DID.
832
+ */
833
+ get userDid() {
834
+ if (this._userDid) {
835
+ return this._userDid;
836
+ }
837
+ return void 0;
838
+ }
839
+ // ===========================================================================
840
+ // List Spaces
841
+ // ===========================================================================
842
+ /**
843
+ * List all spaces the user has access to.
844
+ *
845
+ * Combines owned spaces (from the server) with delegated spaces
846
+ * (from the capability registry).
847
+ */
848
+ async list() {
849
+ if (!this.session) {
850
+ return err(
851
+ serviceError(SpaceErrorCodes.AUTH_REQUIRED, "Authentication required", SERVICE_NAME)
852
+ );
853
+ }
854
+ try {
855
+ const spaces = [];
856
+ const ownedResult = await this.listOwnedSpaces();
857
+ if (ownedResult.ok) {
858
+ spaces.push(...ownedResult.data);
859
+ }
860
+ if (this.capabilityRegistry) {
861
+ const delegatedSpaces = this.discoverDelegatedSpaces();
862
+ spaces.push(...delegatedSpaces);
863
+ }
864
+ const uniqueSpaces = this.deduplicateSpaces(spaces);
865
+ return ok(uniqueSpaces);
866
+ } catch (error) {
867
+ return err(
868
+ serviceError(
869
+ SpaceErrorCodes.NETWORK_ERROR,
870
+ `Failed to list spaces: ${String(error)}`,
871
+ SERVICE_NAME,
872
+ { cause: error instanceof Error ? error : void 0 }
873
+ )
874
+ );
875
+ }
876
+ }
877
+ /**
878
+ * List owned spaces from the server.
879
+ */
880
+ async listOwnedSpaces() {
881
+ try {
882
+ const headers = this.invoke(this.session, "space", "", "tinycloud.space/list");
883
+ const response = await this.fetchFn(`${this.host}/invoke`, {
884
+ method: "POST",
885
+ headers
886
+ });
887
+ if (!response.ok) {
888
+ const errorText = await response.text();
889
+ return err(
890
+ serviceError(
891
+ SpaceErrorCodes.NETWORK_ERROR,
892
+ `Failed to list owned spaces: ${response.status} - ${errorText}`,
893
+ SERVICE_NAME,
894
+ { meta: { status: response.status } }
895
+ )
896
+ );
897
+ }
898
+ const rawData = await response.json();
899
+ const validationResult = validateServerOwnedSpacesResponse(rawData);
900
+ if (!validationResult.ok) {
901
+ return err(
902
+ serviceError(
903
+ SpaceErrorCodes.NETWORK_ERROR,
904
+ validationResult.error.message,
905
+ SERVICE_NAME,
906
+ { meta: validationResult.error.meta }
907
+ )
908
+ );
909
+ }
910
+ const spaces = validationResult.data.map((item) => ({
911
+ id: item.id,
912
+ name: item.name ?? this.extractNameFromId(item.id),
913
+ owner: item.owner,
914
+ type: "owned",
915
+ permissions: ["*"]
916
+ // Full permissions for owned spaces
917
+ }));
918
+ return ok(spaces);
919
+ } catch (error) {
920
+ return err(
921
+ serviceError(
922
+ SpaceErrorCodes.NETWORK_ERROR,
923
+ `Network error listing owned spaces: ${String(error)}`,
924
+ SERVICE_NAME,
925
+ { cause: error instanceof Error ? error : void 0 }
926
+ )
927
+ );
928
+ }
929
+ }
930
+ /**
931
+ * Discover delegated spaces from the capability registry.
932
+ */
933
+ discoverDelegatedSpaces() {
934
+ if (!this.capabilityRegistry) {
935
+ return [];
936
+ }
937
+ const spaces = /* @__PURE__ */ new Map();
938
+ const capabilities = this.capabilityRegistry.getAllCapabilities();
939
+ for (const capability of capabilities) {
940
+ const spaceId = capability.delegation.spaceId;
941
+ if (spaces.has(spaceId)) {
942
+ const existing = spaces.get(spaceId);
943
+ if (existing.permissions) {
944
+ const actions = capability.delegation.actions;
945
+ for (const action of actions) {
946
+ if (!existing.permissions.includes(action)) {
947
+ existing.permissions.push(action);
948
+ }
949
+ }
950
+ }
951
+ continue;
952
+ }
953
+ if (spaceId === this.session?.spaceId) {
954
+ continue;
955
+ }
956
+ const parsed = parseSpaceUri(spaceId);
957
+ spaces.set(spaceId, {
958
+ id: spaceId,
959
+ name: parsed?.name ?? this.extractNameFromId(spaceId),
960
+ owner: capability.delegation.delegatorDID ?? parsed?.owner ?? "",
961
+ type: "delegated",
962
+ permissions: [...capability.delegation.actions],
963
+ expiresAt: capability.expiresAt
964
+ });
965
+ }
966
+ return Array.from(spaces.values());
967
+ }
968
+ /**
969
+ * Extract space name from a full space ID.
970
+ */
971
+ extractNameFromId(id) {
972
+ const parsed = parseSpaceUri(id);
973
+ if (parsed) {
974
+ return parsed.name;
975
+ }
976
+ const parts = id.split(":");
977
+ return parts[parts.length - 1] || id;
978
+ }
979
+ /**
980
+ * Deduplicate spaces, preferring owned over delegated.
981
+ */
982
+ deduplicateSpaces(spaces) {
983
+ const seen = /* @__PURE__ */ new Map();
984
+ for (const space of spaces) {
985
+ const existing = seen.get(space.id);
986
+ if (!existing || existing.type === "delegated" && space.type === "owned") {
987
+ seen.set(space.id, space);
988
+ }
989
+ }
990
+ return Array.from(seen.values());
991
+ }
992
+ // ===========================================================================
993
+ // Create Space
994
+ // ===========================================================================
995
+ /**
996
+ * Create a new space.
997
+ *
998
+ * @param name - The name for the new space
999
+ */
1000
+ async create(name) {
1001
+ if (!this.session) {
1002
+ return err(
1003
+ serviceError(SpaceErrorCodes.AUTH_REQUIRED, "Authentication required", SERVICE_NAME)
1004
+ );
1005
+ }
1006
+ if (!name || !/^[a-zA-Z0-9_-]+$/.test(name)) {
1007
+ return err(
1008
+ serviceError(
1009
+ SpaceErrorCodes.INVALID_NAME,
1010
+ "Space name must contain only alphanumeric characters, underscores, and hyphens",
1011
+ SERVICE_NAME
1012
+ )
1013
+ );
1014
+ }
1015
+ try {
1016
+ const headers = this.invoke(this.session, "space", name, "tinycloud.space/create");
1017
+ const response = await this.fetchFn(`${this.host}/invoke`, {
1018
+ method: "POST",
1019
+ headers,
1020
+ body: JSON.stringify({ name })
1021
+ });
1022
+ if (!response.ok) {
1023
+ const errorText = await response.text();
1024
+ if (response.status === 409) {
1025
+ return err(
1026
+ serviceError(
1027
+ SpaceErrorCodes.ALREADY_EXISTS,
1028
+ `Space "${name}" already exists`,
1029
+ SERVICE_NAME
1030
+ )
1031
+ );
1032
+ }
1033
+ return err(
1034
+ serviceError(
1035
+ SpaceErrorCodes.CREATION_FAILED,
1036
+ `Failed to create space: ${response.status} - ${errorText}`,
1037
+ SERVICE_NAME,
1038
+ { meta: { status: response.status } }
1039
+ )
1040
+ );
1041
+ }
1042
+ const rawData = await response.json();
1043
+ const validationResult = validateServerCreateSpaceResponse(rawData);
1044
+ if (!validationResult.ok) {
1045
+ return err(
1046
+ serviceError(
1047
+ SpaceErrorCodes.CREATION_FAILED,
1048
+ validationResult.error.message,
1049
+ SERVICE_NAME,
1050
+ { meta: validationResult.error.meta }
1051
+ )
1052
+ );
1053
+ }
1054
+ const spaceInfo = {
1055
+ id: validationResult.data.id,
1056
+ name: validationResult.data.name || name,
1057
+ owner: validationResult.data.owner || this.userDid || "",
1058
+ type: "owned",
1059
+ permissions: ["*"]
1060
+ };
1061
+ this.infoCache.set(spaceInfo.id, { info: spaceInfo, cachedAt: Date.now() });
1062
+ return ok(spaceInfo);
1063
+ } catch (error) {
1064
+ return err(
1065
+ serviceError(
1066
+ SpaceErrorCodes.NETWORK_ERROR,
1067
+ `Network error creating space: ${String(error)}`,
1068
+ SERVICE_NAME,
1069
+ { cause: error instanceof Error ? error : void 0 }
1070
+ )
1071
+ );
1072
+ }
1073
+ }
1074
+ // ===========================================================================
1075
+ // Get Space
1076
+ // ===========================================================================
1077
+ /**
1078
+ * Get a Space object by name or full URI.
1079
+ *
1080
+ * @param nameOrUri - Short name or full URI
1081
+ */
1082
+ get(nameOrUri) {
1083
+ const spaceId = this.resolveSpaceId(nameOrUri);
1084
+ const cached = this.spaceCache.get(spaceId);
1085
+ if (cached) {
1086
+ return cached;
1087
+ }
1088
+ const parsed = parseSpaceUri(spaceId);
1089
+ const name = parsed?.name ?? this.extractNameFromId(spaceId);
1090
+ const config = {
1091
+ id: spaceId,
1092
+ name,
1093
+ createKV: this.createSpaceScopedKV.bind(this),
1094
+ createDelegations: this.createSpaceScopedDelegations.bind(this),
1095
+ createSharing: this.createSpaceScopedSharing.bind(this),
1096
+ getInfo: this.getSpaceInfo.bind(this)
1097
+ };
1098
+ const space = new Space(config);
1099
+ this.spaceCache.set(spaceId, space);
1100
+ return space;
1101
+ }
1102
+ /**
1103
+ * Resolve a name or URI to a full space ID.
1104
+ */
1105
+ resolveSpaceId(nameOrUri) {
1106
+ const parsed = parseSpaceUri(nameOrUri);
1107
+ if (!parsed) {
1108
+ return nameOrUri;
1109
+ }
1110
+ if (parsed.owner) {
1111
+ return nameOrUri;
1112
+ }
1113
+ if (this.userDid) {
1114
+ return buildSpaceUri(this.userDid, parsed.name);
1115
+ }
1116
+ return nameOrUri;
1117
+ }
1118
+ // ===========================================================================
1119
+ // Exists Check
1120
+ // ===========================================================================
1121
+ /**
1122
+ * Check if a space exists and the user has access.
1123
+ */
1124
+ async exists(nameOrUri) {
1125
+ const spaceId = this.resolveSpaceId(nameOrUri);
1126
+ const cached = this.infoCache.get(spaceId);
1127
+ if (cached && Date.now() - cached.cachedAt < this.cacheTTL) {
1128
+ return ok(true);
1129
+ }
1130
+ const infoResult = await this.getSpaceInfo(spaceId);
1131
+ return ok(infoResult.ok);
1132
+ }
1133
+ // ===========================================================================
1134
+ // Space Info
1135
+ // ===========================================================================
1136
+ /**
1137
+ * Get space info from server or cache.
1138
+ */
1139
+ async getSpaceInfo(spaceId) {
1140
+ const cached = this.infoCache.get(spaceId);
1141
+ if (cached && Date.now() - cached.cachedAt < this.cacheTTL) {
1142
+ return ok(cached.info);
1143
+ }
1144
+ if (!this.session) {
1145
+ return err(
1146
+ serviceError(SpaceErrorCodes.AUTH_REQUIRED, "Authentication required", SERVICE_NAME)
1147
+ );
1148
+ }
1149
+ try {
1150
+ const headers = this.invoke(this.session, "space", spaceId, "tinycloud.space/info");
1151
+ const response = await this.fetchFn(`${this.host}/invoke`, {
1152
+ method: "POST",
1153
+ headers,
1154
+ body: JSON.stringify({ spaceId })
1155
+ });
1156
+ if (!response.ok) {
1157
+ if (response.status === 404) {
1158
+ return err(
1159
+ serviceError(SpaceErrorCodes.NOT_FOUND, `Space not found: ${spaceId}`, SERVICE_NAME)
1160
+ );
1161
+ }
1162
+ const errorText = await response.text();
1163
+ return err(
1164
+ serviceError(
1165
+ SpaceErrorCodes.NETWORK_ERROR,
1166
+ `Failed to get space info: ${response.status} - ${errorText}`,
1167
+ SERVICE_NAME
1168
+ )
1169
+ );
1170
+ }
1171
+ const rawData = await response.json();
1172
+ const validationResult = validateServerSpaceInfoResponse(rawData);
1173
+ if (!validationResult.ok) {
1174
+ return err(
1175
+ serviceError(
1176
+ SpaceErrorCodes.NETWORK_ERROR,
1177
+ validationResult.error.message,
1178
+ SERVICE_NAME,
1179
+ { meta: validationResult.error.meta }
1180
+ )
1181
+ );
1182
+ }
1183
+ const data = validationResult.data;
1184
+ const spaceInfo = {
1185
+ id: data.id,
1186
+ name: data.name ?? this.extractNameFromId(data.id),
1187
+ owner: data.owner,
1188
+ type: data.type ?? (data.owner === this.userDid ? "owned" : "delegated"),
1189
+ permissions: data.permissions,
1190
+ expiresAt: data.expiresAt ? new Date(data.expiresAt) : void 0
1191
+ };
1192
+ this.infoCache.set(spaceId, { info: spaceInfo, cachedAt: Date.now() });
1193
+ return ok(spaceInfo);
1194
+ } catch (error) {
1195
+ return err(
1196
+ serviceError(
1197
+ SpaceErrorCodes.NETWORK_ERROR,
1198
+ `Network error getting space info: ${String(error)}`,
1199
+ SERVICE_NAME,
1200
+ { cause: error instanceof Error ? error : void 0 }
1201
+ )
1202
+ );
1203
+ }
1204
+ }
1205
+ // ===========================================================================
1206
+ // Space-Scoped Service Factories
1207
+ // ===========================================================================
1208
+ /**
1209
+ * Create a space-scoped KV service.
1210
+ */
1211
+ createSpaceScopedKV(spaceId) {
1212
+ if (this.createKVServiceFn) {
1213
+ return this.createKVServiceFn(spaceId);
1214
+ }
1215
+ return new Proxy({}, {
1216
+ get: () => {
1217
+ throw new Error(
1218
+ "KV service factory not configured. Provide createKVService in SpaceServiceConfig."
1219
+ );
1220
+ }
1221
+ });
1222
+ }
1223
+ /**
1224
+ * Create space-scoped delegation operations.
1225
+ */
1226
+ createSpaceScopedDelegations(spaceId) {
1227
+ const self = this;
1228
+ return {
1229
+ async list() {
1230
+ try {
1231
+ const facts = [
1232
+ {
1233
+ capabilitiesReadParams: {
1234
+ type: "list",
1235
+ filters: { direction: "created" }
1236
+ }
1237
+ }
1238
+ ];
1239
+ const headers = self.invoke(
1240
+ self.session,
1241
+ "capabilities",
1242
+ "all",
1243
+ "tinycloud.capabilities/read",
1244
+ facts
1245
+ );
1246
+ const response = await self.fetchFn(`${self.host}/invoke`, {
1247
+ method: "POST",
1248
+ headers
1249
+ });
1250
+ if (!response.ok) {
1251
+ const errorText = await response.text();
1252
+ return err(
1253
+ serviceError(
1254
+ SpaceErrorCodes.NETWORK_ERROR,
1255
+ `Failed to list delegations: ${response.status} - ${errorText}`,
1256
+ SERVICE_NAME
1257
+ )
1258
+ );
1259
+ }
1260
+ const rawData = await response.json();
1261
+ const validationResult = validateServerDelegationsResponse(rawData);
1262
+ if (!validationResult.ok) {
1263
+ return err(
1264
+ serviceError(
1265
+ SpaceErrorCodes.NETWORK_ERROR,
1266
+ validationResult.error.message,
1267
+ SERVICE_NAME,
1268
+ { meta: validationResult.error.meta }
1269
+ )
1270
+ );
1271
+ }
1272
+ const delegations = transformServerDelegations(validationResult.data, spaceId);
1273
+ return ok(delegations);
1274
+ } catch (error) {
1275
+ return err(
1276
+ serviceError(
1277
+ SpaceErrorCodes.NETWORK_ERROR,
1278
+ `Network error listing delegations: ${String(error)}`,
1279
+ SERVICE_NAME
1280
+ )
1281
+ );
1282
+ }
1283
+ },
1284
+ async listReceived() {
1285
+ try {
1286
+ const facts = [
1287
+ {
1288
+ capabilitiesReadParams: {
1289
+ type: "list",
1290
+ filters: { direction: "received" }
1291
+ }
1292
+ }
1293
+ ];
1294
+ const headers = self.invoke(
1295
+ self.session,
1296
+ "capabilities",
1297
+ "all",
1298
+ "tinycloud.capabilities/read",
1299
+ facts
1300
+ );
1301
+ const response = await self.fetchFn(`${self.host}/invoke`, {
1302
+ method: "POST",
1303
+ headers
1304
+ });
1305
+ if (!response.ok) {
1306
+ const errorText = await response.text();
1307
+ return err(
1308
+ serviceError(
1309
+ SpaceErrorCodes.NETWORK_ERROR,
1310
+ `Failed to list received delegations: ${response.status} - ${errorText}`,
1311
+ SERVICE_NAME
1312
+ )
1313
+ );
1314
+ }
1315
+ const rawData = await response.json();
1316
+ const validationResult = validateServerDelegationsResponse(rawData);
1317
+ if (!validationResult.ok) {
1318
+ return err(
1319
+ serviceError(
1320
+ SpaceErrorCodes.NETWORK_ERROR,
1321
+ validationResult.error.message,
1322
+ SERVICE_NAME,
1323
+ { meta: validationResult.error.meta }
1324
+ )
1325
+ );
1326
+ }
1327
+ const delegations = transformServerDelegations(validationResult.data, spaceId);
1328
+ return ok(delegations);
1329
+ } catch (error) {
1330
+ return err(
1331
+ serviceError(
1332
+ SpaceErrorCodes.NETWORK_ERROR,
1333
+ `Network error listing received delegations: ${String(error)}`,
1334
+ SERVICE_NAME
1335
+ )
1336
+ );
1337
+ }
1338
+ },
1339
+ async create(params) {
1340
+ if (self.createDelegationFn) {
1341
+ return self.createDelegationFn({ ...params, spaceId });
1342
+ }
1343
+ return err(
1344
+ serviceError(
1345
+ SpaceErrorCodes.NOT_INITIALIZED,
1346
+ "Delegation creation requires a createDelegation function. This should be provided by the platform SDK (web-sdk or node-sdk).",
1347
+ SERVICE_NAME
1348
+ )
1349
+ );
1350
+ },
1351
+ async revoke(cid) {
1352
+ try {
1353
+ const headers = self.invoke(
1354
+ self.session,
1355
+ "delegation",
1356
+ cid,
1357
+ "tinycloud.delegation/revoke"
1358
+ );
1359
+ const response = await self.fetchFn(`${self.host}/revoke`, {
1360
+ method: "POST",
1361
+ headers,
1362
+ body: JSON.stringify({ cid, spaceId })
1363
+ });
1364
+ if (!response.ok) {
1365
+ const errorText = await response.text();
1366
+ return err(
1367
+ serviceError(
1368
+ SpaceErrorCodes.NETWORK_ERROR,
1369
+ `Failed to revoke delegation: ${response.status} - ${errorText}`,
1370
+ SERVICE_NAME
1371
+ )
1372
+ );
1373
+ }
1374
+ return ok(void 0);
1375
+ } catch (error) {
1376
+ return err(
1377
+ serviceError(
1378
+ SpaceErrorCodes.NETWORK_ERROR,
1379
+ `Network error revoking delegation: ${String(error)}`,
1380
+ SERVICE_NAME
1381
+ )
1382
+ );
1383
+ }
1384
+ }
1385
+ };
1386
+ }
1387
+ /**
1388
+ * Create space-scoped sharing operations.
1389
+ *
1390
+ * When a SharingService is configured, delegates to client-side v2 sharing.
1391
+ * V2 sharing links are self-contained with embedded private keys - no server tracking.
1392
+ */
1393
+ createSpaceScopedSharing(spaceId) {
1394
+ const self = this;
1395
+ return {
1396
+ async generate(params) {
1397
+ if (self.sharingService) {
1398
+ const result = await self.sharingService.generate(params);
1399
+ if (!result.ok) {
1400
+ return err(
1401
+ serviceError(
1402
+ SpaceErrorCodes.NETWORK_ERROR,
1403
+ result.error.message || "Failed to generate share link",
1404
+ SERVICE_NAME
1405
+ )
1406
+ );
1407
+ }
1408
+ return ok(result.data);
1409
+ }
1410
+ return err(
1411
+ serviceError(
1412
+ SpaceErrorCodes.NOT_INITIALIZED,
1413
+ "SharingService not configured. V2 sharing requires a SharingService instance.",
1414
+ SERVICE_NAME
1415
+ )
1416
+ );
1417
+ },
1418
+ async list() {
1419
+ return err(
1420
+ serviceError(
1421
+ SpaceErrorCodes.NOT_INITIALIZED,
1422
+ "Listing share links is not supported in v2. Share links are self-contained tokens that are not tracked on the server.",
1423
+ SERVICE_NAME
1424
+ )
1425
+ );
1426
+ },
1427
+ async revoke(token) {
1428
+ return err(
1429
+ serviceError(
1430
+ SpaceErrorCodes.NOT_INITIALIZED,
1431
+ "Revoking share links by token is not supported in v2. To revoke access, revoke the underlying delegation using space.delegations.revoke(cid).",
1432
+ SERVICE_NAME
1433
+ )
1434
+ );
1435
+ }
1436
+ };
1437
+ }
1438
+ };
1439
+ function createSpaceService(config) {
1440
+ return new SpaceService(config);
1441
+ }
1442
+
1443
+ // src/TinyCloud.ts
1444
+ var TinyCloud = class _TinyCloud {
1445
+ /**
1446
+ * Create a new TinyCloud SDK instance.
1447
+ *
1448
+ * @param userAuthorization - Platform-specific authorization implementation
1449
+ * @param config - Optional SDK configuration
1450
+ */
1451
+ constructor(userAuthorization, config) {
1452
+ /**
1453
+ * Registered extensions.
1454
+ */
1455
+ this.extensions = [];
1456
+ /**
1457
+ * Registered services by name.
1458
+ */
1459
+ this._services = /* @__PURE__ */ new Map();
1460
+ /**
1461
+ * Whether services have been initialized.
1462
+ */
1463
+ this._servicesInitialized = false;
1464
+ this.userAuthorization = userAuthorization;
1465
+ this.config = config || {};
1466
+ }
1467
+ // === Service Management ===
1468
+ /**
1469
+ * Initialize services with platform dependencies.
1470
+ * Must be called before using services.
1471
+ *
1472
+ * @param invoke - Platform-specific invoke function from WASM binding
1473
+ * @param hosts - TinyCloud host URLs (optional, uses config.hosts)
1474
+ * @param fetchFn - Custom fetch implementation (optional)
1475
+ */
1476
+ initializeServices(invoke, hosts, fetchFn) {
1477
+ const effectiveInvoke = invoke ?? this.config.invoke;
1478
+ const effectiveHosts = hosts ?? this.config.hosts;
1479
+ if (!effectiveInvoke) {
1480
+ throw new Error(
1481
+ "invoke function is required to initialize services. Provide it via config.invoke or initializeServices()."
1482
+ );
1483
+ }
1484
+ if (!effectiveHosts || effectiveHosts.length === 0) {
1485
+ throw new Error(
1486
+ "hosts are required to initialize services. Provide them via config.hosts or initializeServices()."
1487
+ );
1488
+ }
1489
+ this._serviceContext = new ServiceContext({
1490
+ invoke: effectiveInvoke,
1491
+ fetch: fetchFn ?? this.config.fetch ?? globalThis.fetch.bind(globalThis),
1492
+ hosts: effectiveHosts,
1493
+ retryPolicy: this.config.retryPolicy
1494
+ });
1495
+ const serviceConstructors = {
1496
+ kv: KVService,
1497
+ sql: SQLService,
1498
+ duckdb: DuckDbService,
1499
+ ...this.config.services
1500
+ };
1501
+ for (const [name, ServiceClass] of Object.entries(serviceConstructors)) {
1502
+ const serviceConfig = this.config.serviceConfigs?.[name] ?? {};
1503
+ const service = new ServiceClass(serviceConfig);
1504
+ service.initialize(this._serviceContext);
1505
+ this._serviceContext.registerService(name, service);
1506
+ this._services.set(name, service);
1507
+ }
1508
+ this._servicesInitialized = true;
1509
+ }
1510
+ /**
1511
+ * Get the service context.
1512
+ * @throws Error if services are not initialized
1513
+ */
1514
+ get serviceContext() {
1515
+ if (!this._serviceContext) {
1516
+ throw new Error(
1517
+ "Services not initialized. Call initializeServices() first."
1518
+ );
1519
+ }
1520
+ return this._serviceContext;
1521
+ }
1522
+ /**
1523
+ * Get a registered service by name.
1524
+ *
1525
+ * @param name - Service name (e.g., 'kv')
1526
+ * @returns The service instance or undefined
1527
+ */
1528
+ getService(name) {
1529
+ return this._services.get(name);
1530
+ }
1531
+ /**
1532
+ * Get the KV service.
1533
+ * @throws Error if services are not initialized
1534
+ */
1535
+ get kv() {
1536
+ if (!this._servicesInitialized) {
1537
+ throw new Error(
1538
+ "Services not initialized. Call initializeServices() first, or use TinyCloudWeb/TinyCloudNode which handles this automatically."
1539
+ );
1540
+ }
1541
+ const service = this._services.get("kv");
1542
+ if (!service) {
1543
+ throw new Error("KV service is not registered.");
1544
+ }
1545
+ return service;
1546
+ }
1547
+ /**
1548
+ * Get the SQL service.
1549
+ * @throws Error if services are not initialized
1550
+ */
1551
+ get sql() {
1552
+ if (!this._servicesInitialized) {
1553
+ throw new Error(
1554
+ "Services not initialized. Call initializeServices() first, or use TinyCloudWeb/TinyCloudNode which handles this automatically."
1555
+ );
1556
+ }
1557
+ const service = this._services.get("sql");
1558
+ if (!service) {
1559
+ throw new Error("SQL service is not registered.");
1560
+ }
1561
+ return service;
1562
+ }
1563
+ /**
1564
+ * Get the DuckDB service.
1565
+ * @throws Error if services are not initialized
1566
+ */
1567
+ get duckdb() {
1568
+ if (!this._servicesInitialized) {
1569
+ throw new Error(
1570
+ "Services not initialized. Call initializeServices() first, or use TinyCloudWeb/TinyCloudNode which handles this automatically."
1571
+ );
1572
+ }
1573
+ const service = this._services.get("duckdb");
1574
+ if (!service) {
1575
+ throw new Error("DuckDB service is not registered.");
1576
+ }
1577
+ return service;
1578
+ }
1579
+ /**
1580
+ * Get the Data Vault service.
1581
+ * @throws Error if services are not initialized or vault service is not registered
1582
+ */
1583
+ get vault() {
1584
+ if (!this._servicesInitialized) {
1585
+ throw new Error(
1586
+ "Services not initialized. Call initializeServices() first, or use TinyCloudWeb/TinyCloudNode which handles this automatically."
1587
+ );
1588
+ }
1589
+ const service = this._services.get("vault");
1590
+ if (!service) {
1591
+ throw new Error("Vault service is not registered.");
1592
+ }
1593
+ return service;
1594
+ }
1595
+ /**
1596
+ * Notify services of session change.
1597
+ * Called internally after sign-in and sign-out.
1598
+ *
1599
+ * @param session - The new session, or null if signed out
1600
+ */
1601
+ notifyServicesOfSessionChange(session) {
1602
+ if (this._serviceContext) {
1603
+ this._serviceContext.setSession(session);
1604
+ }
1605
+ }
1606
+ /**
1607
+ * Abort all pending service operations.
1608
+ * Called internally before sign-out.
1609
+ */
1610
+ abortServiceOperations() {
1611
+ if (this._serviceContext) {
1612
+ this._serviceContext.abort();
1613
+ }
1614
+ }
1615
+ /**
1616
+ * Convert ClientSession to ServiceSession.
1617
+ * Returns null if session lacks required fields.
1618
+ */
1619
+ toServiceSession(clientSession) {
1620
+ if (!clientSession) return null;
1621
+ const tcSession = clientSession.tinycloudSession;
1622
+ if (!tcSession) return null;
1623
+ return {
1624
+ delegationHeader: tcSession.delegationHeader,
1625
+ delegationCid: tcSession.delegationCid,
1626
+ spaceId: tcSession.spaceId,
1627
+ verificationMethod: tcSession.verificationMethod,
1628
+ jwk: tcSession.jwk
1629
+ };
1630
+ }
1631
+ /**
1632
+ * Add an extension to the SDK.
1633
+ * Extensions can add capabilities and lifecycle hooks.
1634
+ */
1635
+ extend(extension) {
1636
+ this.extensions.push(extension);
1637
+ this.userAuthorization.extend(extension);
1638
+ }
1639
+ /**
1640
+ * Check if an extension is enabled.
1641
+ * @param namespace - The extension namespace to check
1642
+ */
1643
+ isExtensionEnabled(namespace) {
1644
+ return this.extensions.some((ext) => ext.namespace === namespace);
1645
+ }
1646
+ // === Authentication Methods (delegate to userAuthorization) ===
1647
+ /**
1648
+ * Get the current session, if signed in.
1649
+ */
1650
+ get session() {
1651
+ return this.userAuthorization.session;
1652
+ }
1653
+ /**
1654
+ * Check if the user is signed in.
1655
+ */
1656
+ get isSignedIn() {
1657
+ return !!this.userAuthorization.session;
1658
+ }
1659
+ /**
1660
+ * Sign in and create a new session.
1661
+ * Notifies services of the new session after successful sign-in.
1662
+ * @returns The new session
1663
+ */
1664
+ async signIn() {
1665
+ const session = await this.userAuthorization.signIn();
1666
+ const serviceSession = this.toServiceSession(session);
1667
+ this.notifyServicesOfSessionChange(serviceSession);
1668
+ return session;
1669
+ }
1670
+ /**
1671
+ * Sign out and clear the current session.
1672
+ * Aborts pending service operations and notifies services.
1673
+ */
1674
+ async signOut() {
1675
+ this.abortServiceOperations();
1676
+ await this.userAuthorization.signOut();
1677
+ this._publicKV = void 0;
1678
+ this.notifyServicesOfSessionChange(null);
1679
+ }
1680
+ /**
1681
+ * Get the current wallet address.
1682
+ */
1683
+ address() {
1684
+ return this.userAuthorization.address();
1685
+ }
1686
+ /**
1687
+ * Get the current chain ID.
1688
+ */
1689
+ chainId() {
1690
+ return this.userAuthorization.chainId();
1691
+ }
1692
+ /**
1693
+ * Sign a message with the connected wallet.
1694
+ * @param message - Message to sign
1695
+ */
1696
+ async signMessage(message) {
1697
+ return this.userAuthorization.signMessage(message);
1698
+ }
1699
+ /**
1700
+ * Construct the deterministic public space ID for a given address and chain ID.
1701
+ *
1702
+ * @param address - Ethereum address (0x-prefixed)
1703
+ * @param chainId - Chain ID (e.g., 1 for mainnet)
1704
+ * @returns The public space ID
1705
+ */
1706
+ static makePublicSpaceId(address, chainId) {
1707
+ return makePublicSpaceId(address, chainId);
1708
+ }
1709
+ /**
1710
+ * Ensure the user's public space exists.
1711
+ * Creates it via spaces.create('public') if it doesn't.
1712
+ * Called automatically by modules that need to publish data.
1713
+ *
1714
+ * Requires the user to be signed in and services to be initialized.
1715
+ */
1716
+ async ensurePublicSpace() {
1717
+ const address = this.address();
1718
+ const chainId = this.chainId();
1719
+ if (!address || !chainId) {
1720
+ return err2(
1721
+ serviceError2(
1722
+ ErrorCodes.AUTH_REQUIRED,
1723
+ "Must be signed in to ensure public space",
1724
+ "public-space"
1725
+ )
1726
+ );
1727
+ }
1728
+ if (!this._serviceContext) {
1729
+ return err2(
1730
+ serviceError2(
1731
+ ErrorCodes.AUTH_REQUIRED,
1732
+ "Services not initialized. Call initializeServices() or signIn() first.",
1733
+ "public-space"
1734
+ )
1735
+ );
1736
+ }
1737
+ const spaceId = makePublicSpaceId(address, chainId);
1738
+ try {
1739
+ const session = this._serviceContext.session;
1740
+ if (!session) {
1741
+ return err2(
1742
+ serviceError2(
1743
+ ErrorCodes.AUTH_REQUIRED,
1744
+ "No active session",
1745
+ "public-space"
1746
+ )
1747
+ );
1748
+ }
1749
+ const headers = this._serviceContext.invoke(
1750
+ session,
1751
+ "space",
1752
+ spaceId,
1753
+ "tinycloud.space/info"
1754
+ );
1755
+ const response = await this._serviceContext.fetch(
1756
+ `${this._serviceContext.hosts[0]}/invoke`,
1757
+ { method: "POST", headers, body: JSON.stringify({ spaceId }) }
1758
+ );
1759
+ if (response.ok) {
1760
+ return ok2(void 0);
1761
+ }
1762
+ if (response.status === 404) {
1763
+ const createHeaders = this._serviceContext.invoke(
1764
+ session,
1765
+ "space",
1766
+ "public",
1767
+ "tinycloud.space/create"
1768
+ );
1769
+ const createResponse = await this._serviceContext.fetch(
1770
+ `${this._serviceContext.hosts[0]}/invoke`,
1771
+ {
1772
+ method: "POST",
1773
+ headers: createHeaders,
1774
+ body: JSON.stringify({ name: "public" })
1775
+ }
1776
+ );
1777
+ if (!createResponse.ok) {
1778
+ if (createResponse.status === 409) {
1779
+ return ok2(void 0);
1780
+ }
1781
+ const errorText2 = await createResponse.text();
1782
+ return err2(
1783
+ serviceError2(
1784
+ ErrorCodes.NETWORK_ERROR,
1785
+ `Failed to create public space: ${createResponse.status} - ${errorText2}`,
1786
+ "public-space"
1787
+ )
1788
+ );
1789
+ }
1790
+ return ok2(void 0);
1791
+ }
1792
+ const errorText = await response.text();
1793
+ return err2(
1794
+ serviceError2(
1795
+ ErrorCodes.NETWORK_ERROR,
1796
+ `Failed to check public space: ${response.status} - ${errorText}`,
1797
+ "public-space"
1798
+ )
1799
+ );
1800
+ } catch (error) {
1801
+ return err2(
1802
+ serviceError2(
1803
+ ErrorCodes.NETWORK_ERROR,
1804
+ `Network error ensuring public space: ${String(error)}`,
1805
+ "public-space",
1806
+ { cause: error instanceof Error ? error : void 0 }
1807
+ )
1808
+ );
1809
+ }
1810
+ }
1811
+ /**
1812
+ * Get a KVService scoped to the user's own public space.
1813
+ * Writes require authentication (owner/delegate).
1814
+ *
1815
+ * @throws Error if not signed in or services not initialized
1816
+ */
1817
+ get publicKV() {
1818
+ if (!this._servicesInitialized || !this._serviceContext) {
1819
+ throw new Error(
1820
+ "Services not initialized. Call initializeServices() first, or use TinyCloudWeb/TinyCloudNode which handles this automatically."
1821
+ );
1822
+ }
1823
+ const address = this.address();
1824
+ const chainId = this.chainId();
1825
+ if (!address || !chainId) {
1826
+ throw new Error("Must be signed in to access publicKV.");
1827
+ }
1828
+ if (this._publicKV) {
1829
+ return this._publicKV;
1830
+ }
1831
+ const publicSpaceId = makePublicSpaceId(address, chainId);
1832
+ const session = this._serviceContext.session;
1833
+ if (!session) {
1834
+ throw new Error("No active session. Sign in first.");
1835
+ }
1836
+ const publicKV = new KVService({ prefix: "" });
1837
+ const publicContext = new ServiceContext({
1838
+ invoke: this._serviceContext.invoke,
1839
+ fetch: this._serviceContext.fetch,
1840
+ hosts: this._serviceContext.hosts,
1841
+ retryPolicy: this.config.retryPolicy
1842
+ });
1843
+ publicContext.setSession({
1844
+ ...session,
1845
+ spaceId: publicSpaceId
1846
+ });
1847
+ publicKV.initialize(publicContext);
1848
+ this._publicKV = publicKV;
1849
+ return this._publicKV;
1850
+ }
1851
+ /**
1852
+ * Read from any user's public space (unauthenticated).
1853
+ * Uses the public REST endpoint — no session needed.
1854
+ *
1855
+ * @param host - TinyCloud server URL (e.g., "https://node.tinycloud.xyz")
1856
+ * @param spaceId - Full public space ID
1857
+ * @param key - Key to read
1858
+ * @param fetchFn - Optional custom fetch function
1859
+ * @returns The data at the key
1860
+ */
1861
+ static async readPublicSpace(host, spaceId, key, fetchFn) {
1862
+ const doFetch = fetchFn ?? globalThis.fetch.bind(globalThis);
1863
+ const encodedKey = key.split("/").map(encodeURIComponent).join("/");
1864
+ const url = `${host}/public/${encodeURIComponent(spaceId)}/kv/${encodedKey}`;
1865
+ try {
1866
+ const response = await doFetch(url, { method: "GET" });
1867
+ if (!response.ok) {
1868
+ if (response.status === 404) {
1869
+ return err2(
1870
+ serviceError2(
1871
+ ErrorCodes.NOT_FOUND,
1872
+ `Key not found: ${key} in space ${spaceId}`,
1873
+ "public-space"
1874
+ )
1875
+ );
1876
+ }
1877
+ const errorText = await response.text();
1878
+ return err2(
1879
+ serviceError2(
1880
+ ErrorCodes.NETWORK_ERROR,
1881
+ `Failed to read public space: ${response.status} - ${errorText}`,
1882
+ "public-space",
1883
+ { meta: { status: response.status } }
1884
+ )
1885
+ );
1886
+ }
1887
+ const contentType = response.headers.get("content-type");
1888
+ let data;
1889
+ if (contentType?.includes("application/json")) {
1890
+ data = await response.json();
1891
+ } else {
1892
+ const text = await response.text();
1893
+ try {
1894
+ data = JSON.parse(text);
1895
+ } catch {
1896
+ data = text;
1897
+ }
1898
+ }
1899
+ return ok2(data);
1900
+ } catch (error) {
1901
+ return err2(
1902
+ serviceError2(
1903
+ ErrorCodes.NETWORK_ERROR,
1904
+ `Network error reading public space: ${String(error)}`,
1905
+ "public-space",
1906
+ { cause: error instanceof Error ? error : void 0 }
1907
+ )
1908
+ );
1909
+ }
1910
+ }
1911
+ /**
1912
+ * Read from any user's public space by address (unauthenticated).
1913
+ * Convenience method that constructs the space ID from address and chain ID.
1914
+ *
1915
+ * @param host - TinyCloud server URL
1916
+ * @param address - Ethereum address (0x-prefixed)
1917
+ * @param chainId - Chain ID (e.g., 1 for mainnet)
1918
+ * @param key - Key to read
1919
+ * @param fetchFn - Optional custom fetch function
1920
+ * @returns The data at the key
1921
+ */
1922
+ static async readPublicKey(host, address, chainId, key, fetchFn) {
1923
+ const spaceId = makePublicSpaceId(address, chainId);
1924
+ return _TinyCloud.readPublicSpace(host, spaceId, key, fetchFn);
1925
+ }
1926
+ };
1927
+
1928
+ // src/index.ts
1929
+ import {
1930
+ ServiceContext as ServiceContext2,
1931
+ KVService as KVService2,
1932
+ PrefixedKVService,
1933
+ ok as ok4,
1934
+ err as err4,
1935
+ serviceError as serviceError4,
1936
+ ErrorCodes as ErrorCodes2,
1937
+ defaultRetryPolicy as defaultRetryPolicy2,
1938
+ SQLService as SQLService2,
1939
+ DatabaseHandle,
1940
+ SQLAction,
1941
+ DuckDbService as DuckDbService2,
1942
+ DuckDbDatabaseHandle,
1943
+ DuckDbAction,
1944
+ DataVaultService,
1945
+ VaultHeaders,
1946
+ VaultPublicSpaceKVActions,
1947
+ createVaultCrypto
1948
+ } from "@tinycloud/sdk-services";
1949
+
1950
+ // src/space.ts
1951
+ async function fetchPeerId(host, spaceId) {
1952
+ const res = await fetch(
1953
+ `${host}/peer/generate/${encodeURIComponent(spaceId)}`
1954
+ );
1955
+ if (!res.ok) {
1956
+ const error = await res.text().catch(() => res.statusText);
1957
+ throw new Error(`Failed to get peer ID: ${res.status} - ${error}`);
1958
+ }
1959
+ return res.text();
1960
+ }
1961
+ async function submitHostDelegation(host, headers) {
1962
+ const res = await fetch(`${host}/delegate`, {
1963
+ method: "POST",
1964
+ headers
1965
+ });
1966
+ return {
1967
+ success: res.ok,
1968
+ status: res.status,
1969
+ error: res.ok ? void 0 : await res.text().catch(() => res.statusText)
1970
+ };
1971
+ }
1972
+ async function activateSessionWithHost(host, delegationHeader) {
1973
+ const res = await fetch(`${host}/delegate`, {
1974
+ method: "POST",
1975
+ headers: delegationHeader
1976
+ });
1977
+ if (res.ok) {
1978
+ try {
1979
+ const body = await res.json();
1980
+ return {
1981
+ success: true,
1982
+ status: res.status,
1983
+ activated: body.activated ?? [],
1984
+ skipped: body.skipped ?? []
1985
+ };
1986
+ } catch {
1987
+ return {
1988
+ success: true,
1989
+ status: res.status,
1990
+ activated: [],
1991
+ skipped: []
1992
+ };
1993
+ }
1994
+ }
1995
+ return {
1996
+ success: false,
1997
+ status: res.status,
1998
+ error: await res.text().catch(() => res.statusText)
1999
+ };
2000
+ }
2001
+
2002
+ // src/delegations/DelegationManager.ts
2003
+ var DelegationAction = {
2004
+ CREATE: "tinycloud.delegation/create",
2005
+ REVOKE: "tinycloud.delegation/revoke",
2006
+ LIST: "tinycloud.delegation/list",
2007
+ GET: "tinycloud.delegation/get",
2008
+ CHECK: "tinycloud.delegation/check"
2009
+ };
2010
+ function createError(code, message, cause, meta) {
2011
+ return {
2012
+ code,
2013
+ message,
2014
+ service: "delegation",
2015
+ cause,
2016
+ meta
2017
+ };
2018
+ }
2019
+ var DelegationManager = class {
2020
+ /**
2021
+ * Creates a new DelegationManager instance.
2022
+ *
2023
+ * @param config - Configuration including hosts, session, and invoke function
2024
+ */
2025
+ constructor(config) {
2026
+ this.hosts = config.hosts;
2027
+ this.session = config.session;
2028
+ this.invoke = config.invoke;
2029
+ this.fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);
2030
+ }
2031
+ /**
2032
+ * Updates the session (e.g., after re-authentication).
2033
+ *
2034
+ * @param session - New session to use for operations
2035
+ */
2036
+ updateSession(session) {
2037
+ this.session = session;
2038
+ }
2039
+ /**
2040
+ * Gets the primary host URL.
2041
+ */
2042
+ get host() {
2043
+ return this.hosts[0];
2044
+ }
2045
+ /**
2046
+ * Executes an invoke operation against the delegation API.
2047
+ */
2048
+ async invokeOperation(path, action, body) {
2049
+ const headers = this.invoke(this.session, "delegation", path, action);
2050
+ return this.fetchFn(`${this.host}/invoke`, {
2051
+ method: "POST",
2052
+ headers,
2053
+ body
2054
+ });
2055
+ }
2056
+ /**
2057
+ * Creates a new delegation.
2058
+ *
2059
+ * Delegates specific permissions to another DID for a given path.
2060
+ * The delegatee can then use these permissions to access resources
2061
+ * within the specified scope.
2062
+ *
2063
+ * @param params - Parameters for the delegation
2064
+ * @returns Result containing the created Delegation or an error
2065
+ *
2066
+ * @example
2067
+ * ```typescript
2068
+ * const result = await manager.create({
2069
+ * delegateDID: bob.did,
2070
+ * path: "documents/shared/",
2071
+ * actions: ["tinycloud.kv/get", "tinycloud.kv/put"],
2072
+ * expiry: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days
2073
+ * });
2074
+ * ```
2075
+ */
2076
+ async create(params) {
2077
+ if (!params.delegateDID) {
2078
+ return {
2079
+ ok: false,
2080
+ error: createError(
2081
+ DelegationErrorCodes.INVALID_INPUT,
2082
+ "delegateDID is required"
2083
+ )
2084
+ };
2085
+ }
2086
+ if (!params.path) {
2087
+ return {
2088
+ ok: false,
2089
+ error: createError(
2090
+ DelegationErrorCodes.INVALID_INPUT,
2091
+ "path is required"
2092
+ )
2093
+ };
2094
+ }
2095
+ if (!params.actions || params.actions.length === 0) {
2096
+ return {
2097
+ ok: false,
2098
+ error: createError(
2099
+ DelegationErrorCodes.INVALID_INPUT,
2100
+ "at least one action is required"
2101
+ )
2102
+ };
2103
+ }
2104
+ try {
2105
+ const body = JSON.stringify({
2106
+ delegateDID: params.delegateDID,
2107
+ path: params.path,
2108
+ actions: params.actions,
2109
+ expiry: params.expiry?.toISOString(),
2110
+ disableSubDelegation: params.disableSubDelegation ?? false,
2111
+ statement: params.statement
2112
+ });
2113
+ const response = await this.invokeOperation(
2114
+ params.path,
2115
+ DelegationAction.CREATE,
2116
+ body
2117
+ );
2118
+ if (!response.ok) {
2119
+ const errorText = await response.text();
2120
+ return {
2121
+ ok: false,
2122
+ error: createError(
2123
+ DelegationErrorCodes.CREATION_FAILED,
2124
+ `Failed to create delegation: ${response.status} - ${errorText}`,
2125
+ void 0,
2126
+ { status: response.status, path: params.path }
2127
+ )
2128
+ };
2129
+ }
2130
+ const apiResponse = await response.json();
2131
+ const delegation = {
2132
+ cid: apiResponse.cid ?? "",
2133
+ delegateDID: params.delegateDID,
2134
+ spaceId: this.session.spaceId,
2135
+ path: params.path,
2136
+ actions: params.actions,
2137
+ expiry: params.expiry ?? new Date(Date.now() + 24 * 60 * 60 * 1e3),
2138
+ isRevoked: false,
2139
+ allowSubDelegation: !(params.disableSubDelegation ?? false),
2140
+ createdAt: /* @__PURE__ */ new Date()
2141
+ };
2142
+ return { ok: true, data: delegation };
2143
+ } catch (error) {
2144
+ if (error instanceof Error && error.name === "AbortError") {
2145
+ return {
2146
+ ok: false,
2147
+ error: createError(
2148
+ DelegationErrorCodes.ABORTED,
2149
+ "Request aborted",
2150
+ error
2151
+ )
2152
+ };
2153
+ }
2154
+ return {
2155
+ ok: false,
2156
+ error: createError(
2157
+ DelegationErrorCodes.NETWORK_ERROR,
2158
+ `Network error during delegation creation: ${String(error)}`,
2159
+ error instanceof Error ? error : void 0
2160
+ )
2161
+ };
2162
+ }
2163
+ }
2164
+ /**
2165
+ * Revokes an existing delegation.
2166
+ *
2167
+ * Once revoked, the delegation can no longer be used to access resources.
2168
+ * This also invalidates any sub-delegations derived from this delegation.
2169
+ *
2170
+ * @param cid - The CID of the delegation to revoke
2171
+ * @returns Result indicating success or an error
2172
+ *
2173
+ * @example
2174
+ * ```typescript
2175
+ * const result = await manager.revoke("bafy...");
2176
+ * if (result.ok) {
2177
+ * console.log("Delegation revoked successfully");
2178
+ * }
2179
+ * ```
2180
+ */
2181
+ async revoke(cid) {
2182
+ if (!cid) {
2183
+ return {
2184
+ ok: false,
2185
+ error: createError(
2186
+ DelegationErrorCodes.INVALID_INPUT,
2187
+ "cid is required"
2188
+ )
2189
+ };
2190
+ }
2191
+ try {
2192
+ const body = JSON.stringify({ cid });
2193
+ const response = await this.invokeOperation(
2194
+ cid,
2195
+ DelegationAction.REVOKE,
2196
+ body
2197
+ );
2198
+ if (!response.ok) {
2199
+ const errorText = await response.text();
2200
+ if (response.status === 404) {
2201
+ return {
2202
+ ok: false,
2203
+ error: createError(
2204
+ DelegationErrorCodes.NOT_FOUND,
2205
+ `Delegation not found: ${cid}`
2206
+ )
2207
+ };
2208
+ }
2209
+ return {
2210
+ ok: false,
2211
+ error: createError(
2212
+ DelegationErrorCodes.REVOCATION_FAILED,
2213
+ `Failed to revoke delegation: ${response.status} - ${errorText}`,
2214
+ void 0,
2215
+ { status: response.status, cid }
2216
+ )
2217
+ };
2218
+ }
2219
+ return { ok: true, data: void 0 };
2220
+ } catch (error) {
2221
+ if (error instanceof Error && error.name === "AbortError") {
2222
+ return {
2223
+ ok: false,
2224
+ error: createError(
2225
+ DelegationErrorCodes.ABORTED,
2226
+ "Request aborted",
2227
+ error
2228
+ )
2229
+ };
2230
+ }
2231
+ return {
2232
+ ok: false,
2233
+ error: createError(
2234
+ DelegationErrorCodes.NETWORK_ERROR,
2235
+ `Network error during delegation revocation: ${String(error)}`,
2236
+ error instanceof Error ? error : void 0
2237
+ )
2238
+ };
2239
+ }
2240
+ }
2241
+ /**
2242
+ * Lists all delegations for the current session's space.
2243
+ *
2244
+ * Returns both delegations created by the current user (as delegator)
2245
+ * and delegations granted to the current user (as delegatee).
2246
+ *
2247
+ * @returns Result containing an array of Delegations or an error
2248
+ *
2249
+ * @example
2250
+ * ```typescript
2251
+ * const result = await manager.list();
2252
+ * if (result.ok) {
2253
+ * for (const delegation of result.data) {
2254
+ * console.log(`${delegation.cid}: ${delegation.path} -> ${delegation.delegateDID}`);
2255
+ * }
2256
+ * }
2257
+ * ```
2258
+ */
2259
+ async list() {
2260
+ try {
2261
+ const response = await this.invokeOperation("", DelegationAction.LIST);
2262
+ if (!response.ok) {
2263
+ const errorText = await response.text();
2264
+ return {
2265
+ ok: false,
2266
+ error: createError(
2267
+ DelegationErrorCodes.NETWORK_ERROR,
2268
+ `Failed to list delegations: ${response.status} - ${errorText}`,
2269
+ void 0,
2270
+ { status: response.status }
2271
+ )
2272
+ };
2273
+ }
2274
+ const data = await response.json();
2275
+ const delegations = data.map((item) => ({
2276
+ cid: item.cid,
2277
+ delegateDID: item.delegateDID,
2278
+ delegatorDID: item.delegatorDID,
2279
+ spaceId: item.spaceId,
2280
+ path: item.path,
2281
+ actions: item.actions,
2282
+ expiry: new Date(item.expiry),
2283
+ isRevoked: item.isRevoked,
2284
+ createdAt: item.createdAt ? new Date(item.createdAt) : void 0,
2285
+ parentCid: item.parentCid,
2286
+ allowSubDelegation: item.allowSubDelegation
2287
+ }));
2288
+ return { ok: true, data: delegations };
2289
+ } catch (error) {
2290
+ if (error instanceof Error && error.name === "AbortError") {
2291
+ return {
2292
+ ok: false,
2293
+ error: createError(
2294
+ DelegationErrorCodes.ABORTED,
2295
+ "Request aborted",
2296
+ error
2297
+ )
2298
+ };
2299
+ }
2300
+ return {
2301
+ ok: false,
2302
+ error: createError(
2303
+ DelegationErrorCodes.NETWORK_ERROR,
2304
+ `Network error during delegation list: ${String(error)}`,
2305
+ error instanceof Error ? error : void 0
2306
+ )
2307
+ };
2308
+ }
2309
+ }
2310
+ /**
2311
+ * Gets the full delegation chain for a given delegation.
2312
+ *
2313
+ * Returns the chain of delegations from the root (original delegator)
2314
+ * to the specified delegation, including all intermediate sub-delegations.
2315
+ *
2316
+ * @param cid - The CID of the delegation to get the chain for
2317
+ * @returns Result containing the DelegationChain or an error
2318
+ *
2319
+ * @example
2320
+ * ```typescript
2321
+ * const result = await manager.getChain("bafy...");
2322
+ * if (result.ok) {
2323
+ * console.log("Chain length:", result.data.length);
2324
+ * for (const delegation of result.data) {
2325
+ * console.log(`- ${delegation.delegatorDID} -> ${delegation.delegateDID}`);
2326
+ * }
2327
+ * }
2328
+ * ```
2329
+ */
2330
+ async getChain(cid) {
2331
+ if (!cid) {
2332
+ return {
2333
+ ok: false,
2334
+ error: createError(
2335
+ DelegationErrorCodes.INVALID_INPUT,
2336
+ "cid is required"
2337
+ )
2338
+ };
2339
+ }
2340
+ try {
2341
+ const body = JSON.stringify({ cid, includeChain: true });
2342
+ const response = await this.invokeOperation(
2343
+ cid,
2344
+ DelegationAction.GET,
2345
+ body
2346
+ );
2347
+ if (!response.ok) {
2348
+ const errorText = await response.text();
2349
+ if (response.status === 404) {
2350
+ return {
2351
+ ok: false,
2352
+ error: createError(
2353
+ DelegationErrorCodes.NOT_FOUND,
2354
+ `Delegation not found: ${cid}`
2355
+ )
2356
+ };
2357
+ }
2358
+ return {
2359
+ ok: false,
2360
+ error: createError(
2361
+ DelegationErrorCodes.NETWORK_ERROR,
2362
+ `Failed to get delegation chain: ${response.status} - ${errorText}`,
2363
+ void 0,
2364
+ { status: response.status, cid }
2365
+ )
2366
+ };
2367
+ }
2368
+ const data = await response.json();
2369
+ const chain = data.chain.map((item) => ({
2370
+ cid: item.cid,
2371
+ delegateDID: item.delegateDID,
2372
+ delegatorDID: item.delegatorDID,
2373
+ spaceId: item.spaceId,
2374
+ path: item.path,
2375
+ actions: item.actions,
2376
+ expiry: new Date(item.expiry),
2377
+ isRevoked: item.isRevoked,
2378
+ createdAt: item.createdAt ? new Date(item.createdAt) : void 0,
2379
+ parentCid: item.parentCid,
2380
+ allowSubDelegation: item.allowSubDelegation
2381
+ }));
2382
+ return { ok: true, data: chain };
2383
+ } catch (error) {
2384
+ if (error instanceof Error && error.name === "AbortError") {
2385
+ return {
2386
+ ok: false,
2387
+ error: createError(
2388
+ DelegationErrorCodes.ABORTED,
2389
+ "Request aborted",
2390
+ error
2391
+ )
2392
+ };
2393
+ }
2394
+ return {
2395
+ ok: false,
2396
+ error: createError(
2397
+ DelegationErrorCodes.NETWORK_ERROR,
2398
+ `Network error during chain retrieval: ${String(error)}`,
2399
+ error instanceof Error ? error : void 0
2400
+ )
2401
+ };
2402
+ }
2403
+ }
2404
+ /**
2405
+ * Checks if the current session has permission for a given path and action.
2406
+ *
2407
+ * This can be used to verify permissions before attempting an operation,
2408
+ * or to implement custom access control logic.
2409
+ *
2410
+ * @param path - The resource path to check
2411
+ * @param action - The action to check (e.g., "tinycloud.kv/get")
2412
+ * @returns Result containing a boolean indicating permission or an error
2413
+ *
2414
+ * @example
2415
+ * ```typescript
2416
+ * const result = await manager.checkPermission("documents/private/", "tinycloud.kv/put");
2417
+ * if (result.ok && result.data) {
2418
+ * console.log("Permission granted");
2419
+ * } else {
2420
+ * console.log("Permission denied");
2421
+ * }
2422
+ * ```
2423
+ */
2424
+ async checkPermission(path, action) {
2425
+ if (!path) {
2426
+ return {
2427
+ ok: false,
2428
+ error: createError(
2429
+ DelegationErrorCodes.INVALID_INPUT,
2430
+ "path is required"
2431
+ )
2432
+ };
2433
+ }
2434
+ if (!action) {
2435
+ return {
2436
+ ok: false,
2437
+ error: createError(
2438
+ DelegationErrorCodes.INVALID_INPUT,
2439
+ "action is required"
2440
+ )
2441
+ };
2442
+ }
2443
+ try {
2444
+ const body = JSON.stringify({ path, action });
2445
+ const response = await this.invokeOperation(
2446
+ path,
2447
+ DelegationAction.CHECK,
2448
+ body
2449
+ );
2450
+ if (!response.ok) {
2451
+ if (response.status === 403) {
2452
+ return { ok: true, data: false };
2453
+ }
2454
+ const errorText = await response.text();
2455
+ return {
2456
+ ok: false,
2457
+ error: createError(
2458
+ DelegationErrorCodes.NETWORK_ERROR,
2459
+ `Failed to check permission: ${response.status} - ${errorText}`,
2460
+ void 0,
2461
+ { status: response.status, path, action }
2462
+ )
2463
+ };
2464
+ }
2465
+ const data = await response.json();
2466
+ return { ok: true, data: data.allowed };
2467
+ } catch (error) {
2468
+ if (error instanceof Error && error.name === "AbortError") {
2469
+ return {
2470
+ ok: false,
2471
+ error: createError(
2472
+ DelegationErrorCodes.ABORTED,
2473
+ "Request aborted",
2474
+ error
2475
+ )
2476
+ };
2477
+ }
2478
+ return {
2479
+ ok: false,
2480
+ error: createError(
2481
+ DelegationErrorCodes.NETWORK_ERROR,
2482
+ `Network error during permission check: ${String(error)}`,
2483
+ error instanceof Error ? error : void 0
2484
+ )
2485
+ };
2486
+ }
2487
+ }
2488
+ };
2489
+
2490
+ // src/delegations/SharingService.schema.ts
2491
+ import { z as z5 } from "zod";
2492
+ var EncodedShareDataSchema = z5.object({
2493
+ /** Private key in JWK format (must include d parameter) */
2494
+ key: JWKSchema.refine(
2495
+ (jwk) => typeof jwk.d === "string" && jwk.d.length > 0,
2496
+ { message: "JWK must include private key (d parameter)" }
2497
+ ),
2498
+ /** DID of the key */
2499
+ keyDid: z5.string().min(1, "keyDid is required"),
2500
+ /** The delegation granting access */
2501
+ delegation: DelegationSchema,
2502
+ /** Resource path this link grants access to */
2503
+ path: z5.string().min(1, "path is required"),
2504
+ /** TinyCloud host URL */
2505
+ host: z5.string().url("host must be a valid URL"),
2506
+ /** Space ID */
2507
+ spaceId: z5.string().min(1, "spaceId is required"),
2508
+ /** Schema version (must be 1) */
2509
+ version: z5.literal(1)
2510
+ });
2511
+ var ReceiveOptionsSchema = z5.object({
2512
+ /**
2513
+ * Whether to automatically create a sub-delegation to the current session key.
2514
+ * Default: true
2515
+ */
2516
+ autoSubdelegate: z5.boolean().optional(),
2517
+ /**
2518
+ * Whether to use the current session key for operations (requires autoSubdelegate).
2519
+ * Default: true
2520
+ */
2521
+ useSessionKey: z5.boolean().optional(),
2522
+ /**
2523
+ * Ingestion options passed to CapabilityKeyRegistry.
2524
+ */
2525
+ ingestOptions: IngestOptionsSchema.optional()
2526
+ });
2527
+ var SharingServiceConfigSchema = z5.object({
2528
+ /** TinyCloud host URLs */
2529
+ hosts: z5.array(z5.string().url()).min(1, "At least one host URL is required"),
2530
+ /**
2531
+ * Active session for authentication.
2532
+ * Required for generate(), optional for receive().
2533
+ */
2534
+ session: z5.unknown().refine(
2535
+ (val) => val === void 0 || val !== null && typeof val === "object",
2536
+ { message: "Expected a ServiceSession object or undefined" }
2537
+ ).optional(),
2538
+ /** Platform-specific invoke function */
2539
+ invoke: z5.unknown().refine((val) => typeof val === "function", {
2540
+ message: "Expected an invoke function"
2541
+ }),
2542
+ /** Optional custom fetch implementation */
2543
+ fetch: z5.unknown().refine(
2544
+ (val) => val === void 0 || typeof val === "function",
2545
+ { message: "Expected a fetch function or undefined" }
2546
+ ).optional(),
2547
+ /** Key provider for cryptographic operations */
2548
+ keyProvider: KeyProviderSchema,
2549
+ /** Capability key registry for key/delegation management */
2550
+ registry: z5.unknown().refine(
2551
+ (val) => val !== null && typeof val === "object",
2552
+ { message: "Expected an ICapabilityKeyRegistry object" }
2553
+ ),
2554
+ /**
2555
+ * Delegation manager for creating delegations.
2556
+ * Required for generate(), optional for receive().
2557
+ */
2558
+ delegationManager: z5.unknown().refine(
2559
+ (val) => val === void 0 || val !== null && typeof val === "object",
2560
+ { message: "Expected a DelegationManager object or undefined" }
2561
+ ).optional(),
2562
+ /** Factory for creating KV service instances */
2563
+ createKVService: z5.unknown().refine(
2564
+ (val) => typeof val === "function",
2565
+ { message: "Expected a createKVService factory function" }
2566
+ ),
2567
+ /** Base URL for sharing links (e.g., "https://share.myapp.com") */
2568
+ baseUrl: z5.string().optional(),
2569
+ /**
2570
+ * Custom delegation creation function.
2571
+ */
2572
+ createDelegation: z5.unknown().refine((val) => val === void 0 || typeof val === "function", {
2573
+ message: "Expected a createDelegation function or undefined"
2574
+ }).optional(),
2575
+ /**
2576
+ * WASM function for client-side delegation creation.
2577
+ */
2578
+ createDelegationWasm: z5.unknown().refine((val) => val === void 0 || typeof val === "function", {
2579
+ message: "Expected a createDelegationWasm function or undefined"
2580
+ }).optional(),
2581
+ /**
2582
+ * Path prefix for KV operations.
2583
+ */
2584
+ pathPrefix: z5.string().optional(),
2585
+ /**
2586
+ * Session expiry time.
2587
+ */
2588
+ sessionExpiry: z5.date().optional(),
2589
+ /**
2590
+ * Callback to create a DIRECT delegation from wallet to share key.
2591
+ * This is the preferred method for long-lived share links because it
2592
+ * bypasses the session delegation chain entirely.
2593
+ */
2594
+ onRootDelegationNeeded: z5.unknown().refine((val) => val === void 0 || typeof val === "function", {
2595
+ message: "Expected an onRootDelegationNeeded function or undefined"
2596
+ }).optional()
2597
+ });
2598
+ function validateEncodedShareData(data) {
2599
+ const result = EncodedShareDataSchema.safeParse(data);
2600
+ if (!result.success) {
2601
+ return {
2602
+ ok: false,
2603
+ error: {
2604
+ code: DelegationErrorCodes.VALIDATION_ERROR,
2605
+ message: `Invalid share data: ${result.error.message}`,
2606
+ service: "delegation",
2607
+ meta: { issues: result.error.issues }
2608
+ }
2609
+ };
2610
+ }
2611
+ return { ok: true, data: result.data };
2612
+ }
2613
+
2614
+ // src/delegations/SharingService.ts
2615
+ var DEFAULT_READ_ACTIONS = ["tinycloud.kv/get", "tinycloud.kv/metadata"];
2616
+ var DEFAULT_EXPIRY_MS = 24 * 60 * 60 * 1e3;
2617
+ var BASE64_PREFIX = "tc1:";
2618
+ function createError2(code, message, cause, meta) {
2619
+ return {
2620
+ code,
2621
+ message,
2622
+ service: "delegation",
2623
+ cause,
2624
+ meta
2625
+ };
2626
+ }
2627
+ function base64UrlEncode(data) {
2628
+ let base64;
2629
+ if (typeof btoa !== "undefined") {
2630
+ base64 = btoa(unescape(encodeURIComponent(data)));
2631
+ } else if (typeof Buffer !== "undefined") {
2632
+ base64 = Buffer.from(data, "utf-8").toString("base64");
2633
+ } else {
2634
+ throw new Error("No base64 encoding available");
2635
+ }
2636
+ return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
2637
+ }
2638
+ function base64UrlDecode(encoded) {
2639
+ let base64 = encoded.replace(/-/g, "+").replace(/_/g, "/");
2640
+ while (base64.length % 4) {
2641
+ base64 += "=";
2642
+ }
2643
+ if (typeof atob !== "undefined") {
2644
+ return decodeURIComponent(escape(atob(base64)));
2645
+ } else if (typeof Buffer !== "undefined") {
2646
+ return Buffer.from(base64, "base64").toString("utf-8");
2647
+ } else {
2648
+ throw new Error("No base64 decoding available");
2649
+ }
2650
+ }
2651
+ var SharingService = class {
2652
+ /**
2653
+ * Creates a new SharingService instance.
2654
+ */
2655
+ constructor(config) {
2656
+ this.hosts = config.hosts;
2657
+ this.session = config.session;
2658
+ this.invoke = config.invoke;
2659
+ this.fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);
2660
+ this.keyProvider = config.keyProvider;
2661
+ this.registry = config.registry;
2662
+ this.delegationManager = config.delegationManager;
2663
+ this.createKVService = config.createKVService;
2664
+ this.baseUrl = (config.baseUrl ?? "").replace(/\/$/, "");
2665
+ this.createDelegationFn = config.createDelegation;
2666
+ this.createDelegationWasmFn = config.createDelegationWasm;
2667
+ this.pathPrefix = config.pathPrefix ?? "";
2668
+ this.sessionExpiry = config.sessionExpiry;
2669
+ this.onRootDelegationNeeded = config.onRootDelegationNeeded;
2670
+ }
2671
+ /**
2672
+ * Gets the primary host URL.
2673
+ */
2674
+ get host() {
2675
+ return this.hosts[0];
2676
+ }
2677
+ /**
2678
+ * Updates the session (e.g., after re-authentication).
2679
+ */
2680
+ updateSession(session) {
2681
+ this.session = session;
2682
+ }
2683
+ /**
2684
+ * Updates the service configuration.
2685
+ * Used to add full capabilities (session, delegationManager, createDelegation, createDelegationWasm) after signIn.
2686
+ */
2687
+ updateConfig(config) {
2688
+ if (config.session !== void 0) {
2689
+ this.session = config.session;
2690
+ }
2691
+ if (config.delegationManager !== void 0) {
2692
+ this.delegationManager = config.delegationManager;
2693
+ }
2694
+ if (config.createDelegation !== void 0) {
2695
+ this.createDelegationFn = config.createDelegation;
2696
+ }
2697
+ if (config.createDelegationWasm !== void 0) {
2698
+ this.createDelegationWasmFn = config.createDelegationWasm;
2699
+ }
2700
+ if (config.sessionExpiry !== void 0) {
2701
+ this.sessionExpiry = config.sessionExpiry;
2702
+ }
2703
+ if (config.onRootDelegationNeeded !== void 0) {
2704
+ this.onRootDelegationNeeded = config.onRootDelegationNeeded;
2705
+ }
2706
+ }
2707
+ /**
2708
+ * Generate a sharing link with an embedded private key.
2709
+ *
2710
+ * Flow:
2711
+ * 1. Spawn new session key (unique per share)
2712
+ * 2. Create delegation from current session to spawned key
2713
+ * 3. Package: { key (with private!), delegation, path, host }
2714
+ * 4. Encode based on schema (base64 for now)
2715
+ * 5. Return link string
2716
+ */
2717
+ async generate(params) {
2718
+ if (!this.session) {
2719
+ return {
2720
+ ok: false,
2721
+ error: createError2(
2722
+ DelegationErrorCodes.NOT_INITIALIZED,
2723
+ "Session required for generating sharing links. Call signIn() first."
2724
+ )
2725
+ };
2726
+ }
2727
+ if (!this.createDelegationWasmFn && !this.createDelegationFn && !this.delegationManager) {
2728
+ return {
2729
+ ok: false,
2730
+ error: createError2(
2731
+ DelegationErrorCodes.NOT_INITIALIZED,
2732
+ "DelegationManager, createDelegation, or createDelegationWasm function required for generating sharing links."
2733
+ )
2734
+ };
2735
+ }
2736
+ if (!params.path) {
2737
+ return {
2738
+ ok: false,
2739
+ error: createError2(
2740
+ DelegationErrorCodes.INVALID_INPUT,
2741
+ "path is required"
2742
+ )
2743
+ };
2744
+ }
2745
+ const actions = params.actions ?? DEFAULT_READ_ACTIONS;
2746
+ const requestedExpiry = params.expiry ?? new Date(Date.now() + DEFAULT_EXPIRY_MS);
2747
+ let expiry = requestedExpiry;
2748
+ const schema = params.schema ?? "base64";
2749
+ const fullPath = this.pathPrefix ? `${this.pathPrefix}/${params.path}`.replace(/\/+/g, "/") : params.path;
2750
+ if (schema !== "base64") {
2751
+ return {
2752
+ ok: false,
2753
+ error: createError2(
2754
+ DelegationErrorCodes.INVALID_INPUT,
2755
+ `Schema '${schema}' not implemented. Only 'base64' is supported.`
2756
+ )
2757
+ };
2758
+ }
2759
+ let keyId;
2760
+ let keyDid;
2761
+ let keyJwk;
2762
+ try {
2763
+ const shareKeyName = `share:${Date.now()}:${Math.random().toString(36).substring(2, 10)}`;
2764
+ keyId = await this.keyProvider.createSessionKey(shareKeyName);
2765
+ keyDid = await this.keyProvider.getDID(keyId);
2766
+ keyJwk = this.keyProvider.getJWK(keyId);
2767
+ if (!keyJwk.d) {
2768
+ return {
2769
+ ok: false,
2770
+ error: createError2(
2771
+ DelegationErrorCodes.CREATION_FAILED,
2772
+ "KeyProvider did not return private key (d parameter) in JWK"
2773
+ )
2774
+ };
2775
+ }
2776
+ } catch (err5) {
2777
+ return {
2778
+ ok: false,
2779
+ error: createError2(
2780
+ DelegationErrorCodes.CREATION_FAILED,
2781
+ `Failed to generate session key for share: ${err5 instanceof Error ? err5.message : String(err5)}`,
2782
+ err5 instanceof Error ? err5 : void 0
2783
+ )
2784
+ };
2785
+ }
2786
+ let delegation;
2787
+ const plainDID = keyDid.split("#")[0];
2788
+ const handleDelegationResult = (result) => {
2789
+ if (result && typeof result === "object" && "ok" in result) {
2790
+ return result;
2791
+ }
2792
+ return result;
2793
+ };
2794
+ const canSatisfyFromRegistry = this.findSuitableKeyForDelegation(
2795
+ fullPath,
2796
+ actions,
2797
+ requestedExpiry
2798
+ );
2799
+ if (canSatisfyFromRegistry) {
2800
+ const delegationResult = await this.createSessionDelegation(plainDID, fullPath, actions, expiry);
2801
+ const parsed = handleDelegationResult(delegationResult);
2802
+ if ("ok" in parsed && parsed.ok === false) {
2803
+ return parsed;
2804
+ }
2805
+ delegation = parsed;
2806
+ } else if (this.onRootDelegationNeeded) {
2807
+ try {
2808
+ const rootDelegation = await this.onRootDelegationNeeded({
2809
+ shareKeyDID: plainDID,
2810
+ spaceId: this.session.spaceId,
2811
+ path: fullPath,
2812
+ actions,
2813
+ requestedExpiry
2814
+ });
2815
+ if (rootDelegation) {
2816
+ delegation = rootDelegation;
2817
+ expiry = requestedExpiry;
2818
+ } else {
2819
+ const fallbackResult = await this.handleSessionExtensionFallback(requestedExpiry);
2820
+ expiry = fallbackResult.expiry;
2821
+ const delegationResult = await this.createSessionDelegation(plainDID, fullPath, actions, expiry);
2822
+ const parsed = handleDelegationResult(delegationResult);
2823
+ if ("ok" in parsed && parsed.ok === false) {
2824
+ return parsed;
2825
+ }
2826
+ delegation = parsed;
2827
+ }
2828
+ } catch (err5) {
2829
+ const fallbackResult = await this.handleSessionExtensionFallback(requestedExpiry);
2830
+ expiry = fallbackResult.expiry;
2831
+ const delegationResult = await this.createSessionDelegation(plainDID, fullPath, actions, expiry);
2832
+ const parsed = handleDelegationResult(delegationResult);
2833
+ if ("ok" in parsed && parsed.ok === false) {
2834
+ return parsed;
2835
+ }
2836
+ delegation = parsed;
2837
+ }
2838
+ } else {
2839
+ const fallbackResult = await this.handleSessionExtensionFallback(requestedExpiry);
2840
+ expiry = fallbackResult.expiry;
2841
+ const delegationResult = await this.createSessionDelegation(plainDID, fullPath, actions, expiry);
2842
+ const parsed = handleDelegationResult(delegationResult);
2843
+ if ("ok" in parsed && parsed.ok === false) {
2844
+ return parsed;
2845
+ }
2846
+ delegation = parsed;
2847
+ }
2848
+ const shareData = {
2849
+ key: keyJwk,
2850
+ keyDid,
2851
+ delegation,
2852
+ path: fullPath,
2853
+ host: this.host,
2854
+ spaceId: this.session.spaceId,
2855
+ version: 1
2856
+ };
2857
+ const encodedData = this.encodeLink(shareData, schema);
2858
+ const baseUrl = params.baseUrl ?? this.baseUrl;
2859
+ const url = baseUrl ? `${baseUrl}/share/${encodedData}` : encodedData;
2860
+ const shareLink = {
2861
+ token: encodedData,
2862
+ url,
2863
+ delegation,
2864
+ schema,
2865
+ expiresAt: expiry,
2866
+ description: params.description
2867
+ };
2868
+ return { ok: true, data: shareLink };
2869
+ }
2870
+ /**
2871
+ * Check if any key in the registry can satisfy the delegation request.
2872
+ * A key can satisfy if it has a delegation that:
2873
+ * 1. Covers the required path (exact match or parent path)
2874
+ * 2. Has all required actions
2875
+ * 3. Has sufficient expiry (delegation.expiry >= requestedExpiry)
2876
+ * 4. Allows sub-delegation
2877
+ * @internal
2878
+ */
2879
+ findSuitableKeyForDelegation(path, actions, requestedExpiry) {
2880
+ if (this.sessionExpiry && requestedExpiry <= this.sessionExpiry) {
2881
+ return true;
2882
+ }
2883
+ const allKeys = this.registry.getAllKeys();
2884
+ for (const key of allKeys) {
2885
+ const delegations = this.registry.getDelegationsForKey(key.id);
2886
+ for (const delegation of delegations) {
2887
+ if (!this.registry.isDelegationValid(delegation)) {
2888
+ continue;
2889
+ }
2890
+ if (delegation.expiry < requestedExpiry) {
2891
+ continue;
2892
+ }
2893
+ if (delegation.allowSubDelegation === false) {
2894
+ continue;
2895
+ }
2896
+ const delegationPath = delegation.path || "";
2897
+ if (!this.pathMatches(delegationPath, path)) {
2898
+ continue;
2899
+ }
2900
+ const delegationActions = delegation.actions || [];
2901
+ const hasAllActions = actions.every(
2902
+ (action) => delegationActions.includes(action) || delegationActions.includes("*")
2903
+ );
2904
+ if (!hasAllActions) {
2905
+ continue;
2906
+ }
2907
+ return true;
2908
+ }
2909
+ }
2910
+ return false;
2911
+ }
2912
+ /**
2913
+ * Check if a delegation path matches/covers the requested path.
2914
+ * A delegation path covers the request if:
2915
+ * - It's an exact match
2916
+ * - It's a parent path (e.g., delegation for "" covers "foo/bar")
2917
+ * - It uses wildcards that match
2918
+ * @internal
2919
+ */
2920
+ pathMatches(delegationPath, requestedPath) {
2921
+ if (delegationPath === "" || delegationPath === "*") {
2922
+ return true;
2923
+ }
2924
+ if (delegationPath === requestedPath) {
2925
+ return true;
2926
+ }
2927
+ const normalizedDelegation = delegationPath.replace(/\/$/, "");
2928
+ const normalizedRequest = requestedPath.replace(/\/$/, "");
2929
+ if (normalizedRequest.startsWith(normalizedDelegation + "/")) {
2930
+ return true;
2931
+ }
2932
+ return false;
2933
+ }
2934
+ /**
2935
+ * Handle fallback to session extension when root delegation is not available.
2936
+ * @internal
2937
+ */
2938
+ async handleSessionExtensionFallback(requestedExpiry) {
2939
+ return { expiry: this.sessionExpiry ?? requestedExpiry };
2940
+ }
2941
+ /**
2942
+ * Create a delegation from the current session to a share key.
2943
+ * This is the fallback path when root delegation is not available.
2944
+ * @internal
2945
+ */
2946
+ async createSessionDelegation(delegateDID, path, actions, expiry) {
2947
+ if (!this.session) {
2948
+ return {
2949
+ ok: false,
2950
+ error: createError2(
2951
+ DelegationErrorCodes.NOT_INITIALIZED,
2952
+ "Session required for creating delegation"
2953
+ )
2954
+ };
2955
+ }
2956
+ if (this.createDelegationWasmFn) {
2957
+ try {
2958
+ const wasmResult = this.createDelegationWasmFn({
2959
+ session: this.session,
2960
+ delegateDID,
2961
+ spaceId: this.session.spaceId,
2962
+ path,
2963
+ actions,
2964
+ expirationSecs: Math.floor(expiry.getTime() / 1e3)
2965
+ });
2966
+ const registerRes = await this.fetchFn(`${this.host}/delegate`, {
2967
+ method: "POST",
2968
+ headers: {
2969
+ Authorization: wasmResult.delegation
2970
+ }
2971
+ });
2972
+ if (!registerRes.ok) {
2973
+ const errorText = await registerRes.text();
2974
+ return {
2975
+ ok: false,
2976
+ error: createError2(
2977
+ DelegationErrorCodes.CREATION_FAILED,
2978
+ `Failed to register delegation with server: ${registerRes.status} ${errorText}`
2979
+ )
2980
+ };
2981
+ }
2982
+ return {
2983
+ cid: wasmResult.cid,
2984
+ delegateDID: wasmResult.delegateDID,
2985
+ spaceId: this.session.spaceId,
2986
+ path: wasmResult.path,
2987
+ actions: wasmResult.actions,
2988
+ expiry: wasmResult.expiry,
2989
+ isRevoked: false,
2990
+ authHeader: wasmResult.delegation,
2991
+ allowSubDelegation: true,
2992
+ createdAt: /* @__PURE__ */ new Date()
2993
+ };
2994
+ } catch (err5) {
2995
+ return {
2996
+ ok: false,
2997
+ error: createError2(
2998
+ DelegationErrorCodes.CREATION_FAILED,
2999
+ `Failed to create delegation via WASM: ${err5 instanceof Error ? err5.message : String(err5)}`,
3000
+ err5 instanceof Error ? err5 : void 0
3001
+ )
3002
+ };
3003
+ }
3004
+ } else {
3005
+ const delegationParams = {
3006
+ delegateDID,
3007
+ path,
3008
+ actions,
3009
+ expiry,
3010
+ disableSubDelegation: false
3011
+ };
3012
+ const delegationResult = this.createDelegationFn ? await this.createDelegationFn(delegationParams) : await this.delegationManager.create(delegationParams);
3013
+ if (!delegationResult.ok) {
3014
+ return {
3015
+ ok: false,
3016
+ error: createError2(
3017
+ DelegationErrorCodes.CREATION_FAILED,
3018
+ `Failed to create delegation for share: ${delegationResult.error.message}`,
3019
+ delegationResult.error.cause,
3020
+ delegationResult.error.meta
3021
+ )
3022
+ };
3023
+ }
3024
+ return delegationResult.data;
3025
+ }
3026
+ }
3027
+ /**
3028
+ * Receive and activate a sharing link.
3029
+ *
3030
+ * Flow:
3031
+ * 1. Decode link -> extract { key, delegation, path, host }
3032
+ * 2. Ingest key into CapabilityKeyRegistry
3033
+ * 3. If autoSubdelegate (default true) + useSessionKey:
3034
+ * - Create sub-delegation from ingested key -> current session
3035
+ * - Register sub-delegation capabilities
3036
+ * 4. Return ShareAccess with pre-configured KV service
3037
+ */
3038
+ async receive(link, options = {}) {
3039
+ const {
3040
+ autoSubdelegate = true,
3041
+ useSessionKey = true,
3042
+ ingestOptions
3043
+ } = options;
3044
+ const decodeResult = this.decodeLinkWithValidation(link);
3045
+ if (!decodeResult.ok) {
3046
+ return decodeResult;
3047
+ }
3048
+ const shareData = decodeResult.data;
3049
+ const delegationExpiry = new Date(shareData.delegation.expiry);
3050
+ if (delegationExpiry < /* @__PURE__ */ new Date()) {
3051
+ return {
3052
+ ok: false,
3053
+ error: createError2(
3054
+ DelegationErrorCodes.AUTH_EXPIRED,
3055
+ "Sharing link has expired"
3056
+ )
3057
+ };
3058
+ }
3059
+ if (shareData.delegation.isRevoked) {
3060
+ return {
3061
+ ok: false,
3062
+ error: createError2(
3063
+ DelegationErrorCodes.REVOKED,
3064
+ "Sharing link has been revoked"
3065
+ )
3066
+ };
3067
+ }
3068
+ const keyInfo = {
3069
+ id: `ingested:${shareData.keyDid}`,
3070
+ did: shareData.keyDid,
3071
+ type: "ingested",
3072
+ jwk: shareData.key,
3073
+ priority: 2
3074
+ // Ingested keys have lowest priority
3075
+ };
3076
+ this.registry.ingestKey(keyInfo, shareData.delegation, ingestOptions);
3077
+ let activeDelegation = shareData.delegation;
3078
+ let activeKey = keyInfo;
3079
+ if (autoSubdelegate && useSessionKey && this.session) {
3080
+ try {
3081
+ } catch (err5) {
3082
+ console.warn("Auto-subdelegation failed, using ingested key directly:", err5);
3083
+ }
3084
+ }
3085
+ const authHeader = shareData.delegation.authHeader ?? `Bearer ${shareData.delegation.cid}`;
3086
+ const shareSession = {
3087
+ delegationHeader: { Authorization: authHeader },
3088
+ delegationCid: shareData.delegation.cid,
3089
+ spaceId: shareData.spaceId,
3090
+ verificationMethod: shareData.keyDid,
3091
+ jwk: shareData.key
3092
+ };
3093
+ const kvService = this.createKVService({
3094
+ hosts: [shareData.host],
3095
+ session: shareSession,
3096
+ invoke: this.invoke,
3097
+ fetch: this.fetchFn,
3098
+ pathPrefix: shareData.path
3099
+ });
3100
+ const shareAccess = {
3101
+ delegation: activeDelegation,
3102
+ key: activeKey,
3103
+ kv: kvService,
3104
+ spaceId: shareData.spaceId,
3105
+ path: shareData.path
3106
+ };
3107
+ return { ok: true, data: shareAccess };
3108
+ }
3109
+ /**
3110
+ * Encode sharing data into a link string.
3111
+ *
3112
+ * @param data - The share data to encode
3113
+ * @param schema - The encoding schema (default: "base64")
3114
+ * @returns Encoded link string
3115
+ */
3116
+ encodeLink(data, schema = "base64") {
3117
+ if (schema !== "base64") {
3118
+ throw new Error(`Schema '${schema}' not implemented. Only 'base64' is supported.`);
3119
+ }
3120
+ const jsonString = JSON.stringify(data);
3121
+ const encoded = base64UrlEncode(jsonString);
3122
+ return `${BASE64_PREFIX}${encoded}`;
3123
+ }
3124
+ /**
3125
+ * Decode a link string into sharing data.
3126
+ *
3127
+ * @param link - The encoded link string (may include URL prefix)
3128
+ * @returns Decoded share data
3129
+ * @throws Error if link format is invalid or data fails validation
3130
+ */
3131
+ decodeLink(link) {
3132
+ const result = this.decodeLinkWithValidation(link);
3133
+ if (!result.ok) {
3134
+ throw new Error(result.error.message);
3135
+ }
3136
+ return result.data;
3137
+ }
3138
+ /**
3139
+ * Decode and validate a link string into sharing data.
3140
+ *
3141
+ * Internal method that returns a Result instead of throwing.
3142
+ * Used by receive() for proper error handling.
3143
+ *
3144
+ * @param link - The encoded link string (may include URL prefix)
3145
+ * @returns Result with decoded share data or validation error
3146
+ */
3147
+ decodeLinkWithValidation(link) {
3148
+ let encoded = link;
3149
+ if (link.includes("/share/")) {
3150
+ const parts = link.split("/share/");
3151
+ encoded = parts[parts.length - 1];
3152
+ }
3153
+ if (link.includes("?share=")) {
3154
+ try {
3155
+ const url = new URL(link);
3156
+ encoded = url.searchParams.get("share") ?? encoded;
3157
+ } catch {
3158
+ return {
3159
+ ok: false,
3160
+ error: createError2(
3161
+ DelegationErrorCodes.INVALID_TOKEN,
3162
+ "Invalid URL format in sharing link"
3163
+ )
3164
+ };
3165
+ }
3166
+ }
3167
+ if (!encoded.startsWith(BASE64_PREFIX)) {
3168
+ return {
3169
+ ok: false,
3170
+ error: createError2(
3171
+ DelegationErrorCodes.INVALID_TOKEN,
3172
+ `Invalid sharing link format. Expected prefix '${BASE64_PREFIX}'`
3173
+ )
3174
+ };
3175
+ }
3176
+ const base64Data = encoded.slice(BASE64_PREFIX.length);
3177
+ let jsonString;
3178
+ try {
3179
+ jsonString = base64UrlDecode(base64Data);
3180
+ } catch (err5) {
3181
+ return {
3182
+ ok: false,
3183
+ error: createError2(
3184
+ DelegationErrorCodes.INVALID_TOKEN,
3185
+ `Failed to decode base64 data: ${err5 instanceof Error ? err5.message : String(err5)}`,
3186
+ err5 instanceof Error ? err5 : void 0
3187
+ )
3188
+ };
3189
+ }
3190
+ let parsed;
3191
+ try {
3192
+ parsed = JSON.parse(jsonString);
3193
+ } catch (err5) {
3194
+ return {
3195
+ ok: false,
3196
+ error: createError2(
3197
+ DelegationErrorCodes.INVALID_TOKEN,
3198
+ `Failed to parse share data JSON: ${err5 instanceof Error ? err5.message : String(err5)}`,
3199
+ err5 instanceof Error ? err5 : void 0
3200
+ )
3201
+ };
3202
+ }
3203
+ if (parsed && typeof parsed === "object" && "delegation" in parsed && parsed.delegation && typeof parsed.delegation === "object" && "expiry" in parsed.delegation && typeof parsed.delegation.expiry === "string") {
3204
+ parsed.delegation.expiry = new Date(parsed.delegation.expiry);
3205
+ }
3206
+ const validationResult = validateEncodedShareData(parsed);
3207
+ if (!validationResult.ok) {
3208
+ return {
3209
+ ok: false,
3210
+ error: createError2(
3211
+ DelegationErrorCodes.INVALID_TOKEN,
3212
+ validationResult.error.message,
3213
+ void 0,
3214
+ validationResult.error.meta
3215
+ )
3216
+ };
3217
+ }
3218
+ return { ok: true, data: validationResult.data };
3219
+ }
3220
+ };
3221
+ function createSharingService(config) {
3222
+ return new SharingService(config);
3223
+ }
3224
+
3225
+ // src/authorization/CapabilityKeyRegistry.ts
3226
+ import { ok as ok3, err as err3, serviceError as serviceError3 } from "@tinycloud/sdk-services";
3227
+ var SERVICE_NAME2 = "capability-key-registry";
3228
+ var CapabilityKeyRegistryErrorCodes = {
3229
+ /** Key not found in registry */
3230
+ KEY_NOT_FOUND: "KEY_NOT_FOUND",
3231
+ /** No key available for the requested capability */
3232
+ NO_CAPABLE_KEY: "NO_CAPABLE_KEY",
3233
+ /** Delegation has expired */
3234
+ DELEGATION_EXPIRED: "DELEGATION_EXPIRED",
3235
+ /** Delegation has been revoked */
3236
+ DELEGATION_REVOKED: "DELEGATION_REVOKED",
3237
+ /** Invalid delegation data */
3238
+ INVALID_DELEGATION: "INVALID_DELEGATION",
3239
+ /** Key already registered */
3240
+ KEY_EXISTS: "KEY_EXISTS"
3241
+ };
3242
+ var CapabilityKeyRegistry = class {
3243
+ constructor() {
3244
+ /**
3245
+ * Registry of all keys indexed by ID.
3246
+ */
3247
+ this.keys = /* @__PURE__ */ new Map();
3248
+ /**
3249
+ * Delegation storage.
3250
+ */
3251
+ this.store = {
3252
+ byKey: /* @__PURE__ */ new Map(),
3253
+ byCid: /* @__PURE__ */ new Map(),
3254
+ byCapability: /* @__PURE__ */ new Map()
3255
+ };
3256
+ }
3257
+ // ===========================================================================
3258
+ // Key Management
3259
+ // ===========================================================================
3260
+ /**
3261
+ * Register a key with its associated delegations.
3262
+ *
3263
+ * @param key - Key information
3264
+ * @param delegations - Delegations granted to this key
3265
+ */
3266
+ registerKey(key, delegations) {
3267
+ this.keys.set(key.id, key);
3268
+ if (!this.store.byKey.has(key.id)) {
3269
+ this.store.byKey.set(key.id, []);
3270
+ }
3271
+ for (const delegation of delegations) {
3272
+ this.addDelegation(key, delegation);
3273
+ }
3274
+ }
3275
+ /**
3276
+ * Remove a key and all its associated delegations.
3277
+ *
3278
+ * @param keyId - The key ID to remove
3279
+ */
3280
+ removeKey(keyId) {
3281
+ const delegations = this.store.byKey.get(keyId) || [];
3282
+ for (const delegation of delegations) {
3283
+ this.store.byCid.delete(delegation.cid);
3284
+ }
3285
+ for (const [capKey, entries] of this.store.byCapability) {
3286
+ const filtered = entries.filter(
3287
+ (entry) => !entry.keys.some((k) => k.id === keyId)
3288
+ );
3289
+ if (filtered.length === 0) {
3290
+ this.store.byCapability.delete(capKey);
3291
+ } else {
3292
+ for (const entry of filtered) {
3293
+ entry.keys = entry.keys.filter((k) => k.id !== keyId);
3294
+ }
3295
+ this.store.byCapability.set(capKey, filtered.filter((e) => e.keys.length > 0));
3296
+ }
3297
+ }
3298
+ this.store.byKey.delete(keyId);
3299
+ this.keys.delete(keyId);
3300
+ }
3301
+ // ===========================================================================
3302
+ // Capability Lookup
3303
+ // ===========================================================================
3304
+ /**
3305
+ * Get a key that can exercise the specified capability.
3306
+ *
3307
+ * Key selection algorithm:
3308
+ * 1. Filter keys that have the required capability
3309
+ * 2. Check delegation validity (not expired, not revoked)
3310
+ * 3. Sort by priority (session=0, main=1, ingested=2)
3311
+ * 4. Return highest priority valid key
3312
+ *
3313
+ * @param resource - Resource URI
3314
+ * @param action - Action to perform
3315
+ * @returns The best matching key, or null if none available
3316
+ */
3317
+ getKeyForCapability(resource, action) {
3318
+ const matchingEntries = this.findMatchingEntries(resource, action);
3319
+ if (matchingEntries.length === 0) {
3320
+ return null;
3321
+ }
3322
+ const validKeys = [];
3323
+ for (const entry of matchingEntries) {
3324
+ if (!this.isDelegationValid(entry.delegation)) {
3325
+ continue;
3326
+ }
3327
+ for (const key of entry.keys) {
3328
+ if (!validKeys.some((k) => k.id === key.id)) {
3329
+ validKeys.push(key);
3330
+ }
3331
+ }
3332
+ }
3333
+ if (validKeys.length === 0) {
3334
+ return null;
3335
+ }
3336
+ validKeys.sort((a, b) => a.priority - b.priority);
3337
+ return validKeys[0];
3338
+ }
3339
+ /**
3340
+ * Get all registered capabilities.
3341
+ *
3342
+ * @returns All capability entries in the registry
3343
+ */
3344
+ getAllCapabilities() {
3345
+ const all = [];
3346
+ for (const entries of this.store.byCapability.values()) {
3347
+ all.push(...entries);
3348
+ }
3349
+ return all;
3350
+ }
3351
+ // ===========================================================================
3352
+ // Delegation Tracking
3353
+ // ===========================================================================
3354
+ /**
3355
+ * Get all delegations for a specific key.
3356
+ *
3357
+ * @param keyId - The key ID
3358
+ * @returns Array of delegations for this key
3359
+ */
3360
+ getDelegationsForKey(keyId) {
3361
+ return this.store.byKey.get(keyId) || [];
3362
+ }
3363
+ // ===========================================================================
3364
+ // Ingestion
3365
+ // ===========================================================================
3366
+ /**
3367
+ * Ingest a key and delegation from an external source.
3368
+ *
3369
+ * @param key - Key information to ingest
3370
+ * @param delegation - Delegation to associate with the key
3371
+ * @param options - Ingestion options
3372
+ */
3373
+ ingestKey(key, delegation, options) {
3374
+ const keyToStore = options?.priority !== void 0 ? { ...key, priority: options.priority } : key;
3375
+ this.keys.set(keyToStore.id, keyToStore);
3376
+ if (!this.store.byKey.has(keyToStore.id)) {
3377
+ this.store.byKey.set(keyToStore.id, []);
3378
+ }
3379
+ this.addDelegation(keyToStore, delegation);
3380
+ }
3381
+ // ===========================================================================
3382
+ // Validation
3383
+ // ===========================================================================
3384
+ /**
3385
+ * Check if a delegation is currently valid.
3386
+ *
3387
+ * @param delegation - The delegation to check
3388
+ * @returns true if valid, false if expired or revoked
3389
+ */
3390
+ isDelegationValid(delegation) {
3391
+ if (delegation.isRevoked) {
3392
+ return false;
3393
+ }
3394
+ const now = /* @__PURE__ */ new Date();
3395
+ if (delegation.expiry && delegation.expiry < now) {
3396
+ return false;
3397
+ }
3398
+ return true;
3399
+ }
3400
+ // ===========================================================================
3401
+ // Key Access
3402
+ // ===========================================================================
3403
+ /**
3404
+ * Get a key by its ID.
3405
+ *
3406
+ * @param keyId - The key ID
3407
+ * @returns The key info, or undefined if not found
3408
+ */
3409
+ getKey(keyId) {
3410
+ return this.keys.get(keyId);
3411
+ }
3412
+ /**
3413
+ * Get all registered keys.
3414
+ *
3415
+ * @returns Array of all registered keys
3416
+ */
3417
+ getAllKeys() {
3418
+ return Array.from(this.keys.values());
3419
+ }
3420
+ // ===========================================================================
3421
+ // Clear
3422
+ // ===========================================================================
3423
+ /**
3424
+ * Clear all registered keys and delegations.
3425
+ */
3426
+ clear() {
3427
+ this.keys.clear();
3428
+ this.store.byKey.clear();
3429
+ this.store.byCid.clear();
3430
+ this.store.byCapability.clear();
3431
+ }
3432
+ // ===========================================================================
3433
+ // Revocation
3434
+ // ===========================================================================
3435
+ /**
3436
+ * Revoke a delegation by CID.
3437
+ *
3438
+ * @param cid - The delegation CID to revoke
3439
+ * @returns Result indicating success or failure
3440
+ */
3441
+ revokeDelegation(cid) {
3442
+ const stored = this.store.byCid.get(cid);
3443
+ if (!stored) {
3444
+ return err3(
3445
+ serviceError3(
3446
+ CapabilityKeyRegistryErrorCodes.KEY_NOT_FOUND,
3447
+ `Delegation not found: ${cid}`,
3448
+ SERVICE_NAME2
3449
+ )
3450
+ );
3451
+ }
3452
+ stored.delegation.isRevoked = true;
3453
+ const keyDelegations = this.store.byKey.get(stored.keyId);
3454
+ if (keyDelegations) {
3455
+ const delegation = keyDelegations.find((d) => d.cid === cid);
3456
+ if (delegation) {
3457
+ delegation.isRevoked = true;
3458
+ }
3459
+ }
3460
+ for (const entries of this.store.byCapability.values()) {
3461
+ for (const entry of entries) {
3462
+ if (entry.delegation.cid === cid) {
3463
+ entry.delegation.isRevoked = true;
3464
+ }
3465
+ }
3466
+ }
3467
+ return ok3(void 0);
3468
+ }
3469
+ // ===========================================================================
3470
+ // Search
3471
+ // ===========================================================================
3472
+ /**
3473
+ * Find capabilities that match a resource path pattern.
3474
+ *
3475
+ * @param resourcePattern - Resource pattern (supports wildcards)
3476
+ * @param action - Optional action filter
3477
+ * @returns Matching capability entries
3478
+ */
3479
+ findCapabilities(resourcePattern, action) {
3480
+ const results = [];
3481
+ for (const entries of this.store.byCapability.values()) {
3482
+ for (const entry of entries) {
3483
+ if (action && entry.action !== action) {
3484
+ continue;
3485
+ }
3486
+ if (this.matchesResourcePattern(entry.resource, resourcePattern)) {
3487
+ results.push(entry);
3488
+ }
3489
+ }
3490
+ }
3491
+ return results;
3492
+ }
3493
+ // ===========================================================================
3494
+ // Private Methods
3495
+ // ===========================================================================
3496
+ /**
3497
+ * Add a delegation to the store.
3498
+ *
3499
+ * @param key - The key associated with this delegation
3500
+ * @param delegation - The delegation to add
3501
+ */
3502
+ addDelegation(key, delegation) {
3503
+ const keyDelegations = this.store.byKey.get(key.id) || [];
3504
+ if (!keyDelegations.some((d) => d.cid === delegation.cid)) {
3505
+ keyDelegations.push(delegation);
3506
+ this.store.byKey.set(key.id, keyDelegations);
3507
+ }
3508
+ if (!this.store.byCid.has(delegation.cid)) {
3509
+ this.store.byCid.set(delegation.cid, {
3510
+ delegation,
3511
+ parentCid: delegation.parentCid,
3512
+ keyId: key.id,
3513
+ storedAt: /* @__PURE__ */ new Date()
3514
+ });
3515
+ }
3516
+ for (const action of delegation.actions) {
3517
+ const capKey = this.makeCapabilityKey(delegation.path, action);
3518
+ const entries = this.store.byCapability.get(capKey) || [];
3519
+ const existingEntry = entries.find((e) => e.delegation.cid === delegation.cid);
3520
+ if (existingEntry) {
3521
+ if (!existingEntry.keys.some((k) => k.id === key.id)) {
3522
+ existingEntry.keys.push(key);
3523
+ existingEntry.keys.sort((a, b) => a.priority - b.priority);
3524
+ }
3525
+ } else {
3526
+ const entry = {
3527
+ resource: delegation.path,
3528
+ action,
3529
+ keys: [key],
3530
+ delegation,
3531
+ expiresAt: delegation.expiry
3532
+ };
3533
+ entries.push(entry);
3534
+ this.store.byCapability.set(capKey, entries);
3535
+ }
3536
+ }
3537
+ }
3538
+ /**
3539
+ * Create a capability key for indexing.
3540
+ *
3541
+ * @param resource - Resource path
3542
+ * @param action - Action
3543
+ * @returns Combined key string
3544
+ */
3545
+ makeCapabilityKey(resource, action) {
3546
+ return `${resource}|${action}`;
3547
+ }
3548
+ /**
3549
+ * Find capability entries that match a resource and action.
3550
+ *
3551
+ * @param resource - Resource to match
3552
+ * @param action - Action to match
3553
+ * @returns Matching entries
3554
+ */
3555
+ findMatchingEntries(resource, action) {
3556
+ const results = [];
3557
+ const exactKey = this.makeCapabilityKey(resource, action);
3558
+ const exactEntries = this.store.byCapability.get(exactKey);
3559
+ if (exactEntries) {
3560
+ results.push(...exactEntries);
3561
+ }
3562
+ for (const [capKey, entries] of this.store.byCapability) {
3563
+ if (capKey === exactKey) continue;
3564
+ for (const entry of entries) {
3565
+ if (!this.actionMatches(entry.action, action)) {
3566
+ continue;
3567
+ }
3568
+ if (this.resourceMatchesPattern(resource, entry.resource)) {
3569
+ if (!results.some((r) => r.delegation.cid === entry.delegation.cid)) {
3570
+ results.push(entry);
3571
+ }
3572
+ }
3573
+ }
3574
+ }
3575
+ return results;
3576
+ }
3577
+ /**
3578
+ * Check if an action pattern matches a specific action.
3579
+ *
3580
+ * @param pattern - Action pattern (may include wildcard like "tinycloud.kv/*")
3581
+ * @param action - Specific action to check
3582
+ * @returns true if pattern matches action
3583
+ */
3584
+ actionMatches(pattern, action) {
3585
+ if (pattern === action) {
3586
+ return true;
3587
+ }
3588
+ if (pattern.endsWith("/*")) {
3589
+ const prefix = pattern.slice(0, -2);
3590
+ return action.startsWith(prefix + "/") || action === prefix;
3591
+ }
3592
+ return false;
3593
+ }
3594
+ /**
3595
+ * Check if a resource matches a pattern.
3596
+ *
3597
+ * Patterns support:
3598
+ * - Exact match: "/kv/data" matches "/kv/data"
3599
+ * - Wildcard suffix: "/kv/*" matches "/kv/anything"
3600
+ * - Double wildcard: "/kv/**" matches "/kv/any/nested/path"
3601
+ *
3602
+ * @param resource - The specific resource being accessed
3603
+ * @param pattern - The pattern from the delegation
3604
+ * @returns true if resource matches pattern
3605
+ */
3606
+ resourceMatchesPattern(resource, pattern) {
3607
+ if (pattern === resource) {
3608
+ return true;
3609
+ }
3610
+ if (pattern.endsWith("/**")) {
3611
+ const prefix = pattern.slice(0, -3);
3612
+ return resource.startsWith(prefix);
3613
+ }
3614
+ if (pattern.endsWith("/*")) {
3615
+ const prefix = pattern.slice(0, -2);
3616
+ if (!resource.startsWith(prefix)) {
3617
+ return false;
3618
+ }
3619
+ const remainder = resource.slice(prefix.length);
3620
+ return !remainder.includes("/") || remainder === "/";
3621
+ }
3622
+ if (pattern.endsWith("/") && resource.startsWith(pattern)) {
3623
+ return true;
3624
+ }
3625
+ return false;
3626
+ }
3627
+ /**
3628
+ * Check if a specific resource matches a resource pattern for searching.
3629
+ *
3630
+ * @param entryResource - The resource from a capability entry
3631
+ * @param searchPattern - The pattern to search for
3632
+ * @returns true if entry resource matches search pattern
3633
+ */
3634
+ matchesResourcePattern(entryResource, searchPattern) {
3635
+ return this.resourceMatchesPattern(entryResource, searchPattern) || this.resourceMatchesPattern(searchPattern, entryResource);
3636
+ }
3637
+ };
3638
+ function createCapabilityKeyRegistry() {
3639
+ return new CapabilityKeyRegistry();
3640
+ }
3641
+
3642
+ // src/authorization/strategies.ts
3643
+ var defaultSignStrategy = { type: "auto-sign" };
3644
+
3645
+ // src/authorization/spaceCreation.ts
3646
+ var AutoApproveSpaceCreationHandler = class {
3647
+ /**
3648
+ * Always returns true to auto-approve space creation.
3649
+ */
3650
+ async confirmSpaceCreation() {
3651
+ return true;
3652
+ }
3653
+ };
3654
+ var defaultSpaceCreationHandler = new AutoApproveSpaceCreationHandler();
3655
+
3656
+ // src/version.ts
3657
+ var ProtocolMismatchError = class extends Error {
3658
+ constructor(sdkProtocol, nodeProtocol, nodeVersion, host) {
3659
+ super(
3660
+ `SDK protocol version ${sdkProtocol} is incompatible with node protocol version ${nodeProtocol} (node v${nodeVersion}) at ${host}. ` + (sdkProtocol < nodeProtocol ? "Please update your SDK." : "Please update the TinyCloud node.")
3661
+ );
3662
+ this.sdkProtocol = sdkProtocol;
3663
+ this.nodeProtocol = nodeProtocol;
3664
+ this.nodeVersion = nodeVersion;
3665
+ this.host = host;
3666
+ this.name = "ProtocolMismatchError";
3667
+ }
3668
+ };
3669
+ var VersionCheckError = class extends Error {
3670
+ constructor(host, cause) {
3671
+ super(
3672
+ `Failed to fetch node info at ${host}. Ensure the node is running and the /info endpoint is accessible.`
3673
+ );
3674
+ this.host = host;
3675
+ this.cause = cause;
3676
+ this.name = "VersionCheckError";
3677
+ }
3678
+ };
3679
+ var UnsupportedFeatureError = class extends Error {
3680
+ constructor(feature, host, availableFeatures) {
3681
+ super(
3682
+ `Feature "${feature}" is not supported by the node at ${host}. Available features: ${availableFeatures.join(", ") || "none"}.`
3683
+ );
3684
+ this.feature = feature;
3685
+ this.host = host;
3686
+ this.availableFeatures = availableFeatures;
3687
+ this.name = "UnsupportedFeatureError";
3688
+ }
3689
+ };
3690
+ async function checkNodeInfo(host, sdkProtocol, fetchFn = globalThis.fetch.bind(globalThis)) {
3691
+ let response;
3692
+ try {
3693
+ response = await fetchFn(`${host}/info`, {
3694
+ signal: AbortSignal.timeout(5e3)
3695
+ });
3696
+ } catch (err5) {
3697
+ throw new VersionCheckError(host, err5);
3698
+ }
3699
+ if (!response.ok) {
3700
+ throw new VersionCheckError(host);
3701
+ }
3702
+ const data = await response.json();
3703
+ if (sdkProtocol !== data.protocol) {
3704
+ throw new ProtocolMismatchError(
3705
+ sdkProtocol,
3706
+ data.protocol,
3707
+ data.version,
3708
+ host
3709
+ );
3710
+ }
3711
+ return {
3712
+ features: data.features ?? [],
3713
+ quotaUrl: data.quota_url
3714
+ };
3715
+ }
3716
+ export {
3717
+ AutoApproveSpaceCreationHandler,
3718
+ CapabilityKeyRegistry,
3719
+ CapabilityKeyRegistryErrorCodes,
3720
+ ClientSessionSchema,
3721
+ DataVaultService,
3722
+ DatabaseHandle,
3723
+ DelegationErrorCodes,
3724
+ DelegationManager,
3725
+ DuckDbAction,
3726
+ DuckDbDatabaseHandle,
3727
+ DuckDbService2 as DuckDbService,
3728
+ EnsDataSchema,
3729
+ ErrorCodes2 as ErrorCodes,
3730
+ KVService2 as KVService,
3731
+ PrefixedKVService,
3732
+ ProtocolMismatchError,
3733
+ SQLAction,
3734
+ SQLService2 as SQLService,
3735
+ ServiceContext2 as ServiceContext,
3736
+ SharingService,
3737
+ SilentNotificationHandler,
3738
+ SiweConfigSchema,
3739
+ SiweMessage,
3740
+ Space,
3741
+ SpaceErrorCodes,
3742
+ SpaceService,
3743
+ TinyCloud,
3744
+ UnsupportedFeatureError,
3745
+ VaultHeaders,
3746
+ VaultPublicSpaceKVActions,
3747
+ VersionCheckError,
3748
+ activateSessionWithHost,
3749
+ buildSpaceUri,
3750
+ checkNodeInfo,
3751
+ createCapabilityKeyRegistry,
3752
+ createSharingService,
3753
+ createSpaceService,
3754
+ createVaultCrypto,
3755
+ defaultRetryPolicy2 as defaultRetryPolicy,
3756
+ defaultSignStrategy,
3757
+ defaultSpaceCreationHandler,
3758
+ err4 as err,
3759
+ fetchPeerId,
3760
+ makePublicSpaceId,
3761
+ ok4 as ok,
3762
+ parseSpaceUri,
3763
+ serviceError4 as serviceError,
3764
+ submitHostDelegation,
3765
+ validateClientSession,
3766
+ validatePersistedSessionData
3767
+ };
62
3768
  //# sourceMappingURL=index.js.map