@tinycloud/sdk-core 2.4.0-beta.1 → 2.4.0-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -32,57 +32,58 @@ var index_exports = {};
32
32
  __export(index_exports, {
33
33
  ACCOUNT_REGISTRY_PATH: () => ACCOUNT_REGISTRY_PATH,
34
34
  ACCOUNT_REGISTRY_SPACE: () => ACCOUNT_REGISTRY_SPACE,
35
+ AccountService: () => AccountService,
35
36
  AutoApproveSpaceCreationHandler: () => AutoApproveSpaceCreationHandler,
36
37
  CapabilityKeyRegistry: () => CapabilityKeyRegistry,
37
38
  CapabilityKeyRegistryErrorCodes: () => CapabilityKeyRegistryErrorCodes,
38
39
  ClientSessionSchema: () => ClientSessionSchema,
39
40
  CloudLocationResolutionError: () => CloudLocationResolutionError,
40
- DECRYPT_ACTION: () => import_sdk_services6.DECRYPT_ACTION,
41
- DECRYPT_FACT_TYPE: () => import_sdk_services6.DECRYPT_FACT_TYPE,
42
- DECRYPT_RESULT_TYPE: () => import_sdk_services6.DECRYPT_RESULT_TYPE,
41
+ DECRYPT_ACTION: () => import_sdk_services7.DECRYPT_ACTION,
42
+ DECRYPT_FACT_TYPE: () => import_sdk_services7.DECRYPT_FACT_TYPE,
43
+ DECRYPT_RESULT_TYPE: () => import_sdk_services7.DECRYPT_RESULT_TYPE,
43
44
  DEFAULT_DEFAULTS: () => DEFAULT_DEFAULTS,
44
- DEFAULT_ENCRYPTION_ALG: () => import_sdk_services6.DEFAULT_ENCRYPTION_ALG,
45
+ DEFAULT_ENCRYPTION_ALG: () => import_sdk_services7.DEFAULT_ENCRYPTION_ALG,
45
46
  DEFAULT_EXPIRY: () => DEFAULT_EXPIRY,
46
- DEFAULT_KEY_VERSION: () => import_sdk_services6.DEFAULT_KEY_VERSION,
47
+ DEFAULT_KEY_VERSION: () => import_sdk_services7.DEFAULT_KEY_VERSION,
47
48
  DEFAULT_MANIFEST_SPACE: () => DEFAULT_MANIFEST_SPACE,
48
49
  DEFAULT_MANIFEST_VERSION: () => DEFAULT_MANIFEST_VERSION,
49
50
  DEFAULT_SIGNED_READ_URL_EXPIRY_MS: () => DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
50
51
  DEFAULT_TINYCLOUD_FALLBACK_HOST: () => DEFAULT_TINYCLOUD_FALLBACK_HOST,
51
52
  DEFAULT_TINYCLOUD_LOCATION_REGISTRY_URL: () => DEFAULT_TINYCLOUD_LOCATION_REGISTRY_URL,
52
- DataVaultService: () => import_sdk_services6.DataVaultService,
53
- DatabaseHandle: () => import_sdk_services6.DatabaseHandle,
53
+ DataVaultService: () => import_sdk_services7.DataVaultService,
54
+ DatabaseHandle: () => import_sdk_services7.DatabaseHandle,
54
55
  DelegationErrorCodes: () => DelegationErrorCodes,
55
56
  DelegationManager: () => DelegationManager,
56
- DuckDbAction: () => import_sdk_services6.DuckDbAction,
57
- DuckDbDatabaseHandle: () => import_sdk_services6.DuckDbDatabaseHandle,
58
- DuckDbService: () => import_sdk_services6.DuckDbService,
57
+ DuckDbAction: () => import_sdk_services7.DuckDbAction,
58
+ DuckDbDatabaseHandle: () => import_sdk_services7.DuckDbDatabaseHandle,
59
+ DuckDbService: () => import_sdk_services7.DuckDbService,
59
60
  ENCRYPTION_MANIFEST_SPACE: () => ENCRYPTION_MANIFEST_SPACE,
60
- ENCRYPTION_NETWORK_URN_PREFIX: () => import_sdk_services6.ENCRYPTION_NETWORK_URN_PREFIX,
61
+ ENCRYPTION_NETWORK_URN_PREFIX: () => import_sdk_services7.ENCRYPTION_NETWORK_URN_PREFIX,
61
62
  ENCRYPTION_PERMISSION_SERVICE: () => ENCRYPTION_PERMISSION_SERVICE,
62
- ENCRYPTION_SERVICE: () => import_sdk_services6.ENCRYPTION_SERVICE,
63
- ENCRYPTION_SERVICE_SHORT: () => import_sdk_services6.ENCRYPTION_SERVICE_SHORT,
64
- ENVELOPE_VERSION: () => import_sdk_services6.ENVELOPE_VERSION,
63
+ ENCRYPTION_SERVICE: () => import_sdk_services7.ENCRYPTION_SERVICE,
64
+ ENCRYPTION_SERVICE_SHORT: () => import_sdk_services7.ENCRYPTION_SERVICE_SHORT,
65
+ ENVELOPE_VERSION: () => import_sdk_services7.ENVELOPE_VERSION,
65
66
  EXPIRY: () => EXPIRY,
66
- EncryptionService: () => import_sdk_services6.EncryptionService,
67
+ EncryptionService: () => import_sdk_services7.EncryptionService,
67
68
  EnsDataSchema: () => EnsDataSchema,
68
- ErrorCodes: () => import_sdk_services6.ErrorCodes,
69
- HooksService: () => import_sdk_services6.HooksService,
69
+ ErrorCodes: () => import_sdk_services7.ErrorCodes,
70
+ HooksService: () => import_sdk_services7.HooksService,
70
71
  IdentityParseError: () => IdentityParseError,
71
- KVService: () => import_sdk_services6.KVService,
72
+ KVService: () => import_sdk_services7.KVService,
72
73
  LocationRecordValidationError: () => LocationRecordValidationError,
73
74
  ManifestValidationError: () => ManifestValidationError,
74
- NETWORK_NAME_PATTERN: () => import_sdk_services6.NETWORK_NAME_PATTERN,
75
- NetworkIdError: () => import_sdk_services6.NetworkIdError,
75
+ NETWORK_NAME_PATTERN: () => import_sdk_services7.NETWORK_NAME_PATTERN,
76
+ NetworkIdError: () => import_sdk_services7.NetworkIdError,
76
77
  PermissionNotInManifestError: () => PermissionNotInManifestError,
77
- PrefixedKVService: () => import_sdk_services6.PrefixedKVService,
78
+ PrefixedKVService: () => import_sdk_services7.PrefixedKVService,
78
79
  ProtocolMismatchError: () => ProtocolMismatchError,
79
- SECRET_NAME_RE: () => import_sdk_services6.SECRET_NAME_RE,
80
+ SECRET_NAME_RE: () => import_sdk_services7.SECRET_NAME_RE,
80
81
  SERVICE_LONG_TO_SHORT: () => SERVICE_LONG_TO_SHORT,
81
82
  SERVICE_SHORT_TO_LONG: () => SERVICE_SHORT_TO_LONG,
82
- SQLAction: () => import_sdk_services6.SQLAction,
83
- SQLService: () => import_sdk_services6.SQLService,
84
- SecretsService: () => import_sdk_services6.SecretsService,
85
- ServiceContext: () => import_sdk_services6.ServiceContext,
83
+ SQLAction: () => import_sdk_services7.SQLAction,
84
+ SQLService: () => import_sdk_services7.SQLService,
85
+ SecretsService: () => import_sdk_services7.SecretsService,
86
+ ServiceContext: () => import_sdk_services7.ServiceContext,
86
87
  SessionExpiredError: () => SessionExpiredError,
87
88
  SharingService: () => SharingService,
88
89
  SilentNotificationHandler: () => SilentNotificationHandler,
@@ -94,75 +95,75 @@ __export(index_exports, {
94
95
  TinyCloud: () => TinyCloud,
95
96
  UnsupportedFeatureError: () => UnsupportedFeatureError,
96
97
  VAULT_PERMISSION_SERVICE: () => VAULT_PERMISSION_SERVICE,
97
- VaultHeaders: () => import_sdk_services6.VaultHeaders,
98
- VaultPublicSpaceKVActions: () => import_sdk_services6.VaultPublicSpaceKVActions,
98
+ VaultHeaders: () => import_sdk_services7.VaultHeaders,
99
+ VaultPublicSpaceKVActions: () => import_sdk_services7.VaultPublicSpaceKVActions,
99
100
  VersionCheckError: () => VersionCheckError,
100
101
  activateSessionWithHost: () => activateSessionWithHost,
101
102
  addressStorageKey: () => addressStorageKey,
102
103
  applyPrefix: () => applyPrefix,
103
- buildCanonicalDecryptRequest: () => import_sdk_services6.buildCanonicalDecryptRequest,
104
- buildDecryptAttenuation: () => import_sdk_services6.buildDecryptAttenuation,
105
- buildDecryptFacts: () => import_sdk_services6.buildDecryptFacts,
106
- buildDecryptInvocation: () => import_sdk_services6.buildDecryptInvocation,
107
- buildNetworkId: () => import_sdk_services6.buildNetworkId,
104
+ buildCanonicalDecryptRequest: () => import_sdk_services7.buildCanonicalDecryptRequest,
105
+ buildDecryptAttenuation: () => import_sdk_services7.buildDecryptAttenuation,
106
+ buildDecryptFacts: () => import_sdk_services7.buildDecryptFacts,
107
+ buildDecryptInvocation: () => import_sdk_services7.buildDecryptInvocation,
108
+ buildNetworkId: () => import_sdk_services7.buildNetworkId,
108
109
  buildSpaceUri: () => buildSpaceUri,
109
- canonicalHashHex: () => import_sdk_services6.canonicalHashHex,
110
+ canonicalHashHex: () => import_sdk_services7.canonicalHashHex,
110
111
  canonicalLocationPayload: () => canonicalLocationPayload,
111
- canonicalSignedResponse: () => import_sdk_services6.canonicalSignedResponse,
112
+ canonicalSignedResponse: () => import_sdk_services7.canonicalSignedResponse,
112
113
  canonicalizeAddress: () => canonicalizeAddress,
113
114
  canonicalizeDid: () => canonicalizeDid,
114
115
  canonicalizeDidUrl: () => canonicalizeDidUrl,
115
- canonicalizeEncryptionJson: () => import_sdk_services6.canonicalizeEncryptionJson,
116
+ canonicalizeEncryptionJson: () => import_sdk_services7.canonicalizeEncryptionJson,
116
117
  canonicalizeNetworkId: () => canonicalizeNetworkId,
117
- canonicalizeSecretScope: () => import_sdk_services6.canonicalizeSecretScope,
118
- checkDecryptInvocationInput: () => import_sdk_services6.checkDecryptInvocationInput,
118
+ canonicalizeSecretScope: () => import_sdk_services7.canonicalizeSecretScope,
119
+ checkDecryptInvocationInput: () => import_sdk_services7.checkDecryptInvocationInput,
119
120
  checkNodeInfo: () => checkNodeInfo,
120
121
  composeManifestRequest: () => composeManifestRequest,
121
122
  createCapabilityKeyRegistry: () => createCapabilityKeyRegistry,
122
123
  createSharingService: () => createSharingService,
123
124
  createSpaceService: () => createSpaceService,
124
- createVaultCrypto: () => import_sdk_services6.createVaultCrypto,
125
- decryptEnvelopeWithKey: () => import_sdk_services6.decryptEnvelopeWithKey,
126
- defaultRetryPolicy: () => import_sdk_services6.defaultRetryPolicy,
125
+ createVaultCrypto: () => import_sdk_services7.createVaultCrypto,
126
+ decryptEnvelopeWithKey: () => import_sdk_services7.decryptEnvelopeWithKey,
127
+ defaultRetryPolicy: () => import_sdk_services7.defaultRetryPolicy,
127
128
  defaultSignStrategy: () => defaultSignStrategy,
128
129
  defaultSpaceCreationHandler: () => defaultSpaceCreationHandler,
129
- deriveSignedReceiverKey: () => import_sdk_services6.deriveSignedReceiverKey,
130
+ deriveSignedReceiverKey: () => import_sdk_services7.deriveSignedReceiverKey,
130
131
  didCacheKey: () => didCacheKey,
131
132
  didEquals: () => didEquals,
132
- discoverNetwork: () => import_sdk_services6.discoverNetwork,
133
- encryptToNetwork: () => import_sdk_services6.encryptToNetwork,
134
- encryptionBase64Decode: () => import_sdk_services6.base64Decode,
135
- encryptionBase64Encode: () => import_sdk_services6.base64Encode,
136
- encryptionError: () => import_sdk_services6.encryptionError,
137
- encryptionUtf8Decode: () => import_sdk_services6.utf8Decode,
138
- encryptionUtf8Encode: () => import_sdk_services6.utf8Encode,
139
- ensureNetworkUsableForDecrypt: () => import_sdk_services6.ensureNetworkUsableForDecrypt,
140
- err: () => import_sdk_services6.err,
133
+ discoverNetwork: () => import_sdk_services7.discoverNetwork,
134
+ encryptToNetwork: () => import_sdk_services7.encryptToNetwork,
135
+ encryptionBase64Decode: () => import_sdk_services7.base64Decode,
136
+ encryptionBase64Encode: () => import_sdk_services7.base64Encode,
137
+ encryptionError: () => import_sdk_services7.encryptionError,
138
+ encryptionUtf8Decode: () => import_sdk_services7.utf8Decode,
139
+ encryptionUtf8Encode: () => import_sdk_services7.utf8Encode,
140
+ ensureNetworkUsableForDecrypt: () => import_sdk_services7.ensureNetworkUsableForDecrypt,
141
+ err: () => import_sdk_services7.err,
141
142
  expandActionShortNames: () => expandActionShortNames,
142
143
  expandPermissionEntries: () => expandPermissionEntries,
143
144
  expandPermissionEntry: () => expandPermissionEntry,
144
145
  fetchLocationRecord: () => fetchLocationRecord,
145
146
  fetchPeerId: () => fetchPeerId,
146
- generateRandomReceiverKey: () => import_sdk_services6.generateRandomReceiverKey,
147
- hexDecode: () => import_sdk_services6.hexDecode,
148
- hexEncode: () => import_sdk_services6.hexEncode,
147
+ generateRandomReceiverKey: () => import_sdk_services7.generateRandomReceiverKey,
148
+ hexDecode: () => import_sdk_services7.hexDecode,
149
+ hexEncode: () => import_sdk_services7.hexEncode,
149
150
  httpUrlToMultiaddr: () => httpUrlToMultiaddr,
150
151
  isCapabilitySubset: () => isCapabilitySubset,
151
152
  isEvmAddress: () => isEvmAddress,
152
- isNetworkId: () => import_sdk_services6.isNetworkId,
153
+ isNetworkId: () => import_sdk_services7.isNetworkId,
153
154
  loadManifest: () => loadManifest,
154
155
  locationPayloadForRecord: () => locationPayloadForRecord,
155
156
  makePkhSpaceId: () => makePkhSpaceId,
156
157
  makePublicSpaceId: () => makePublicSpaceId,
157
158
  manifestAbilitiesUnion: () => manifestAbilitiesUnion,
158
159
  multiaddrToHttpUrl: () => multiaddrToHttpUrl,
159
- networkDiscoveryKey: () => import_sdk_services6.networkDiscoveryKey,
160
+ networkDiscoveryKey: () => import_sdk_services7.networkDiscoveryKey,
160
161
  normalizeDefaults: () => normalizeDefaults,
161
- ok: () => import_sdk_services6.ok,
162
- openWrappedKey: () => import_sdk_services6.openWrappedKey,
162
+ ok: () => import_sdk_services7.ok,
163
+ openWrappedKey: () => import_sdk_services7.openWrappedKey,
163
164
  parseCanonicalNetworkId: () => parseCanonicalNetworkId,
164
165
  parseExpiry: () => parseExpiry,
165
- parseNetworkId: () => import_sdk_services6.parseNetworkId,
166
+ parseNetworkId: () => import_sdk_services7.parseNetworkId,
166
167
  parsePkhDid: () => parsePkhDid,
167
168
  parseRecapCapabilities: () => parseRecapCapabilities,
168
169
  parseSpaceUri: () => parseSpaceUri,
@@ -171,21 +172,21 @@ __export(index_exports, {
171
172
  principalDidEquals: () => principalDidEquals,
172
173
  resolveCloudLocation: () => resolveCloudLocation,
173
174
  resolveManifest: () => resolveManifest,
174
- resolveSecretListPrefix: () => import_sdk_services6.resolveSecretListPrefix,
175
- resolveSecretPath: () => import_sdk_services6.resolveSecretPath,
175
+ resolveSecretListPrefix: () => import_sdk_services7.resolveSecretListPrefix,
176
+ resolveSecretPath: () => import_sdk_services7.resolveSecretPath,
176
177
  resolveTinyCloudHosts: () => resolveTinyCloudHosts,
177
178
  resourceCapabilitiesToAbilitiesMap: () => resourceCapabilitiesToAbilitiesMap,
178
179
  resourceCapabilitiesToSpaceAbilitiesMap: () => resourceCapabilitiesToSpaceAbilitiesMap,
179
- serviceError: () => import_sdk_services6.serviceError,
180
+ serviceError: () => import_sdk_services7.serviceError,
180
181
  signLocationRecord: () => signLocationRecord,
181
182
  submitHostDelegation: () => submitHostDelegation,
182
183
  validateClientSession: () => validateClientSession,
183
- validateEnvelope: () => import_sdk_services6.validateEnvelope,
184
+ validateEnvelope: () => import_sdk_services7.validateEnvelope,
184
185
  validateLocationRecord: () => validateLocationRecord,
185
186
  validateLocationRecordPayload: () => validateLocationRecordPayload,
186
187
  validateManifest: () => validateManifest,
187
188
  validatePersistedSessionData: () => validatePersistedSessionData,
188
- verifyDecryptResponse: () => import_sdk_services6.verifyDecryptResponse,
189
+ verifyDecryptResponse: () => import_sdk_services7.verifyDecryptResponse,
189
190
  verifyDidKeyEd25519Signature: () => verifyDidKeyEd25519Signature,
190
191
  verifyLocationRecord: () => verifyLocationRecord
191
192
  });
@@ -378,7 +379,15 @@ var PersistedSessionDataSchema = import_zod2.z.object({
378
379
  /** Schema version for migrations */
379
380
  version: import_zod2.z.string(),
380
381
  /** Optional ENS data */
381
- ens: EnsDataSchema2.optional()
382
+ ens: EnsDataSchema2.optional(),
383
+ /**
384
+ * TinyCloud hosts this session was created against. Persisted so a
385
+ * restored session resolves to the same node without re-running the
386
+ * registry/fallback resolution (or the wallet sign-in flow). Optional
387
+ * for backward compatibility with sessions persisted before this field
388
+ * existed — those restore and lazily re-resolve their hosts.
389
+ */
390
+ tinycloudHosts: import_zod2.z.array(import_zod2.z.string()).optional()
382
391
  });
383
392
  var TinyCloudSessionSchema = import_zod2.z.object({
384
393
  /** User's Ethereum address */
@@ -1150,6 +1159,7 @@ var SpaceService = class {
1150
1159
  this._userDid = config.userDid;
1151
1160
  this.sharingService = config.sharingService;
1152
1161
  this.createDelegationFn = config.createDelegation;
1162
+ this.onSpaceRegisteredFn = config.onSpaceRegistered;
1153
1163
  }
1154
1164
  /**
1155
1165
  * Update the service configuration.
@@ -1165,6 +1175,7 @@ var SpaceService = class {
1165
1175
  if (config.userDid !== void 0) this._userDid = config.userDid;
1166
1176
  if (config.sharingService) this.sharingService = config.sharingService;
1167
1177
  if (config.createDelegation) this.createDelegationFn = config.createDelegation;
1178
+ if (config.onSpaceRegistered) this.onSpaceRegisteredFn = config.onSpaceRegistered;
1168
1179
  this.spaceCache.clear();
1169
1180
  this.infoCache.clear();
1170
1181
  }
@@ -1215,6 +1226,9 @@ var SpaceService = class {
1215
1226
  spaces.push(...delegatedSpaces);
1216
1227
  }
1217
1228
  const uniqueSpaces = this.deduplicateSpaces(spaces);
1229
+ for (const space of uniqueSpaces) {
1230
+ this.notifySpaceRegistered(space);
1231
+ }
1218
1232
  return (0, import_sdk_services2.ok)(uniqueSpaces);
1219
1233
  } catch (error) {
1220
1234
  return (0, import_sdk_services2.err)(
@@ -1412,6 +1426,7 @@ var SpaceService = class {
1412
1426
  permissions: ["*"]
1413
1427
  };
1414
1428
  this.infoCache.set(spaceInfo.id, { info: spaceInfo, cachedAt: Date.now() });
1429
+ this.notifySpaceRegistered(spaceInfo);
1415
1430
  return (0, import_sdk_services2.ok)(spaceInfo);
1416
1431
  } catch (error) {
1417
1432
  return (0, import_sdk_services2.err)(
@@ -1424,6 +1439,11 @@ var SpaceService = class {
1424
1439
  );
1425
1440
  }
1426
1441
  }
1442
+ notifySpaceRegistered(space) {
1443
+ if (!this.onSpaceRegisteredFn) return;
1444
+ void Promise.resolve(this.onSpaceRegisteredFn(space)).catch(() => {
1445
+ });
1446
+ }
1427
1447
  // ===========================================================================
1428
1448
  // Get Space
1429
1449
  // ===========================================================================
@@ -2331,1409 +2351,2257 @@ var TinyCloud = class _TinyCloud {
2331
2351
  }
2332
2352
  };
2333
2353
 
2334
- // src/index.ts
2335
- var import_sdk_services6 = require("@tinycloud/sdk-services");
2354
+ // src/account/AccountService.ts
2355
+ var import_sdk_services5 = require("@tinycloud/sdk-services");
2336
2356
 
2337
- // src/space.ts
2338
- async function fetchPeerId(host, spaceId) {
2339
- const res = await fetch(
2340
- `${host}/peer/generate/${encodeURIComponent(spaceId)}`
2341
- );
2342
- if (!res.ok) {
2343
- const error = await res.text().catch(() => res.statusText);
2344
- throw new Error(`Failed to get peer ID: ${res.status} - ${error}`);
2357
+ // src/manifest.ts
2358
+ var import_ms = __toESM(require("ms"), 1);
2359
+ var import_sdk_services4 = require("@tinycloud/sdk-services");
2360
+ var ManifestValidationError = class extends Error {
2361
+ constructor(message) {
2362
+ super(`Manifest validation failed: ${message}`);
2363
+ this.name = "ManifestValidationError";
2345
2364
  }
2346
- return res.text();
2365
+ };
2366
+ var DEFAULT_EXPIRY = "30d";
2367
+ var DEFAULT_DEFAULTS = true;
2368
+ var DEFAULT_MANIFEST_VERSION = 1;
2369
+ var DEFAULT_MANIFEST_SPACE = "applications";
2370
+ var ACCOUNT_REGISTRY_SPACE = "account";
2371
+ var ACCOUNT_REGISTRY_PATH = "applications/";
2372
+ var SECRETS_SPACE = "secrets";
2373
+ var VAULT_PERMISSION_SERVICE = "tinycloud.vault";
2374
+ var SERVICE_SHORT_TO_LONG = Object.freeze({
2375
+ kv: "tinycloud.kv",
2376
+ sql: "tinycloud.sql",
2377
+ duckdb: "tinycloud.duckdb",
2378
+ capabilities: "tinycloud.capabilities",
2379
+ hooks: "tinycloud.hooks",
2380
+ encryption: "tinycloud.encryption"
2381
+ });
2382
+ var ENCRYPTION_PERMISSION_SERVICE = "tinycloud.encryption";
2383
+ var ENCRYPTION_MANIFEST_SPACE = "encryption";
2384
+ var SERVICE_LONG_TO_SHORT = Object.freeze(
2385
+ Object.fromEntries(
2386
+ Object.entries(SERVICE_SHORT_TO_LONG).map(([s, l]) => [l, s])
2387
+ )
2388
+ );
2389
+ var DEFAULT_STANDARD_ENTRIES = [
2390
+ {
2391
+ service: "tinycloud.kv",
2392
+ space: DEFAULT_MANIFEST_SPACE,
2393
+ path: "/",
2394
+ actions: ["get", "put", "del", "list", "metadata"]
2395
+ },
2396
+ {
2397
+ service: "tinycloud.sql",
2398
+ space: DEFAULT_MANIFEST_SPACE,
2399
+ path: "/",
2400
+ actions: ["read", "write"]
2401
+ }
2402
+ ];
2403
+ var DEFAULT_ADMIN_ENTRIES = [
2404
+ {
2405
+ service: "tinycloud.kv",
2406
+ space: DEFAULT_MANIFEST_SPACE,
2407
+ path: "/",
2408
+ actions: ["get", "put", "del", "list", "metadata"]
2409
+ },
2410
+ {
2411
+ service: "tinycloud.sql",
2412
+ space: DEFAULT_MANIFEST_SPACE,
2413
+ path: "/",
2414
+ actions: ["read", "write", "ddl"]
2415
+ }
2416
+ ];
2417
+ var DEFAULT_ALL_ENTRIES = [
2418
+ {
2419
+ service: "tinycloud.kv",
2420
+ space: DEFAULT_MANIFEST_SPACE,
2421
+ path: "/",
2422
+ actions: ["get", "put", "del", "list", "metadata"]
2423
+ },
2424
+ {
2425
+ service: "tinycloud.sql",
2426
+ space: DEFAULT_MANIFEST_SPACE,
2427
+ path: "/",
2428
+ actions: ["read", "write", "ddl"]
2429
+ },
2430
+ {
2431
+ service: "tinycloud.duckdb",
2432
+ space: DEFAULT_MANIFEST_SPACE,
2433
+ path: "/",
2434
+ actions: ["read", "write"]
2435
+ }
2436
+ ];
2437
+ function parseExpiry(duration) {
2438
+ if (typeof duration !== "string" || duration.length === 0) {
2439
+ throw new ManifestValidationError(
2440
+ `expiry must be a non-empty duration string (got ${JSON.stringify(duration)})`
2441
+ );
2442
+ }
2443
+ const parsed = (0, import_ms.default)(duration);
2444
+ if (typeof parsed !== "number" || !Number.isFinite(parsed) || parsed <= 0) {
2445
+ throw new ManifestValidationError(
2446
+ `invalid expiry duration: ${JSON.stringify(duration)}`
2447
+ );
2448
+ }
2449
+ return parsed;
2347
2450
  }
2348
- async function submitHostDelegation(host, headers) {
2349
- const res = await fetch(`${host}/delegate`, {
2350
- method: "POST",
2351
- headers
2451
+ function expandActionShortNames(service, actions) {
2452
+ return actions.map((a) => {
2453
+ if (a.includes("/")) {
2454
+ return a;
2455
+ }
2456
+ return `${service}/${a}`;
2352
2457
  });
2353
- return {
2354
- success: res.ok,
2355
- status: res.status,
2356
- error: res.ok ? void 0 : await res.text().catch(() => res.statusText)
2357
- };
2358
2458
  }
2359
- async function activateSessionWithHost(host, delegationHeader) {
2360
- const res = await fetch(`${host}/delegate`, {
2361
- method: "POST",
2362
- headers: delegationHeader
2363
- });
2364
- if (res.ok) {
2365
- try {
2366
- const body = await res.json();
2367
- return {
2368
- success: true,
2369
- status: res.status,
2370
- activated: body.activated ?? [],
2371
- skipped: body.skipped ?? []
2372
- };
2373
- } catch {
2374
- return {
2375
- success: true,
2376
- status: res.status,
2377
- activated: [],
2378
- skipped: []
2379
- };
2459
+ function expandPermissionEntry(entry) {
2460
+ if (entry.service === ENCRYPTION_PERMISSION_SERVICE) {
2461
+ return expandEncryptionPermissionEntry(entry);
2462
+ }
2463
+ if (entry.service !== VAULT_PERMISSION_SERVICE) {
2464
+ return [
2465
+ {
2466
+ ...entry,
2467
+ actions: expandActionShortNames(entry.service, entry.actions)
2468
+ }
2469
+ ];
2470
+ }
2471
+ return expandVaultPermissionEntry(entry);
2472
+ }
2473
+ function expandEncryptionPermissionEntry(entry) {
2474
+ if (typeof entry.path !== "string" || !entry.path.startsWith("urn:tinycloud:encryption:")) {
2475
+ throw new ManifestValidationError(
2476
+ `tinycloud.encryption entries require path to be a networkId URN (got ${JSON.stringify(entry.path)})`
2477
+ );
2478
+ }
2479
+ const normalizedActions = [];
2480
+ for (const action of entry.actions) {
2481
+ if (action === "decrypt" || action === "tinycloud.encryption/decrypt") {
2482
+ normalizedActions.push("tinycloud.encryption/decrypt");
2483
+ continue;
2484
+ }
2485
+ if (action === "network.create" || action === "tinycloud.encryption/network.create") {
2486
+ normalizedActions.push("tinycloud.encryption/network.create");
2487
+ continue;
2488
+ }
2489
+ if (action === "network.revoke" || action === "tinycloud.encryption/network.revoke") {
2490
+ normalizedActions.push("tinycloud.encryption/network.revoke");
2491
+ continue;
2492
+ }
2493
+ if (action.includes("/")) {
2494
+ throw new ManifestValidationError(
2495
+ `unknown encryption action ${JSON.stringify(action)}; expected decrypt, network.create, or network.revoke`
2496
+ );
2380
2497
  }
2498
+ throw new ManifestValidationError(
2499
+ `unknown encryption action ${JSON.stringify(action)}; expected decrypt, network.create, or network.revoke`
2500
+ );
2381
2501
  }
2382
- return {
2383
- success: false,
2384
- status: res.status,
2385
- error: await res.text().catch(() => res.statusText)
2386
- };
2502
+ const dedupedActions = [];
2503
+ const seen = /* @__PURE__ */ new Set();
2504
+ for (const a of normalizedActions) {
2505
+ if (!seen.has(a)) {
2506
+ dedupedActions.push(a);
2507
+ seen.add(a);
2508
+ }
2509
+ }
2510
+ return [
2511
+ {
2512
+ service: ENCRYPTION_PERMISSION_SERVICE,
2513
+ space: ENCRYPTION_MANIFEST_SPACE,
2514
+ path: entry.path,
2515
+ actions: dedupedActions,
2516
+ skipPrefix: true,
2517
+ ...entry.expiry !== void 0 ? { expiry: entry.expiry } : {},
2518
+ ...entry.description !== void 0 ? { description: entry.description } : {}
2519
+ }
2520
+ ];
2387
2521
  }
2388
-
2389
- // src/delegations/DelegationManager.ts
2390
- var DelegationAction = {
2391
- CREATE: "tinycloud.delegation/create",
2392
- REVOKE: "tinycloud.delegation/revoke",
2393
- LIST: "tinycloud.delegation/list",
2394
- GET: "tinycloud.delegation/get",
2395
- CHECK: "tinycloud.delegation/check"
2396
- };
2397
- function createError(code, message, cause, meta) {
2398
- return {
2399
- code,
2400
- message,
2401
- service: "delegation",
2402
- cause,
2403
- meta
2404
- };
2522
+ function expandPermissionEntries(entries) {
2523
+ return entries.flatMap(expandPermissionEntry);
2405
2524
  }
2406
- var DelegationManager = class {
2407
- /**
2408
- * Creates a new DelegationManager instance.
2409
- *
2410
- * @param config - Configuration including hosts, session, and invoke function
2411
- */
2412
- constructor(config) {
2413
- this.hosts = config.hosts;
2414
- this.session = config.session;
2415
- this.invoke = config.invoke;
2416
- this.fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);
2525
+ function applyPrefix(prefix, path, skipPrefix) {
2526
+ if (skipPrefix) {
2527
+ return path;
2417
2528
  }
2418
- /**
2419
- * Updates the session (e.g., after re-authentication).
2420
- *
2421
- * @param session - New session to use for operations
2422
- */
2423
- updateSession(session) {
2424
- this.session = session;
2529
+ if (prefix === "") {
2530
+ return path;
2425
2531
  }
2426
- /**
2427
- * Gets the primary host URL.
2428
- */
2429
- get host() {
2430
- return this.hosts[0];
2532
+ if (path.startsWith("/")) {
2533
+ return `${prefix}${path}`;
2431
2534
  }
2432
- /**
2433
- * Executes an invoke operation against the delegation API.
2434
- */
2435
- async invokeOperation(path, action, body) {
2436
- const headers = this.invoke(this.session, "delegation", path, action);
2437
- return this.fetchFn(`${this.host}/invoke`, {
2438
- method: "POST",
2439
- headers,
2440
- body
2441
- });
2535
+ return `${prefix}/${path}`;
2536
+ }
2537
+ async function loadManifest(url) {
2538
+ const fetchFn = globalThis.fetch;
2539
+ if (typeof fetchFn !== "function") {
2540
+ throw new ManifestValidationError(
2541
+ "loadManifest requires a global fetch; pass the manifest object directly on runtimes without fetch"
2542
+ );
2442
2543
  }
2443
- /**
2444
- * Creates a new delegation.
2445
- *
2446
- * Delegates specific permissions to another DID for a given path.
2447
- * The delegatee can then use these permissions to access resources
2448
- * within the specified scope.
2449
- *
2450
- * @param params - Parameters for the delegation
2451
- * @returns Result containing the created Delegation or an error
2452
- *
2453
- * @example
2454
- * ```typescript
2455
- * const result = await manager.create({
2456
- * delegateDID: bob.did,
2457
- * path: "documents/shared/",
2458
- * actions: ["tinycloud.kv/get", "tinycloud.kv/put"],
2459
- * expiry: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days
2460
- * });
2461
- * ```
2462
- */
2463
- async create(params) {
2464
- if (!params.delegateDID) {
2465
- return {
2466
- ok: false,
2467
- error: createError(
2468
- DelegationErrorCodes.INVALID_INPUT,
2469
- "delegateDID is required"
2470
- )
2471
- };
2472
- }
2473
- if (!params.path) {
2474
- return {
2475
- ok: false,
2476
- error: createError(
2477
- DelegationErrorCodes.INVALID_INPUT,
2478
- "path is required"
2479
- )
2480
- };
2544
+ const res = await fetchFn(url);
2545
+ if (!res.ok) {
2546
+ throw new ManifestValidationError(
2547
+ `failed to fetch manifest from ${url}: HTTP ${res.status}`
2548
+ );
2549
+ }
2550
+ const json = await res.json();
2551
+ return validateManifest(json);
2552
+ }
2553
+ function validateManifest(input) {
2554
+ if (input === null || typeof input !== "object") {
2555
+ throw new ManifestValidationError("manifest must be an object");
2556
+ }
2557
+ const m = input;
2558
+ if (m.manifest_version !== void 0 && m.manifest_version !== DEFAULT_MANIFEST_VERSION) {
2559
+ throw new ManifestValidationError(
2560
+ `manifest.manifest_version must be ${DEFAULT_MANIFEST_VERSION}`
2561
+ );
2562
+ }
2563
+ if (typeof m.app_id !== "string" || m.app_id.length === 0) {
2564
+ throw new ManifestValidationError(
2565
+ "manifest.app_id is required and must be a non-empty string"
2566
+ );
2567
+ }
2568
+ if (typeof m.name !== "string" || m.name.length === 0) {
2569
+ throw new ManifestValidationError(
2570
+ "manifest.name is required and must be a non-empty string"
2571
+ );
2572
+ }
2573
+ if (m.did !== void 0 && (typeof m.did !== "string" || m.did.length === 0)) {
2574
+ throw new ManifestValidationError(
2575
+ "manifest.did must be a non-empty DID string"
2576
+ );
2577
+ }
2578
+ if (m.space !== void 0 && (typeof m.space !== "string" || m.space.length === 0)) {
2579
+ throw new ManifestValidationError(
2580
+ "manifest.space must be a non-empty string"
2581
+ );
2582
+ }
2583
+ if (m.expiry !== void 0) {
2584
+ parseExpiry(m.expiry);
2585
+ }
2586
+ if (m.permissions !== void 0) {
2587
+ if (!Array.isArray(m.permissions)) {
2588
+ throw new ManifestValidationError(
2589
+ "manifest.permissions must be an array"
2590
+ );
2481
2591
  }
2482
- if (!params.actions || params.actions.length === 0) {
2483
- return {
2484
- ok: false,
2485
- error: createError(
2486
- DelegationErrorCodes.INVALID_INPUT,
2487
- "at least one action is required"
2488
- )
2489
- };
2592
+ m.permissions.forEach(
2593
+ (p, i) => validatePermissionEntry(p, `permissions[${i}]`)
2594
+ );
2595
+ }
2596
+ if (m.secrets !== void 0) {
2597
+ validateManifestSecrets(m.secrets);
2598
+ }
2599
+ return m;
2600
+ }
2601
+ function validateManifestSecrets(secrets) {
2602
+ if (secrets === null || typeof secrets !== "object" || Array.isArray(secrets)) {
2603
+ throw new ManifestValidationError("manifest.secrets must be an object");
2604
+ }
2605
+ for (const [name, spec] of Object.entries(secrets)) {
2606
+ if (!import_sdk_services4.SECRET_NAME_RE.test(name)) {
2607
+ throw new ManifestValidationError(
2608
+ `manifest.secrets.${name} must match ${import_sdk_services4.SECRET_NAME_RE.source}`
2609
+ );
2490
2610
  }
2491
2611
  try {
2492
- const body = JSON.stringify({
2493
- delegateDID: params.delegateDID,
2494
- path: params.path,
2495
- actions: params.actions,
2496
- expiry: params.expiry?.toISOString(),
2497
- disableSubDelegation: params.disableSubDelegation ?? false,
2498
- statement: params.statement
2499
- });
2500
- const response = await this.invokeOperation(
2501
- params.path,
2502
- DelegationAction.CREATE,
2503
- body
2612
+ (0, import_sdk_services4.resolveSecretPath)(
2613
+ secretNameFromSpec(name, spec),
2614
+ { scope: secretScopeFromSpec(spec) }
2504
2615
  );
2505
- if (!response.ok) {
2506
- const errorText = await response.text();
2507
- return {
2508
- ok: false,
2509
- error: createError(
2510
- DelegationErrorCodes.CREATION_FAILED,
2511
- `Failed to create delegation: ${response.status} - ${errorText}`,
2512
- void 0,
2513
- { status: response.status, path: params.path }
2514
- )
2515
- };
2516
- }
2517
- const apiResponse = await response.json();
2518
- const delegation = {
2519
- cid: apiResponse.cid ?? "",
2520
- delegateDID: params.delegateDID,
2521
- spaceId: this.session.spaceId,
2522
- path: params.path,
2523
- actions: params.actions,
2524
- expiry: params.expiry ?? new Date(Date.now() + EXPIRY.SHARE_MS),
2525
- isRevoked: false,
2526
- allowSubDelegation: !(params.disableSubDelegation ?? false),
2527
- createdAt: /* @__PURE__ */ new Date()
2528
- };
2529
- return { ok: true, data: delegation };
2530
2616
  } catch (error) {
2531
- if (error instanceof Error && error.name === "AbortError") {
2532
- return {
2533
- ok: false,
2534
- error: createError(
2535
- DelegationErrorCodes.ABORTED,
2536
- "Request aborted",
2537
- error
2538
- )
2539
- };
2617
+ throw new ManifestValidationError(
2618
+ `manifest.secrets.${name}: ${error instanceof Error ? error.message : String(error)}`
2619
+ );
2620
+ }
2621
+ const actions = secretActionsFromSpec(name, spec);
2622
+ if (actions.length === 0) {
2623
+ throw new ManifestValidationError(
2624
+ `manifest.secrets.${name} actions must be non-empty`
2625
+ );
2626
+ }
2627
+ for (const action of actions) {
2628
+ if (typeof action !== "string" || action.length === 0) {
2629
+ throw new ManifestValidationError(
2630
+ `manifest.secrets.${name} actions must be non-empty strings`
2631
+ );
2540
2632
  }
2541
- return {
2542
- ok: false,
2543
- error: createError(
2544
- DelegationErrorCodes.NETWORK_ERROR,
2545
- `Network error during delegation creation: ${String(error)}`,
2546
- error instanceof Error ? error : void 0
2547
- )
2548
- };
2549
2633
  }
2550
- }
2551
- /**
2552
- * Revokes an existing delegation.
2553
- *
2554
- * Once revoked, the delegation can no longer be used to access resources.
2555
- * This also invalidates any sub-delegations derived from this delegation.
2556
- *
2557
- * @param cid - The CID of the delegation to revoke
2558
- * @returns Result indicating success or an error
2559
- *
2560
- * @example
2561
- * ```typescript
2562
- * const result = await manager.revoke("bafy...");
2563
- * if (result.ok) {
2564
- * console.log("Delegation revoked successfully");
2565
- * }
2566
- * ```
2567
- */
2568
- async revoke(cid) {
2569
- if (!cid) {
2570
- return {
2571
- ok: false,
2572
- error: createError(
2573
- DelegationErrorCodes.INVALID_INPUT,
2574
- "cid is required"
2575
- )
2576
- };
2634
+ if (spec !== null && typeof spec === "object" && !Array.isArray(spec) && spec.expiry !== void 0) {
2635
+ parseExpiry(spec.expiry);
2577
2636
  }
2578
- try {
2579
- const body = JSON.stringify({ cid });
2580
- const response = await this.invokeOperation(
2581
- cid,
2582
- DelegationAction.REVOKE,
2583
- body
2584
- );
2585
- if (!response.ok) {
2586
- const errorText = await response.text();
2587
- if (response.status === 404) {
2588
- return {
2589
- ok: false,
2590
- error: createError(
2591
- DelegationErrorCodes.NOT_FOUND,
2592
- `Delegation not found: ${cid}`
2593
- )
2594
- };
2595
- }
2596
- return {
2597
- ok: false,
2598
- error: createError(
2599
- DelegationErrorCodes.REVOCATION_FAILED,
2600
- `Failed to revoke delegation: ${response.status} - ${errorText}`,
2601
- void 0,
2602
- { status: response.status, cid }
2603
- )
2604
- };
2605
- }
2606
- return { ok: true, data: void 0 };
2607
- } catch (error) {
2608
- if (error instanceof Error && error.name === "AbortError") {
2609
- return {
2610
- ok: false,
2611
- error: createError(
2612
- DelegationErrorCodes.ABORTED,
2613
- "Request aborted",
2614
- error
2615
- )
2616
- };
2617
- }
2618
- return {
2619
- ok: false,
2620
- error: createError(
2621
- DelegationErrorCodes.NETWORK_ERROR,
2622
- `Network error during delegation revocation: ${String(error)}`,
2623
- error instanceof Error ? error : void 0
2624
- )
2625
- };
2626
- }
2627
- }
2628
- /**
2629
- * Lists all delegations for the current session's space.
2630
- *
2631
- * Returns both delegations created by the current user (as delegator)
2632
- * and delegations granted to the current user (as delegatee).
2633
- *
2634
- * @returns Result containing an array of Delegations or an error
2635
- *
2636
- * @example
2637
- * ```typescript
2638
- * const result = await manager.list();
2639
- * if (result.ok) {
2640
- * for (const delegation of result.data) {
2641
- * console.log(`${delegation.cid}: ${delegation.path} -> ${delegation.delegateDID}`);
2642
- * }
2643
- * }
2644
- * ```
2645
- */
2646
- async list() {
2647
- try {
2648
- const response = await this.invokeOperation("", DelegationAction.LIST);
2649
- if (!response.ok) {
2650
- const errorText = await response.text();
2651
- return {
2652
- ok: false,
2653
- error: createError(
2654
- DelegationErrorCodes.NETWORK_ERROR,
2655
- `Failed to list delegations: ${response.status} - ${errorText}`,
2656
- void 0,
2657
- { status: response.status }
2658
- )
2659
- };
2660
- }
2661
- const data = await response.json();
2662
- const delegations = data.map((item) => ({
2663
- cid: item.cid,
2664
- delegateDID: item.delegateDID,
2665
- delegatorDID: item.delegatorDID,
2666
- spaceId: item.spaceId,
2667
- path: item.path,
2668
- actions: item.actions,
2669
- expiry: new Date(item.expiry),
2670
- isRevoked: item.isRevoked,
2671
- createdAt: item.createdAt ? new Date(item.createdAt) : void 0,
2672
- parentCid: item.parentCid,
2673
- allowSubDelegation: item.allowSubDelegation
2674
- }));
2675
- return { ok: true, data: delegations };
2676
- } catch (error) {
2677
- if (error instanceof Error && error.name === "AbortError") {
2678
- return {
2679
- ok: false,
2680
- error: createError(
2681
- DelegationErrorCodes.ABORTED,
2682
- "Request aborted",
2683
- error
2684
- )
2685
- };
2686
- }
2687
- return {
2688
- ok: false,
2689
- error: createError(
2690
- DelegationErrorCodes.NETWORK_ERROR,
2691
- `Network error during delegation list: ${String(error)}`,
2692
- error instanceof Error ? error : void 0
2693
- )
2694
- };
2695
- }
2696
- }
2697
- /**
2698
- * Gets the full delegation chain for a given delegation.
2699
- *
2700
- * Returns the chain of delegations from the root (original delegator)
2701
- * to the specified delegation, including all intermediate sub-delegations.
2702
- *
2703
- * @param cid - The CID of the delegation to get the chain for
2704
- * @returns Result containing the DelegationChain or an error
2705
- *
2706
- * @example
2707
- * ```typescript
2708
- * const result = await manager.getChain("bafy...");
2709
- * if (result.ok) {
2710
- * console.log("Chain length:", result.data.length);
2711
- * for (const delegation of result.data) {
2712
- * console.log(`- ${delegation.delegatorDID} -> ${delegation.delegateDID}`);
2713
- * }
2714
- * }
2715
- * ```
2716
- */
2717
- async getChain(cid) {
2718
- if (!cid) {
2719
- return {
2720
- ok: false,
2721
- error: createError(
2722
- DelegationErrorCodes.INVALID_INPUT,
2723
- "cid is required"
2724
- )
2725
- };
2726
- }
2727
- try {
2728
- const body = JSON.stringify({ cid, includeChain: true });
2729
- const response = await this.invokeOperation(
2730
- cid,
2731
- DelegationAction.GET,
2732
- body
2733
- );
2734
- if (!response.ok) {
2735
- const errorText = await response.text();
2736
- if (response.status === 404) {
2737
- return {
2738
- ok: false,
2739
- error: createError(
2740
- DelegationErrorCodes.NOT_FOUND,
2741
- `Delegation not found: ${cid}`
2742
- )
2743
- };
2744
- }
2745
- return {
2746
- ok: false,
2747
- error: createError(
2748
- DelegationErrorCodes.NETWORK_ERROR,
2749
- `Failed to get delegation chain: ${response.status} - ${errorText}`,
2750
- void 0,
2751
- { status: response.status, cid }
2752
- )
2753
- };
2754
- }
2755
- const data = await response.json();
2756
- const chain = data.chain.map((item) => ({
2757
- cid: item.cid,
2758
- delegateDID: item.delegateDID,
2759
- delegatorDID: item.delegatorDID,
2760
- spaceId: item.spaceId,
2761
- path: item.path,
2762
- actions: item.actions,
2763
- expiry: new Date(item.expiry),
2764
- isRevoked: item.isRevoked,
2765
- createdAt: item.createdAt ? new Date(item.createdAt) : void 0,
2766
- parentCid: item.parentCid,
2767
- allowSubDelegation: item.allowSubDelegation
2768
- }));
2769
- return { ok: true, data: chain };
2770
- } catch (error) {
2771
- if (error instanceof Error && error.name === "AbortError") {
2772
- return {
2773
- ok: false,
2774
- error: createError(
2775
- DelegationErrorCodes.ABORTED,
2776
- "Request aborted",
2777
- error
2778
- )
2779
- };
2780
- }
2781
- return {
2782
- ok: false,
2783
- error: createError(
2784
- DelegationErrorCodes.NETWORK_ERROR,
2785
- `Network error during chain retrieval: ${String(error)}`,
2786
- error instanceof Error ? error : void 0
2787
- )
2788
- };
2789
- }
2790
- }
2791
- /**
2792
- * Checks if the current session has permission for a given path and action.
2793
- *
2794
- * This can be used to verify permissions before attempting an operation,
2795
- * or to implement custom access control logic.
2796
- *
2797
- * @param path - The resource path to check
2798
- * @param action - The action to check (e.g., "tinycloud.kv/get")
2799
- * @returns Result containing a boolean indicating permission or an error
2800
- *
2801
- * @example
2802
- * ```typescript
2803
- * const result = await manager.checkPermission("documents/private/", "tinycloud.kv/put");
2804
- * if (result.ok && result.data) {
2805
- * console.log("Permission granted");
2806
- * } else {
2807
- * console.log("Permission denied");
2808
- * }
2809
- * ```
2810
- */
2811
- async checkPermission(path, action) {
2812
- if (!path) {
2813
- return {
2814
- ok: false,
2815
- error: createError(
2816
- DelegationErrorCodes.INVALID_INPUT,
2817
- "path is required"
2818
- )
2819
- };
2820
- }
2821
- if (!action) {
2822
- return {
2823
- ok: false,
2824
- error: createError(
2825
- DelegationErrorCodes.INVALID_INPUT,
2826
- "action is required"
2827
- )
2828
- };
2829
- }
2830
- try {
2831
- const body = JSON.stringify({ path, action });
2832
- const response = await this.invokeOperation(
2833
- path,
2834
- DelegationAction.CHECK,
2835
- body
2836
- );
2837
- if (!response.ok) {
2838
- if (response.status === 403) {
2839
- return { ok: true, data: false };
2840
- }
2841
- const errorText = await response.text();
2842
- return {
2843
- ok: false,
2844
- error: createError(
2845
- DelegationErrorCodes.NETWORK_ERROR,
2846
- `Failed to check permission: ${response.status} - ${errorText}`,
2847
- void 0,
2848
- { status: response.status, path, action }
2849
- )
2850
- };
2851
- }
2852
- const data = await response.json();
2853
- return { ok: true, data: data.allowed };
2854
- } catch (error) {
2855
- if (error instanceof Error && error.name === "AbortError") {
2856
- return {
2857
- ok: false,
2858
- error: createError(
2859
- DelegationErrorCodes.ABORTED,
2860
- "Request aborted",
2861
- error
2862
- )
2863
- };
2864
- }
2865
- return {
2866
- ok: false,
2867
- error: createError(
2868
- DelegationErrorCodes.NETWORK_ERROR,
2869
- `Network error during permission check: ${String(error)}`,
2870
- error instanceof Error ? error : void 0
2871
- )
2872
- };
2873
- }
2874
- }
2875
- };
2876
-
2877
- // src/delegations/SharingService.schema.ts
2878
- var import_zod5 = require("zod");
2879
- var EncodedShareDataSchema = import_zod5.z.object({
2880
- /** Private key in JWK format (must include d parameter) */
2881
- key: JWKSchema.refine(
2882
- (jwk) => typeof jwk.d === "string" && jwk.d.length > 0,
2883
- { message: "JWK must include private key (d parameter)" }
2884
- ),
2885
- /** DID of the key */
2886
- keyDid: import_zod5.z.string().min(1, "keyDid is required"),
2887
- /** The delegation granting access */
2888
- delegation: DelegationSchema,
2889
- /** Resource path this link grants access to */
2890
- path: import_zod5.z.string().min(1, "path is required"),
2891
- /** TinyCloud host URL */
2892
- host: import_zod5.z.string().url("host must be a valid URL"),
2893
- /** Space ID */
2894
- spaceId: import_zod5.z.string().min(1, "spaceId is required"),
2895
- /** Schema version (must be 1) */
2896
- version: import_zod5.z.literal(1)
2897
- });
2898
- var ReceiveOptionsSchema = import_zod5.z.object({
2899
- /**
2900
- * Whether to automatically create a sub-delegation to the current session key.
2901
- * Default: true
2902
- */
2903
- autoSubdelegate: import_zod5.z.boolean().optional(),
2904
- /**
2905
- * Whether to use the current session key for operations (requires autoSubdelegate).
2906
- * Default: true
2907
- */
2908
- useSessionKey: import_zod5.z.boolean().optional(),
2909
- /**
2910
- * Ingestion options passed to CapabilityKeyRegistry.
2911
- */
2912
- ingestOptions: IngestOptionsSchema.optional()
2913
- });
2914
- var SharingServiceConfigSchema = import_zod5.z.object({
2915
- /** TinyCloud host URLs */
2916
- hosts: import_zod5.z.array(import_zod5.z.string().url()).min(1, "At least one host URL is required"),
2917
- /**
2918
- * Active session for authentication.
2919
- * Required for generate(), optional for receive().
2920
- */
2921
- session: import_zod5.z.unknown().refine(
2922
- (val) => val === void 0 || val !== null && typeof val === "object",
2923
- { message: "Expected a ServiceSession object or undefined" }
2924
- ).optional(),
2925
- /** Platform-specific invoke function */
2926
- invoke: import_zod5.z.unknown().refine((val) => typeof val === "function", {
2927
- message: "Expected an invoke function"
2928
- }),
2929
- /** Optional custom fetch implementation */
2930
- fetch: import_zod5.z.unknown().refine(
2931
- (val) => val === void 0 || typeof val === "function",
2932
- { message: "Expected a fetch function or undefined" }
2933
- ).optional(),
2934
- /** Key provider for cryptographic operations */
2935
- keyProvider: KeyProviderSchema,
2936
- /** Capability key registry for key/delegation management */
2937
- registry: import_zod5.z.unknown().refine(
2938
- (val) => val !== null && typeof val === "object",
2939
- { message: "Expected an ICapabilityKeyRegistry object" }
2940
- ),
2941
- /**
2942
- * Delegation manager for creating delegations.
2943
- * Required for generate(), optional for receive().
2944
- */
2945
- delegationManager: import_zod5.z.unknown().refine(
2946
- (val) => val === void 0 || val !== null && typeof val === "object",
2947
- { message: "Expected a DelegationManager object or undefined" }
2948
- ).optional(),
2949
- /** Factory for creating KV service instances */
2950
- createKVService: import_zod5.z.unknown().refine(
2951
- (val) => typeof val === "function",
2952
- { message: "Expected a createKVService factory function" }
2953
- ),
2954
- /** Base URL for sharing links (e.g., "https://share.myapp.com") */
2955
- baseUrl: import_zod5.z.string().optional(),
2956
- /**
2957
- * Custom delegation creation function.
2958
- */
2959
- createDelegation: import_zod5.z.unknown().refine((val) => val === void 0 || typeof val === "function", {
2960
- message: "Expected a createDelegation function or undefined"
2961
- }).optional(),
2962
- /**
2963
- * WASM function for client-side delegation creation.
2964
- */
2965
- createDelegationWasm: import_zod5.z.unknown().refine((val) => val === void 0 || typeof val === "function", {
2966
- message: "Expected a createDelegationWasm function or undefined"
2967
- }).optional(),
2968
- /**
2969
- * Path prefix for KV operations.
2970
- */
2971
- pathPrefix: import_zod5.z.string().optional(),
2972
- /**
2973
- * Session expiry time.
2974
- */
2975
- sessionExpiry: import_zod5.z.date().optional(),
2976
- /**
2977
- * Callback to create a DIRECT delegation from wallet to share key.
2978
- * This is the preferred method for long-lived share links because it
2979
- * bypasses the session delegation chain entirely.
2980
- */
2981
- onRootDelegationNeeded: import_zod5.z.unknown().refine((val) => val === void 0 || typeof val === "function", {
2982
- message: "Expected an onRootDelegationNeeded function or undefined"
2983
- }).optional()
2984
- });
2985
- function validateEncodedShareData(data) {
2986
- const result = EncodedShareDataSchema.safeParse(data);
2987
- if (!result.success) {
2988
- return {
2989
- ok: false,
2990
- error: {
2991
- code: DelegationErrorCodes.VALIDATION_ERROR,
2992
- message: `Invalid share data: ${result.error.message}`,
2993
- service: "delegation",
2994
- meta: { issues: result.error.issues }
2995
- }
2996
- };
2997
2637
  }
2998
- return { ok: true, data: result.data };
2999
2638
  }
3000
-
3001
- // src/manifest.ts
3002
- var import_ms = __toESM(require("ms"), 1);
3003
- var import_sdk_services4 = require("@tinycloud/sdk-services");
3004
- var ManifestValidationError = class extends Error {
3005
- constructor(message) {
3006
- super(`Manifest validation failed: ${message}`);
3007
- this.name = "ManifestValidationError";
3008
- }
3009
- };
3010
- var DEFAULT_EXPIRY = "30d";
3011
- var DEFAULT_DEFAULTS = true;
3012
- var DEFAULT_MANIFEST_VERSION = 1;
3013
- var DEFAULT_MANIFEST_SPACE = "applications";
3014
- var ACCOUNT_REGISTRY_SPACE = "account";
3015
- var ACCOUNT_REGISTRY_PATH = "applications/";
3016
- var SECRETS_SPACE = "secrets";
3017
- var VAULT_PERMISSION_SERVICE = "tinycloud.vault";
3018
- var SERVICE_SHORT_TO_LONG = Object.freeze({
3019
- kv: "tinycloud.kv",
3020
- sql: "tinycloud.sql",
3021
- duckdb: "tinycloud.duckdb",
3022
- capabilities: "tinycloud.capabilities",
3023
- hooks: "tinycloud.hooks",
3024
- encryption: "tinycloud.encryption"
3025
- });
3026
- var ENCRYPTION_PERMISSION_SERVICE = "tinycloud.encryption";
3027
- var ENCRYPTION_MANIFEST_SPACE = "encryption";
3028
- var SERVICE_LONG_TO_SHORT = Object.freeze(
3029
- Object.fromEntries(
3030
- Object.entries(SERVICE_SHORT_TO_LONG).map(([s, l]) => [l, s])
3031
- )
3032
- );
3033
- var DEFAULT_STANDARD_ENTRIES = [
3034
- {
3035
- service: "tinycloud.kv",
3036
- space: DEFAULT_MANIFEST_SPACE,
3037
- path: "/",
3038
- actions: ["get", "put", "del", "list", "metadata"]
3039
- },
3040
- {
3041
- service: "tinycloud.sql",
3042
- space: DEFAULT_MANIFEST_SPACE,
3043
- path: "/",
3044
- actions: ["read", "write"]
2639
+ function validatePermissionEntry(p, path) {
2640
+ if (p === null || typeof p !== "object") {
2641
+ throw new ManifestValidationError(`${path} must be an object`);
3045
2642
  }
3046
- ];
3047
- var DEFAULT_ADMIN_ENTRIES = [
3048
- {
3049
- service: "tinycloud.kv",
3050
- space: DEFAULT_MANIFEST_SPACE,
3051
- path: "/",
3052
- actions: ["get", "put", "del", "list", "metadata"]
3053
- },
3054
- {
3055
- service: "tinycloud.sql",
3056
- space: DEFAULT_MANIFEST_SPACE,
3057
- path: "/",
3058
- actions: ["read", "write", "ddl"]
2643
+ const entry = p;
2644
+ if (typeof entry.service !== "string" || entry.service.length === 0) {
2645
+ throw new ManifestValidationError(`${path}.service is required`);
3059
2646
  }
3060
- ];
3061
- var DEFAULT_ALL_ENTRIES = [
3062
- {
3063
- service: "tinycloud.kv",
3064
- space: DEFAULT_MANIFEST_SPACE,
3065
- path: "/",
3066
- actions: ["get", "put", "del", "list", "metadata"]
3067
- },
3068
- {
3069
- service: "tinycloud.sql",
3070
- space: DEFAULT_MANIFEST_SPACE,
3071
- path: "/",
3072
- actions: ["read", "write", "ddl"]
3073
- },
3074
- {
3075
- service: "tinycloud.duckdb",
3076
- space: DEFAULT_MANIFEST_SPACE,
3077
- path: "/",
3078
- actions: ["read", "write"]
2647
+ if (entry.space !== void 0 && (typeof entry.space !== "string" || entry.space.length === 0)) {
2648
+ throw new ManifestValidationError(
2649
+ `${path}.space must be a non-empty string`
2650
+ );
3079
2651
  }
3080
- ];
3081
- function parseExpiry(duration) {
3082
- if (typeof duration !== "string" || duration.length === 0) {
2652
+ if (typeof entry.path !== "string") {
3083
2653
  throw new ManifestValidationError(
3084
- `expiry must be a non-empty duration string (got ${JSON.stringify(duration)})`
2654
+ `${path}.path is required (use "" or "/" for root)`
3085
2655
  );
3086
2656
  }
3087
- const parsed = (0, import_ms.default)(duration);
3088
- if (typeof parsed !== "number" || !Number.isFinite(parsed) || parsed <= 0) {
2657
+ if (!Array.isArray(entry.actions) || entry.actions.length === 0) {
3089
2658
  throw new ManifestValidationError(
3090
- `invalid expiry duration: ${JSON.stringify(duration)}`
2659
+ `${path}.actions must be a non-empty array`
3091
2660
  );
3092
2661
  }
3093
- return parsed;
3094
- }
3095
- function expandActionShortNames(service, actions) {
3096
- return actions.map((a) => {
3097
- if (a.includes("/")) {
3098
- return a;
2662
+ for (const action of entry.actions) {
2663
+ if (typeof action !== "string" || action.length === 0) {
2664
+ throw new ManifestValidationError(
2665
+ `${path}.actions must contain non-empty strings`
2666
+ );
3099
2667
  }
3100
- return `${service}/${a}`;
3101
- });
2668
+ if (entry.service === VAULT_PERMISSION_SERVICE) {
2669
+ vaultActionExpansion(action);
2670
+ }
2671
+ }
2672
+ if (entry.expiry !== void 0) {
2673
+ parseExpiry(entry.expiry);
2674
+ }
3102
2675
  }
3103
- function expandPermissionEntry(entry) {
3104
- if (entry.service === ENCRYPTION_PERMISSION_SERVICE) {
3105
- return expandEncryptionPermissionEntry(entry);
2676
+ function normalizeDefaults(value) {
2677
+ if (value === void 0) {
2678
+ return DEFAULT_DEFAULTS;
3106
2679
  }
3107
- if (entry.service !== VAULT_PERMISSION_SERVICE) {
3108
- return [
3109
- {
3110
- ...entry,
3111
- actions: expandActionShortNames(entry.service, entry.actions)
3112
- }
3113
- ];
2680
+ if (typeof value === "boolean") {
2681
+ return value;
3114
2682
  }
3115
- return expandVaultPermissionEntry(entry);
2683
+ if (typeof value !== "string") {
2684
+ return true;
2685
+ }
2686
+ const normalized = value.trim().toLowerCase();
2687
+ if (normalized === "admin" || normalized === "all") {
2688
+ return normalized;
2689
+ }
2690
+ return true;
3116
2691
  }
3117
- function expandEncryptionPermissionEntry(entry) {
3118
- if (typeof entry.path !== "string" || !entry.path.startsWith("urn:tinycloud:encryption:")) {
3119
- throw new ManifestValidationError(
3120
- `tinycloud.encryption entries require path to be a networkId URN (got ${JSON.stringify(entry.path)})`
3121
- );
2692
+ function defaultEntriesForTier(tier) {
2693
+ if (tier === false) {
2694
+ return [];
3122
2695
  }
3123
- const normalizedActions = [];
3124
- for (const action of entry.actions) {
3125
- if (action === "decrypt" || action === "tinycloud.encryption/decrypt") {
3126
- normalizedActions.push("tinycloud.encryption/decrypt");
3127
- continue;
2696
+ const source = tier === "admin" ? DEFAULT_ADMIN_ENTRIES : tier === "all" ? DEFAULT_ALL_ENTRIES : DEFAULT_STANDARD_ENTRIES;
2697
+ return source.map((e) => ({
2698
+ service: e.service,
2699
+ space: e.space,
2700
+ path: e.path,
2701
+ actions: [...e.actions],
2702
+ ...e.skipPrefix !== void 0 ? { skipPrefix: e.skipPrefix } : {}
2703
+ }));
2704
+ }
2705
+ function resolveManifest(input) {
2706
+ const manifest = validateManifest(input);
2707
+ const prefix = manifest.prefix !== void 0 ? manifest.prefix : manifest.app_id;
2708
+ const space = manifest.space ?? DEFAULT_MANIFEST_SPACE;
2709
+ const expiryMs = parseExpiry(manifest.expiry ?? DEFAULT_EXPIRY);
2710
+ const includePublicSpace = manifest.includePublicSpace ?? true;
2711
+ const tier = normalizeDefaults(manifest.defaults);
2712
+ const defaultEntries = defaultEntriesForTier(tier);
2713
+ const explicitEntries = manifest.permissions ?? [];
2714
+ const secretEntries = secretEntriesForManifest(manifest.secrets);
2715
+ const allEntries = [
2716
+ ...defaultEntries,
2717
+ ...explicitEntries,
2718
+ ...secretEntries
2719
+ ];
2720
+ const resources = withCapabilitiesReadForSpaces(
2721
+ allEntries.flatMap((entry) => resolveEntry(entry, prefix, expiryMs, space))
2722
+ );
2723
+ const additionalDelegates = manifest.did === void 0 ? [] : [
2724
+ {
2725
+ did: manifest.did,
2726
+ name: manifest.name,
2727
+ expiryMs,
2728
+ permissions: resources.map(cloneResourceCapability)
3128
2729
  }
3129
- if (action === "network.create" || action === "tinycloud.encryption/network.create") {
3130
- normalizedActions.push("tinycloud.encryption/network.create");
3131
- continue;
2730
+ ];
2731
+ return {
2732
+ app_id: manifest.app_id,
2733
+ ...manifest.did !== void 0 ? { did: manifest.did } : {},
2734
+ space,
2735
+ resources,
2736
+ expiryMs,
2737
+ includePublicSpace,
2738
+ additionalDelegates
2739
+ };
2740
+ }
2741
+ function normalizeSecretActions(actions) {
2742
+ const out = [];
2743
+ const seen = /* @__PURE__ */ new Set();
2744
+ const add = (action) => {
2745
+ if (!seen.has(action)) {
2746
+ out.push(action);
2747
+ seen.add(action);
3132
2748
  }
3133
- if (action === "network.revoke" || action === "tinycloud.encryption/network.revoke") {
3134
- normalizedActions.push("tinycloud.encryption/network.revoke");
2749
+ };
2750
+ for (const action of actions) {
2751
+ if (action === "read") {
2752
+ add("get");
3135
2753
  continue;
3136
2754
  }
3137
- if (action.includes("/")) {
3138
- throw new ManifestValidationError(
3139
- `unknown encryption action ${JSON.stringify(action)}; expected decrypt, network.create, or network.revoke`
3140
- );
2755
+ if (action === "write") {
2756
+ add("put");
2757
+ continue;
3141
2758
  }
3142
- throw new ManifestValidationError(
3143
- `unknown encryption action ${JSON.stringify(action)}; expected decrypt, network.create, or network.revoke`
3144
- );
3145
- }
3146
- const dedupedActions = [];
3147
- const seen = /* @__PURE__ */ new Set();
3148
- for (const a of normalizedActions) {
3149
- if (!seen.has(a)) {
3150
- dedupedActions.push(a);
3151
- seen.add(a);
2759
+ if (action === "delete") {
2760
+ add("del");
2761
+ continue;
3152
2762
  }
3153
- }
3154
- return [
3155
- {
3156
- service: ENCRYPTION_PERMISSION_SERVICE,
3157
- space: ENCRYPTION_MANIFEST_SPACE,
3158
- path: entry.path,
3159
- actions: dedupedActions,
3160
- skipPrefix: true,
3161
- ...entry.expiry !== void 0 ? { expiry: entry.expiry } : {},
3162
- ...entry.description !== void 0 ? { description: entry.description } : {}
2763
+ if (action === "get" || action === "put" || action === "del" || action === "list" || action === "metadata") {
2764
+ add(action);
2765
+ continue;
2766
+ }
2767
+ if (action === "tinycloud.kv/get" || action === "tinycloud.kv/put" || action === "tinycloud.kv/del" || action === "tinycloud.kv/list" || action === "tinycloud.kv/metadata") {
2768
+ add(action);
2769
+ continue;
3163
2770
  }
3164
- ];
3165
- }
3166
- function expandPermissionEntries(entries) {
3167
- return entries.flatMap(expandPermissionEntry);
3168
- }
3169
- function applyPrefix(prefix, path, skipPrefix) {
3170
- if (skipPrefix) {
3171
- return path;
3172
- }
3173
- if (prefix === "") {
3174
- return path;
3175
- }
3176
- if (path.startsWith("/")) {
3177
- return `${prefix}${path}`;
3178
- }
3179
- return `${prefix}/${path}`;
3180
- }
3181
- async function loadManifest(url) {
3182
- const fetchFn = globalThis.fetch;
3183
- if (typeof fetchFn !== "function") {
3184
- throw new ManifestValidationError(
3185
- "loadManifest requires a global fetch; pass the manifest object directly on runtimes without fetch"
3186
- );
3187
- }
3188
- const res = await fetchFn(url);
3189
- if (!res.ok) {
3190
2771
  throw new ManifestValidationError(
3191
- `failed to fetch manifest from ${url}: HTTP ${res.status}`
2772
+ `unknown secret action ${JSON.stringify(action)}; expected read, write, delete, list, or metadata`
3192
2773
  );
3193
2774
  }
3194
- const json = await res.json();
3195
- return validateManifest(json);
2775
+ return out;
3196
2776
  }
3197
- function validateManifest(input) {
3198
- if (input === null || typeof input !== "object") {
3199
- throw new ManifestValidationError("manifest must be an object");
2777
+ function secretNameFromSpec(fallbackName, spec) {
2778
+ if (spec !== null && typeof spec === "object" && !Array.isArray(spec)) {
2779
+ return spec.name ?? fallbackName;
3200
2780
  }
3201
- const m = input;
3202
- if (m.manifest_version !== void 0 && m.manifest_version !== DEFAULT_MANIFEST_VERSION) {
3203
- throw new ManifestValidationError(
3204
- `manifest.manifest_version must be ${DEFAULT_MANIFEST_VERSION}`
3205
- );
2781
+ return fallbackName;
2782
+ }
2783
+ function secretScopeFromSpec(spec) {
2784
+ if (spec !== null && typeof spec === "object" && !Array.isArray(spec)) {
2785
+ return spec.scope;
3206
2786
  }
3207
- if (typeof m.app_id !== "string" || m.app_id.length === 0) {
3208
- throw new ManifestValidationError(
3209
- "manifest.app_id is required and must be a non-empty string"
3210
- );
2787
+ return void 0;
2788
+ }
2789
+ function secretActionsFromSpec(name, spec) {
2790
+ if (spec === true) {
2791
+ return ["read"];
3211
2792
  }
3212
- if (typeof m.name !== "string" || m.name.length === 0) {
3213
- throw new ManifestValidationError(
3214
- "manifest.name is required and must be a non-empty string"
3215
- );
2793
+ if (typeof spec === "string") {
2794
+ return [spec];
3216
2795
  }
3217
- if (m.did !== void 0 && (typeof m.did !== "string" || m.did.length === 0)) {
3218
- throw new ManifestValidationError(
3219
- "manifest.did must be a non-empty DID string"
3220
- );
2796
+ if (Array.isArray(spec)) {
2797
+ return spec;
3221
2798
  }
3222
- if (m.space !== void 0 && (typeof m.space !== "string" || m.space.length === 0)) {
2799
+ if (spec === null || typeof spec !== "object") {
3223
2800
  throw new ManifestValidationError(
3224
- "manifest.space must be a non-empty string"
2801
+ `manifest.secrets.${name} must be true, a string action, an actions array, or an object`
3225
2802
  );
3226
2803
  }
3227
- if (m.expiry !== void 0) {
3228
- parseExpiry(m.expiry);
2804
+ if (spec.actions === void 0) {
2805
+ return ["read"];
3229
2806
  }
3230
- if (m.permissions !== void 0) {
3231
- if (!Array.isArray(m.permissions)) {
3232
- throw new ManifestValidationError(
3233
- "manifest.permissions must be an array"
3234
- );
3235
- }
3236
- m.permissions.forEach(
3237
- (p, i) => validatePermissionEntry(p, `permissions[${i}]`)
3238
- );
2807
+ if (typeof spec.actions === "string") {
2808
+ return [spec.actions];
3239
2809
  }
3240
- if (m.secrets !== void 0) {
3241
- validateManifestSecrets(m.secrets);
2810
+ if (Array.isArray(spec.actions)) {
2811
+ return spec.actions;
3242
2812
  }
3243
- return m;
2813
+ throw new ManifestValidationError(
2814
+ `manifest.secrets.${name}.actions must be a string or array`
2815
+ );
3244
2816
  }
3245
- function validateManifestSecrets(secrets) {
3246
- if (secrets === null || typeof secrets !== "object" || Array.isArray(secrets)) {
3247
- throw new ManifestValidationError("manifest.secrets must be an object");
2817
+ function secretEntriesForManifest(secrets) {
2818
+ if (secrets === void 0) {
2819
+ return [];
3248
2820
  }
2821
+ const entries = [];
3249
2822
  for (const [name, spec] of Object.entries(secrets)) {
3250
- if (!import_sdk_services4.SECRET_NAME_RE.test(name)) {
3251
- throw new ManifestValidationError(
3252
- `manifest.secrets.${name} must match ${import_sdk_services4.SECRET_NAME_RE.source}`
3253
- );
3254
- }
3255
- try {
3256
- (0, import_sdk_services4.resolveSecretPath)(
3257
- secretNameFromSpec(name, spec),
3258
- { scope: secretScopeFromSpec(spec) }
3259
- );
3260
- } catch (error) {
3261
- throw new ManifestValidationError(
3262
- `manifest.secrets.${name}: ${error instanceof Error ? error.message : String(error)}`
3263
- );
3264
- }
3265
2823
  const actions = secretActionsFromSpec(name, spec);
3266
- if (actions.length === 0) {
3267
- throw new ManifestValidationError(
3268
- `manifest.secrets.${name} actions must be non-empty`
3269
- );
3270
- }
3271
- for (const action of actions) {
3272
- if (typeof action !== "string" || action.length === 0) {
3273
- throw new ManifestValidationError(
3274
- `manifest.secrets.${name} actions must be non-empty strings`
3275
- );
2824
+ const secretPath = (0, import_sdk_services4.resolveSecretPath)(
2825
+ secretNameFromSpec(name, spec),
2826
+ { scope: secretScopeFromSpec(spec) }
2827
+ );
2828
+ const extra = spec !== true && typeof spec === "object" && !Array.isArray(spec) ? spec : {};
2829
+ entries.push({
2830
+ service: VAULT_PERMISSION_SERVICE,
2831
+ space: SECRETS_SPACE,
2832
+ path: secretPath.vaultKey,
2833
+ actions: normalizeSecretActions(actions),
2834
+ skipPrefix: true,
2835
+ ...extra.expiry !== void 0 ? { expiry: extra.expiry } : {},
2836
+ ...extra.description !== void 0 ? { description: extra.description } : {}
2837
+ });
2838
+ }
2839
+ return entries;
2840
+ }
2841
+ function resolveEntry(entry, prefix, _inheritedExpiryMs, inheritedSpace) {
2842
+ const skipPrefixForEntry = entry.skipPrefix === true || entry.service === ENCRYPTION_PERMISSION_SERVICE;
2843
+ const resolvedPath = applyPrefix(prefix, entry.path, skipPrefixForEntry);
2844
+ const entryExpiryMs = entry.expiry !== void 0 ? parseExpiry(entry.expiry) : void 0;
2845
+ return expandPermissionEntry({
2846
+ ...entry,
2847
+ space: entry.space ?? inheritedSpace,
2848
+ path: resolvedPath,
2849
+ skipPrefix: true
2850
+ }).map((expanded) => ({
2851
+ service: expanded.service,
2852
+ space: expanded.space ?? inheritedSpace,
2853
+ path: expanded.path,
2854
+ actions: expanded.actions,
2855
+ // Only populate `expiryMs` when the entry had its own expiry override.
2856
+ // When absent, callers use the parent (delegation or manifest) expiry
2857
+ // which is carried on ResolvedDelegate.expiryMs / ResolvedCapabilities.expiryMs.
2858
+ ...entryExpiryMs !== void 0 ? { expiryMs: entryExpiryMs } : {},
2859
+ ...entry.description !== void 0 ? { description: entry.description } : {}
2860
+ }));
2861
+ }
2862
+ function expandVaultPermissionEntry(entry) {
2863
+ const byBase = /* @__PURE__ */ new Map();
2864
+ for (const action of entry.actions) {
2865
+ const expansion = vaultActionExpansion(action);
2866
+ for (const base of expansion.bases) {
2867
+ const actions = byBase.get(base) ?? [];
2868
+ if (!actions.includes(expansion.action)) {
2869
+ actions.push(expansion.action);
3276
2870
  }
3277
- }
3278
- if (spec !== null && typeof spec === "object" && !Array.isArray(spec) && spec.expiry !== void 0) {
3279
- parseExpiry(spec.expiry);
2871
+ byBase.set(base, actions);
3280
2872
  }
3281
2873
  }
2874
+ return [...byBase.entries()].map(([base, actions]) => ({
2875
+ ...entry,
2876
+ service: "tinycloud.kv",
2877
+ path: vaultKVPath(base, entry.path),
2878
+ actions,
2879
+ skipPrefix: true
2880
+ }));
3282
2881
  }
3283
- function validatePermissionEntry(p, path) {
3284
- if (p === null || typeof p !== "object") {
3285
- throw new ManifestValidationError(`${path} must be an object`);
2882
+ function vaultActionExpansion(action) {
2883
+ const normalized = normalizeVaultAction(action);
2884
+ if (normalized === "read" || normalized === "get") {
2885
+ return { bases: ["vault"], action: "tinycloud.kv/get" };
3286
2886
  }
3287
- const entry = p;
3288
- if (typeof entry.service !== "string" || entry.service.length === 0) {
3289
- throw new ManifestValidationError(`${path}.service is required`);
2887
+ if (normalized === "write" || normalized === "put") {
2888
+ return { bases: ["vault"], action: "tinycloud.kv/put" };
3290
2889
  }
3291
- if (entry.space !== void 0 && (typeof entry.space !== "string" || entry.space.length === 0)) {
3292
- throw new ManifestValidationError(
3293
- `${path}.space must be a non-empty string`
3294
- );
2890
+ if (normalized === "delete" || normalized === "del") {
2891
+ return { bases: ["vault"], action: "tinycloud.kv/del" };
3295
2892
  }
3296
- if (typeof entry.path !== "string") {
3297
- throw new ManifestValidationError(
3298
- `${path}.path is required (use "" or "/" for root)`
3299
- );
2893
+ if (normalized === "list") {
2894
+ return { bases: ["vault"], action: "tinycloud.kv/list" };
3300
2895
  }
3301
- if (!Array.isArray(entry.actions) || entry.actions.length === 0) {
2896
+ if (normalized === "head") {
2897
+ return { bases: ["vault"], action: "tinycloud.kv/get" };
2898
+ }
2899
+ if (normalized === "metadata") {
2900
+ return { bases: ["vault"], action: "tinycloud.kv/metadata" };
2901
+ }
2902
+ throw new ManifestValidationError(
2903
+ `unknown vault action ${JSON.stringify(action)}; expected read, write, delete, get, put, del, list, head, or metadata`
2904
+ );
2905
+ }
2906
+ function normalizeVaultAction(action) {
2907
+ if (action.startsWith(`${VAULT_PERMISSION_SERVICE}/`)) {
2908
+ return action.slice(`${VAULT_PERMISSION_SERVICE}/`.length);
2909
+ }
2910
+ if (action.startsWith("tinycloud.kv/")) {
2911
+ return action.slice("tinycloud.kv/".length);
2912
+ }
2913
+ if (action.includes("/")) {
3302
2914
  throw new ManifestValidationError(
3303
- `${path}.actions must be a non-empty array`
2915
+ `unknown vault action ${JSON.stringify(action)}; expected a tinycloud.vault or tinycloud.kv action`
3304
2916
  );
3305
2917
  }
3306
- for (const action of entry.actions) {
3307
- if (typeof action !== "string" || action.length === 0) {
3308
- throw new ManifestValidationError(
3309
- `${path}.actions must contain non-empty strings`
3310
- );
2918
+ return action;
2919
+ }
2920
+ function vaultKVPath(base, path) {
2921
+ const normalized = path.startsWith("/") ? path.slice(1) : path;
2922
+ return `${base}/${normalized}`;
2923
+ }
2924
+ function cloneResourceCapability(entry) {
2925
+ return {
2926
+ service: entry.service,
2927
+ space: entry.space,
2928
+ path: entry.path,
2929
+ actions: [...entry.actions],
2930
+ ...entry.expiryMs !== void 0 ? { expiryMs: entry.expiryMs } : {},
2931
+ ...entry.description !== void 0 ? { description: entry.description } : {}
2932
+ };
2933
+ }
2934
+ function clonePermissionEntry(entry) {
2935
+ return {
2936
+ service: entry.service,
2937
+ ...entry.space !== void 0 ? { space: entry.space } : {},
2938
+ path: entry.path,
2939
+ actions: [...entry.actions],
2940
+ ...entry.skipPrefix !== void 0 ? { skipPrefix: entry.skipPrefix } : {},
2941
+ ...entry.expiry !== void 0 ? { expiry: entry.expiry } : {},
2942
+ ...entry.description !== void 0 ? { description: entry.description } : {}
2943
+ };
2944
+ }
2945
+ function dedupeResources(resources) {
2946
+ const byKey = /* @__PURE__ */ new Map();
2947
+ for (const resource of resources) {
2948
+ const key = `${resource.service}\0${resource.space}\0${resource.path}\0${resource.expiryMs ?? ""}`;
2949
+ const existing = byKey.get(key);
2950
+ if (existing === void 0) {
2951
+ byKey.set(key, cloneResourceCapability(resource));
2952
+ continue;
3311
2953
  }
3312
- if (entry.service === VAULT_PERMISSION_SERVICE) {
3313
- vaultActionExpansion(action);
2954
+ const seen = new Set(existing.actions);
2955
+ for (const action of resource.actions) {
2956
+ if (!seen.has(action)) {
2957
+ existing.actions.push(action);
2958
+ seen.add(action);
2959
+ }
2960
+ }
2961
+ if (existing.description === void 0 && resource.description !== void 0) {
2962
+ existing.description = resource.description;
3314
2963
  }
3315
2964
  }
3316
- if (entry.expiry !== void 0) {
3317
- parseExpiry(entry.expiry);
3318
- }
2965
+ return [...byKey.values()];
3319
2966
  }
3320
- function normalizeDefaults(value) {
3321
- if (value === void 0) {
3322
- return DEFAULT_DEFAULTS;
3323
- }
3324
- if (typeof value === "boolean") {
3325
- return value;
3326
- }
3327
- if (typeof value !== "string") {
3328
- return true;
3329
- }
3330
- const normalized = value.trim().toLowerCase();
3331
- if (normalized === "admin" || normalized === "all") {
3332
- return normalized;
3333
- }
3334
- return true;
2967
+ function capabilitiesReadPermission(space) {
2968
+ return {
2969
+ service: "tinycloud.capabilities",
2970
+ space,
2971
+ path: "",
2972
+ actions: ["tinycloud.capabilities/read"]
2973
+ };
3335
2974
  }
3336
- function defaultEntriesForTier(tier) {
3337
- if (tier === false) {
2975
+ function withCapabilitiesReadForSpaces(resources) {
2976
+ if (resources.length === 0) {
3338
2977
  return [];
3339
2978
  }
3340
- const source = tier === "admin" ? DEFAULT_ADMIN_ENTRIES : tier === "all" ? DEFAULT_ALL_ENTRIES : DEFAULT_STANDARD_ENTRIES;
3341
- return source.map((e) => ({
3342
- service: e.service,
3343
- space: e.space,
3344
- path: e.path,
3345
- actions: [...e.actions],
3346
- ...e.skipPrefix !== void 0 ? { skipPrefix: e.skipPrefix } : {}
2979
+ const spaces = new Set(
2980
+ resources.filter((resource) => resource.service !== ENCRYPTION_PERMISSION_SERVICE).map((resource) => resource.space)
2981
+ );
2982
+ return dedupeResources([
2983
+ ...resources,
2984
+ ...[...spaces].map(capabilitiesReadPermission)
2985
+ ]);
2986
+ }
2987
+ function accountRegistryPermissions() {
2988
+ return [ACCOUNT_REGISTRY_PATH, "spaces/"].map((path) => ({
2989
+ service: "tinycloud.kv",
2990
+ space: ACCOUNT_REGISTRY_SPACE,
2991
+ path,
2992
+ actions: ["tinycloud.kv/get", "tinycloud.kv/put", "tinycloud.kv/list"]
3347
2993
  }));
3348
2994
  }
3349
- function resolveManifest(input) {
3350
- const manifest = validateManifest(input);
3351
- const prefix = manifest.prefix !== void 0 ? manifest.prefix : manifest.app_id;
3352
- const space = manifest.space ?? DEFAULT_MANIFEST_SPACE;
3353
- const expiryMs = parseExpiry(manifest.expiry ?? DEFAULT_EXPIRY);
3354
- const includePublicSpace = manifest.includePublicSpace ?? true;
3355
- const tier = normalizeDefaults(manifest.defaults);
3356
- const defaultEntries = defaultEntriesForTier(tier);
3357
- const explicitEntries = manifest.permissions ?? [];
3358
- const secretEntries = secretEntriesForManifest(manifest.secrets);
3359
- const allEntries = [
3360
- ...defaultEntries,
3361
- ...explicitEntries,
3362
- ...secretEntries
3363
- ];
3364
- const resources = withCapabilitiesReadForSpaces(
3365
- allEntries.flatMap((entry) => resolveEntry(entry, prefix, expiryMs, space))
3366
- );
3367
- const additionalDelegates = manifest.did === void 0 ? [] : [
3368
- {
3369
- did: manifest.did,
3370
- name: manifest.name,
3371
- expiryMs,
3372
- permissions: resources.map(cloneResourceCapability)
3373
- }
3374
- ];
2995
+ function accountRegistryIndexPermission() {
3375
2996
  return {
3376
- app_id: manifest.app_id,
3377
- ...manifest.did !== void 0 ? { did: manifest.did } : {},
3378
- space,
3379
- resources,
3380
- expiryMs,
3381
- includePublicSpace,
3382
- additionalDelegates
2997
+ service: "tinycloud.sql",
2998
+ space: ACCOUNT_REGISTRY_SPACE,
2999
+ path: "account",
3000
+ actions: ["tinycloud.sql/read", "tinycloud.sql/write", "tinycloud.sql/ddl"]
3383
3001
  };
3384
3002
  }
3385
- function normalizeSecretActions(actions) {
3386
- const out = [];
3387
- const seen = /* @__PURE__ */ new Set();
3388
- const add = (action) => {
3389
- if (!seen.has(action)) {
3390
- out.push(action);
3391
- seen.add(action);
3003
+ function composeManifestRequest(inputs, options = {}) {
3004
+ if (!Array.isArray(inputs) || inputs.length === 0) {
3005
+ throw new ManifestValidationError(
3006
+ "composeManifestRequest requires at least one manifest"
3007
+ );
3008
+ }
3009
+ const includeAccountRegistryPermissions = options.includeAccountRegistryPermissions ?? true;
3010
+ const manifests = inputs.map(validateManifest);
3011
+ const resolved = manifests.map(resolveManifest);
3012
+ const resources = resolved.flatMap((entry) => entry.resources);
3013
+ const delegationTargets = resolved.flatMap(
3014
+ (entry) => entry.additionalDelegates.map((delegate) => ({
3015
+ ...delegate,
3016
+ permissions: dedupeResources(delegate.permissions)
3017
+ }))
3018
+ );
3019
+ if (includeAccountRegistryPermissions) {
3020
+ resources.push(...accountRegistryPermissions());
3021
+ resources.push(accountRegistryIndexPermission());
3022
+ }
3023
+ const resourcesWithImplicitCapabilities = withCapabilitiesReadForSpaces(resources);
3024
+ const manifestsByAppId = /* @__PURE__ */ new Map();
3025
+ for (const manifest of manifests) {
3026
+ const current = manifestsByAppId.get(manifest.app_id);
3027
+ if (current === void 0) {
3028
+ manifestsByAppId.set(manifest.app_id, [manifest]);
3029
+ } else {
3030
+ current.push(manifest);
3392
3031
  }
3032
+ }
3033
+ const registryRecords = includeAccountRegistryPermissions ? [...manifestsByAppId.entries()].map(([app_id, appManifests]) => ({
3034
+ key: `${ACCOUNT_REGISTRY_PATH}${app_id}`,
3035
+ app_id,
3036
+ manifests: appManifests.map((manifest) => ({
3037
+ ...manifest,
3038
+ permissions: manifest.permissions?.map(clonePermissionEntry)
3039
+ }))
3040
+ })) : [];
3041
+ return {
3042
+ manifests,
3043
+ resources: resourcesWithImplicitCapabilities,
3044
+ delegationTargets,
3045
+ registryRecords,
3046
+ expiryMs: Math.max(...resolved.map((entry) => entry.expiryMs)),
3047
+ includePublicSpace: resolved.some((entry) => entry.includePublicSpace)
3393
3048
  };
3394
- for (const action of actions) {
3395
- if (action === "read") {
3396
- add("get");
3397
- continue;
3049
+ }
3050
+ function resourceCapabilitiesToAbilitiesMap(resources) {
3051
+ const out = {};
3052
+ for (const r of resources) {
3053
+ const shortService = SERVICE_LONG_TO_SHORT[r.service];
3054
+ if (shortService === void 0) {
3055
+ throw new ManifestValidationError(
3056
+ `unknown service '${r.service}' \u2014 no short-form mapping. Known services: ${Object.keys(SERVICE_LONG_TO_SHORT).join(", ")}`
3057
+ );
3398
3058
  }
3399
- if (action === "write") {
3400
- add("put");
3401
- continue;
3059
+ if (out[shortService] === void 0) {
3060
+ out[shortService] = {};
3402
3061
  }
3403
- if (action === "delete") {
3404
- add("del");
3405
- continue;
3062
+ const pathsMap = out[shortService];
3063
+ const existing = pathsMap[r.path];
3064
+ if (existing === void 0) {
3065
+ pathsMap[r.path] = [...r.actions];
3066
+ } else {
3067
+ const seen = new Set(existing);
3068
+ for (const action of r.actions) {
3069
+ if (!seen.has(action)) {
3070
+ existing.push(action);
3071
+ seen.add(action);
3072
+ }
3073
+ }
3406
3074
  }
3407
- if (action === "get" || action === "put" || action === "del" || action === "list" || action === "metadata") {
3408
- add(action);
3409
- continue;
3075
+ }
3076
+ return out;
3077
+ }
3078
+ function resourceCapabilitiesToSpaceAbilitiesMap(resources) {
3079
+ const grouped = /* @__PURE__ */ new Map();
3080
+ for (const resource of resources) {
3081
+ const entries = grouped.get(resource.space);
3082
+ if (entries === void 0) {
3083
+ grouped.set(resource.space, [resource]);
3084
+ } else {
3085
+ entries.push(resource);
3410
3086
  }
3411
- if (action === "tinycloud.kv/get" || action === "tinycloud.kv/put" || action === "tinycloud.kv/del" || action === "tinycloud.kv/list" || action === "tinycloud.kv/metadata") {
3412
- add(action);
3413
- continue;
3087
+ }
3088
+ const out = {};
3089
+ for (const [space, entries] of grouped.entries()) {
3090
+ out[space] = resourceCapabilitiesToAbilitiesMap(entries);
3091
+ }
3092
+ return out;
3093
+ }
3094
+ function manifestAbilitiesUnion(resolved) {
3095
+ const all = [...resolved.resources];
3096
+ for (const delegate of resolved.additionalDelegates) {
3097
+ for (const perm of delegate.permissions) {
3098
+ all.push(perm);
3414
3099
  }
3415
- throw new ManifestValidationError(
3416
- `unknown secret action ${JSON.stringify(action)}; expected read, write, delete, list, or metadata`
3417
- );
3418
3100
  }
3419
- return out;
3420
- }
3421
- function secretNameFromSpec(fallbackName, spec) {
3422
- if (spec !== null && typeof spec === "object" && !Array.isArray(spec)) {
3423
- return spec.name ?? fallbackName;
3101
+ return resourceCapabilitiesToAbilitiesMap(all);
3102
+ }
3103
+
3104
+ // src/account/AccountService.ts
3105
+ var SERVICE_NAME2 = "account";
3106
+ var ACCOUNT_INDEX_DB = "account";
3107
+ var ACCOUNT_INDEX_NAMESPACE = "tinycloud.account.index";
3108
+ var ACCOUNT_SPACES_PATH = "spaces/";
3109
+ var AccountService = class {
3110
+ constructor(config) {
3111
+ this.config = config;
3112
+ this.applications = {
3113
+ list: async (options = {}) => {
3114
+ if (options.preferIndex) {
3115
+ const indexed = await this.index.applications.list();
3116
+ if (indexed.ok && indexed.data.length > 0) return indexed;
3117
+ if (!indexed.ok && !isMissingIndexError(indexed.error)) return indexed;
3118
+ const canonical = await this.applications.list();
3119
+ if (canonical.ok && options.refreshIndex !== false) {
3120
+ await this.replaceApplicationsIndexQuietly(canonical.data);
3121
+ }
3122
+ return canonical;
3123
+ }
3124
+ const kvResult = this.accountKV();
3125
+ if (!kvResult.ok) return kvResult;
3126
+ const listed = await kvResult.data.list({ prefix: ACCOUNT_REGISTRY_PATH });
3127
+ if (!listed.ok) return accountErr(listed.error);
3128
+ const applications = [];
3129
+ for (const key of listed.data.keys) {
3130
+ const loaded = await kvResult.data.get(key);
3131
+ if (!loaded.ok) return accountErr(loaded.error);
3132
+ applications.push(applicationFromRecord(key, loaded.data.data));
3133
+ }
3134
+ applications.sort((a, b) => a.appId.localeCompare(b.appId));
3135
+ return (0, import_sdk_services5.ok)(applications);
3136
+ },
3137
+ get: async (appId) => {
3138
+ const kvResult = this.accountKV();
3139
+ if (!kvResult.ok) return kvResult;
3140
+ const key = applicationKey(appId);
3141
+ const loaded = await kvResult.data.get(key);
3142
+ if (!loaded.ok) return accountErr(loaded.error);
3143
+ return (0, import_sdk_services5.ok)(applicationFromRecord(key, loaded.data.data));
3144
+ },
3145
+ register: async (manifest) => {
3146
+ const manifests = Array.isArray(manifest) ? manifest : [manifest];
3147
+ const request = composeManifestRequest(manifests);
3148
+ if (request.registryRecords.length === 0) {
3149
+ return (0, import_sdk_services5.err)(
3150
+ (0, import_sdk_services5.serviceError)(
3151
+ "INVALID_MANIFEST",
3152
+ "Manifest did not produce an account application registry record",
3153
+ SERVICE_NAME2
3154
+ )
3155
+ );
3156
+ }
3157
+ await this.config.ensureAccountSpaceHosted?.();
3158
+ const kvResult = this.accountKV();
3159
+ if (!kvResult.ok) return kvResult;
3160
+ let registered;
3161
+ for (const record of request.registryRecords) {
3162
+ const manifestHash = hashJson(record.manifests);
3163
+ if (await this.indexHasApplicationHash(record.app_id, manifestHash)) {
3164
+ registered = {
3165
+ appId: record.app_id,
3166
+ manifests: record.manifests,
3167
+ manifestHash,
3168
+ name: record.manifests[0]?.name,
3169
+ description: record.manifests[0]?.description
3170
+ };
3171
+ continue;
3172
+ }
3173
+ const stored = {
3174
+ app_id: record.app_id,
3175
+ manifests: record.manifests,
3176
+ manifest_hash: manifestHash,
3177
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
3178
+ };
3179
+ const written = await kvResult.data.put(record.key, stored);
3180
+ if (!written.ok) return accountErr(written.error);
3181
+ registered = applicationFromRecord(record.key, stored);
3182
+ await this.upsertApplicationIndexQuietly(registered);
3183
+ }
3184
+ return (0, import_sdk_services5.ok)(registered);
3185
+ },
3186
+ remove: async (appId) => {
3187
+ const kvResult = this.accountKV();
3188
+ if (!kvResult.ok) return kvResult;
3189
+ const removed = await kvResult.data.delete(applicationKey(appId));
3190
+ if (!removed.ok) return accountErr(removed.error);
3191
+ await this.deleteApplicationIndexQuietly(appId);
3192
+ return (0, import_sdk_services5.ok)(void 0);
3193
+ }
3194
+ };
3195
+ this.spaces = {
3196
+ list: async (options = {}) => {
3197
+ if (options.preferIndex) {
3198
+ const indexed = await this.index.spaces.list();
3199
+ if (indexed.ok && indexed.data.length > 0) return indexed;
3200
+ if (!indexed.ok && !isMissingIndexError(indexed.error)) return indexed;
3201
+ const canonical = await this.spaces.syncAccessible();
3202
+ if (canonical.ok && options.refreshIndex !== false) {
3203
+ await this.replaceSpacesIndexQuietly(canonical.data);
3204
+ }
3205
+ return canonical;
3206
+ }
3207
+ const kvResult = this.accountKV();
3208
+ if (!kvResult.ok) return kvResult;
3209
+ const listed = await kvResult.data.list({ prefix: ACCOUNT_SPACES_PATH });
3210
+ if (!listed.ok) return accountErr(listed.error);
3211
+ const spaces = [];
3212
+ for (const key of listed.data.keys) {
3213
+ const loaded = await kvResult.data.get(key);
3214
+ if (!loaded.ok) return accountErr(loaded.error);
3215
+ spaces.push(spaceFromRecord(key, loaded.data.data));
3216
+ }
3217
+ spaces.sort((a, b) => a.name.localeCompare(b.name) || a.spaceId.localeCompare(b.spaceId));
3218
+ return (0, import_sdk_services5.ok)(spaces);
3219
+ },
3220
+ get: async (spaceId) => {
3221
+ const kvResult = this.accountKV();
3222
+ if (!kvResult.ok) return kvResult;
3223
+ const loaded = await kvResult.data.get(spaceKey(spaceId));
3224
+ if (!loaded.ok) return accountErr(loaded.error);
3225
+ return (0, import_sdk_services5.ok)(spaceFromRecord(spaceKey(spaceId), loaded.data.data));
3226
+ },
3227
+ register: async (space) => {
3228
+ await this.config.ensureAccountSpaceHosted?.();
3229
+ const kvResult = this.accountKV();
3230
+ if (!kvResult.ok) return kvResult;
3231
+ const stored = spaceRecordFromInput(space);
3232
+ const written = await kvResult.data.put(spaceKey(stored.space_id), stored);
3233
+ if (!written.ok) return accountErr(written.error);
3234
+ const registered = spaceFromRecord(spaceKey(stored.space_id), stored);
3235
+ await this.upsertSpaceIndexQuietly(registered);
3236
+ return (0, import_sdk_services5.ok)(registered);
3237
+ },
3238
+ syncAccessible: async () => {
3239
+ const listed = await this.config.getSpaces().list();
3240
+ if (!listed.ok) return accountErr(listed.error);
3241
+ const registered = [];
3242
+ for (const space of listed.data) {
3243
+ const result = await this.spaces.register(space);
3244
+ if (!result.ok) return result;
3245
+ registered.push(result.data);
3246
+ }
3247
+ return (0, import_sdk_services5.ok)(registered);
3248
+ },
3249
+ remove: async (spaceId) => {
3250
+ const kvResult = this.accountKV();
3251
+ if (!kvResult.ok) return kvResult;
3252
+ const removed = await kvResult.data.delete(spaceKey(spaceId));
3253
+ if (!removed.ok) return accountErr(removed.error);
3254
+ await this.deleteSpaceIndexQuietly(spaceId);
3255
+ return (0, import_sdk_services5.ok)(void 0);
3256
+ }
3257
+ };
3258
+ this.delegations = {
3259
+ list: async (options = {}) => {
3260
+ if (options.preferIndex) {
3261
+ const indexed = await this.index.delegations.list(options);
3262
+ if (indexed.ok && indexed.data.length > 0) return indexed;
3263
+ if (!indexed.ok && !isMissingIndexError(indexed.error)) return indexed;
3264
+ const live = await this.delegations.list({
3265
+ direction: options.direction,
3266
+ space: options.space
3267
+ });
3268
+ if (live.ok && options.refreshIndex !== false) {
3269
+ await this.replaceDelegationsIndexQuietly(live.data);
3270
+ }
3271
+ return live;
3272
+ }
3273
+ const spaces = await this.config.getSpaces().list();
3274
+ if (!spaces.ok) return accountErr(spaces.error);
3275
+ const targetSpaces = options.space ? spaces.data.filter((space) => space.id === options.space || space.name === options.space) : spaces.data;
3276
+ const delegations = [];
3277
+ for (const space of targetSpaces) {
3278
+ const scoped = this.config.getSpaces().get(space.id).delegations;
3279
+ if (options.direction !== "received") {
3280
+ const granted = await scoped.list();
3281
+ if (!granted.ok) return accountErr(granted.error);
3282
+ delegations.push(...granted.data.map((d) => mapDelegation(d, space, "granted")));
3283
+ }
3284
+ if (options.direction !== "granted") {
3285
+ const received = await scoped.listReceived();
3286
+ if (!received.ok) return accountErr(received.error);
3287
+ delegations.push(...received.data.map((d) => mapDelegation(d, space, "received")));
3288
+ }
3289
+ }
3290
+ delegations.sort((a, b) => a.spaceId.localeCompare(b.spaceId) || a.cid.localeCompare(b.cid));
3291
+ return (0, import_sdk_services5.ok)(delegations);
3292
+ },
3293
+ revoke: async (options) => {
3294
+ const space = await this.resolveSpace(options.space);
3295
+ if (!space.ok) return space;
3296
+ const revoked = await this.config.getSpaces().get(space.data.id).delegations.revoke(options.cid);
3297
+ if (!revoked.ok) return accountErr(revoked.error);
3298
+ return (0, import_sdk_services5.ok)(void 0);
3299
+ }
3300
+ };
3301
+ this.index = {
3302
+ rebuild: async () => {
3303
+ const dbResult = this.accountDb();
3304
+ if (!dbResult.ok) return dbResult;
3305
+ const applications = await this.applications.list();
3306
+ if (!applications.ok) return applications;
3307
+ const spaces = await this.spaces.list();
3308
+ if (!spaces.ok) return spaces;
3309
+ const delegations = await this.delegations.list();
3310
+ if (!delegations.ok) return delegations;
3311
+ const syncedAt = (/* @__PURE__ */ new Date()).toISOString();
3312
+ const schema = await this.ensureAccountIndex(dbResult.data);
3313
+ if (!schema.ok) return schema;
3314
+ const statements = [
3315
+ { sql: "DELETE FROM applications" },
3316
+ { sql: "DELETE FROM application_state" },
3317
+ { sql: "DELETE FROM spaces" },
3318
+ { sql: "DELETE FROM delegations" },
3319
+ { sql: "DELETE FROM sync_state" },
3320
+ ...applications.data.map((app) => ({
3321
+ sql: "INSERT INTO applications (app_id, name, description, updated_at, manifest_json) VALUES (?, ?, ?, ?, ?)",
3322
+ params: [
3323
+ app.appId,
3324
+ app.name ?? null,
3325
+ app.description ?? null,
3326
+ app.updatedAt ?? syncedAt,
3327
+ JSON.stringify(app.manifests)
3328
+ ]
3329
+ })),
3330
+ ...applications.data.map((app) => ({
3331
+ sql: "INSERT OR REPLACE INTO application_state (app_id, manifest_hash, indexed_at) VALUES (?, ?, ?)",
3332
+ params: [app.appId, app.manifestHash ?? hashJson(app.manifests), syncedAt]
3333
+ })),
3334
+ ...spaces.data.map((space) => ({
3335
+ sql: "INSERT OR REPLACE INTO spaces (space_id, name, owner_did, type, permissions_json, status, registered_at, updated_at, expires_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
3336
+ params: [
3337
+ space.spaceId,
3338
+ space.name,
3339
+ space.ownerDid,
3340
+ space.type,
3341
+ JSON.stringify(space.permissions),
3342
+ space.status,
3343
+ space.registeredAt ?? syncedAt,
3344
+ space.updatedAt ?? syncedAt,
3345
+ space.expiresAt?.toISOString() ?? null
3346
+ ]
3347
+ })),
3348
+ ...delegations.data.map((delegation) => ({
3349
+ sql: "INSERT INTO delegations (cid, direction, space_id, space_name, counterparty_did, delegate_did, delegator_did, path, actions_json, expiry, status, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
3350
+ params: [
3351
+ delegation.cid,
3352
+ delegation.direction,
3353
+ delegation.spaceId,
3354
+ delegation.spaceName ?? null,
3355
+ delegation.counterpartyDid,
3356
+ delegation.delegateDid,
3357
+ delegation.delegatorDid ?? null,
3358
+ delegation.path,
3359
+ JSON.stringify(delegation.actions),
3360
+ delegation.expiry.toISOString(),
3361
+ delegation.status,
3362
+ delegation.createdAt?.toISOString() ?? null,
3363
+ syncedAt
3364
+ ]
3365
+ })),
3366
+ {
3367
+ sql: "INSERT INTO sync_state (source, synced_at, count) VALUES (?, ?, ?)",
3368
+ params: ["applications", syncedAt, applications.data.length]
3369
+ },
3370
+ {
3371
+ sql: "INSERT INTO sync_state (source, synced_at, count) VALUES (?, ?, ?)",
3372
+ params: ["spaces", syncedAt, spaces.data.length]
3373
+ },
3374
+ {
3375
+ sql: "INSERT INTO sync_state (source, synced_at, count) VALUES (?, ?, ?)",
3376
+ params: ["delegations", syncedAt, delegations.data.length]
3377
+ }
3378
+ ];
3379
+ const rebuilt = await dbResult.data.batch(statements);
3380
+ if (!rebuilt.ok) return accountErr(rebuilt.error);
3381
+ return (0, import_sdk_services5.ok)({
3382
+ database: ACCOUNT_INDEX_DB,
3383
+ applications: applications.data.length,
3384
+ spaces: spaces.data.length,
3385
+ delegations: delegations.data.length,
3386
+ syncedAt
3387
+ });
3388
+ },
3389
+ applications: {
3390
+ list: async () => {
3391
+ const dbResult = this.accountDb();
3392
+ if (!dbResult.ok) return dbResult;
3393
+ const queried = await dbResult.data.query(
3394
+ "SELECT applications.app_id, name, description, updated_at, manifest_json, application_state.manifest_hash FROM applications LEFT JOIN application_state ON applications.app_id = application_state.app_id ORDER BY applications.app_id"
3395
+ );
3396
+ if (!queried.ok) return accountErr(queried.error);
3397
+ return (0, import_sdk_services5.ok)(queried.data.rows.map(indexedApplicationFromRow));
3398
+ }
3399
+ },
3400
+ spaces: {
3401
+ list: async () => {
3402
+ const dbResult = this.accountDb();
3403
+ if (!dbResult.ok) return dbResult;
3404
+ const queried = await dbResult.data.query(
3405
+ "SELECT space_id, name, owner_did, type, permissions_json, status, registered_at, updated_at, expires_at FROM spaces ORDER BY name, space_id"
3406
+ );
3407
+ if (!queried.ok) return accountErr(queried.error);
3408
+ return (0, import_sdk_services5.ok)(queried.data.rows.map(indexedSpaceFromRow));
3409
+ }
3410
+ },
3411
+ delegations: {
3412
+ list: async (options = {}) => {
3413
+ const dbResult = this.accountDb();
3414
+ if (!dbResult.ok) return dbResult;
3415
+ const where = [];
3416
+ const params = [];
3417
+ if (options.direction && options.direction !== "all") {
3418
+ where.push("direction = ?");
3419
+ params.push(options.direction);
3420
+ }
3421
+ if (options.space) {
3422
+ where.push("(space_id = ? OR space_name = ?)");
3423
+ params.push(options.space, options.space);
3424
+ }
3425
+ const queried = await dbResult.data.query(
3426
+ `SELECT cid, direction, space_id, space_name, counterparty_did, delegate_did, delegator_did, path, actions_json, expiry, status, created_at FROM delegations${where.length > 0 ? ` WHERE ${where.join(" AND ")}` : ""} ORDER BY space_id, cid`,
3427
+ params
3428
+ );
3429
+ if (!queried.ok) return accountErr(queried.error);
3430
+ return (0, import_sdk_services5.ok)(queried.data.rows.map(indexedDelegationFromRow));
3431
+ }
3432
+ },
3433
+ query: async (sql, params) => {
3434
+ const dbResult = this.accountDb();
3435
+ if (!dbResult.ok) return dbResult;
3436
+ const queried = await dbResult.data.query(sql, params);
3437
+ if (!queried.ok) return accountErr(queried.error);
3438
+ return (0, import_sdk_services5.ok)(queried.data);
3439
+ },
3440
+ status: async () => {
3441
+ const dbResult = this.accountDb();
3442
+ if (!dbResult.ok) return dbResult;
3443
+ const queried = await dbResult.data.query(
3444
+ "SELECT source, synced_at, count FROM sync_state ORDER BY source"
3445
+ );
3446
+ if (!queried.ok) {
3447
+ if (isMissingIndexError(queried.error)) {
3448
+ return (0, import_sdk_services5.ok)({ database: ACCOUNT_INDEX_DB, state: "missing", sources: [] });
3449
+ }
3450
+ return accountErr(queried.error);
3451
+ }
3452
+ return (0, import_sdk_services5.ok)({
3453
+ database: ACCOUNT_INDEX_DB,
3454
+ state: "ready",
3455
+ sources: queried.data.rows.map(([source, syncedAt, count]) => ({
3456
+ source,
3457
+ syncedAt,
3458
+ count
3459
+ }))
3460
+ });
3461
+ }
3462
+ };
3463
+ }
3464
+ async status() {
3465
+ const apps = await this.applications.list();
3466
+ if (!apps.ok) return apps;
3467
+ const delegations = await this.delegations.list();
3468
+ if (!delegations.ok) return delegations;
3469
+ const spaces = await this.spaces.list();
3470
+ if (!spaces.ok) return spaces;
3471
+ return (0, import_sdk_services5.ok)({
3472
+ did: this.config.getDid(),
3473
+ host: this.config.getHost(),
3474
+ primarySpaceId: this.config.getPrimarySpaceId(),
3475
+ accountSpaceId: this.config.getAccountSpaceId(),
3476
+ applications: apps.data.length,
3477
+ spaces: spaces.data.length,
3478
+ grantedDelegations: delegations.data.filter((d) => d.direction === "granted").length,
3479
+ receivedDelegations: delegations.data.filter((d) => d.direction === "received").length
3480
+ });
3424
3481
  }
3425
- return fallbackName;
3426
- }
3427
- function secretScopeFromSpec(spec) {
3428
- if (spec !== null && typeof spec === "object" && !Array.isArray(spec)) {
3429
- return spec.scope;
3482
+ accountKV() {
3483
+ const accountSpaceId = this.config.getAccountSpaceId();
3484
+ if (!accountSpaceId) {
3485
+ return (0, import_sdk_services5.err)(
3486
+ (0, import_sdk_services5.serviceError)(
3487
+ "ACCOUNT_SPACE_UNAVAILABLE",
3488
+ "Account space is unavailable. Sign in with a wallet-backed profile first.",
3489
+ SERVICE_NAME2
3490
+ )
3491
+ );
3492
+ }
3493
+ return (0, import_sdk_services5.ok)(this.config.getSpaces().get(accountSpaceId).kv);
3430
3494
  }
3431
- return void 0;
3432
- }
3433
- function secretActionsFromSpec(name, spec) {
3434
- if (spec === true) {
3435
- return ["read"];
3495
+ accountDb() {
3496
+ const db = this.config.getAccountDb?.();
3497
+ if (!db) {
3498
+ return (0, import_sdk_services5.err)(
3499
+ (0, import_sdk_services5.serviceError)(
3500
+ "ACCOUNT_INDEX_UNAVAILABLE",
3501
+ "Account index database is unavailable. Sign in with a wallet-backed profile first.",
3502
+ SERVICE_NAME2
3503
+ )
3504
+ );
3505
+ }
3506
+ return (0, import_sdk_services5.ok)(db);
3436
3507
  }
3437
- if (typeof spec === "string") {
3438
- return [spec];
3508
+ async indexHasApplicationHash(appId, manifestHash) {
3509
+ const dbResult = this.accountDb();
3510
+ if (!dbResult.ok) return false;
3511
+ const schema = await this.ensureAccountIndex(dbResult.data);
3512
+ if (!schema.ok) return false;
3513
+ const queried = await dbResult.data.query(
3514
+ "SELECT 1 FROM application_state WHERE app_id = ? AND manifest_hash = ? LIMIT 1",
3515
+ [appId, manifestHash]
3516
+ );
3517
+ return queried.ok && queried.data.rows.length > 0;
3518
+ }
3519
+ async upsertApplicationIndexQuietly(app) {
3520
+ await ignoreIndexFailure(() => this.upsertApplicationIndex(app));
3521
+ }
3522
+ async upsertApplicationIndex(app) {
3523
+ const dbResult = this.accountDb();
3524
+ if (!dbResult.ok) return (0, import_sdk_services5.ok)(void 0);
3525
+ const schema = await this.ensureAccountIndex(dbResult.data);
3526
+ if (!schema.ok) return schema;
3527
+ const updatedAt = app.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString();
3528
+ const manifestHash = app.manifestHash ?? hashJson(app.manifests);
3529
+ const written = await dbResult.data.batch([
3530
+ {
3531
+ sql: "INSERT OR REPLACE INTO applications (app_id, name, description, updated_at, manifest_json) VALUES (?, ?, ?, ?, ?)",
3532
+ params: [
3533
+ app.appId,
3534
+ app.name ?? null,
3535
+ app.description ?? null,
3536
+ updatedAt,
3537
+ JSON.stringify(app.manifests)
3538
+ ]
3539
+ },
3540
+ {
3541
+ sql: "INSERT OR REPLACE INTO application_state (app_id, manifest_hash, indexed_at) VALUES (?, ?, ?)",
3542
+ params: [app.appId, manifestHash, updatedAt]
3543
+ }
3544
+ ]);
3545
+ if (!written.ok) return accountErr(written.error);
3546
+ return (0, import_sdk_services5.ok)(void 0);
3439
3547
  }
3440
- if (Array.isArray(spec)) {
3441
- return spec;
3548
+ async deleteApplicationIndexQuietly(appId) {
3549
+ await ignoreIndexFailure(() => this.deleteApplicationIndex(appId));
3550
+ }
3551
+ async deleteApplicationIndex(appId) {
3552
+ const dbResult = this.accountDb();
3553
+ if (!dbResult.ok) return (0, import_sdk_services5.ok)(void 0);
3554
+ const schema = await this.ensureAccountIndex(dbResult.data);
3555
+ if (!schema.ok) return schema;
3556
+ const deleted = await dbResult.data.batch([
3557
+ { sql: "DELETE FROM applications WHERE app_id = ?", params: [appId] },
3558
+ { sql: "DELETE FROM application_state WHERE app_id = ?", params: [appId] }
3559
+ ]);
3560
+ if (!deleted.ok) return accountErr(deleted.error);
3561
+ return (0, import_sdk_services5.ok)(void 0);
3442
3562
  }
3443
- if (spec === null || typeof spec !== "object") {
3444
- throw new ManifestValidationError(
3445
- `manifest.secrets.${name} must be true, a string action, an actions array, or an object`
3446
- );
3563
+ async upsertSpaceIndexQuietly(space) {
3564
+ await ignoreIndexFailure(() => this.upsertSpaceIndex(space));
3447
3565
  }
3448
- if (spec.actions === void 0) {
3449
- return ["read"];
3566
+ async upsertSpaceIndex(space) {
3567
+ const dbResult = this.accountDb();
3568
+ if (!dbResult.ok) return (0, import_sdk_services5.ok)(void 0);
3569
+ const schema = await this.ensureAccountIndex(dbResult.data);
3570
+ if (!schema.ok) return schema;
3571
+ const updatedAt = space.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString();
3572
+ const written = await dbResult.data.batch([
3573
+ {
3574
+ sql: "INSERT OR REPLACE INTO spaces (space_id, name, owner_did, type, permissions_json, status, registered_at, updated_at, expires_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
3575
+ params: [
3576
+ space.spaceId,
3577
+ space.name,
3578
+ space.ownerDid,
3579
+ space.type,
3580
+ JSON.stringify(space.permissions),
3581
+ space.status,
3582
+ space.registeredAt ?? updatedAt,
3583
+ updatedAt,
3584
+ space.expiresAt?.toISOString() ?? null
3585
+ ]
3586
+ }
3587
+ ]);
3588
+ if (!written.ok) return accountErr(written.error);
3589
+ return (0, import_sdk_services5.ok)(void 0);
3450
3590
  }
3451
- if (typeof spec.actions === "string") {
3452
- return [spec.actions];
3591
+ async deleteSpaceIndexQuietly(spaceId) {
3592
+ await ignoreIndexFailure(() => this.deleteSpaceIndex(spaceId));
3593
+ }
3594
+ async deleteSpaceIndex(spaceId) {
3595
+ const dbResult = this.accountDb();
3596
+ if (!dbResult.ok) return (0, import_sdk_services5.ok)(void 0);
3597
+ const schema = await this.ensureAccountIndex(dbResult.data);
3598
+ if (!schema.ok) return schema;
3599
+ const deleted = await dbResult.data.batch([
3600
+ { sql: "DELETE FROM spaces WHERE space_id = ?", params: [spaceId] }
3601
+ ]);
3602
+ if (!deleted.ok) return accountErr(deleted.error);
3603
+ return (0, import_sdk_services5.ok)(void 0);
3453
3604
  }
3454
- if (Array.isArray(spec.actions)) {
3455
- return spec.actions;
3605
+ async resolveSpace(space) {
3606
+ const listed = await this.config.getSpaces().list();
3607
+ if (!listed.ok) return accountErr(listed.error);
3608
+ const found = listed.data.find((candidate) => candidate.id === space || candidate.name === space);
3609
+ if (!found) {
3610
+ return (0, import_sdk_services5.err)(
3611
+ (0, import_sdk_services5.serviceError)("SPACE_NOT_FOUND", `No account space found for ${JSON.stringify(space)}`, SERVICE_NAME2)
3612
+ );
3613
+ }
3614
+ return (0, import_sdk_services5.ok)(found);
3615
+ }
3616
+ async replaceApplicationsIndexQuietly(applications) {
3617
+ await ignoreIndexFailure(async () => {
3618
+ const dbResult = this.accountDb();
3619
+ if (!dbResult.ok) return;
3620
+ const syncedAt = (/* @__PURE__ */ new Date()).toISOString();
3621
+ const schema = await this.ensureAccountIndex(dbResult.data);
3622
+ if (!schema.ok) return;
3623
+ await dbResult.data.batch([
3624
+ { sql: "DELETE FROM applications" },
3625
+ { sql: "DELETE FROM application_state" },
3626
+ ...applications.map((app) => ({
3627
+ sql: "INSERT OR REPLACE INTO applications (app_id, name, description, updated_at, manifest_json) VALUES (?, ?, ?, ?, ?)",
3628
+ params: [
3629
+ app.appId,
3630
+ app.name ?? null,
3631
+ app.description ?? null,
3632
+ app.updatedAt ?? syncedAt,
3633
+ JSON.stringify(app.manifests)
3634
+ ]
3635
+ })),
3636
+ ...applications.map((app) => ({
3637
+ sql: "INSERT OR REPLACE INTO application_state (app_id, manifest_hash, indexed_at) VALUES (?, ?, ?)",
3638
+ params: [app.appId, app.manifestHash ?? hashJson(app.manifests), syncedAt]
3639
+ })),
3640
+ {
3641
+ sql: "INSERT OR REPLACE INTO sync_state (source, synced_at, count) VALUES (?, ?, ?)",
3642
+ params: ["applications", syncedAt, applications.length]
3643
+ }
3644
+ ]);
3645
+ });
3456
3646
  }
3457
- throw new ManifestValidationError(
3458
- `manifest.secrets.${name}.actions must be a string or array`
3459
- );
3460
- }
3461
- function secretEntriesForManifest(secrets) {
3462
- if (secrets === void 0) {
3463
- return [];
3647
+ async replaceSpacesIndexQuietly(spaces) {
3648
+ await ignoreIndexFailure(async () => {
3649
+ const dbResult = this.accountDb();
3650
+ if (!dbResult.ok) return;
3651
+ const syncedAt = (/* @__PURE__ */ new Date()).toISOString();
3652
+ const schema = await this.ensureAccountIndex(dbResult.data);
3653
+ if (!schema.ok) return;
3654
+ await dbResult.data.batch([
3655
+ { sql: "DELETE FROM spaces" },
3656
+ ...spaces.map((space) => ({
3657
+ sql: "INSERT OR REPLACE INTO spaces (space_id, name, owner_did, type, permissions_json, status, registered_at, updated_at, expires_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
3658
+ params: [
3659
+ space.spaceId,
3660
+ space.name,
3661
+ space.ownerDid,
3662
+ space.type,
3663
+ JSON.stringify(space.permissions),
3664
+ space.status,
3665
+ space.registeredAt ?? syncedAt,
3666
+ space.updatedAt ?? syncedAt,
3667
+ space.expiresAt?.toISOString() ?? null
3668
+ ]
3669
+ })),
3670
+ {
3671
+ sql: "INSERT OR REPLACE INTO sync_state (source, synced_at, count) VALUES (?, ?, ?)",
3672
+ params: ["spaces", syncedAt, spaces.length]
3673
+ }
3674
+ ]);
3675
+ });
3464
3676
  }
3465
- const entries = [];
3466
- for (const [name, spec] of Object.entries(secrets)) {
3467
- const actions = secretActionsFromSpec(name, spec);
3468
- const secretPath = (0, import_sdk_services4.resolveSecretPath)(
3469
- secretNameFromSpec(name, spec),
3470
- { scope: secretScopeFromSpec(spec) }
3471
- );
3472
- const extra = spec !== true && typeof spec === "object" && !Array.isArray(spec) ? spec : {};
3473
- entries.push({
3474
- service: VAULT_PERMISSION_SERVICE,
3475
- space: SECRETS_SPACE,
3476
- path: secretPath.vaultKey,
3477
- actions: normalizeSecretActions(actions),
3478
- skipPrefix: true,
3479
- ...extra.expiry !== void 0 ? { expiry: extra.expiry } : {},
3480
- ...extra.description !== void 0 ? { description: extra.description } : {}
3677
+ async replaceDelegationsIndexQuietly(delegations) {
3678
+ await ignoreIndexFailure(async () => {
3679
+ const dbResult = this.accountDb();
3680
+ if (!dbResult.ok) return;
3681
+ const syncedAt = (/* @__PURE__ */ new Date()).toISOString();
3682
+ const schema = await this.ensureAccountIndex(dbResult.data);
3683
+ if (!schema.ok) return;
3684
+ await dbResult.data.batch([
3685
+ { sql: "DELETE FROM delegations" },
3686
+ ...delegations.map((delegation) => ({
3687
+ sql: "INSERT OR REPLACE INTO delegations (cid, direction, space_id, space_name, counterparty_did, delegate_did, delegator_did, path, actions_json, expiry, status, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
3688
+ params: [
3689
+ delegation.cid,
3690
+ delegation.direction,
3691
+ delegation.spaceId,
3692
+ delegation.spaceName ?? null,
3693
+ delegation.counterpartyDid,
3694
+ delegation.delegateDid,
3695
+ delegation.delegatorDid ?? null,
3696
+ delegation.path,
3697
+ JSON.stringify(delegation.actions),
3698
+ delegation.expiry.toISOString(),
3699
+ delegation.status,
3700
+ delegation.createdAt?.toISOString() ?? null,
3701
+ syncedAt
3702
+ ]
3703
+ })),
3704
+ {
3705
+ sql: "INSERT OR REPLACE INTO sync_state (source, synced_at, count) VALUES (?, ?, ?)",
3706
+ params: ["delegations", syncedAt, delegations.length]
3707
+ }
3708
+ ]);
3481
3709
  });
3482
3710
  }
3483
- return entries;
3711
+ async ensureAccountIndex(db) {
3712
+ const migrated = await db.migrations.apply({
3713
+ namespace: ACCOUNT_INDEX_NAMESPACE,
3714
+ migrations: [
3715
+ {
3716
+ id: "001_initial",
3717
+ sql: ACCOUNT_INDEX_SCHEMA
3718
+ }
3719
+ ]
3720
+ });
3721
+ if (!migrated.ok) return accountErr(migrated.error);
3722
+ return (0, import_sdk_services5.ok)(void 0);
3723
+ }
3724
+ };
3725
+ var ACCOUNT_INDEX_SCHEMA = [
3726
+ `CREATE TABLE IF NOT EXISTS applications (
3727
+ app_id TEXT PRIMARY KEY,
3728
+ name TEXT,
3729
+ description TEXT,
3730
+ updated_at TEXT,
3731
+ manifest_json TEXT NOT NULL
3732
+ )`,
3733
+ `CREATE TABLE IF NOT EXISTS application_state (
3734
+ app_id TEXT PRIMARY KEY,
3735
+ manifest_hash TEXT NOT NULL,
3736
+ indexed_at TEXT NOT NULL
3737
+ )`,
3738
+ `CREATE TABLE IF NOT EXISTS spaces (
3739
+ space_id TEXT PRIMARY KEY,
3740
+ name TEXT NOT NULL,
3741
+ owner_did TEXT NOT NULL,
3742
+ type TEXT NOT NULL,
3743
+ permissions_json TEXT NOT NULL,
3744
+ status TEXT NOT NULL,
3745
+ registered_at TEXT,
3746
+ updated_at TEXT NOT NULL,
3747
+ expires_at TEXT
3748
+ )`,
3749
+ `CREATE TABLE IF NOT EXISTS delegations (
3750
+ cid TEXT PRIMARY KEY,
3751
+ direction TEXT NOT NULL,
3752
+ space_id TEXT NOT NULL,
3753
+ space_name TEXT,
3754
+ counterparty_did TEXT NOT NULL,
3755
+ delegate_did TEXT NOT NULL,
3756
+ delegator_did TEXT,
3757
+ path TEXT NOT NULL,
3758
+ actions_json TEXT NOT NULL,
3759
+ expiry TEXT NOT NULL,
3760
+ status TEXT NOT NULL,
3761
+ created_at TEXT,
3762
+ updated_at TEXT NOT NULL
3763
+ )`,
3764
+ `CREATE TABLE IF NOT EXISTS sync_state (
3765
+ source TEXT PRIMARY KEY,
3766
+ synced_at TEXT NOT NULL,
3767
+ count INTEGER NOT NULL
3768
+ )`,
3769
+ "CREATE INDEX IF NOT EXISTS idx_delegations_direction ON delegations(direction)",
3770
+ "CREATE INDEX IF NOT EXISTS idx_delegations_space ON delegations(space_id)",
3771
+ "CREATE INDEX IF NOT EXISTS idx_delegations_counterparty ON delegations(counterparty_did)",
3772
+ "CREATE INDEX IF NOT EXISTS idx_spaces_owner ON spaces(owner_did)",
3773
+ "CREATE INDEX IF NOT EXISTS idx_spaces_type ON spaces(type)"
3774
+ ];
3775
+ function applicationKey(appId) {
3776
+ return `${ACCOUNT_REGISTRY_PATH}${appId}`;
3484
3777
  }
3485
- function resolveEntry(entry, prefix, _inheritedExpiryMs, inheritedSpace) {
3486
- const skipPrefixForEntry = entry.skipPrefix === true || entry.service === ENCRYPTION_PERMISSION_SERVICE;
3487
- const resolvedPath = applyPrefix(prefix, entry.path, skipPrefixForEntry);
3488
- const entryExpiryMs = entry.expiry !== void 0 ? parseExpiry(entry.expiry) : void 0;
3489
- return expandPermissionEntry({
3490
- ...entry,
3491
- space: entry.space ?? inheritedSpace,
3492
- path: resolvedPath,
3493
- skipPrefix: true
3494
- }).map((expanded) => ({
3495
- service: expanded.service,
3496
- space: expanded.space ?? inheritedSpace,
3497
- path: expanded.path,
3498
- actions: expanded.actions,
3499
- // Only populate `expiryMs` when the entry had its own expiry override.
3500
- // When absent, callers use the parent (delegation or manifest) expiry
3501
- // which is carried on ResolvedDelegate.expiryMs / ResolvedCapabilities.expiryMs.
3502
- ...entryExpiryMs !== void 0 ? { expiryMs: entryExpiryMs } : {},
3503
- ...entry.description !== void 0 ? { description: entry.description } : {}
3504
- }));
3778
+ function appIdFromKey(key) {
3779
+ return key.startsWith(ACCOUNT_REGISTRY_PATH) ? key.slice(ACCOUNT_REGISTRY_PATH.length) : key;
3505
3780
  }
3506
- function expandVaultPermissionEntry(entry) {
3507
- const byBase = /* @__PURE__ */ new Map();
3508
- for (const action of entry.actions) {
3509
- const expansion = vaultActionExpansion(action);
3510
- for (const base of expansion.bases) {
3511
- const actions = byBase.get(base) ?? [];
3512
- if (!actions.includes(expansion.action)) {
3513
- actions.push(expansion.action);
3514
- }
3515
- byBase.set(base, actions);
3516
- }
3517
- }
3518
- return [...byBase.entries()].map(([base, actions]) => ({
3519
- ...entry,
3520
- service: "tinycloud.kv",
3521
- path: vaultKVPath(base, entry.path),
3522
- actions,
3523
- skipPrefix: true
3524
- }));
3781
+ function applicationFromRecord(key, record) {
3782
+ const manifests = Array.isArray(record.manifests) ? record.manifests : [];
3783
+ const first = manifests[0];
3784
+ return {
3785
+ appId: record.app_id ?? record.appId ?? first?.app_id ?? appIdFromKey(key),
3786
+ manifests,
3787
+ updatedAt: record.updated_at ?? record.updatedAt,
3788
+ name: first?.name,
3789
+ description: first?.description,
3790
+ manifestHash: record.manifest_hash ?? record.manifestHash ?? hashJson(manifests)
3791
+ };
3525
3792
  }
3526
- function vaultActionExpansion(action) {
3527
- const normalized = normalizeVaultAction(action);
3528
- if (normalized === "read" || normalized === "get") {
3529
- return { bases: ["vault"], action: "tinycloud.kv/get" };
3530
- }
3531
- if (normalized === "write" || normalized === "put") {
3532
- return { bases: ["vault"], action: "tinycloud.kv/put" };
3533
- }
3534
- if (normalized === "delete" || normalized === "del") {
3535
- return { bases: ["vault"], action: "tinycloud.kv/del" };
3536
- }
3537
- if (normalized === "list") {
3538
- return { bases: ["vault"], action: "tinycloud.kv/list" };
3539
- }
3540
- if (normalized === "head") {
3541
- return { bases: ["vault"], action: "tinycloud.kv/get" };
3793
+ function indexedApplicationFromRow(row) {
3794
+ const [appId, name, description, updatedAt, manifestJson, manifestHash] = row;
3795
+ return {
3796
+ appId,
3797
+ name: name ?? void 0,
3798
+ description: description ?? void 0,
3799
+ updatedAt: updatedAt ?? void 0,
3800
+ manifests: JSON.parse(manifestJson),
3801
+ manifestHash: manifestHash ?? void 0
3802
+ };
3803
+ }
3804
+ function spaceKey(spaceId) {
3805
+ return `${ACCOUNT_SPACES_PATH}${spaceId}`;
3806
+ }
3807
+ function spaceIdFromKey(key) {
3808
+ return key.startsWith(ACCOUNT_SPACES_PATH) ? key.slice(ACCOUNT_SPACES_PATH.length) : key;
3809
+ }
3810
+ function spaceRecordFromInput(space) {
3811
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3812
+ const accountSpace = "spaceId" in space ? space : {
3813
+ spaceId: space.id,
3814
+ name: space.name ?? space.id.split(":").pop() ?? space.id,
3815
+ ownerDid: space.owner ?? "",
3816
+ type: space.type ?? "discovered",
3817
+ permissions: space.permissions ?? [],
3818
+ status: "active",
3819
+ expiresAt: space.expiresAt
3820
+ };
3821
+ return {
3822
+ space_id: accountSpace.spaceId,
3823
+ name: accountSpace.name,
3824
+ owner_did: accountSpace.ownerDid,
3825
+ type: accountSpace.type,
3826
+ permissions: accountSpace.permissions,
3827
+ status: accountSpace.status,
3828
+ registered_at: accountSpace.registeredAt ?? now,
3829
+ updated_at: now,
3830
+ expires_at: accountSpace.expiresAt instanceof Date ? accountSpace.expiresAt.toISOString() : accountSpace.expiresAt
3831
+ };
3832
+ }
3833
+ function spaceFromRecord(key, record) {
3834
+ const expiresAt = record.expires_at ?? record.expiresAt;
3835
+ return {
3836
+ spaceId: record.space_id ?? record.spaceId ?? spaceIdFromKey(key),
3837
+ name: record.name ?? spaceIdFromKey(key).split(":").pop() ?? spaceIdFromKey(key),
3838
+ ownerDid: record.owner_did ?? record.ownerDid ?? record.owner ?? "",
3839
+ type: record.type ?? "discovered",
3840
+ permissions: Array.isArray(record.permissions) ? record.permissions : [],
3841
+ status: record.status ?? "active",
3842
+ registeredAt: record.registered_at ?? record.registeredAt,
3843
+ updatedAt: record.updated_at ?? record.updatedAt,
3844
+ expiresAt: expiresAt ? new Date(expiresAt) : void 0
3845
+ };
3846
+ }
3847
+ function indexedSpaceFromRow(row) {
3848
+ const [spaceId, name, ownerDid, type, permissionsJson, status, registeredAt, updatedAt, expiresAt] = row;
3849
+ return {
3850
+ spaceId,
3851
+ name,
3852
+ ownerDid,
3853
+ type,
3854
+ permissions: JSON.parse(permissionsJson),
3855
+ status,
3856
+ registeredAt: registeredAt ?? void 0,
3857
+ updatedAt,
3858
+ expiresAt: expiresAt ? new Date(expiresAt) : void 0
3859
+ };
3860
+ }
3861
+ function hashJson(value) {
3862
+ const input = stableJson(value);
3863
+ let hash = 0xcbf29ce484222325n;
3864
+ const prime = 0x100000001b3n;
3865
+ for (let index = 0; index < input.length; index += 1) {
3866
+ hash ^= BigInt(input.charCodeAt(index));
3867
+ hash = BigInt.asUintN(64, hash * prime);
3868
+ }
3869
+ return hash.toString(16).padStart(16, "0");
3870
+ }
3871
+ function stableJson(value) {
3872
+ if (value === null || typeof value !== "object") {
3873
+ return JSON.stringify(value);
3542
3874
  }
3543
- if (normalized === "metadata") {
3544
- return { bases: ["vault"], action: "tinycloud.kv/metadata" };
3875
+ if (Array.isArray(value)) {
3876
+ return `[${value.map(stableJson).join(",")}]`;
3545
3877
  }
3546
- throw new ManifestValidationError(
3547
- `unknown vault action ${JSON.stringify(action)}; expected read, write, delete, get, put, del, list, head, or metadata`
3548
- );
3878
+ const object = value;
3879
+ return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${stableJson(object[key])}`).join(",")}}`;
3549
3880
  }
3550
- function normalizeVaultAction(action) {
3551
- if (action.startsWith(`${VAULT_PERMISSION_SERVICE}/`)) {
3552
- return action.slice(`${VAULT_PERMISSION_SERVICE}/`.length);
3881
+ function indexedDelegationFromRow(row) {
3882
+ const [
3883
+ cid,
3884
+ direction,
3885
+ spaceId,
3886
+ spaceName,
3887
+ counterpartyDid,
3888
+ delegateDid,
3889
+ delegatorDid,
3890
+ path,
3891
+ actionsJson,
3892
+ expiry,
3893
+ status,
3894
+ createdAt
3895
+ ] = row;
3896
+ return {
3897
+ cid,
3898
+ direction,
3899
+ spaceId,
3900
+ spaceName: spaceName ?? void 0,
3901
+ counterpartyDid,
3902
+ delegateDid,
3903
+ delegatorDid: delegatorDid ?? void 0,
3904
+ path,
3905
+ actions: JSON.parse(actionsJson),
3906
+ expiry: new Date(expiry),
3907
+ status,
3908
+ createdAt: createdAt ? new Date(createdAt) : void 0
3909
+ };
3910
+ }
3911
+ function mapDelegation(delegation, space, direction) {
3912
+ return {
3913
+ cid: delegation.cid,
3914
+ direction,
3915
+ spaceId: delegation.spaceId || space.id,
3916
+ spaceName: space.name,
3917
+ counterpartyDid: direction === "granted" ? delegation.delegateDID : delegation.delegatorDID ?? delegation.delegateDID,
3918
+ delegateDid: delegation.delegateDID,
3919
+ delegatorDid: delegation.delegatorDID,
3920
+ path: delegation.path,
3921
+ actions: delegation.actions,
3922
+ expiry: delegation.expiry,
3923
+ status: delegation.isRevoked ? "revoked" : delegation.expiry.getTime() <= Date.now() ? "expired" : "active",
3924
+ createdAt: delegation.createdAt
3925
+ };
3926
+ }
3927
+ function accountErr(error) {
3928
+ return (0, import_sdk_services5.err)((0, import_sdk_services5.serviceError)(error.code, error.message, SERVICE_NAME2, { cause: error.cause, meta: error.meta }));
3929
+ }
3930
+ function isMissingIndexError(error) {
3931
+ return /no such table:/i.test(error.message);
3932
+ }
3933
+ async function ignoreIndexFailure(task) {
3934
+ try {
3935
+ await task();
3936
+ } catch {
3553
3937
  }
3554
- if (action.startsWith("tinycloud.kv/")) {
3555
- return action.slice("tinycloud.kv/".length);
3938
+ }
3939
+
3940
+ // src/index.ts
3941
+ var import_sdk_services7 = require("@tinycloud/sdk-services");
3942
+
3943
+ // src/space.ts
3944
+ async function fetchPeerId(host, spaceId) {
3945
+ const res = await fetch(
3946
+ `${host}/peer/generate/${encodeURIComponent(spaceId)}`
3947
+ );
3948
+ if (!res.ok) {
3949
+ const error = await res.text().catch(() => res.statusText);
3950
+ throw new Error(`Failed to get peer ID: ${res.status} - ${error}`);
3556
3951
  }
3557
- if (action.includes("/")) {
3558
- throw new ManifestValidationError(
3559
- `unknown vault action ${JSON.stringify(action)}; expected a tinycloud.vault or tinycloud.kv action`
3560
- );
3952
+ return res.text();
3953
+ }
3954
+ async function submitHostDelegation(host, headers) {
3955
+ const res = await fetch(`${host}/delegate`, {
3956
+ method: "POST",
3957
+ headers
3958
+ });
3959
+ return {
3960
+ success: res.ok,
3961
+ status: res.status,
3962
+ error: res.ok ? void 0 : await res.text().catch(() => res.statusText)
3963
+ };
3964
+ }
3965
+ async function activateSessionWithHost(host, delegationHeader) {
3966
+ const res = await fetch(`${host}/delegate`, {
3967
+ method: "POST",
3968
+ headers: delegationHeader
3969
+ });
3970
+ if (res.ok) {
3971
+ try {
3972
+ const body = await res.json();
3973
+ return {
3974
+ success: true,
3975
+ status: res.status,
3976
+ activated: body.activated ?? [],
3977
+ skipped: body.skipped ?? []
3978
+ };
3979
+ } catch {
3980
+ return {
3981
+ success: true,
3982
+ status: res.status,
3983
+ activated: [],
3984
+ skipped: []
3985
+ };
3986
+ }
3561
3987
  }
3562
- return action;
3563
- }
3564
- function vaultKVPath(base, path) {
3565
- const normalized = path.startsWith("/") ? path.slice(1) : path;
3566
- return `${base}/${normalized}`;
3567
- }
3568
- function cloneResourceCapability(entry) {
3569
3988
  return {
3570
- service: entry.service,
3571
- space: entry.space,
3572
- path: entry.path,
3573
- actions: [...entry.actions],
3574
- ...entry.expiryMs !== void 0 ? { expiryMs: entry.expiryMs } : {},
3575
- ...entry.description !== void 0 ? { description: entry.description } : {}
3989
+ success: false,
3990
+ status: res.status,
3991
+ error: await res.text().catch(() => res.statusText)
3576
3992
  };
3577
3993
  }
3578
- function clonePermissionEntry(entry) {
3994
+
3995
+ // src/delegations/DelegationManager.ts
3996
+ var DelegationAction = {
3997
+ CREATE: "tinycloud.delegation/create",
3998
+ REVOKE: "tinycloud.delegation/revoke",
3999
+ LIST: "tinycloud.delegation/list",
4000
+ GET: "tinycloud.delegation/get",
4001
+ CHECK: "tinycloud.delegation/check"
4002
+ };
4003
+ function createError(code, message, cause, meta) {
3579
4004
  return {
3580
- service: entry.service,
3581
- ...entry.space !== void 0 ? { space: entry.space } : {},
3582
- path: entry.path,
3583
- actions: [...entry.actions],
3584
- ...entry.skipPrefix !== void 0 ? { skipPrefix: entry.skipPrefix } : {},
3585
- ...entry.expiry !== void 0 ? { expiry: entry.expiry } : {},
3586
- ...entry.description !== void 0 ? { description: entry.description } : {}
4005
+ code,
4006
+ message,
4007
+ service: "delegation",
4008
+ cause,
4009
+ meta
3587
4010
  };
3588
4011
  }
3589
- function dedupeResources(resources) {
3590
- const byKey = /* @__PURE__ */ new Map();
3591
- for (const resource of resources) {
3592
- const key = `${resource.service}\0${resource.space}\0${resource.path}\0${resource.expiryMs ?? ""}`;
3593
- const existing = byKey.get(key);
3594
- if (existing === void 0) {
3595
- byKey.set(key, cloneResourceCapability(resource));
3596
- continue;
4012
+ var DelegationManager = class {
4013
+ /**
4014
+ * Creates a new DelegationManager instance.
4015
+ *
4016
+ * @param config - Configuration including hosts, session, and invoke function
4017
+ */
4018
+ constructor(config) {
4019
+ this.hosts = config.hosts;
4020
+ this.session = config.session;
4021
+ this.invoke = config.invoke;
4022
+ this.fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);
4023
+ }
4024
+ /**
4025
+ * Updates the session (e.g., after re-authentication).
4026
+ *
4027
+ * @param session - New session to use for operations
4028
+ */
4029
+ updateSession(session) {
4030
+ this.session = session;
4031
+ }
4032
+ /**
4033
+ * Gets the primary host URL.
4034
+ */
4035
+ get host() {
4036
+ return this.hosts[0];
4037
+ }
4038
+ /**
4039
+ * Executes an invoke operation against the delegation API.
4040
+ */
4041
+ async invokeOperation(path, action, body) {
4042
+ const headers = this.invoke(this.session, "delegation", path, action);
4043
+ return this.fetchFn(`${this.host}/invoke`, {
4044
+ method: "POST",
4045
+ headers,
4046
+ body
4047
+ });
4048
+ }
4049
+ /**
4050
+ * Creates a new delegation.
4051
+ *
4052
+ * Delegates specific permissions to another DID for a given path.
4053
+ * The delegatee can then use these permissions to access resources
4054
+ * within the specified scope.
4055
+ *
4056
+ * @param params - Parameters for the delegation
4057
+ * @returns Result containing the created Delegation or an error
4058
+ *
4059
+ * @example
4060
+ * ```typescript
4061
+ * const result = await manager.create({
4062
+ * delegateDID: bob.did,
4063
+ * path: "documents/shared/",
4064
+ * actions: ["tinycloud.kv/get", "tinycloud.kv/put"],
4065
+ * expiry: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days
4066
+ * });
4067
+ * ```
4068
+ */
4069
+ async create(params) {
4070
+ if (!params.delegateDID) {
4071
+ return {
4072
+ ok: false,
4073
+ error: createError(
4074
+ DelegationErrorCodes.INVALID_INPUT,
4075
+ "delegateDID is required"
4076
+ )
4077
+ };
3597
4078
  }
3598
- const seen = new Set(existing.actions);
3599
- for (const action of resource.actions) {
3600
- if (!seen.has(action)) {
3601
- existing.actions.push(action);
3602
- seen.add(action);
4079
+ if (!params.path) {
4080
+ return {
4081
+ ok: false,
4082
+ error: createError(
4083
+ DelegationErrorCodes.INVALID_INPUT,
4084
+ "path is required"
4085
+ )
4086
+ };
4087
+ }
4088
+ if (!params.actions || params.actions.length === 0) {
4089
+ return {
4090
+ ok: false,
4091
+ error: createError(
4092
+ DelegationErrorCodes.INVALID_INPUT,
4093
+ "at least one action is required"
4094
+ )
4095
+ };
4096
+ }
4097
+ try {
4098
+ const body = JSON.stringify({
4099
+ delegateDID: params.delegateDID,
4100
+ path: params.path,
4101
+ actions: params.actions,
4102
+ expiry: params.expiry?.toISOString(),
4103
+ disableSubDelegation: params.disableSubDelegation ?? false,
4104
+ statement: params.statement
4105
+ });
4106
+ const response = await this.invokeOperation(
4107
+ params.path,
4108
+ DelegationAction.CREATE,
4109
+ body
4110
+ );
4111
+ if (!response.ok) {
4112
+ const errorText = await response.text();
4113
+ return {
4114
+ ok: false,
4115
+ error: createError(
4116
+ DelegationErrorCodes.CREATION_FAILED,
4117
+ `Failed to create delegation: ${response.status} - ${errorText}`,
4118
+ void 0,
4119
+ { status: response.status, path: params.path }
4120
+ )
4121
+ };
4122
+ }
4123
+ const apiResponse = await response.json();
4124
+ const delegation = {
4125
+ cid: apiResponse.cid ?? "",
4126
+ delegateDID: params.delegateDID,
4127
+ spaceId: this.session.spaceId,
4128
+ path: params.path,
4129
+ actions: params.actions,
4130
+ expiry: params.expiry ?? new Date(Date.now() + EXPIRY.SHARE_MS),
4131
+ isRevoked: false,
4132
+ allowSubDelegation: !(params.disableSubDelegation ?? false),
4133
+ createdAt: /* @__PURE__ */ new Date()
4134
+ };
4135
+ return { ok: true, data: delegation };
4136
+ } catch (error) {
4137
+ if (error instanceof Error && error.name === "AbortError") {
4138
+ return {
4139
+ ok: false,
4140
+ error: createError(
4141
+ DelegationErrorCodes.ABORTED,
4142
+ "Request aborted",
4143
+ error
4144
+ )
4145
+ };
4146
+ }
4147
+ return {
4148
+ ok: false,
4149
+ error: createError(
4150
+ DelegationErrorCodes.NETWORK_ERROR,
4151
+ `Network error during delegation creation: ${String(error)}`,
4152
+ error instanceof Error ? error : void 0
4153
+ )
4154
+ };
4155
+ }
4156
+ }
4157
+ /**
4158
+ * Revokes an existing delegation.
4159
+ *
4160
+ * Once revoked, the delegation can no longer be used to access resources.
4161
+ * This also invalidates any sub-delegations derived from this delegation.
4162
+ *
4163
+ * @param cid - The CID of the delegation to revoke
4164
+ * @returns Result indicating success or an error
4165
+ *
4166
+ * @example
4167
+ * ```typescript
4168
+ * const result = await manager.revoke("bafy...");
4169
+ * if (result.ok) {
4170
+ * console.log("Delegation revoked successfully");
4171
+ * }
4172
+ * ```
4173
+ */
4174
+ async revoke(cid) {
4175
+ if (!cid) {
4176
+ return {
4177
+ ok: false,
4178
+ error: createError(
4179
+ DelegationErrorCodes.INVALID_INPUT,
4180
+ "cid is required"
4181
+ )
4182
+ };
4183
+ }
4184
+ try {
4185
+ const body = JSON.stringify({ cid });
4186
+ const response = await this.invokeOperation(
4187
+ cid,
4188
+ DelegationAction.REVOKE,
4189
+ body
4190
+ );
4191
+ if (!response.ok) {
4192
+ const errorText = await response.text();
4193
+ if (response.status === 404) {
4194
+ return {
4195
+ ok: false,
4196
+ error: createError(
4197
+ DelegationErrorCodes.NOT_FOUND,
4198
+ `Delegation not found: ${cid}`
4199
+ )
4200
+ };
4201
+ }
4202
+ return {
4203
+ ok: false,
4204
+ error: createError(
4205
+ DelegationErrorCodes.REVOCATION_FAILED,
4206
+ `Failed to revoke delegation: ${response.status} - ${errorText}`,
4207
+ void 0,
4208
+ { status: response.status, cid }
4209
+ )
4210
+ };
3603
4211
  }
4212
+ return { ok: true, data: void 0 };
4213
+ } catch (error) {
4214
+ if (error instanceof Error && error.name === "AbortError") {
4215
+ return {
4216
+ ok: false,
4217
+ error: createError(
4218
+ DelegationErrorCodes.ABORTED,
4219
+ "Request aborted",
4220
+ error
4221
+ )
4222
+ };
4223
+ }
4224
+ return {
4225
+ ok: false,
4226
+ error: createError(
4227
+ DelegationErrorCodes.NETWORK_ERROR,
4228
+ `Network error during delegation revocation: ${String(error)}`,
4229
+ error instanceof Error ? error : void 0
4230
+ )
4231
+ };
3604
4232
  }
3605
- if (existing.description === void 0 && resource.description !== void 0) {
3606
- existing.description = resource.description;
3607
- }
3608
- }
3609
- return [...byKey.values()];
3610
- }
3611
- function capabilitiesReadPermission(space) {
3612
- return {
3613
- service: "tinycloud.capabilities",
3614
- space,
3615
- path: "",
3616
- actions: ["tinycloud.capabilities/read"]
3617
- };
3618
- }
3619
- function withCapabilitiesReadForSpaces(resources) {
3620
- if (resources.length === 0) {
3621
- return [];
3622
- }
3623
- const spaces = new Set(
3624
- resources.filter((resource) => resource.service !== ENCRYPTION_PERMISSION_SERVICE).map((resource) => resource.space)
3625
- );
3626
- return dedupeResources([
3627
- ...resources,
3628
- ...[...spaces].map(capabilitiesReadPermission)
3629
- ]);
3630
- }
3631
- function accountRegistryPermission() {
3632
- return {
3633
- service: "tinycloud.kv",
3634
- space: ACCOUNT_REGISTRY_SPACE,
3635
- path: ACCOUNT_REGISTRY_PATH,
3636
- actions: ["tinycloud.kv/get", "tinycloud.kv/put", "tinycloud.kv/list"]
3637
- };
3638
- }
3639
- function composeManifestRequest(inputs, options = {}) {
3640
- if (!Array.isArray(inputs) || inputs.length === 0) {
3641
- throw new ManifestValidationError(
3642
- "composeManifestRequest requires at least one manifest"
3643
- );
3644
4233
  }
3645
- const includeAccountRegistryPermissions = options.includeAccountRegistryPermissions ?? true;
3646
- const manifests = inputs.map(validateManifest);
3647
- const resolved = manifests.map(resolveManifest);
3648
- const resources = resolved.flatMap((entry) => entry.resources);
3649
- const delegationTargets = resolved.flatMap(
3650
- (entry) => entry.additionalDelegates.map((delegate) => ({
3651
- ...delegate,
3652
- permissions: dedupeResources(delegate.permissions)
3653
- }))
3654
- );
3655
- if (includeAccountRegistryPermissions) {
3656
- resources.push(accountRegistryPermission());
3657
- }
3658
- const resourcesWithImplicitCapabilities = withCapabilitiesReadForSpaces(resources);
3659
- const manifestsByAppId = /* @__PURE__ */ new Map();
3660
- for (const manifest of manifests) {
3661
- const current = manifestsByAppId.get(manifest.app_id);
3662
- if (current === void 0) {
3663
- manifestsByAppId.set(manifest.app_id, [manifest]);
3664
- } else {
3665
- current.push(manifest);
4234
+ /**
4235
+ * Lists all delegations for the current session's space.
4236
+ *
4237
+ * Returns both delegations created by the current user (as delegator)
4238
+ * and delegations granted to the current user (as delegatee).
4239
+ *
4240
+ * @returns Result containing an array of Delegations or an error
4241
+ *
4242
+ * @example
4243
+ * ```typescript
4244
+ * const result = await manager.list();
4245
+ * if (result.ok) {
4246
+ * for (const delegation of result.data) {
4247
+ * console.log(`${delegation.cid}: ${delegation.path} -> ${delegation.delegateDID}`);
4248
+ * }
4249
+ * }
4250
+ * ```
4251
+ */
4252
+ async list() {
4253
+ try {
4254
+ const response = await this.invokeOperation("", DelegationAction.LIST);
4255
+ if (!response.ok) {
4256
+ const errorText = await response.text();
4257
+ return {
4258
+ ok: false,
4259
+ error: createError(
4260
+ DelegationErrorCodes.NETWORK_ERROR,
4261
+ `Failed to list delegations: ${response.status} - ${errorText}`,
4262
+ void 0,
4263
+ { status: response.status }
4264
+ )
4265
+ };
4266
+ }
4267
+ const data = await response.json();
4268
+ const delegations = data.map((item) => ({
4269
+ cid: item.cid,
4270
+ delegateDID: item.delegateDID,
4271
+ delegatorDID: item.delegatorDID,
4272
+ spaceId: item.spaceId,
4273
+ path: item.path,
4274
+ actions: item.actions,
4275
+ expiry: new Date(item.expiry),
4276
+ isRevoked: item.isRevoked,
4277
+ createdAt: item.createdAt ? new Date(item.createdAt) : void 0,
4278
+ parentCid: item.parentCid,
4279
+ allowSubDelegation: item.allowSubDelegation
4280
+ }));
4281
+ return { ok: true, data: delegations };
4282
+ } catch (error) {
4283
+ if (error instanceof Error && error.name === "AbortError") {
4284
+ return {
4285
+ ok: false,
4286
+ error: createError(
4287
+ DelegationErrorCodes.ABORTED,
4288
+ "Request aborted",
4289
+ error
4290
+ )
4291
+ };
4292
+ }
4293
+ return {
4294
+ ok: false,
4295
+ error: createError(
4296
+ DelegationErrorCodes.NETWORK_ERROR,
4297
+ `Network error during delegation list: ${String(error)}`,
4298
+ error instanceof Error ? error : void 0
4299
+ )
4300
+ };
3666
4301
  }
3667
4302
  }
3668
- const registryRecords = includeAccountRegistryPermissions ? [...manifestsByAppId.entries()].map(([app_id, appManifests]) => ({
3669
- key: `${ACCOUNT_REGISTRY_PATH}${app_id}`,
3670
- app_id,
3671
- manifests: appManifests.map((manifest) => ({
3672
- ...manifest,
3673
- permissions: manifest.permissions?.map(clonePermissionEntry)
3674
- }))
3675
- })) : [];
3676
- return {
3677
- manifests,
3678
- resources: resourcesWithImplicitCapabilities,
3679
- delegationTargets,
3680
- registryRecords,
3681
- expiryMs: Math.max(...resolved.map((entry) => entry.expiryMs)),
3682
- includePublicSpace: resolved.some((entry) => entry.includePublicSpace)
3683
- };
3684
- }
3685
- function resourceCapabilitiesToAbilitiesMap(resources) {
3686
- const out = {};
3687
- for (const r of resources) {
3688
- const shortService = SERVICE_LONG_TO_SHORT[r.service];
3689
- if (shortService === void 0) {
3690
- throw new ManifestValidationError(
3691
- `unknown service '${r.service}' \u2014 no short-form mapping. Known services: ${Object.keys(SERVICE_LONG_TO_SHORT).join(", ")}`
4303
+ /**
4304
+ * Gets the full delegation chain for a given delegation.
4305
+ *
4306
+ * Returns the chain of delegations from the root (original delegator)
4307
+ * to the specified delegation, including all intermediate sub-delegations.
4308
+ *
4309
+ * @param cid - The CID of the delegation to get the chain for
4310
+ * @returns Result containing the DelegationChain or an error
4311
+ *
4312
+ * @example
4313
+ * ```typescript
4314
+ * const result = await manager.getChain("bafy...");
4315
+ * if (result.ok) {
4316
+ * console.log("Chain length:", result.data.length);
4317
+ * for (const delegation of result.data) {
4318
+ * console.log(`- ${delegation.delegatorDID} -> ${delegation.delegateDID}`);
4319
+ * }
4320
+ * }
4321
+ * ```
4322
+ */
4323
+ async getChain(cid) {
4324
+ if (!cid) {
4325
+ return {
4326
+ ok: false,
4327
+ error: createError(
4328
+ DelegationErrorCodes.INVALID_INPUT,
4329
+ "cid is required"
4330
+ )
4331
+ };
4332
+ }
4333
+ try {
4334
+ const body = JSON.stringify({ cid, includeChain: true });
4335
+ const response = await this.invokeOperation(
4336
+ cid,
4337
+ DelegationAction.GET,
4338
+ body
3692
4339
  );
4340
+ if (!response.ok) {
4341
+ const errorText = await response.text();
4342
+ if (response.status === 404) {
4343
+ return {
4344
+ ok: false,
4345
+ error: createError(
4346
+ DelegationErrorCodes.NOT_FOUND,
4347
+ `Delegation not found: ${cid}`
4348
+ )
4349
+ };
4350
+ }
4351
+ return {
4352
+ ok: false,
4353
+ error: createError(
4354
+ DelegationErrorCodes.NETWORK_ERROR,
4355
+ `Failed to get delegation chain: ${response.status} - ${errorText}`,
4356
+ void 0,
4357
+ { status: response.status, cid }
4358
+ )
4359
+ };
4360
+ }
4361
+ const data = await response.json();
4362
+ const chain = data.chain.map((item) => ({
4363
+ cid: item.cid,
4364
+ delegateDID: item.delegateDID,
4365
+ delegatorDID: item.delegatorDID,
4366
+ spaceId: item.spaceId,
4367
+ path: item.path,
4368
+ actions: item.actions,
4369
+ expiry: new Date(item.expiry),
4370
+ isRevoked: item.isRevoked,
4371
+ createdAt: item.createdAt ? new Date(item.createdAt) : void 0,
4372
+ parentCid: item.parentCid,
4373
+ allowSubDelegation: item.allowSubDelegation
4374
+ }));
4375
+ return { ok: true, data: chain };
4376
+ } catch (error) {
4377
+ if (error instanceof Error && error.name === "AbortError") {
4378
+ return {
4379
+ ok: false,
4380
+ error: createError(
4381
+ DelegationErrorCodes.ABORTED,
4382
+ "Request aborted",
4383
+ error
4384
+ )
4385
+ };
4386
+ }
4387
+ return {
4388
+ ok: false,
4389
+ error: createError(
4390
+ DelegationErrorCodes.NETWORK_ERROR,
4391
+ `Network error during chain retrieval: ${String(error)}`,
4392
+ error instanceof Error ? error : void 0
4393
+ )
4394
+ };
3693
4395
  }
3694
- if (out[shortService] === void 0) {
3695
- out[shortService] = {};
4396
+ }
4397
+ /**
4398
+ * Checks if the current session has permission for a given path and action.
4399
+ *
4400
+ * This can be used to verify permissions before attempting an operation,
4401
+ * or to implement custom access control logic.
4402
+ *
4403
+ * @param path - The resource path to check
4404
+ * @param action - The action to check (e.g., "tinycloud.kv/get")
4405
+ * @returns Result containing a boolean indicating permission or an error
4406
+ *
4407
+ * @example
4408
+ * ```typescript
4409
+ * const result = await manager.checkPermission("documents/private/", "tinycloud.kv/put");
4410
+ * if (result.ok && result.data) {
4411
+ * console.log("Permission granted");
4412
+ * } else {
4413
+ * console.log("Permission denied");
4414
+ * }
4415
+ * ```
4416
+ */
4417
+ async checkPermission(path, action) {
4418
+ if (!path) {
4419
+ return {
4420
+ ok: false,
4421
+ error: createError(
4422
+ DelegationErrorCodes.INVALID_INPUT,
4423
+ "path is required"
4424
+ )
4425
+ };
3696
4426
  }
3697
- const pathsMap = out[shortService];
3698
- const existing = pathsMap[r.path];
3699
- if (existing === void 0) {
3700
- pathsMap[r.path] = [...r.actions];
3701
- } else {
3702
- const seen = new Set(existing);
3703
- for (const action of r.actions) {
3704
- if (!seen.has(action)) {
3705
- existing.push(action);
3706
- seen.add(action);
4427
+ if (!action) {
4428
+ return {
4429
+ ok: false,
4430
+ error: createError(
4431
+ DelegationErrorCodes.INVALID_INPUT,
4432
+ "action is required"
4433
+ )
4434
+ };
4435
+ }
4436
+ try {
4437
+ const body = JSON.stringify({ path, action });
4438
+ const response = await this.invokeOperation(
4439
+ path,
4440
+ DelegationAction.CHECK,
4441
+ body
4442
+ );
4443
+ if (!response.ok) {
4444
+ if (response.status === 403) {
4445
+ return { ok: true, data: false };
3707
4446
  }
4447
+ const errorText = await response.text();
4448
+ return {
4449
+ ok: false,
4450
+ error: createError(
4451
+ DelegationErrorCodes.NETWORK_ERROR,
4452
+ `Failed to check permission: ${response.status} - ${errorText}`,
4453
+ void 0,
4454
+ { status: response.status, path, action }
4455
+ )
4456
+ };
3708
4457
  }
4458
+ const data = await response.json();
4459
+ return { ok: true, data: data.allowed };
4460
+ } catch (error) {
4461
+ if (error instanceof Error && error.name === "AbortError") {
4462
+ return {
4463
+ ok: false,
4464
+ error: createError(
4465
+ DelegationErrorCodes.ABORTED,
4466
+ "Request aborted",
4467
+ error
4468
+ )
4469
+ };
4470
+ }
4471
+ return {
4472
+ ok: false,
4473
+ error: createError(
4474
+ DelegationErrorCodes.NETWORK_ERROR,
4475
+ `Network error during permission check: ${String(error)}`,
4476
+ error instanceof Error ? error : void 0
4477
+ )
4478
+ };
3709
4479
  }
3710
4480
  }
3711
- return out;
3712
- }
3713
- function resourceCapabilitiesToSpaceAbilitiesMap(resources) {
3714
- const grouped = /* @__PURE__ */ new Map();
3715
- for (const resource of resources) {
3716
- const entries = grouped.get(resource.space);
3717
- if (entries === void 0) {
3718
- grouped.set(resource.space, [resource]);
3719
- } else {
3720
- entries.push(resource);
3721
- }
3722
- }
3723
- const out = {};
3724
- for (const [space, entries] of grouped.entries()) {
3725
- out[space] = resourceCapabilitiesToAbilitiesMap(entries);
3726
- }
3727
- return out;
3728
- }
3729
- function manifestAbilitiesUnion(resolved) {
3730
- const all = [...resolved.resources];
3731
- for (const delegate of resolved.additionalDelegates) {
3732
- for (const perm of delegate.permissions) {
3733
- all.push(perm);
3734
- }
4481
+ };
4482
+
4483
+ // src/delegations/SharingService.schema.ts
4484
+ var import_zod5 = require("zod");
4485
+ var EncodedShareDataSchema = import_zod5.z.object({
4486
+ /** Private key in JWK format (must include d parameter) */
4487
+ key: JWKSchema.refine(
4488
+ (jwk) => typeof jwk.d === "string" && jwk.d.length > 0,
4489
+ { message: "JWK must include private key (d parameter)" }
4490
+ ),
4491
+ /** DID of the key */
4492
+ keyDid: import_zod5.z.string().min(1, "keyDid is required"),
4493
+ /** The delegation granting access */
4494
+ delegation: DelegationSchema,
4495
+ /** Resource path this link grants access to */
4496
+ path: import_zod5.z.string().min(1, "path is required"),
4497
+ /** TinyCloud host URL */
4498
+ host: import_zod5.z.string().url("host must be a valid URL"),
4499
+ /** Space ID */
4500
+ spaceId: import_zod5.z.string().min(1, "spaceId is required"),
4501
+ /** Schema version (must be 1) */
4502
+ version: import_zod5.z.literal(1)
4503
+ });
4504
+ var ReceiveOptionsSchema = import_zod5.z.object({
4505
+ /**
4506
+ * Whether to automatically create a sub-delegation to the current session key.
4507
+ * Default: true
4508
+ */
4509
+ autoSubdelegate: import_zod5.z.boolean().optional(),
4510
+ /**
4511
+ * Whether to use the current session key for operations (requires autoSubdelegate).
4512
+ * Default: true
4513
+ */
4514
+ useSessionKey: import_zod5.z.boolean().optional(),
4515
+ /**
4516
+ * Ingestion options passed to CapabilityKeyRegistry.
4517
+ */
4518
+ ingestOptions: IngestOptionsSchema.optional()
4519
+ });
4520
+ var SharingServiceConfigSchema = import_zod5.z.object({
4521
+ /** TinyCloud host URLs */
4522
+ hosts: import_zod5.z.array(import_zod5.z.string().url()).min(1, "At least one host URL is required"),
4523
+ /**
4524
+ * Active session for authentication.
4525
+ * Required for generate(), optional for receive().
4526
+ */
4527
+ session: import_zod5.z.unknown().refine(
4528
+ (val) => val === void 0 || val !== null && typeof val === "object",
4529
+ { message: "Expected a ServiceSession object or undefined" }
4530
+ ).optional(),
4531
+ /** Platform-specific invoke function */
4532
+ invoke: import_zod5.z.unknown().refine((val) => typeof val === "function", {
4533
+ message: "Expected an invoke function"
4534
+ }),
4535
+ /** Optional custom fetch implementation */
4536
+ fetch: import_zod5.z.unknown().refine(
4537
+ (val) => val === void 0 || typeof val === "function",
4538
+ { message: "Expected a fetch function or undefined" }
4539
+ ).optional(),
4540
+ /** Key provider for cryptographic operations */
4541
+ keyProvider: KeyProviderSchema,
4542
+ /** Capability key registry for key/delegation management */
4543
+ registry: import_zod5.z.unknown().refine(
4544
+ (val) => val !== null && typeof val === "object",
4545
+ { message: "Expected an ICapabilityKeyRegistry object" }
4546
+ ),
4547
+ /**
4548
+ * Delegation manager for creating delegations.
4549
+ * Required for generate(), optional for receive().
4550
+ */
4551
+ delegationManager: import_zod5.z.unknown().refine(
4552
+ (val) => val === void 0 || val !== null && typeof val === "object",
4553
+ { message: "Expected a DelegationManager object or undefined" }
4554
+ ).optional(),
4555
+ /** Factory for creating KV service instances */
4556
+ createKVService: import_zod5.z.unknown().refine(
4557
+ (val) => typeof val === "function",
4558
+ { message: "Expected a createKVService factory function" }
4559
+ ),
4560
+ /** Base URL for sharing links (e.g., "https://share.myapp.com") */
4561
+ baseUrl: import_zod5.z.string().optional(),
4562
+ /**
4563
+ * Custom delegation creation function.
4564
+ */
4565
+ createDelegation: import_zod5.z.unknown().refine((val) => val === void 0 || typeof val === "function", {
4566
+ message: "Expected a createDelegation function or undefined"
4567
+ }).optional(),
4568
+ /**
4569
+ * WASM function for client-side delegation creation.
4570
+ */
4571
+ createDelegationWasm: import_zod5.z.unknown().refine((val) => val === void 0 || typeof val === "function", {
4572
+ message: "Expected a createDelegationWasm function or undefined"
4573
+ }).optional(),
4574
+ /**
4575
+ * Path prefix for KV operations.
4576
+ */
4577
+ pathPrefix: import_zod5.z.string().optional(),
4578
+ /**
4579
+ * Session expiry time.
4580
+ */
4581
+ sessionExpiry: import_zod5.z.date().optional(),
4582
+ /**
4583
+ * Callback to create a DIRECT delegation from wallet to share key.
4584
+ * This is the preferred method for long-lived share links because it
4585
+ * bypasses the session delegation chain entirely.
4586
+ */
4587
+ onRootDelegationNeeded: import_zod5.z.unknown().refine((val) => val === void 0 || typeof val === "function", {
4588
+ message: "Expected an onRootDelegationNeeded function or undefined"
4589
+ }).optional()
4590
+ });
4591
+ function validateEncodedShareData(data) {
4592
+ const result = EncodedShareDataSchema.safeParse(data);
4593
+ if (!result.success) {
4594
+ return {
4595
+ ok: false,
4596
+ error: {
4597
+ code: DelegationErrorCodes.VALIDATION_ERROR,
4598
+ message: `Invalid share data: ${result.error.message}`,
4599
+ service: "delegation",
4600
+ meta: { issues: result.error.issues }
4601
+ }
4602
+ };
3735
4603
  }
3736
- return resourceCapabilitiesToAbilitiesMap(all);
4604
+ return { ok: true, data: result.data };
3737
4605
  }
3738
4606
 
3739
4607
  // src/delegations/SharingService.ts
@@ -3914,13 +4782,13 @@ var SharingService = class {
3914
4782
  )
3915
4783
  };
3916
4784
  }
3917
- } catch (err5) {
4785
+ } catch (err6) {
3918
4786
  return {
3919
4787
  ok: false,
3920
4788
  error: createError2(
3921
4789
  DelegationErrorCodes.CREATION_FAILED,
3922
- `Failed to generate session key for share: ${err5 instanceof Error ? err5.message : String(err5)}`,
3923
- err5 instanceof Error ? err5 : void 0
4790
+ `Failed to generate session key for share: ${err6 instanceof Error ? err6.message : String(err6)}`,
4791
+ err6 instanceof Error ? err6 : void 0
3924
4792
  )
3925
4793
  };
3926
4794
  }
@@ -3966,7 +4834,7 @@ var SharingService = class {
3966
4834
  }
3967
4835
  delegation = parsed;
3968
4836
  }
3969
- } catch (err5) {
4837
+ } catch (err6) {
3970
4838
  const fallbackResult = await this.handleSessionExtensionFallback(requestedExpiry);
3971
4839
  expiry = fallbackResult.expiry;
3972
4840
  const delegationResult = await this.createSessionDelegation(plainDID, fullPath, actions, expiry);
@@ -4164,13 +5032,13 @@ var SharingService = class {
4164
5032
  allowSubDelegation: true,
4165
5033
  createdAt: /* @__PURE__ */ new Date()
4166
5034
  };
4167
- } catch (err5) {
5035
+ } catch (err6) {
4168
5036
  return {
4169
5037
  ok: false,
4170
5038
  error: createError2(
4171
5039
  DelegationErrorCodes.CREATION_FAILED,
4172
- `Failed to create delegation via WASM: ${err5 instanceof Error ? err5.message : String(err5)}`,
4173
- err5 instanceof Error ? err5 : void 0
5040
+ `Failed to create delegation via WASM: ${err6 instanceof Error ? err6.message : String(err6)}`,
5041
+ err6 instanceof Error ? err6 : void 0
4174
5042
  )
4175
5043
  };
4176
5044
  }
@@ -4251,8 +5119,8 @@ var SharingService = class {
4251
5119
  let activeKey = keyInfo;
4252
5120
  if (autoSubdelegate && useSessionKey && this.session) {
4253
5121
  try {
4254
- } catch (err5) {
4255
- console.warn("Auto-subdelegation failed, using ingested key directly:", err5);
5122
+ } catch (err6) {
5123
+ console.warn("Auto-subdelegation failed, using ingested key directly:", err6);
4256
5124
  }
4257
5125
  }
4258
5126
  const authHeader = shareData.delegation.authHeader ?? `Bearer ${shareData.delegation.cid}`;
@@ -4350,26 +5218,26 @@ var SharingService = class {
4350
5218
  let jsonString;
4351
5219
  try {
4352
5220
  jsonString = base64UrlDecode(base64Data);
4353
- } catch (err5) {
5221
+ } catch (err6) {
4354
5222
  return {
4355
5223
  ok: false,
4356
5224
  error: createError2(
4357
5225
  DelegationErrorCodes.INVALID_TOKEN,
4358
- `Failed to decode base64 data: ${err5 instanceof Error ? err5.message : String(err5)}`,
4359
- err5 instanceof Error ? err5 : void 0
5226
+ `Failed to decode base64 data: ${err6 instanceof Error ? err6.message : String(err6)}`,
5227
+ err6 instanceof Error ? err6 : void 0
4360
5228
  )
4361
5229
  };
4362
5230
  }
4363
5231
  let parsed;
4364
5232
  try {
4365
5233
  parsed = JSON.parse(jsonString);
4366
- } catch (err5) {
5234
+ } catch (err6) {
4367
5235
  return {
4368
5236
  ok: false,
4369
5237
  error: createError2(
4370
5238
  DelegationErrorCodes.INVALID_TOKEN,
4371
- `Failed to parse share data JSON: ${err5 instanceof Error ? err5.message : String(err5)}`,
4372
- err5 instanceof Error ? err5 : void 0
5239
+ `Failed to parse share data JSON: ${err6 instanceof Error ? err6.message : String(err6)}`,
5240
+ err6 instanceof Error ? err6 : void 0
4373
5241
  )
4374
5242
  };
4375
5243
  }
@@ -4396,8 +5264,8 @@ function createSharingService(config) {
4396
5264
  }
4397
5265
 
4398
5266
  // src/authorization/CapabilityKeyRegistry.ts
4399
- var import_sdk_services5 = require("@tinycloud/sdk-services");
4400
- var SERVICE_NAME2 = "capability-key-registry";
5267
+ var import_sdk_services6 = require("@tinycloud/sdk-services");
5268
+ var SERVICE_NAME3 = "capability-key-registry";
4401
5269
  var CapabilityKeyRegistryErrorCodes = {
4402
5270
  /** Key not found in registry */
4403
5271
  KEY_NOT_FOUND: "KEY_NOT_FOUND",
@@ -4614,11 +5482,11 @@ var CapabilityKeyRegistry = class {
4614
5482
  revokeDelegation(cid) {
4615
5483
  const stored = this.store.byCid.get(cid);
4616
5484
  if (!stored) {
4617
- return (0, import_sdk_services5.err)(
4618
- (0, import_sdk_services5.serviceError)(
5485
+ return (0, import_sdk_services6.err)(
5486
+ (0, import_sdk_services6.serviceError)(
4619
5487
  CapabilityKeyRegistryErrorCodes.KEY_NOT_FOUND,
4620
5488
  `Delegation not found: ${cid}`,
4621
- SERVICE_NAME2
5489
+ SERVICE_NAME3
4622
5490
  )
4623
5491
  );
4624
5492
  }
@@ -4637,7 +5505,7 @@ var CapabilityKeyRegistry = class {
4637
5505
  }
4638
5506
  }
4639
5507
  }
4640
- return (0, import_sdk_services5.ok)(void 0);
5508
+ return (0, import_sdk_services6.ok)(void 0);
4641
5509
  }
4642
5510
  // ===========================================================================
4643
5511
  // Search
@@ -4866,8 +5734,8 @@ async function checkNodeInfo(host, sdkProtocol, fetchFn = globalThis.fetch.bind(
4866
5734
  response = await fetchFn(`${host}/info`, {
4867
5735
  signal: AbortSignal.timeout(5e3)
4868
5736
  });
4869
- } catch (err5) {
4870
- throw new VersionCheckError(host, err5);
5737
+ } catch (err6) {
5738
+ throw new VersionCheckError(host, err6);
4871
5739
  }
4872
5740
  if (!response.ok) {
4873
5741
  throw new VersionCheckError(host);
@@ -5400,6 +6268,7 @@ function parseRecapCapabilities(parseWasm, siwe) {
5400
6268
  0 && (module.exports = {
5401
6269
  ACCOUNT_REGISTRY_PATH,
5402
6270
  ACCOUNT_REGISTRY_SPACE,
6271
+ AccountService,
5403
6272
  AutoApproveSpaceCreationHandler,
5404
6273
  CapabilityKeyRegistry,
5405
6274
  CapabilityKeyRegistryErrorCodes,