@tinycloud/sdk-core 2.4.0-beta.1 → 2.4.0-beta.10
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 +2216 -1355
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +200 -59
- package/dist/index.d.ts +200 -59
- package/dist/index.js +2212 -1348
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -970,6 +970,7 @@ var SpaceService = class {
|
|
|
970
970
|
this._userDid = config.userDid;
|
|
971
971
|
this.sharingService = config.sharingService;
|
|
972
972
|
this.createDelegationFn = config.createDelegation;
|
|
973
|
+
this.onSpaceRegisteredFn = config.onSpaceRegistered;
|
|
973
974
|
}
|
|
974
975
|
/**
|
|
975
976
|
* Update the service configuration.
|
|
@@ -985,6 +986,7 @@ var SpaceService = class {
|
|
|
985
986
|
if (config.userDid !== void 0) this._userDid = config.userDid;
|
|
986
987
|
if (config.sharingService) this.sharingService = config.sharingService;
|
|
987
988
|
if (config.createDelegation) this.createDelegationFn = config.createDelegation;
|
|
989
|
+
if (config.onSpaceRegistered) this.onSpaceRegisteredFn = config.onSpaceRegistered;
|
|
988
990
|
this.spaceCache.clear();
|
|
989
991
|
this.infoCache.clear();
|
|
990
992
|
}
|
|
@@ -1035,6 +1037,9 @@ var SpaceService = class {
|
|
|
1035
1037
|
spaces.push(...delegatedSpaces);
|
|
1036
1038
|
}
|
|
1037
1039
|
const uniqueSpaces = this.deduplicateSpaces(spaces);
|
|
1040
|
+
for (const space of uniqueSpaces) {
|
|
1041
|
+
this.notifySpaceRegistered(space);
|
|
1042
|
+
}
|
|
1038
1043
|
return ok(uniqueSpaces);
|
|
1039
1044
|
} catch (error) {
|
|
1040
1045
|
return err(
|
|
@@ -1232,6 +1237,7 @@ var SpaceService = class {
|
|
|
1232
1237
|
permissions: ["*"]
|
|
1233
1238
|
};
|
|
1234
1239
|
this.infoCache.set(spaceInfo.id, { info: spaceInfo, cachedAt: Date.now() });
|
|
1240
|
+
this.notifySpaceRegistered(spaceInfo);
|
|
1235
1241
|
return ok(spaceInfo);
|
|
1236
1242
|
} catch (error) {
|
|
1237
1243
|
return err(
|
|
@@ -1244,6 +1250,11 @@ var SpaceService = class {
|
|
|
1244
1250
|
);
|
|
1245
1251
|
}
|
|
1246
1252
|
}
|
|
1253
|
+
notifySpaceRegistered(space) {
|
|
1254
|
+
if (!this.onSpaceRegisteredFn) return;
|
|
1255
|
+
void Promise.resolve(this.onSpaceRegisteredFn(space)).catch(() => {
|
|
1256
|
+
});
|
|
1257
|
+
}
|
|
1247
1258
|
// ===========================================================================
|
|
1248
1259
|
// Get Space
|
|
1249
1260
|
// ===========================================================================
|
|
@@ -2151,1474 +2162,2326 @@ var TinyCloud = class _TinyCloud {
|
|
|
2151
2162
|
}
|
|
2152
2163
|
};
|
|
2153
2164
|
|
|
2154
|
-
// src/
|
|
2165
|
+
// src/account/AccountService.ts
|
|
2155
2166
|
import {
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
ok as ok4,
|
|
2160
|
-
err as err4,
|
|
2161
|
-
serviceError as serviceError4,
|
|
2162
|
-
ErrorCodes as ErrorCodes2,
|
|
2163
|
-
defaultRetryPolicy as defaultRetryPolicy2,
|
|
2164
|
-
SQLService as SQLService2,
|
|
2165
|
-
DatabaseHandle,
|
|
2166
|
-
SQLAction,
|
|
2167
|
-
DuckDbService as DuckDbService2,
|
|
2168
|
-
DuckDbDatabaseHandle,
|
|
2169
|
-
DuckDbAction,
|
|
2170
|
-
HooksService as HooksService2,
|
|
2171
|
-
DataVaultService,
|
|
2172
|
-
VaultHeaders,
|
|
2173
|
-
VaultPublicSpaceKVActions,
|
|
2174
|
-
createVaultCrypto,
|
|
2175
|
-
SecretsService,
|
|
2176
|
-
SECRET_NAME_RE as SECRET_NAME_RE2,
|
|
2177
|
-
canonicalizeSecretScope,
|
|
2178
|
-
resolveSecretListPrefix,
|
|
2179
|
-
resolveSecretPath as resolveSecretPath2,
|
|
2180
|
-
EncryptionService,
|
|
2181
|
-
parseNetworkId as parseNetworkId2,
|
|
2182
|
-
buildNetworkId as buildNetworkId2,
|
|
2183
|
-
isNetworkId,
|
|
2184
|
-
networkDiscoveryKey,
|
|
2185
|
-
NetworkIdError,
|
|
2186
|
-
ENCRYPTION_NETWORK_URN_PREFIX,
|
|
2187
|
-
NETWORK_NAME_PATTERN,
|
|
2188
|
-
canonicalizeEncryptionJson,
|
|
2189
|
-
canonicalHashHex,
|
|
2190
|
-
hexEncode,
|
|
2191
|
-
hexDecode,
|
|
2192
|
-
base64Encode,
|
|
2193
|
-
base64Decode,
|
|
2194
|
-
utf8Encode,
|
|
2195
|
-
utf8Decode,
|
|
2196
|
-
encryptToNetwork,
|
|
2197
|
-
decryptEnvelopeWithKey,
|
|
2198
|
-
validateEnvelope,
|
|
2199
|
-
generateRandomReceiverKey,
|
|
2200
|
-
deriveSignedReceiverKey,
|
|
2201
|
-
buildCanonicalDecryptRequest,
|
|
2202
|
-
buildDecryptFacts,
|
|
2203
|
-
buildDecryptAttenuation,
|
|
2204
|
-
buildDecryptInvocation,
|
|
2205
|
-
checkDecryptInvocationInput,
|
|
2206
|
-
verifyDecryptResponse,
|
|
2207
|
-
canonicalSignedResponse,
|
|
2208
|
-
openWrappedKey,
|
|
2209
|
-
discoverNetwork,
|
|
2210
|
-
ensureNetworkUsableForDecrypt,
|
|
2211
|
-
DEFAULT_ENCRYPTION_ALG,
|
|
2212
|
-
ENVELOPE_VERSION,
|
|
2213
|
-
DEFAULT_KEY_VERSION,
|
|
2214
|
-
DECRYPT_FACT_TYPE,
|
|
2215
|
-
DECRYPT_RESULT_TYPE,
|
|
2216
|
-
DECRYPT_ACTION,
|
|
2217
|
-
ENCRYPTION_SERVICE,
|
|
2218
|
-
ENCRYPTION_SERVICE_SHORT,
|
|
2219
|
-
encryptionError
|
|
2167
|
+
err as err3,
|
|
2168
|
+
ok as ok3,
|
|
2169
|
+
serviceError as serviceError3
|
|
2220
2170
|
} from "@tinycloud/sdk-services";
|
|
2221
2171
|
|
|
2222
|
-
// src/
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
)
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
throw new Error(`Failed to get peer ID: ${res.status} - ${error}`);
|
|
2172
|
+
// src/manifest.ts
|
|
2173
|
+
import ms from "ms";
|
|
2174
|
+
import { resolveSecretPath, SECRET_NAME_RE } from "@tinycloud/sdk-services";
|
|
2175
|
+
var ManifestValidationError = class extends Error {
|
|
2176
|
+
constructor(message) {
|
|
2177
|
+
super(`Manifest validation failed: ${message}`);
|
|
2178
|
+
this.name = "ManifestValidationError";
|
|
2230
2179
|
}
|
|
2231
|
-
|
|
2180
|
+
};
|
|
2181
|
+
var DEFAULT_EXPIRY = "30d";
|
|
2182
|
+
var DEFAULT_DEFAULTS = true;
|
|
2183
|
+
var DEFAULT_MANIFEST_VERSION = 1;
|
|
2184
|
+
var DEFAULT_MANIFEST_SPACE = "applications";
|
|
2185
|
+
var ACCOUNT_REGISTRY_SPACE = "account";
|
|
2186
|
+
var ACCOUNT_REGISTRY_PATH = "applications/";
|
|
2187
|
+
var SECRETS_SPACE = "secrets";
|
|
2188
|
+
var VAULT_PERMISSION_SERVICE = "tinycloud.vault";
|
|
2189
|
+
var SERVICE_SHORT_TO_LONG = Object.freeze({
|
|
2190
|
+
kv: "tinycloud.kv",
|
|
2191
|
+
sql: "tinycloud.sql",
|
|
2192
|
+
duckdb: "tinycloud.duckdb",
|
|
2193
|
+
capabilities: "tinycloud.capabilities",
|
|
2194
|
+
hooks: "tinycloud.hooks",
|
|
2195
|
+
encryption: "tinycloud.encryption"
|
|
2196
|
+
});
|
|
2197
|
+
var ENCRYPTION_PERMISSION_SERVICE = "tinycloud.encryption";
|
|
2198
|
+
var ENCRYPTION_MANIFEST_SPACE = "encryption";
|
|
2199
|
+
var SERVICE_LONG_TO_SHORT = Object.freeze(
|
|
2200
|
+
Object.fromEntries(
|
|
2201
|
+
Object.entries(SERVICE_SHORT_TO_LONG).map(([s, l]) => [l, s])
|
|
2202
|
+
)
|
|
2203
|
+
);
|
|
2204
|
+
var DEFAULT_STANDARD_ENTRIES = [
|
|
2205
|
+
{
|
|
2206
|
+
service: "tinycloud.kv",
|
|
2207
|
+
space: DEFAULT_MANIFEST_SPACE,
|
|
2208
|
+
path: "/",
|
|
2209
|
+
actions: ["get", "put", "del", "list", "metadata"]
|
|
2210
|
+
},
|
|
2211
|
+
{
|
|
2212
|
+
service: "tinycloud.sql",
|
|
2213
|
+
space: DEFAULT_MANIFEST_SPACE,
|
|
2214
|
+
path: "/",
|
|
2215
|
+
actions: ["read", "write"]
|
|
2216
|
+
}
|
|
2217
|
+
];
|
|
2218
|
+
var DEFAULT_ADMIN_ENTRIES = [
|
|
2219
|
+
{
|
|
2220
|
+
service: "tinycloud.kv",
|
|
2221
|
+
space: DEFAULT_MANIFEST_SPACE,
|
|
2222
|
+
path: "/",
|
|
2223
|
+
actions: ["get", "put", "del", "list", "metadata"]
|
|
2224
|
+
},
|
|
2225
|
+
{
|
|
2226
|
+
service: "tinycloud.sql",
|
|
2227
|
+
space: DEFAULT_MANIFEST_SPACE,
|
|
2228
|
+
path: "/",
|
|
2229
|
+
actions: ["read", "write", "ddl"]
|
|
2230
|
+
}
|
|
2231
|
+
];
|
|
2232
|
+
var DEFAULT_ALL_ENTRIES = [
|
|
2233
|
+
{
|
|
2234
|
+
service: "tinycloud.kv",
|
|
2235
|
+
space: DEFAULT_MANIFEST_SPACE,
|
|
2236
|
+
path: "/",
|
|
2237
|
+
actions: ["get", "put", "del", "list", "metadata"]
|
|
2238
|
+
},
|
|
2239
|
+
{
|
|
2240
|
+
service: "tinycloud.sql",
|
|
2241
|
+
space: DEFAULT_MANIFEST_SPACE,
|
|
2242
|
+
path: "/",
|
|
2243
|
+
actions: ["read", "write", "ddl"]
|
|
2244
|
+
},
|
|
2245
|
+
{
|
|
2246
|
+
service: "tinycloud.duckdb",
|
|
2247
|
+
space: DEFAULT_MANIFEST_SPACE,
|
|
2248
|
+
path: "/",
|
|
2249
|
+
actions: ["read", "write"]
|
|
2250
|
+
}
|
|
2251
|
+
];
|
|
2252
|
+
function parseExpiry(duration) {
|
|
2253
|
+
if (typeof duration !== "string" || duration.length === 0) {
|
|
2254
|
+
throw new ManifestValidationError(
|
|
2255
|
+
`expiry must be a non-empty duration string (got ${JSON.stringify(duration)})`
|
|
2256
|
+
);
|
|
2257
|
+
}
|
|
2258
|
+
const parsed = ms(duration);
|
|
2259
|
+
if (typeof parsed !== "number" || !Number.isFinite(parsed) || parsed <= 0) {
|
|
2260
|
+
throw new ManifestValidationError(
|
|
2261
|
+
`invalid expiry duration: ${JSON.stringify(duration)}`
|
|
2262
|
+
);
|
|
2263
|
+
}
|
|
2264
|
+
return parsed;
|
|
2232
2265
|
}
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2266
|
+
function expandActionShortNames(service, actions) {
|
|
2267
|
+
return actions.map((a) => {
|
|
2268
|
+
if (a.includes("/")) {
|
|
2269
|
+
return a;
|
|
2270
|
+
}
|
|
2271
|
+
return `${service}/${a}`;
|
|
2237
2272
|
});
|
|
2238
|
-
return {
|
|
2239
|
-
success: res.ok,
|
|
2240
|
-
status: res.status,
|
|
2241
|
-
error: res.ok ? void 0 : await res.text().catch(() => res.statusText)
|
|
2242
|
-
};
|
|
2243
2273
|
}
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
headers: delegationHeader
|
|
2248
|
-
});
|
|
2249
|
-
if (res.ok) {
|
|
2250
|
-
try {
|
|
2251
|
-
const body = await res.json();
|
|
2252
|
-
return {
|
|
2253
|
-
success: true,
|
|
2254
|
-
status: res.status,
|
|
2255
|
-
activated: body.activated ?? [],
|
|
2256
|
-
skipped: body.skipped ?? []
|
|
2257
|
-
};
|
|
2258
|
-
} catch {
|
|
2259
|
-
return {
|
|
2260
|
-
success: true,
|
|
2261
|
-
status: res.status,
|
|
2262
|
-
activated: [],
|
|
2263
|
-
skipped: []
|
|
2264
|
-
};
|
|
2265
|
-
}
|
|
2274
|
+
function expandPermissionEntry(entry) {
|
|
2275
|
+
if (entry.service === ENCRYPTION_PERMISSION_SERVICE) {
|
|
2276
|
+
return expandEncryptionPermissionEntry(entry);
|
|
2266
2277
|
}
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
}
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
CREATE: "tinycloud.delegation/create",
|
|
2277
|
-
REVOKE: "tinycloud.delegation/revoke",
|
|
2278
|
-
LIST: "tinycloud.delegation/list",
|
|
2279
|
-
GET: "tinycloud.delegation/get",
|
|
2280
|
-
CHECK: "tinycloud.delegation/check"
|
|
2281
|
-
};
|
|
2282
|
-
function createError(code, message, cause, meta) {
|
|
2283
|
-
return {
|
|
2284
|
-
code,
|
|
2285
|
-
message,
|
|
2286
|
-
service: "delegation",
|
|
2287
|
-
cause,
|
|
2288
|
-
meta
|
|
2289
|
-
};
|
|
2278
|
+
if (entry.service !== VAULT_PERMISSION_SERVICE) {
|
|
2279
|
+
return [
|
|
2280
|
+
{
|
|
2281
|
+
...entry,
|
|
2282
|
+
actions: expandActionShortNames(entry.service, entry.actions)
|
|
2283
|
+
}
|
|
2284
|
+
];
|
|
2285
|
+
}
|
|
2286
|
+
return expandVaultPermissionEntry(entry);
|
|
2290
2287
|
}
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
*/
|
|
2297
|
-
constructor(config) {
|
|
2298
|
-
this.hosts = config.hosts;
|
|
2299
|
-
this.session = config.session;
|
|
2300
|
-
this.invoke = config.invoke;
|
|
2301
|
-
this.fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);
|
|
2288
|
+
function expandEncryptionPermissionEntry(entry) {
|
|
2289
|
+
if (typeof entry.path !== "string" || !entry.path.startsWith("urn:tinycloud:encryption:")) {
|
|
2290
|
+
throw new ManifestValidationError(
|
|
2291
|
+
`tinycloud.encryption entries require path to be a networkId URN (got ${JSON.stringify(entry.path)})`
|
|
2292
|
+
);
|
|
2302
2293
|
}
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
updateSession(session) {
|
|
2309
|
-
this.session = session;
|
|
2310
|
-
}
|
|
2311
|
-
/**
|
|
2312
|
-
* Gets the primary host URL.
|
|
2313
|
-
*/
|
|
2314
|
-
get host() {
|
|
2315
|
-
return this.hosts[0];
|
|
2316
|
-
}
|
|
2317
|
-
/**
|
|
2318
|
-
* Executes an invoke operation against the delegation API.
|
|
2319
|
-
*/
|
|
2320
|
-
async invokeOperation(path, action, body) {
|
|
2321
|
-
const headers = this.invoke(this.session, "delegation", path, action);
|
|
2322
|
-
return this.fetchFn(`${this.host}/invoke`, {
|
|
2323
|
-
method: "POST",
|
|
2324
|
-
headers,
|
|
2325
|
-
body
|
|
2326
|
-
});
|
|
2327
|
-
}
|
|
2328
|
-
/**
|
|
2329
|
-
* Creates a new delegation.
|
|
2330
|
-
*
|
|
2331
|
-
* Delegates specific permissions to another DID for a given path.
|
|
2332
|
-
* The delegatee can then use these permissions to access resources
|
|
2333
|
-
* within the specified scope.
|
|
2334
|
-
*
|
|
2335
|
-
* @param params - Parameters for the delegation
|
|
2336
|
-
* @returns Result containing the created Delegation or an error
|
|
2337
|
-
*
|
|
2338
|
-
* @example
|
|
2339
|
-
* ```typescript
|
|
2340
|
-
* const result = await manager.create({
|
|
2341
|
-
* delegateDID: bob.did,
|
|
2342
|
-
* path: "documents/shared/",
|
|
2343
|
-
* actions: ["tinycloud.kv/get", "tinycloud.kv/put"],
|
|
2344
|
-
* expiry: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days
|
|
2345
|
-
* });
|
|
2346
|
-
* ```
|
|
2347
|
-
*/
|
|
2348
|
-
async create(params) {
|
|
2349
|
-
if (!params.delegateDID) {
|
|
2350
|
-
return {
|
|
2351
|
-
ok: false,
|
|
2352
|
-
error: createError(
|
|
2353
|
-
DelegationErrorCodes.INVALID_INPUT,
|
|
2354
|
-
"delegateDID is required"
|
|
2355
|
-
)
|
|
2356
|
-
};
|
|
2294
|
+
const normalizedActions = [];
|
|
2295
|
+
for (const action of entry.actions) {
|
|
2296
|
+
if (action === "decrypt" || action === "tinycloud.encryption/decrypt") {
|
|
2297
|
+
normalizedActions.push("tinycloud.encryption/decrypt");
|
|
2298
|
+
continue;
|
|
2357
2299
|
}
|
|
2358
|
-
if (
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
error: createError(
|
|
2362
|
-
DelegationErrorCodes.INVALID_INPUT,
|
|
2363
|
-
"path is required"
|
|
2364
|
-
)
|
|
2365
|
-
};
|
|
2300
|
+
if (action === "network.create" || action === "tinycloud.encryption/network.create") {
|
|
2301
|
+
normalizedActions.push("tinycloud.encryption/network.create");
|
|
2302
|
+
continue;
|
|
2366
2303
|
}
|
|
2367
|
-
if (
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
error: createError(
|
|
2371
|
-
DelegationErrorCodes.INVALID_INPUT,
|
|
2372
|
-
"at least one action is required"
|
|
2373
|
-
)
|
|
2374
|
-
};
|
|
2304
|
+
if (action === "network.revoke" || action === "tinycloud.encryption/network.revoke") {
|
|
2305
|
+
normalizedActions.push("tinycloud.encryption/network.revoke");
|
|
2306
|
+
continue;
|
|
2375
2307
|
}
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
path: params.path,
|
|
2380
|
-
actions: params.actions,
|
|
2381
|
-
expiry: params.expiry?.toISOString(),
|
|
2382
|
-
disableSubDelegation: params.disableSubDelegation ?? false,
|
|
2383
|
-
statement: params.statement
|
|
2384
|
-
});
|
|
2385
|
-
const response = await this.invokeOperation(
|
|
2386
|
-
params.path,
|
|
2387
|
-
DelegationAction.CREATE,
|
|
2388
|
-
body
|
|
2308
|
+
if (action.includes("/")) {
|
|
2309
|
+
throw new ManifestValidationError(
|
|
2310
|
+
`unknown encryption action ${JSON.stringify(action)}; expected decrypt, network.create, or network.revoke`
|
|
2389
2311
|
);
|
|
2390
|
-
if (!response.ok) {
|
|
2391
|
-
const errorText = await response.text();
|
|
2392
|
-
return {
|
|
2393
|
-
ok: false,
|
|
2394
|
-
error: createError(
|
|
2395
|
-
DelegationErrorCodes.CREATION_FAILED,
|
|
2396
|
-
`Failed to create delegation: ${response.status} - ${errorText}`,
|
|
2397
|
-
void 0,
|
|
2398
|
-
{ status: response.status, path: params.path }
|
|
2399
|
-
)
|
|
2400
|
-
};
|
|
2401
|
-
}
|
|
2402
|
-
const apiResponse = await response.json();
|
|
2403
|
-
const delegation = {
|
|
2404
|
-
cid: apiResponse.cid ?? "",
|
|
2405
|
-
delegateDID: params.delegateDID,
|
|
2406
|
-
spaceId: this.session.spaceId,
|
|
2407
|
-
path: params.path,
|
|
2408
|
-
actions: params.actions,
|
|
2409
|
-
expiry: params.expiry ?? new Date(Date.now() + EXPIRY.SHARE_MS),
|
|
2410
|
-
isRevoked: false,
|
|
2411
|
-
allowSubDelegation: !(params.disableSubDelegation ?? false),
|
|
2412
|
-
createdAt: /* @__PURE__ */ new Date()
|
|
2413
|
-
};
|
|
2414
|
-
return { ok: true, data: delegation };
|
|
2415
|
-
} catch (error) {
|
|
2416
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
2417
|
-
return {
|
|
2418
|
-
ok: false,
|
|
2419
|
-
error: createError(
|
|
2420
|
-
DelegationErrorCodes.ABORTED,
|
|
2421
|
-
"Request aborted",
|
|
2422
|
-
error
|
|
2423
|
-
)
|
|
2424
|
-
};
|
|
2425
|
-
}
|
|
2426
|
-
return {
|
|
2427
|
-
ok: false,
|
|
2428
|
-
error: createError(
|
|
2429
|
-
DelegationErrorCodes.NETWORK_ERROR,
|
|
2430
|
-
`Network error during delegation creation: ${String(error)}`,
|
|
2431
|
-
error instanceof Error ? error : void 0
|
|
2432
|
-
)
|
|
2433
|
-
};
|
|
2434
2312
|
}
|
|
2313
|
+
throw new ManifestValidationError(
|
|
2314
|
+
`unknown encryption action ${JSON.stringify(action)}; expected decrypt, network.create, or network.revoke`
|
|
2315
|
+
);
|
|
2435
2316
|
}
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
* @param cid - The CID of the delegation to revoke
|
|
2443
|
-
* @returns Result indicating success or an error
|
|
2444
|
-
*
|
|
2445
|
-
* @example
|
|
2446
|
-
* ```typescript
|
|
2447
|
-
* const result = await manager.revoke("bafy...");
|
|
2448
|
-
* if (result.ok) {
|
|
2449
|
-
* console.log("Delegation revoked successfully");
|
|
2450
|
-
* }
|
|
2451
|
-
* ```
|
|
2452
|
-
*/
|
|
2453
|
-
async revoke(cid) {
|
|
2454
|
-
if (!cid) {
|
|
2455
|
-
return {
|
|
2456
|
-
ok: false,
|
|
2457
|
-
error: createError(
|
|
2458
|
-
DelegationErrorCodes.INVALID_INPUT,
|
|
2459
|
-
"cid is required"
|
|
2460
|
-
)
|
|
2461
|
-
};
|
|
2317
|
+
const dedupedActions = [];
|
|
2318
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2319
|
+
for (const a of normalizedActions) {
|
|
2320
|
+
if (!seen.has(a)) {
|
|
2321
|
+
dedupedActions.push(a);
|
|
2322
|
+
seen.add(a);
|
|
2462
2323
|
}
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
return {
|
|
2474
|
-
ok: false,
|
|
2475
|
-
error: createError(
|
|
2476
|
-
DelegationErrorCodes.NOT_FOUND,
|
|
2477
|
-
`Delegation not found: ${cid}`
|
|
2478
|
-
)
|
|
2479
|
-
};
|
|
2480
|
-
}
|
|
2481
|
-
return {
|
|
2482
|
-
ok: false,
|
|
2483
|
-
error: createError(
|
|
2484
|
-
DelegationErrorCodes.REVOCATION_FAILED,
|
|
2485
|
-
`Failed to revoke delegation: ${response.status} - ${errorText}`,
|
|
2486
|
-
void 0,
|
|
2487
|
-
{ status: response.status, cid }
|
|
2488
|
-
)
|
|
2489
|
-
};
|
|
2490
|
-
}
|
|
2491
|
-
return { ok: true, data: void 0 };
|
|
2492
|
-
} catch (error) {
|
|
2493
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
2494
|
-
return {
|
|
2495
|
-
ok: false,
|
|
2496
|
-
error: createError(
|
|
2497
|
-
DelegationErrorCodes.ABORTED,
|
|
2498
|
-
"Request aborted",
|
|
2499
|
-
error
|
|
2500
|
-
)
|
|
2501
|
-
};
|
|
2502
|
-
}
|
|
2503
|
-
return {
|
|
2504
|
-
ok: false,
|
|
2505
|
-
error: createError(
|
|
2506
|
-
DelegationErrorCodes.NETWORK_ERROR,
|
|
2507
|
-
`Network error during delegation revocation: ${String(error)}`,
|
|
2508
|
-
error instanceof Error ? error : void 0
|
|
2509
|
-
)
|
|
2510
|
-
};
|
|
2324
|
+
}
|
|
2325
|
+
return [
|
|
2326
|
+
{
|
|
2327
|
+
service: ENCRYPTION_PERMISSION_SERVICE,
|
|
2328
|
+
space: ENCRYPTION_MANIFEST_SPACE,
|
|
2329
|
+
path: entry.path,
|
|
2330
|
+
actions: dedupedActions,
|
|
2331
|
+
skipPrefix: true,
|
|
2332
|
+
...entry.expiry !== void 0 ? { expiry: entry.expiry } : {},
|
|
2333
|
+
...entry.description !== void 0 ? { description: entry.description } : {}
|
|
2511
2334
|
}
|
|
2335
|
+
];
|
|
2336
|
+
}
|
|
2337
|
+
function expandPermissionEntries(entries) {
|
|
2338
|
+
return entries.flatMap(expandPermissionEntry);
|
|
2339
|
+
}
|
|
2340
|
+
function applyPrefix(prefix, path, skipPrefix) {
|
|
2341
|
+
if (skipPrefix) {
|
|
2342
|
+
return path;
|
|
2512
2343
|
}
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
DelegationErrorCodes.NETWORK_ERROR,
|
|
2576
|
-
`Network error during delegation list: ${String(error)}`,
|
|
2577
|
-
error instanceof Error ? error : void 0
|
|
2578
|
-
)
|
|
2579
|
-
};
|
|
2344
|
+
if (prefix === "") {
|
|
2345
|
+
return path;
|
|
2346
|
+
}
|
|
2347
|
+
if (path.startsWith("/")) {
|
|
2348
|
+
return `${prefix}${path}`;
|
|
2349
|
+
}
|
|
2350
|
+
return `${prefix}/${path}`;
|
|
2351
|
+
}
|
|
2352
|
+
async function loadManifest(url) {
|
|
2353
|
+
const fetchFn = globalThis.fetch;
|
|
2354
|
+
if (typeof fetchFn !== "function") {
|
|
2355
|
+
throw new ManifestValidationError(
|
|
2356
|
+
"loadManifest requires a global fetch; pass the manifest object directly on runtimes without fetch"
|
|
2357
|
+
);
|
|
2358
|
+
}
|
|
2359
|
+
const res = await fetchFn(url);
|
|
2360
|
+
if (!res.ok) {
|
|
2361
|
+
throw new ManifestValidationError(
|
|
2362
|
+
`failed to fetch manifest from ${url}: HTTP ${res.status}`
|
|
2363
|
+
);
|
|
2364
|
+
}
|
|
2365
|
+
const json = await res.json();
|
|
2366
|
+
return validateManifest(json);
|
|
2367
|
+
}
|
|
2368
|
+
function validateManifest(input) {
|
|
2369
|
+
if (input === null || typeof input !== "object") {
|
|
2370
|
+
throw new ManifestValidationError("manifest must be an object");
|
|
2371
|
+
}
|
|
2372
|
+
const m = input;
|
|
2373
|
+
if (m.manifest_version !== void 0 && m.manifest_version !== DEFAULT_MANIFEST_VERSION) {
|
|
2374
|
+
throw new ManifestValidationError(
|
|
2375
|
+
`manifest.manifest_version must be ${DEFAULT_MANIFEST_VERSION}`
|
|
2376
|
+
);
|
|
2377
|
+
}
|
|
2378
|
+
if (typeof m.app_id !== "string" || m.app_id.length === 0) {
|
|
2379
|
+
throw new ManifestValidationError(
|
|
2380
|
+
"manifest.app_id is required and must be a non-empty string"
|
|
2381
|
+
);
|
|
2382
|
+
}
|
|
2383
|
+
if (typeof m.name !== "string" || m.name.length === 0) {
|
|
2384
|
+
throw new ManifestValidationError(
|
|
2385
|
+
"manifest.name is required and must be a non-empty string"
|
|
2386
|
+
);
|
|
2387
|
+
}
|
|
2388
|
+
if (m.did !== void 0 && (typeof m.did !== "string" || m.did.length === 0)) {
|
|
2389
|
+
throw new ManifestValidationError(
|
|
2390
|
+
"manifest.did must be a non-empty DID string"
|
|
2391
|
+
);
|
|
2392
|
+
}
|
|
2393
|
+
if (m.space !== void 0 && (typeof m.space !== "string" || m.space.length === 0)) {
|
|
2394
|
+
throw new ManifestValidationError(
|
|
2395
|
+
"manifest.space must be a non-empty string"
|
|
2396
|
+
);
|
|
2397
|
+
}
|
|
2398
|
+
if (m.expiry !== void 0) {
|
|
2399
|
+
parseExpiry(m.expiry);
|
|
2400
|
+
}
|
|
2401
|
+
if (m.permissions !== void 0) {
|
|
2402
|
+
if (!Array.isArray(m.permissions)) {
|
|
2403
|
+
throw new ManifestValidationError(
|
|
2404
|
+
"manifest.permissions must be an array"
|
|
2405
|
+
);
|
|
2580
2406
|
}
|
|
2407
|
+
m.permissions.forEach(
|
|
2408
|
+
(p, i) => validatePermissionEntry(p, `permissions[${i}]`)
|
|
2409
|
+
);
|
|
2581
2410
|
}
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
* for (const delegation of result.data) {
|
|
2597
|
-
* console.log(`- ${delegation.delegatorDID} -> ${delegation.delegateDID}`);
|
|
2598
|
-
* }
|
|
2599
|
-
* }
|
|
2600
|
-
* ```
|
|
2601
|
-
*/
|
|
2602
|
-
async getChain(cid) {
|
|
2603
|
-
if (!cid) {
|
|
2604
|
-
return {
|
|
2605
|
-
ok: false,
|
|
2606
|
-
error: createError(
|
|
2607
|
-
DelegationErrorCodes.INVALID_INPUT,
|
|
2608
|
-
"cid is required"
|
|
2609
|
-
)
|
|
2610
|
-
};
|
|
2411
|
+
if (m.secrets !== void 0) {
|
|
2412
|
+
validateManifestSecrets(m.secrets);
|
|
2413
|
+
}
|
|
2414
|
+
return m;
|
|
2415
|
+
}
|
|
2416
|
+
function validateManifestSecrets(secrets) {
|
|
2417
|
+
if (secrets === null || typeof secrets !== "object" || Array.isArray(secrets)) {
|
|
2418
|
+
throw new ManifestValidationError("manifest.secrets must be an object");
|
|
2419
|
+
}
|
|
2420
|
+
for (const [name, spec] of Object.entries(secrets)) {
|
|
2421
|
+
if (!SECRET_NAME_RE.test(name)) {
|
|
2422
|
+
throw new ManifestValidationError(
|
|
2423
|
+
`manifest.secrets.${name} must match ${SECRET_NAME_RE.source}`
|
|
2424
|
+
);
|
|
2611
2425
|
}
|
|
2612
2426
|
try {
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
DelegationAction.GET,
|
|
2617
|
-
body
|
|
2427
|
+
resolveSecretPath(
|
|
2428
|
+
secretNameFromSpec(name, spec),
|
|
2429
|
+
{ scope: secretScopeFromSpec(spec) }
|
|
2618
2430
|
);
|
|
2619
|
-
if (!response.ok) {
|
|
2620
|
-
const errorText = await response.text();
|
|
2621
|
-
if (response.status === 404) {
|
|
2622
|
-
return {
|
|
2623
|
-
ok: false,
|
|
2624
|
-
error: createError(
|
|
2625
|
-
DelegationErrorCodes.NOT_FOUND,
|
|
2626
|
-
`Delegation not found: ${cid}`
|
|
2627
|
-
)
|
|
2628
|
-
};
|
|
2629
|
-
}
|
|
2630
|
-
return {
|
|
2631
|
-
ok: false,
|
|
2632
|
-
error: createError(
|
|
2633
|
-
DelegationErrorCodes.NETWORK_ERROR,
|
|
2634
|
-
`Failed to get delegation chain: ${response.status} - ${errorText}`,
|
|
2635
|
-
void 0,
|
|
2636
|
-
{ status: response.status, cid }
|
|
2637
|
-
)
|
|
2638
|
-
};
|
|
2639
|
-
}
|
|
2640
|
-
const data = await response.json();
|
|
2641
|
-
const chain = data.chain.map((item) => ({
|
|
2642
|
-
cid: item.cid,
|
|
2643
|
-
delegateDID: item.delegateDID,
|
|
2644
|
-
delegatorDID: item.delegatorDID,
|
|
2645
|
-
spaceId: item.spaceId,
|
|
2646
|
-
path: item.path,
|
|
2647
|
-
actions: item.actions,
|
|
2648
|
-
expiry: new Date(item.expiry),
|
|
2649
|
-
isRevoked: item.isRevoked,
|
|
2650
|
-
createdAt: item.createdAt ? new Date(item.createdAt) : void 0,
|
|
2651
|
-
parentCid: item.parentCid,
|
|
2652
|
-
allowSubDelegation: item.allowSubDelegation
|
|
2653
|
-
}));
|
|
2654
|
-
return { ok: true, data: chain };
|
|
2655
2431
|
} catch (error) {
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2432
|
+
throw new ManifestValidationError(
|
|
2433
|
+
`manifest.secrets.${name}: ${error instanceof Error ? error.message : String(error)}`
|
|
2434
|
+
);
|
|
2435
|
+
}
|
|
2436
|
+
const actions = secretActionsFromSpec(name, spec);
|
|
2437
|
+
if (actions.length === 0) {
|
|
2438
|
+
throw new ManifestValidationError(
|
|
2439
|
+
`manifest.secrets.${name} actions must be non-empty`
|
|
2440
|
+
);
|
|
2441
|
+
}
|
|
2442
|
+
for (const action of actions) {
|
|
2443
|
+
if (typeof action !== "string" || action.length === 0) {
|
|
2444
|
+
throw new ManifestValidationError(
|
|
2445
|
+
`manifest.secrets.${name} actions must be non-empty strings`
|
|
2446
|
+
);
|
|
2665
2447
|
}
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
DelegationErrorCodes.NETWORK_ERROR,
|
|
2670
|
-
`Network error during chain retrieval: ${String(error)}`,
|
|
2671
|
-
error instanceof Error ? error : void 0
|
|
2672
|
-
)
|
|
2673
|
-
};
|
|
2448
|
+
}
|
|
2449
|
+
if (spec !== null && typeof spec === "object" && !Array.isArray(spec) && spec.expiry !== void 0) {
|
|
2450
|
+
parseExpiry(spec.expiry);
|
|
2674
2451
|
}
|
|
2675
2452
|
}
|
|
2676
|
-
/**
|
|
2677
|
-
* Checks if the current session has permission for a given path and action.
|
|
2678
|
-
*
|
|
2679
|
-
* This can be used to verify permissions before attempting an operation,
|
|
2680
|
-
* or to implement custom access control logic.
|
|
2681
|
-
*
|
|
2682
|
-
* @param path - The resource path to check
|
|
2683
|
-
* @param action - The action to check (e.g., "tinycloud.kv/get")
|
|
2684
|
-
* @returns Result containing a boolean indicating permission or an error
|
|
2685
|
-
*
|
|
2686
|
-
* @example
|
|
2687
|
-
* ```typescript
|
|
2688
|
-
* const result = await manager.checkPermission("documents/private/", "tinycloud.kv/put");
|
|
2689
|
-
* if (result.ok && result.data) {
|
|
2690
|
-
* console.log("Permission granted");
|
|
2691
|
-
* } else {
|
|
2692
|
-
* console.log("Permission denied");
|
|
2693
|
-
* }
|
|
2694
|
-
* ```
|
|
2695
|
-
*/
|
|
2696
|
-
async checkPermission(path, action) {
|
|
2697
|
-
if (!path) {
|
|
2698
|
-
return {
|
|
2699
|
-
ok: false,
|
|
2700
|
-
error: createError(
|
|
2701
|
-
DelegationErrorCodes.INVALID_INPUT,
|
|
2702
|
-
"path is required"
|
|
2703
|
-
)
|
|
2704
|
-
};
|
|
2705
|
-
}
|
|
2706
|
-
if (!action) {
|
|
2707
|
-
return {
|
|
2708
|
-
ok: false,
|
|
2709
|
-
error: createError(
|
|
2710
|
-
DelegationErrorCodes.INVALID_INPUT,
|
|
2711
|
-
"action is required"
|
|
2712
|
-
)
|
|
2713
|
-
};
|
|
2714
|
-
}
|
|
2715
|
-
try {
|
|
2716
|
-
const body = JSON.stringify({ path, action });
|
|
2717
|
-
const response = await this.invokeOperation(
|
|
2718
|
-
path,
|
|
2719
|
-
DelegationAction.CHECK,
|
|
2720
|
-
body
|
|
2721
|
-
);
|
|
2722
|
-
if (!response.ok) {
|
|
2723
|
-
if (response.status === 403) {
|
|
2724
|
-
return { ok: true, data: false };
|
|
2725
|
-
}
|
|
2726
|
-
const errorText = await response.text();
|
|
2727
|
-
return {
|
|
2728
|
-
ok: false,
|
|
2729
|
-
error: createError(
|
|
2730
|
-
DelegationErrorCodes.NETWORK_ERROR,
|
|
2731
|
-
`Failed to check permission: ${response.status} - ${errorText}`,
|
|
2732
|
-
void 0,
|
|
2733
|
-
{ status: response.status, path, action }
|
|
2734
|
-
)
|
|
2735
|
-
};
|
|
2736
|
-
}
|
|
2737
|
-
const data = await response.json();
|
|
2738
|
-
return { ok: true, data: data.allowed };
|
|
2739
|
-
} catch (error) {
|
|
2740
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
2741
|
-
return {
|
|
2742
|
-
ok: false,
|
|
2743
|
-
error: createError(
|
|
2744
|
-
DelegationErrorCodes.ABORTED,
|
|
2745
|
-
"Request aborted",
|
|
2746
|
-
error
|
|
2747
|
-
)
|
|
2748
|
-
};
|
|
2749
|
-
}
|
|
2750
|
-
return {
|
|
2751
|
-
ok: false,
|
|
2752
|
-
error: createError(
|
|
2753
|
-
DelegationErrorCodes.NETWORK_ERROR,
|
|
2754
|
-
`Network error during permission check: ${String(error)}`,
|
|
2755
|
-
error instanceof Error ? error : void 0
|
|
2756
|
-
)
|
|
2757
|
-
};
|
|
2758
|
-
}
|
|
2759
|
-
}
|
|
2760
|
-
};
|
|
2761
|
-
|
|
2762
|
-
// src/delegations/SharingService.schema.ts
|
|
2763
|
-
import { z as z5 } from "zod";
|
|
2764
|
-
var EncodedShareDataSchema = z5.object({
|
|
2765
|
-
/** Private key in JWK format (must include d parameter) */
|
|
2766
|
-
key: JWKSchema.refine(
|
|
2767
|
-
(jwk) => typeof jwk.d === "string" && jwk.d.length > 0,
|
|
2768
|
-
{ message: "JWK must include private key (d parameter)" }
|
|
2769
|
-
),
|
|
2770
|
-
/** DID of the key */
|
|
2771
|
-
keyDid: z5.string().min(1, "keyDid is required"),
|
|
2772
|
-
/** The delegation granting access */
|
|
2773
|
-
delegation: DelegationSchema,
|
|
2774
|
-
/** Resource path this link grants access to */
|
|
2775
|
-
path: z5.string().min(1, "path is required"),
|
|
2776
|
-
/** TinyCloud host URL */
|
|
2777
|
-
host: z5.string().url("host must be a valid URL"),
|
|
2778
|
-
/** Space ID */
|
|
2779
|
-
spaceId: z5.string().min(1, "spaceId is required"),
|
|
2780
|
-
/** Schema version (must be 1) */
|
|
2781
|
-
version: z5.literal(1)
|
|
2782
|
-
});
|
|
2783
|
-
var ReceiveOptionsSchema = z5.object({
|
|
2784
|
-
/**
|
|
2785
|
-
* Whether to automatically create a sub-delegation to the current session key.
|
|
2786
|
-
* Default: true
|
|
2787
|
-
*/
|
|
2788
|
-
autoSubdelegate: z5.boolean().optional(),
|
|
2789
|
-
/**
|
|
2790
|
-
* Whether to use the current session key for operations (requires autoSubdelegate).
|
|
2791
|
-
* Default: true
|
|
2792
|
-
*/
|
|
2793
|
-
useSessionKey: z5.boolean().optional(),
|
|
2794
|
-
/**
|
|
2795
|
-
* Ingestion options passed to CapabilityKeyRegistry.
|
|
2796
|
-
*/
|
|
2797
|
-
ingestOptions: IngestOptionsSchema.optional()
|
|
2798
|
-
});
|
|
2799
|
-
var SharingServiceConfigSchema = z5.object({
|
|
2800
|
-
/** TinyCloud host URLs */
|
|
2801
|
-
hosts: z5.array(z5.string().url()).min(1, "At least one host URL is required"),
|
|
2802
|
-
/**
|
|
2803
|
-
* Active session for authentication.
|
|
2804
|
-
* Required for generate(), optional for receive().
|
|
2805
|
-
*/
|
|
2806
|
-
session: z5.unknown().refine(
|
|
2807
|
-
(val) => val === void 0 || val !== null && typeof val === "object",
|
|
2808
|
-
{ message: "Expected a ServiceSession object or undefined" }
|
|
2809
|
-
).optional(),
|
|
2810
|
-
/** Platform-specific invoke function */
|
|
2811
|
-
invoke: z5.unknown().refine((val) => typeof val === "function", {
|
|
2812
|
-
message: "Expected an invoke function"
|
|
2813
|
-
}),
|
|
2814
|
-
/** Optional custom fetch implementation */
|
|
2815
|
-
fetch: z5.unknown().refine(
|
|
2816
|
-
(val) => val === void 0 || typeof val === "function",
|
|
2817
|
-
{ message: "Expected a fetch function or undefined" }
|
|
2818
|
-
).optional(),
|
|
2819
|
-
/** Key provider for cryptographic operations */
|
|
2820
|
-
keyProvider: KeyProviderSchema,
|
|
2821
|
-
/** Capability key registry for key/delegation management */
|
|
2822
|
-
registry: z5.unknown().refine(
|
|
2823
|
-
(val) => val !== null && typeof val === "object",
|
|
2824
|
-
{ message: "Expected an ICapabilityKeyRegistry object" }
|
|
2825
|
-
),
|
|
2826
|
-
/**
|
|
2827
|
-
* Delegation manager for creating delegations.
|
|
2828
|
-
* Required for generate(), optional for receive().
|
|
2829
|
-
*/
|
|
2830
|
-
delegationManager: z5.unknown().refine(
|
|
2831
|
-
(val) => val === void 0 || val !== null && typeof val === "object",
|
|
2832
|
-
{ message: "Expected a DelegationManager object or undefined" }
|
|
2833
|
-
).optional(),
|
|
2834
|
-
/** Factory for creating KV service instances */
|
|
2835
|
-
createKVService: z5.unknown().refine(
|
|
2836
|
-
(val) => typeof val === "function",
|
|
2837
|
-
{ message: "Expected a createKVService factory function" }
|
|
2838
|
-
),
|
|
2839
|
-
/** Base URL for sharing links (e.g., "https://share.myapp.com") */
|
|
2840
|
-
baseUrl: z5.string().optional(),
|
|
2841
|
-
/**
|
|
2842
|
-
* Custom delegation creation function.
|
|
2843
|
-
*/
|
|
2844
|
-
createDelegation: z5.unknown().refine((val) => val === void 0 || typeof val === "function", {
|
|
2845
|
-
message: "Expected a createDelegation function or undefined"
|
|
2846
|
-
}).optional(),
|
|
2847
|
-
/**
|
|
2848
|
-
* WASM function for client-side delegation creation.
|
|
2849
|
-
*/
|
|
2850
|
-
createDelegationWasm: z5.unknown().refine((val) => val === void 0 || typeof val === "function", {
|
|
2851
|
-
message: "Expected a createDelegationWasm function or undefined"
|
|
2852
|
-
}).optional(),
|
|
2853
|
-
/**
|
|
2854
|
-
* Path prefix for KV operations.
|
|
2855
|
-
*/
|
|
2856
|
-
pathPrefix: z5.string().optional(),
|
|
2857
|
-
/**
|
|
2858
|
-
* Session expiry time.
|
|
2859
|
-
*/
|
|
2860
|
-
sessionExpiry: z5.date().optional(),
|
|
2861
|
-
/**
|
|
2862
|
-
* Callback to create a DIRECT delegation from wallet to share key.
|
|
2863
|
-
* This is the preferred method for long-lived share links because it
|
|
2864
|
-
* bypasses the session delegation chain entirely.
|
|
2865
|
-
*/
|
|
2866
|
-
onRootDelegationNeeded: z5.unknown().refine((val) => val === void 0 || typeof val === "function", {
|
|
2867
|
-
message: "Expected an onRootDelegationNeeded function or undefined"
|
|
2868
|
-
}).optional()
|
|
2869
|
-
});
|
|
2870
|
-
function validateEncodedShareData(data) {
|
|
2871
|
-
const result = EncodedShareDataSchema.safeParse(data);
|
|
2872
|
-
if (!result.success) {
|
|
2873
|
-
return {
|
|
2874
|
-
ok: false,
|
|
2875
|
-
error: {
|
|
2876
|
-
code: DelegationErrorCodes.VALIDATION_ERROR,
|
|
2877
|
-
message: `Invalid share data: ${result.error.message}`,
|
|
2878
|
-
service: "delegation",
|
|
2879
|
-
meta: { issues: result.error.issues }
|
|
2880
|
-
}
|
|
2881
|
-
};
|
|
2882
|
-
}
|
|
2883
|
-
return { ok: true, data: result.data };
|
|
2884
2453
|
}
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
import { resolveSecretPath, SECRET_NAME_RE } from "@tinycloud/sdk-services";
|
|
2889
|
-
var ManifestValidationError = class extends Error {
|
|
2890
|
-
constructor(message) {
|
|
2891
|
-
super(`Manifest validation failed: ${message}`);
|
|
2892
|
-
this.name = "ManifestValidationError";
|
|
2893
|
-
}
|
|
2894
|
-
};
|
|
2895
|
-
var DEFAULT_EXPIRY = "30d";
|
|
2896
|
-
var DEFAULT_DEFAULTS = true;
|
|
2897
|
-
var DEFAULT_MANIFEST_VERSION = 1;
|
|
2898
|
-
var DEFAULT_MANIFEST_SPACE = "applications";
|
|
2899
|
-
var ACCOUNT_REGISTRY_SPACE = "account";
|
|
2900
|
-
var ACCOUNT_REGISTRY_PATH = "applications/";
|
|
2901
|
-
var SECRETS_SPACE = "secrets";
|
|
2902
|
-
var VAULT_PERMISSION_SERVICE = "tinycloud.vault";
|
|
2903
|
-
var SERVICE_SHORT_TO_LONG = Object.freeze({
|
|
2904
|
-
kv: "tinycloud.kv",
|
|
2905
|
-
sql: "tinycloud.sql",
|
|
2906
|
-
duckdb: "tinycloud.duckdb",
|
|
2907
|
-
capabilities: "tinycloud.capabilities",
|
|
2908
|
-
hooks: "tinycloud.hooks",
|
|
2909
|
-
encryption: "tinycloud.encryption"
|
|
2910
|
-
});
|
|
2911
|
-
var ENCRYPTION_PERMISSION_SERVICE = "tinycloud.encryption";
|
|
2912
|
-
var ENCRYPTION_MANIFEST_SPACE = "encryption";
|
|
2913
|
-
var SERVICE_LONG_TO_SHORT = Object.freeze(
|
|
2914
|
-
Object.fromEntries(
|
|
2915
|
-
Object.entries(SERVICE_SHORT_TO_LONG).map(([s, l]) => [l, s])
|
|
2916
|
-
)
|
|
2917
|
-
);
|
|
2918
|
-
var DEFAULT_STANDARD_ENTRIES = [
|
|
2919
|
-
{
|
|
2920
|
-
service: "tinycloud.kv",
|
|
2921
|
-
space: DEFAULT_MANIFEST_SPACE,
|
|
2922
|
-
path: "/",
|
|
2923
|
-
actions: ["get", "put", "del", "list", "metadata"]
|
|
2924
|
-
},
|
|
2925
|
-
{
|
|
2926
|
-
service: "tinycloud.sql",
|
|
2927
|
-
space: DEFAULT_MANIFEST_SPACE,
|
|
2928
|
-
path: "/",
|
|
2929
|
-
actions: ["read", "write"]
|
|
2454
|
+
function validatePermissionEntry(p, path) {
|
|
2455
|
+
if (p === null || typeof p !== "object") {
|
|
2456
|
+
throw new ManifestValidationError(`${path} must be an object`);
|
|
2930
2457
|
}
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
service: "tinycloud.kv",
|
|
2935
|
-
space: DEFAULT_MANIFEST_SPACE,
|
|
2936
|
-
path: "/",
|
|
2937
|
-
actions: ["get", "put", "del", "list", "metadata"]
|
|
2938
|
-
},
|
|
2939
|
-
{
|
|
2940
|
-
service: "tinycloud.sql",
|
|
2941
|
-
space: DEFAULT_MANIFEST_SPACE,
|
|
2942
|
-
path: "/",
|
|
2943
|
-
actions: ["read", "write", "ddl"]
|
|
2458
|
+
const entry = p;
|
|
2459
|
+
if (typeof entry.service !== "string" || entry.service.length === 0) {
|
|
2460
|
+
throw new ManifestValidationError(`${path}.service is required`);
|
|
2944
2461
|
}
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
space: DEFAULT_MANIFEST_SPACE,
|
|
2950
|
-
path: "/",
|
|
2951
|
-
actions: ["get", "put", "del", "list", "metadata"]
|
|
2952
|
-
},
|
|
2953
|
-
{
|
|
2954
|
-
service: "tinycloud.sql",
|
|
2955
|
-
space: DEFAULT_MANIFEST_SPACE,
|
|
2956
|
-
path: "/",
|
|
2957
|
-
actions: ["read", "write", "ddl"]
|
|
2958
|
-
},
|
|
2959
|
-
{
|
|
2960
|
-
service: "tinycloud.duckdb",
|
|
2961
|
-
space: DEFAULT_MANIFEST_SPACE,
|
|
2962
|
-
path: "/",
|
|
2963
|
-
actions: ["read", "write"]
|
|
2462
|
+
if (entry.space !== void 0 && (typeof entry.space !== "string" || entry.space.length === 0)) {
|
|
2463
|
+
throw new ManifestValidationError(
|
|
2464
|
+
`${path}.space must be a non-empty string`
|
|
2465
|
+
);
|
|
2964
2466
|
}
|
|
2965
|
-
|
|
2966
|
-
function parseExpiry(duration) {
|
|
2967
|
-
if (typeof duration !== "string" || duration.length === 0) {
|
|
2467
|
+
if (typeof entry.path !== "string") {
|
|
2968
2468
|
throw new ManifestValidationError(
|
|
2969
|
-
|
|
2469
|
+
`${path}.path is required (use "" or "/" for root)`
|
|
2970
2470
|
);
|
|
2971
2471
|
}
|
|
2972
|
-
|
|
2973
|
-
if (typeof parsed !== "number" || !Number.isFinite(parsed) || parsed <= 0) {
|
|
2472
|
+
if (!Array.isArray(entry.actions) || entry.actions.length === 0) {
|
|
2974
2473
|
throw new ManifestValidationError(
|
|
2975
|
-
|
|
2474
|
+
`${path}.actions must be a non-empty array`
|
|
2976
2475
|
);
|
|
2977
2476
|
}
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
return a;
|
|
2477
|
+
for (const action of entry.actions) {
|
|
2478
|
+
if (typeof action !== "string" || action.length === 0) {
|
|
2479
|
+
throw new ManifestValidationError(
|
|
2480
|
+
`${path}.actions must contain non-empty strings`
|
|
2481
|
+
);
|
|
2984
2482
|
}
|
|
2985
|
-
|
|
2986
|
-
|
|
2483
|
+
if (entry.service === VAULT_PERMISSION_SERVICE) {
|
|
2484
|
+
vaultActionExpansion(action);
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
if (entry.expiry !== void 0) {
|
|
2488
|
+
parseExpiry(entry.expiry);
|
|
2489
|
+
}
|
|
2987
2490
|
}
|
|
2988
|
-
function
|
|
2989
|
-
if (
|
|
2990
|
-
return
|
|
2491
|
+
function normalizeDefaults(value) {
|
|
2492
|
+
if (value === void 0) {
|
|
2493
|
+
return DEFAULT_DEFAULTS;
|
|
2991
2494
|
}
|
|
2992
|
-
if (
|
|
2993
|
-
return
|
|
2994
|
-
{
|
|
2995
|
-
...entry,
|
|
2996
|
-
actions: expandActionShortNames(entry.service, entry.actions)
|
|
2997
|
-
}
|
|
2998
|
-
];
|
|
2495
|
+
if (typeof value === "boolean") {
|
|
2496
|
+
return value;
|
|
2999
2497
|
}
|
|
3000
|
-
|
|
2498
|
+
if (typeof value !== "string") {
|
|
2499
|
+
return true;
|
|
2500
|
+
}
|
|
2501
|
+
const normalized = value.trim().toLowerCase();
|
|
2502
|
+
if (normalized === "admin" || normalized === "all") {
|
|
2503
|
+
return normalized;
|
|
2504
|
+
}
|
|
2505
|
+
return true;
|
|
3001
2506
|
}
|
|
3002
|
-
function
|
|
3003
|
-
if (
|
|
3004
|
-
|
|
3005
|
-
`tinycloud.encryption entries require path to be a networkId URN (got ${JSON.stringify(entry.path)})`
|
|
3006
|
-
);
|
|
2507
|
+
function defaultEntriesForTier(tier) {
|
|
2508
|
+
if (tier === false) {
|
|
2509
|
+
return [];
|
|
3007
2510
|
}
|
|
3008
|
-
const
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
2511
|
+
const source = tier === "admin" ? DEFAULT_ADMIN_ENTRIES : tier === "all" ? DEFAULT_ALL_ENTRIES : DEFAULT_STANDARD_ENTRIES;
|
|
2512
|
+
return source.map((e) => ({
|
|
2513
|
+
service: e.service,
|
|
2514
|
+
space: e.space,
|
|
2515
|
+
path: e.path,
|
|
2516
|
+
actions: [...e.actions],
|
|
2517
|
+
...e.skipPrefix !== void 0 ? { skipPrefix: e.skipPrefix } : {}
|
|
2518
|
+
}));
|
|
2519
|
+
}
|
|
2520
|
+
function resolveManifest(input) {
|
|
2521
|
+
const manifest = validateManifest(input);
|
|
2522
|
+
const prefix = manifest.prefix !== void 0 ? manifest.prefix : manifest.app_id;
|
|
2523
|
+
const space = manifest.space ?? DEFAULT_MANIFEST_SPACE;
|
|
2524
|
+
const expiryMs = parseExpiry(manifest.expiry ?? DEFAULT_EXPIRY);
|
|
2525
|
+
const includePublicSpace = manifest.includePublicSpace ?? true;
|
|
2526
|
+
const tier = normalizeDefaults(manifest.defaults);
|
|
2527
|
+
const defaultEntries = defaultEntriesForTier(tier);
|
|
2528
|
+
const explicitEntries = manifest.permissions ?? [];
|
|
2529
|
+
const secretEntries = secretEntriesForManifest(manifest.secrets);
|
|
2530
|
+
const allEntries = [
|
|
2531
|
+
...defaultEntries,
|
|
2532
|
+
...explicitEntries,
|
|
2533
|
+
...secretEntries
|
|
2534
|
+
];
|
|
2535
|
+
const resources = withCapabilitiesReadForSpaces(
|
|
2536
|
+
allEntries.flatMap((entry) => resolveEntry(entry, prefix, expiryMs, space))
|
|
2537
|
+
);
|
|
2538
|
+
const additionalDelegates = manifest.did === void 0 ? [] : [
|
|
2539
|
+
{
|
|
2540
|
+
did: manifest.did,
|
|
2541
|
+
name: manifest.name,
|
|
2542
|
+
expiryMs,
|
|
2543
|
+
permissions: resources.map(cloneResourceCapability)
|
|
2544
|
+
}
|
|
2545
|
+
];
|
|
2546
|
+
return {
|
|
2547
|
+
app_id: manifest.app_id,
|
|
2548
|
+
...manifest.did !== void 0 ? { did: manifest.did } : {},
|
|
2549
|
+
space,
|
|
2550
|
+
resources,
|
|
2551
|
+
expiryMs,
|
|
2552
|
+
includePublicSpace,
|
|
2553
|
+
additionalDelegates
|
|
2554
|
+
};
|
|
2555
|
+
}
|
|
2556
|
+
function normalizeSecretActions(actions) {
|
|
2557
|
+
const out = [];
|
|
2558
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2559
|
+
const add = (action) => {
|
|
2560
|
+
if (!seen.has(action)) {
|
|
2561
|
+
out.push(action);
|
|
2562
|
+
seen.add(action);
|
|
2563
|
+
}
|
|
2564
|
+
};
|
|
2565
|
+
for (const action of actions) {
|
|
2566
|
+
if (action === "read") {
|
|
2567
|
+
add("get");
|
|
3012
2568
|
continue;
|
|
3013
2569
|
}
|
|
3014
|
-
if (action === "
|
|
3015
|
-
|
|
2570
|
+
if (action === "write") {
|
|
2571
|
+
add("put");
|
|
3016
2572
|
continue;
|
|
3017
2573
|
}
|
|
3018
|
-
if (action === "
|
|
3019
|
-
|
|
2574
|
+
if (action === "delete") {
|
|
2575
|
+
add("del");
|
|
3020
2576
|
continue;
|
|
3021
2577
|
}
|
|
3022
|
-
if (action
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
2578
|
+
if (action === "get" || action === "put" || action === "del" || action === "list" || action === "metadata") {
|
|
2579
|
+
add(action);
|
|
2580
|
+
continue;
|
|
2581
|
+
}
|
|
2582
|
+
if (action === "tinycloud.kv/get" || action === "tinycloud.kv/put" || action === "tinycloud.kv/del" || action === "tinycloud.kv/list" || action === "tinycloud.kv/metadata") {
|
|
2583
|
+
add(action);
|
|
2584
|
+
continue;
|
|
3026
2585
|
}
|
|
3027
2586
|
throw new ManifestValidationError(
|
|
3028
|
-
`unknown
|
|
2587
|
+
`unknown secret action ${JSON.stringify(action)}; expected read, write, delete, list, or metadata`
|
|
3029
2588
|
);
|
|
3030
2589
|
}
|
|
3031
|
-
|
|
3032
|
-
const seen = /* @__PURE__ */ new Set();
|
|
3033
|
-
for (const a of normalizedActions) {
|
|
3034
|
-
if (!seen.has(a)) {
|
|
3035
|
-
dedupedActions.push(a);
|
|
3036
|
-
seen.add(a);
|
|
3037
|
-
}
|
|
3038
|
-
}
|
|
3039
|
-
return [
|
|
3040
|
-
{
|
|
3041
|
-
service: ENCRYPTION_PERMISSION_SERVICE,
|
|
3042
|
-
space: ENCRYPTION_MANIFEST_SPACE,
|
|
3043
|
-
path: entry.path,
|
|
3044
|
-
actions: dedupedActions,
|
|
3045
|
-
skipPrefix: true,
|
|
3046
|
-
...entry.expiry !== void 0 ? { expiry: entry.expiry } : {},
|
|
3047
|
-
...entry.description !== void 0 ? { description: entry.description } : {}
|
|
3048
|
-
}
|
|
3049
|
-
];
|
|
3050
|
-
}
|
|
3051
|
-
function expandPermissionEntries(entries) {
|
|
3052
|
-
return entries.flatMap(expandPermissionEntry);
|
|
2590
|
+
return out;
|
|
3053
2591
|
}
|
|
3054
|
-
function
|
|
3055
|
-
if (
|
|
3056
|
-
return
|
|
3057
|
-
}
|
|
3058
|
-
if (prefix === "") {
|
|
3059
|
-
return path;
|
|
3060
|
-
}
|
|
3061
|
-
if (path.startsWith("/")) {
|
|
3062
|
-
return `${prefix}${path}`;
|
|
2592
|
+
function secretNameFromSpec(fallbackName, spec) {
|
|
2593
|
+
if (spec !== null && typeof spec === "object" && !Array.isArray(spec)) {
|
|
2594
|
+
return spec.name ?? fallbackName;
|
|
3063
2595
|
}
|
|
3064
|
-
return
|
|
2596
|
+
return fallbackName;
|
|
3065
2597
|
}
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
throw new ManifestValidationError(
|
|
3070
|
-
"loadManifest requires a global fetch; pass the manifest object directly on runtimes without fetch"
|
|
3071
|
-
);
|
|
3072
|
-
}
|
|
3073
|
-
const res = await fetchFn(url);
|
|
3074
|
-
if (!res.ok) {
|
|
3075
|
-
throw new ManifestValidationError(
|
|
3076
|
-
`failed to fetch manifest from ${url}: HTTP ${res.status}`
|
|
3077
|
-
);
|
|
2598
|
+
function secretScopeFromSpec(spec) {
|
|
2599
|
+
if (spec !== null && typeof spec === "object" && !Array.isArray(spec)) {
|
|
2600
|
+
return spec.scope;
|
|
3078
2601
|
}
|
|
3079
|
-
|
|
3080
|
-
return validateManifest(json);
|
|
2602
|
+
return void 0;
|
|
3081
2603
|
}
|
|
3082
|
-
function
|
|
3083
|
-
if (
|
|
3084
|
-
|
|
3085
|
-
}
|
|
3086
|
-
const m = input;
|
|
3087
|
-
if (m.manifest_version !== void 0 && m.manifest_version !== DEFAULT_MANIFEST_VERSION) {
|
|
3088
|
-
throw new ManifestValidationError(
|
|
3089
|
-
`manifest.manifest_version must be ${DEFAULT_MANIFEST_VERSION}`
|
|
3090
|
-
);
|
|
3091
|
-
}
|
|
3092
|
-
if (typeof m.app_id !== "string" || m.app_id.length === 0) {
|
|
3093
|
-
throw new ManifestValidationError(
|
|
3094
|
-
"manifest.app_id is required and must be a non-empty string"
|
|
3095
|
-
);
|
|
2604
|
+
function secretActionsFromSpec(name, spec) {
|
|
2605
|
+
if (spec === true) {
|
|
2606
|
+
return ["read"];
|
|
3096
2607
|
}
|
|
3097
|
-
if (typeof
|
|
3098
|
-
|
|
3099
|
-
"manifest.name is required and must be a non-empty string"
|
|
3100
|
-
);
|
|
2608
|
+
if (typeof spec === "string") {
|
|
2609
|
+
return [spec];
|
|
3101
2610
|
}
|
|
3102
|
-
if (
|
|
3103
|
-
|
|
3104
|
-
"manifest.did must be a non-empty DID string"
|
|
3105
|
-
);
|
|
2611
|
+
if (Array.isArray(spec)) {
|
|
2612
|
+
return spec;
|
|
3106
2613
|
}
|
|
3107
|
-
if (
|
|
2614
|
+
if (spec === null || typeof spec !== "object") {
|
|
3108
2615
|
throw new ManifestValidationError(
|
|
3109
|
-
|
|
2616
|
+
`manifest.secrets.${name} must be true, a string action, an actions array, or an object`
|
|
3110
2617
|
);
|
|
3111
2618
|
}
|
|
3112
|
-
if (
|
|
3113
|
-
|
|
2619
|
+
if (spec.actions === void 0) {
|
|
2620
|
+
return ["read"];
|
|
3114
2621
|
}
|
|
3115
|
-
if (
|
|
3116
|
-
|
|
3117
|
-
throw new ManifestValidationError(
|
|
3118
|
-
"manifest.permissions must be an array"
|
|
3119
|
-
);
|
|
3120
|
-
}
|
|
3121
|
-
m.permissions.forEach(
|
|
3122
|
-
(p, i) => validatePermissionEntry(p, `permissions[${i}]`)
|
|
3123
|
-
);
|
|
2622
|
+
if (typeof spec.actions === "string") {
|
|
2623
|
+
return [spec.actions];
|
|
3124
2624
|
}
|
|
3125
|
-
if (
|
|
3126
|
-
|
|
2625
|
+
if (Array.isArray(spec.actions)) {
|
|
2626
|
+
return spec.actions;
|
|
3127
2627
|
}
|
|
3128
|
-
|
|
2628
|
+
throw new ManifestValidationError(
|
|
2629
|
+
`manifest.secrets.${name}.actions must be a string or array`
|
|
2630
|
+
);
|
|
3129
2631
|
}
|
|
3130
|
-
function
|
|
3131
|
-
if (secrets ===
|
|
3132
|
-
|
|
2632
|
+
function secretEntriesForManifest(secrets) {
|
|
2633
|
+
if (secrets === void 0) {
|
|
2634
|
+
return [];
|
|
3133
2635
|
}
|
|
2636
|
+
const entries = [];
|
|
3134
2637
|
for (const [name, spec] of Object.entries(secrets)) {
|
|
3135
|
-
if (!SECRET_NAME_RE.test(name)) {
|
|
3136
|
-
throw new ManifestValidationError(
|
|
3137
|
-
`manifest.secrets.${name} must match ${SECRET_NAME_RE.source}`
|
|
3138
|
-
);
|
|
3139
|
-
}
|
|
3140
|
-
try {
|
|
3141
|
-
resolveSecretPath(
|
|
3142
|
-
secretNameFromSpec(name, spec),
|
|
3143
|
-
{ scope: secretScopeFromSpec(spec) }
|
|
3144
|
-
);
|
|
3145
|
-
} catch (error) {
|
|
3146
|
-
throw new ManifestValidationError(
|
|
3147
|
-
`manifest.secrets.${name}: ${error instanceof Error ? error.message : String(error)}`
|
|
3148
|
-
);
|
|
3149
|
-
}
|
|
3150
2638
|
const actions = secretActionsFromSpec(name, spec);
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
}
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
2639
|
+
const secretPath = resolveSecretPath(
|
|
2640
|
+
secretNameFromSpec(name, spec),
|
|
2641
|
+
{ scope: secretScopeFromSpec(spec) }
|
|
2642
|
+
);
|
|
2643
|
+
const extra = spec !== true && typeof spec === "object" && !Array.isArray(spec) ? spec : {};
|
|
2644
|
+
entries.push({
|
|
2645
|
+
service: VAULT_PERMISSION_SERVICE,
|
|
2646
|
+
space: SECRETS_SPACE,
|
|
2647
|
+
path: secretPath.vaultKey,
|
|
2648
|
+
actions: normalizeSecretActions(actions),
|
|
2649
|
+
skipPrefix: true,
|
|
2650
|
+
...extra.expiry !== void 0 ? { expiry: extra.expiry } : {},
|
|
2651
|
+
...extra.description !== void 0 ? { description: extra.description } : {}
|
|
2652
|
+
});
|
|
2653
|
+
}
|
|
2654
|
+
return entries;
|
|
2655
|
+
}
|
|
2656
|
+
function resolveEntry(entry, prefix, _inheritedExpiryMs, inheritedSpace) {
|
|
2657
|
+
const skipPrefixForEntry = entry.skipPrefix === true || entry.service === ENCRYPTION_PERMISSION_SERVICE;
|
|
2658
|
+
const resolvedPath = applyPrefix(prefix, entry.path, skipPrefixForEntry);
|
|
2659
|
+
const entryExpiryMs = entry.expiry !== void 0 ? parseExpiry(entry.expiry) : void 0;
|
|
2660
|
+
return expandPermissionEntry({
|
|
2661
|
+
...entry,
|
|
2662
|
+
space: entry.space ?? inheritedSpace,
|
|
2663
|
+
path: resolvedPath,
|
|
2664
|
+
skipPrefix: true
|
|
2665
|
+
}).map((expanded) => ({
|
|
2666
|
+
service: expanded.service,
|
|
2667
|
+
space: expanded.space ?? inheritedSpace,
|
|
2668
|
+
path: expanded.path,
|
|
2669
|
+
actions: expanded.actions,
|
|
2670
|
+
// Only populate `expiryMs` when the entry had its own expiry override.
|
|
2671
|
+
// When absent, callers use the parent (delegation or manifest) expiry
|
|
2672
|
+
// which is carried on ResolvedDelegate.expiryMs / ResolvedCapabilities.expiryMs.
|
|
2673
|
+
...entryExpiryMs !== void 0 ? { expiryMs: entryExpiryMs } : {},
|
|
2674
|
+
...entry.description !== void 0 ? { description: entry.description } : {}
|
|
2675
|
+
}));
|
|
2676
|
+
}
|
|
2677
|
+
function expandVaultPermissionEntry(entry) {
|
|
2678
|
+
const byBase = /* @__PURE__ */ new Map();
|
|
2679
|
+
for (const action of entry.actions) {
|
|
2680
|
+
const expansion = vaultActionExpansion(action);
|
|
2681
|
+
for (const base of expansion.bases) {
|
|
2682
|
+
const actions = byBase.get(base) ?? [];
|
|
2683
|
+
if (!actions.includes(expansion.action)) {
|
|
2684
|
+
actions.push(expansion.action);
|
|
3161
2685
|
}
|
|
3162
|
-
|
|
3163
|
-
if (spec !== null && typeof spec === "object" && !Array.isArray(spec) && spec.expiry !== void 0) {
|
|
3164
|
-
parseExpiry(spec.expiry);
|
|
2686
|
+
byBase.set(base, actions);
|
|
3165
2687
|
}
|
|
3166
2688
|
}
|
|
2689
|
+
return [...byBase.entries()].map(([base, actions]) => ({
|
|
2690
|
+
...entry,
|
|
2691
|
+
service: "tinycloud.kv",
|
|
2692
|
+
path: vaultKVPath(base, entry.path),
|
|
2693
|
+
actions,
|
|
2694
|
+
skipPrefix: true
|
|
2695
|
+
}));
|
|
3167
2696
|
}
|
|
3168
|
-
function
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
const entry = p;
|
|
3173
|
-
if (typeof entry.service !== "string" || entry.service.length === 0) {
|
|
3174
|
-
throw new ManifestValidationError(`${path}.service is required`);
|
|
2697
|
+
function vaultActionExpansion(action) {
|
|
2698
|
+
const normalized = normalizeVaultAction(action);
|
|
2699
|
+
if (normalized === "read" || normalized === "get") {
|
|
2700
|
+
return { bases: ["vault"], action: "tinycloud.kv/get" };
|
|
3175
2701
|
}
|
|
3176
|
-
if (
|
|
3177
|
-
|
|
3178
|
-
`${path}.space must be a non-empty string`
|
|
3179
|
-
);
|
|
2702
|
+
if (normalized === "write" || normalized === "put") {
|
|
2703
|
+
return { bases: ["vault"], action: "tinycloud.kv/put" };
|
|
3180
2704
|
}
|
|
3181
|
-
if (
|
|
3182
|
-
|
|
3183
|
-
`${path}.path is required (use "" or "/" for root)`
|
|
3184
|
-
);
|
|
2705
|
+
if (normalized === "delete" || normalized === "del") {
|
|
2706
|
+
return { bases: ["vault"], action: "tinycloud.kv/del" };
|
|
3185
2707
|
}
|
|
3186
|
-
if (
|
|
3187
|
-
|
|
3188
|
-
`${path}.actions must be a non-empty array`
|
|
3189
|
-
);
|
|
2708
|
+
if (normalized === "list") {
|
|
2709
|
+
return { bases: ["vault"], action: "tinycloud.kv/list" };
|
|
3190
2710
|
}
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
throw new ManifestValidationError(
|
|
3194
|
-
`${path}.actions must contain non-empty strings`
|
|
3195
|
-
);
|
|
3196
|
-
}
|
|
3197
|
-
if (entry.service === VAULT_PERMISSION_SERVICE) {
|
|
3198
|
-
vaultActionExpansion(action);
|
|
3199
|
-
}
|
|
2711
|
+
if (normalized === "head") {
|
|
2712
|
+
return { bases: ["vault"], action: "tinycloud.kv/get" };
|
|
3200
2713
|
}
|
|
3201
|
-
if (
|
|
3202
|
-
|
|
2714
|
+
if (normalized === "metadata") {
|
|
2715
|
+
return { bases: ["vault"], action: "tinycloud.kv/metadata" };
|
|
3203
2716
|
}
|
|
2717
|
+
throw new ManifestValidationError(
|
|
2718
|
+
`unknown vault action ${JSON.stringify(action)}; expected read, write, delete, get, put, del, list, head, or metadata`
|
|
2719
|
+
);
|
|
3204
2720
|
}
|
|
3205
|
-
function
|
|
3206
|
-
if (
|
|
3207
|
-
return
|
|
2721
|
+
function normalizeVaultAction(action) {
|
|
2722
|
+
if (action.startsWith(`${VAULT_PERMISSION_SERVICE}/`)) {
|
|
2723
|
+
return action.slice(`${VAULT_PERMISSION_SERVICE}/`.length);
|
|
3208
2724
|
}
|
|
3209
|
-
if (
|
|
3210
|
-
return
|
|
2725
|
+
if (action.startsWith("tinycloud.kv/")) {
|
|
2726
|
+
return action.slice("tinycloud.kv/".length);
|
|
3211
2727
|
}
|
|
3212
|
-
if (
|
|
3213
|
-
|
|
2728
|
+
if (action.includes("/")) {
|
|
2729
|
+
throw new ManifestValidationError(
|
|
2730
|
+
`unknown vault action ${JSON.stringify(action)}; expected a tinycloud.vault or tinycloud.kv action`
|
|
2731
|
+
);
|
|
3214
2732
|
}
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
2733
|
+
return action;
|
|
2734
|
+
}
|
|
2735
|
+
function vaultKVPath(base, path) {
|
|
2736
|
+
const normalized = path.startsWith("/") ? path.slice(1) : path;
|
|
2737
|
+
return `${base}/${normalized}`;
|
|
2738
|
+
}
|
|
2739
|
+
function cloneResourceCapability(entry) {
|
|
2740
|
+
return {
|
|
2741
|
+
service: entry.service,
|
|
2742
|
+
space: entry.space,
|
|
2743
|
+
path: entry.path,
|
|
2744
|
+
actions: [...entry.actions],
|
|
2745
|
+
...entry.expiryMs !== void 0 ? { expiryMs: entry.expiryMs } : {},
|
|
2746
|
+
...entry.description !== void 0 ? { description: entry.description } : {}
|
|
2747
|
+
};
|
|
2748
|
+
}
|
|
2749
|
+
function clonePermissionEntry(entry) {
|
|
2750
|
+
return {
|
|
2751
|
+
service: entry.service,
|
|
2752
|
+
...entry.space !== void 0 ? { space: entry.space } : {},
|
|
2753
|
+
path: entry.path,
|
|
2754
|
+
actions: [...entry.actions],
|
|
2755
|
+
...entry.skipPrefix !== void 0 ? { skipPrefix: entry.skipPrefix } : {},
|
|
2756
|
+
...entry.expiry !== void 0 ? { expiry: entry.expiry } : {},
|
|
2757
|
+
...entry.description !== void 0 ? { description: entry.description } : {}
|
|
2758
|
+
};
|
|
2759
|
+
}
|
|
2760
|
+
function dedupeResources(resources) {
|
|
2761
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
2762
|
+
for (const resource of resources) {
|
|
2763
|
+
const key = `${resource.service}\0${resource.space}\0${resource.path}\0${resource.expiryMs ?? ""}`;
|
|
2764
|
+
const existing = byKey.get(key);
|
|
2765
|
+
if (existing === void 0) {
|
|
2766
|
+
byKey.set(key, cloneResourceCapability(resource));
|
|
2767
|
+
continue;
|
|
2768
|
+
}
|
|
2769
|
+
const seen = new Set(existing.actions);
|
|
2770
|
+
for (const action of resource.actions) {
|
|
2771
|
+
if (!seen.has(action)) {
|
|
2772
|
+
existing.actions.push(action);
|
|
2773
|
+
seen.add(action);
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2776
|
+
if (existing.description === void 0 && resource.description !== void 0) {
|
|
2777
|
+
existing.description = resource.description;
|
|
2778
|
+
}
|
|
3218
2779
|
}
|
|
3219
|
-
return
|
|
2780
|
+
return [...byKey.values()];
|
|
3220
2781
|
}
|
|
3221
|
-
function
|
|
3222
|
-
|
|
2782
|
+
function capabilitiesReadPermission(space) {
|
|
2783
|
+
return {
|
|
2784
|
+
service: "tinycloud.capabilities",
|
|
2785
|
+
space,
|
|
2786
|
+
path: "",
|
|
2787
|
+
actions: ["tinycloud.capabilities/read"]
|
|
2788
|
+
};
|
|
2789
|
+
}
|
|
2790
|
+
function withCapabilitiesReadForSpaces(resources) {
|
|
2791
|
+
if (resources.length === 0) {
|
|
3223
2792
|
return [];
|
|
3224
2793
|
}
|
|
3225
|
-
const
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
2794
|
+
const spaces = new Set(
|
|
2795
|
+
resources.filter((resource) => resource.service !== ENCRYPTION_PERMISSION_SERVICE).map((resource) => resource.space)
|
|
2796
|
+
);
|
|
2797
|
+
return dedupeResources([
|
|
2798
|
+
...resources,
|
|
2799
|
+
...[...spaces].map(capabilitiesReadPermission)
|
|
2800
|
+
]);
|
|
2801
|
+
}
|
|
2802
|
+
function accountRegistryPermissions() {
|
|
2803
|
+
return [ACCOUNT_REGISTRY_PATH, "spaces/"].map((path) => ({
|
|
2804
|
+
service: "tinycloud.kv",
|
|
2805
|
+
space: ACCOUNT_REGISTRY_SPACE,
|
|
2806
|
+
path,
|
|
2807
|
+
actions: ["tinycloud.kv/get", "tinycloud.kv/put", "tinycloud.kv/list"]
|
|
3232
2808
|
}));
|
|
3233
2809
|
}
|
|
3234
|
-
function
|
|
3235
|
-
const manifest = validateManifest(input);
|
|
3236
|
-
const prefix = manifest.prefix !== void 0 ? manifest.prefix : manifest.app_id;
|
|
3237
|
-
const space = manifest.space ?? DEFAULT_MANIFEST_SPACE;
|
|
3238
|
-
const expiryMs = parseExpiry(manifest.expiry ?? DEFAULT_EXPIRY);
|
|
3239
|
-
const includePublicSpace = manifest.includePublicSpace ?? true;
|
|
3240
|
-
const tier = normalizeDefaults(manifest.defaults);
|
|
3241
|
-
const defaultEntries = defaultEntriesForTier(tier);
|
|
3242
|
-
const explicitEntries = manifest.permissions ?? [];
|
|
3243
|
-
const secretEntries = secretEntriesForManifest(manifest.secrets);
|
|
3244
|
-
const allEntries = [
|
|
3245
|
-
...defaultEntries,
|
|
3246
|
-
...explicitEntries,
|
|
3247
|
-
...secretEntries
|
|
3248
|
-
];
|
|
3249
|
-
const resources = withCapabilitiesReadForSpaces(
|
|
3250
|
-
allEntries.flatMap((entry) => resolveEntry(entry, prefix, expiryMs, space))
|
|
3251
|
-
);
|
|
3252
|
-
const additionalDelegates = manifest.did === void 0 ? [] : [
|
|
3253
|
-
{
|
|
3254
|
-
did: manifest.did,
|
|
3255
|
-
name: manifest.name,
|
|
3256
|
-
expiryMs,
|
|
3257
|
-
permissions: resources.map(cloneResourceCapability)
|
|
3258
|
-
}
|
|
3259
|
-
];
|
|
2810
|
+
function accountRegistryIndexPermission() {
|
|
3260
2811
|
return {
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
expiryMs,
|
|
3266
|
-
includePublicSpace,
|
|
3267
|
-
additionalDelegates
|
|
2812
|
+
service: "tinycloud.sql",
|
|
2813
|
+
space: ACCOUNT_REGISTRY_SPACE,
|
|
2814
|
+
path: "account",
|
|
2815
|
+
actions: ["tinycloud.sql/read", "tinycloud.sql/write", "tinycloud.sql/ddl"]
|
|
3268
2816
|
};
|
|
3269
2817
|
}
|
|
3270
|
-
function
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
2818
|
+
function composeManifestRequest(inputs, options = {}) {
|
|
2819
|
+
if (!Array.isArray(inputs) || inputs.length === 0) {
|
|
2820
|
+
throw new ManifestValidationError(
|
|
2821
|
+
"composeManifestRequest requires at least one manifest"
|
|
2822
|
+
);
|
|
2823
|
+
}
|
|
2824
|
+
const includeAccountRegistryPermissions = options.includeAccountRegistryPermissions ?? true;
|
|
2825
|
+
const manifests = inputs.map(validateManifest);
|
|
2826
|
+
const resolved = manifests.map(resolveManifest);
|
|
2827
|
+
const resources = resolved.flatMap((entry) => entry.resources);
|
|
2828
|
+
const delegationTargets = resolved.flatMap(
|
|
2829
|
+
(entry) => entry.additionalDelegates.map((delegate) => ({
|
|
2830
|
+
...delegate,
|
|
2831
|
+
permissions: dedupeResources(delegate.permissions)
|
|
2832
|
+
}))
|
|
2833
|
+
);
|
|
2834
|
+
if (includeAccountRegistryPermissions) {
|
|
2835
|
+
resources.push(...accountRegistryPermissions());
|
|
2836
|
+
resources.push(accountRegistryIndexPermission());
|
|
2837
|
+
}
|
|
2838
|
+
const resourcesWithImplicitCapabilities = withCapabilitiesReadForSpaces(resources);
|
|
2839
|
+
const manifestsByAppId = /* @__PURE__ */ new Map();
|
|
2840
|
+
for (const manifest of manifests) {
|
|
2841
|
+
const current = manifestsByAppId.get(manifest.app_id);
|
|
2842
|
+
if (current === void 0) {
|
|
2843
|
+
manifestsByAppId.set(manifest.app_id, [manifest]);
|
|
2844
|
+
} else {
|
|
2845
|
+
current.push(manifest);
|
|
3277
2846
|
}
|
|
2847
|
+
}
|
|
2848
|
+
const registryRecords = includeAccountRegistryPermissions ? [...manifestsByAppId.entries()].map(([app_id, appManifests]) => ({
|
|
2849
|
+
key: `${ACCOUNT_REGISTRY_PATH}${app_id}`,
|
|
2850
|
+
app_id,
|
|
2851
|
+
manifests: appManifests.map((manifest) => ({
|
|
2852
|
+
...manifest,
|
|
2853
|
+
permissions: manifest.permissions?.map(clonePermissionEntry)
|
|
2854
|
+
}))
|
|
2855
|
+
})) : [];
|
|
2856
|
+
return {
|
|
2857
|
+
manifests,
|
|
2858
|
+
resources: resourcesWithImplicitCapabilities,
|
|
2859
|
+
delegationTargets,
|
|
2860
|
+
registryRecords,
|
|
2861
|
+
expiryMs: Math.max(...resolved.map((entry) => entry.expiryMs)),
|
|
2862
|
+
includePublicSpace: resolved.some((entry) => entry.includePublicSpace)
|
|
3278
2863
|
};
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
if (
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
if (action === "delete") {
|
|
3289
|
-
add("del");
|
|
3290
|
-
continue;
|
|
2864
|
+
}
|
|
2865
|
+
function resourceCapabilitiesToAbilitiesMap(resources) {
|
|
2866
|
+
const out = {};
|
|
2867
|
+
for (const r of resources) {
|
|
2868
|
+
const shortService = SERVICE_LONG_TO_SHORT[r.service];
|
|
2869
|
+
if (shortService === void 0) {
|
|
2870
|
+
throw new ManifestValidationError(
|
|
2871
|
+
`unknown service '${r.service}' \u2014 no short-form mapping. Known services: ${Object.keys(SERVICE_LONG_TO_SHORT).join(", ")}`
|
|
2872
|
+
);
|
|
3291
2873
|
}
|
|
3292
|
-
if (
|
|
3293
|
-
|
|
3294
|
-
continue;
|
|
2874
|
+
if (out[shortService] === void 0) {
|
|
2875
|
+
out[shortService] = {};
|
|
3295
2876
|
}
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
2877
|
+
const pathsMap = out[shortService];
|
|
2878
|
+
const existing = pathsMap[r.path];
|
|
2879
|
+
if (existing === void 0) {
|
|
2880
|
+
pathsMap[r.path] = [...r.actions];
|
|
2881
|
+
} else {
|
|
2882
|
+
const seen = new Set(existing);
|
|
2883
|
+
for (const action of r.actions) {
|
|
2884
|
+
if (!seen.has(action)) {
|
|
2885
|
+
existing.push(action);
|
|
2886
|
+
seen.add(action);
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
3299
2889
|
}
|
|
3300
|
-
throw new ManifestValidationError(
|
|
3301
|
-
`unknown secret action ${JSON.stringify(action)}; expected read, write, delete, list, or metadata`
|
|
3302
|
-
);
|
|
3303
2890
|
}
|
|
3304
2891
|
return out;
|
|
3305
2892
|
}
|
|
3306
|
-
function
|
|
3307
|
-
|
|
3308
|
-
|
|
2893
|
+
function resourceCapabilitiesToSpaceAbilitiesMap(resources) {
|
|
2894
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
2895
|
+
for (const resource of resources) {
|
|
2896
|
+
const entries = grouped.get(resource.space);
|
|
2897
|
+
if (entries === void 0) {
|
|
2898
|
+
grouped.set(resource.space, [resource]);
|
|
2899
|
+
} else {
|
|
2900
|
+
entries.push(resource);
|
|
2901
|
+
}
|
|
3309
2902
|
}
|
|
3310
|
-
|
|
2903
|
+
const out = {};
|
|
2904
|
+
for (const [space, entries] of grouped.entries()) {
|
|
2905
|
+
out[space] = resourceCapabilitiesToAbilitiesMap(entries);
|
|
2906
|
+
}
|
|
2907
|
+
return out;
|
|
3311
2908
|
}
|
|
3312
|
-
function
|
|
3313
|
-
|
|
3314
|
-
|
|
2909
|
+
function manifestAbilitiesUnion(resolved) {
|
|
2910
|
+
const all = [...resolved.resources];
|
|
2911
|
+
for (const delegate of resolved.additionalDelegates) {
|
|
2912
|
+
for (const perm of delegate.permissions) {
|
|
2913
|
+
all.push(perm);
|
|
2914
|
+
}
|
|
3315
2915
|
}
|
|
3316
|
-
return
|
|
2916
|
+
return resourceCapabilitiesToAbilitiesMap(all);
|
|
3317
2917
|
}
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
2918
|
+
|
|
2919
|
+
// src/account/AccountService.ts
|
|
2920
|
+
var SERVICE_NAME2 = "account";
|
|
2921
|
+
var ACCOUNT_INDEX_DB = "account";
|
|
2922
|
+
var ACCOUNT_INDEX_NAMESPACE = "tinycloud.account.index";
|
|
2923
|
+
var ACCOUNT_SPACES_PATH = "spaces/";
|
|
2924
|
+
var AccountService = class {
|
|
2925
|
+
constructor(config) {
|
|
2926
|
+
this.config = config;
|
|
2927
|
+
this.applications = {
|
|
2928
|
+
list: async (options = {}) => {
|
|
2929
|
+
if (options.preferIndex) {
|
|
2930
|
+
const indexed = await this.index.applications.list();
|
|
2931
|
+
if (indexed.ok && indexed.data.length > 0) return indexed;
|
|
2932
|
+
if (!indexed.ok && !isMissingIndexError(indexed.error)) return indexed;
|
|
2933
|
+
const canonical = await this.applications.list();
|
|
2934
|
+
if (canonical.ok && options.refreshIndex !== false) {
|
|
2935
|
+
await this.replaceApplicationsIndexQuietly(canonical.data);
|
|
2936
|
+
}
|
|
2937
|
+
return canonical;
|
|
2938
|
+
}
|
|
2939
|
+
const kvResult = this.accountKV();
|
|
2940
|
+
if (!kvResult.ok) return kvResult;
|
|
2941
|
+
const listed = await kvResult.data.list({ prefix: ACCOUNT_REGISTRY_PATH });
|
|
2942
|
+
if (!listed.ok) return accountErr(listed.error);
|
|
2943
|
+
const applications = [];
|
|
2944
|
+
for (const key of listed.data.keys) {
|
|
2945
|
+
const loaded = await kvResult.data.get(key);
|
|
2946
|
+
if (!loaded.ok) return accountErr(loaded.error);
|
|
2947
|
+
applications.push(applicationFromRecord(key, loaded.data.data));
|
|
2948
|
+
}
|
|
2949
|
+
applications.sort((a, b) => a.appId.localeCompare(b.appId));
|
|
2950
|
+
return ok3(applications);
|
|
2951
|
+
},
|
|
2952
|
+
get: async (appId) => {
|
|
2953
|
+
const kvResult = this.accountKV();
|
|
2954
|
+
if (!kvResult.ok) return kvResult;
|
|
2955
|
+
const key = applicationKey(appId);
|
|
2956
|
+
const loaded = await kvResult.data.get(key);
|
|
2957
|
+
if (!loaded.ok) return accountErr(loaded.error);
|
|
2958
|
+
return ok3(applicationFromRecord(key, loaded.data.data));
|
|
2959
|
+
},
|
|
2960
|
+
register: async (manifest) => {
|
|
2961
|
+
const manifests = Array.isArray(manifest) ? manifest : [manifest];
|
|
2962
|
+
const request = composeManifestRequest(manifests);
|
|
2963
|
+
if (request.registryRecords.length === 0) {
|
|
2964
|
+
return err3(
|
|
2965
|
+
serviceError3(
|
|
2966
|
+
"INVALID_MANIFEST",
|
|
2967
|
+
"Manifest did not produce an account application registry record",
|
|
2968
|
+
SERVICE_NAME2
|
|
2969
|
+
)
|
|
2970
|
+
);
|
|
2971
|
+
}
|
|
2972
|
+
await this.config.ensureAccountSpaceHosted?.();
|
|
2973
|
+
const kvResult = this.accountKV();
|
|
2974
|
+
if (!kvResult.ok) return kvResult;
|
|
2975
|
+
let registered;
|
|
2976
|
+
for (const record of request.registryRecords) {
|
|
2977
|
+
const manifestHash = hashJson(record.manifests);
|
|
2978
|
+
if (await this.indexHasApplicationHash(record.app_id, manifestHash)) {
|
|
2979
|
+
registered = {
|
|
2980
|
+
appId: record.app_id,
|
|
2981
|
+
manifests: record.manifests,
|
|
2982
|
+
manifestHash,
|
|
2983
|
+
name: record.manifests[0]?.name,
|
|
2984
|
+
description: record.manifests[0]?.description
|
|
2985
|
+
};
|
|
2986
|
+
continue;
|
|
2987
|
+
}
|
|
2988
|
+
const stored = {
|
|
2989
|
+
app_id: record.app_id,
|
|
2990
|
+
manifests: record.manifests,
|
|
2991
|
+
manifest_hash: manifestHash,
|
|
2992
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
2993
|
+
};
|
|
2994
|
+
const written = await kvResult.data.put(record.key, stored);
|
|
2995
|
+
if (!written.ok) return accountErr(written.error);
|
|
2996
|
+
registered = applicationFromRecord(record.key, stored);
|
|
2997
|
+
await this.upsertApplicationIndexQuietly(registered);
|
|
2998
|
+
}
|
|
2999
|
+
return ok3(registered);
|
|
3000
|
+
},
|
|
3001
|
+
remove: async (appId) => {
|
|
3002
|
+
const kvResult = this.accountKV();
|
|
3003
|
+
if (!kvResult.ok) return kvResult;
|
|
3004
|
+
const removed = await kvResult.data.delete(applicationKey(appId));
|
|
3005
|
+
if (!removed.ok) return accountErr(removed.error);
|
|
3006
|
+
await this.deleteApplicationIndexQuietly(appId);
|
|
3007
|
+
return ok3(void 0);
|
|
3008
|
+
}
|
|
3009
|
+
};
|
|
3010
|
+
this.spaces = {
|
|
3011
|
+
list: async (options = {}) => {
|
|
3012
|
+
if (options.preferIndex) {
|
|
3013
|
+
const indexed = await this.index.spaces.list();
|
|
3014
|
+
if (indexed.ok && indexed.data.length > 0) return indexed;
|
|
3015
|
+
if (!indexed.ok && !isMissingIndexError(indexed.error)) return indexed;
|
|
3016
|
+
const canonical = await this.spaces.syncAccessible();
|
|
3017
|
+
if (canonical.ok && options.refreshIndex !== false) {
|
|
3018
|
+
await this.replaceSpacesIndexQuietly(canonical.data);
|
|
3019
|
+
}
|
|
3020
|
+
return canonical;
|
|
3021
|
+
}
|
|
3022
|
+
const kvResult = this.accountKV();
|
|
3023
|
+
if (!kvResult.ok) return kvResult;
|
|
3024
|
+
const listed = await kvResult.data.list({ prefix: ACCOUNT_SPACES_PATH });
|
|
3025
|
+
if (!listed.ok) return accountErr(listed.error);
|
|
3026
|
+
const spaces = [];
|
|
3027
|
+
for (const key of listed.data.keys) {
|
|
3028
|
+
const loaded = await kvResult.data.get(key);
|
|
3029
|
+
if (!loaded.ok) return accountErr(loaded.error);
|
|
3030
|
+
spaces.push(spaceFromRecord(key, loaded.data.data));
|
|
3031
|
+
}
|
|
3032
|
+
spaces.sort((a, b) => a.name.localeCompare(b.name) || a.spaceId.localeCompare(b.spaceId));
|
|
3033
|
+
return ok3(spaces);
|
|
3034
|
+
},
|
|
3035
|
+
get: async (spaceId) => {
|
|
3036
|
+
const kvResult = this.accountKV();
|
|
3037
|
+
if (!kvResult.ok) return kvResult;
|
|
3038
|
+
const loaded = await kvResult.data.get(spaceKey(spaceId));
|
|
3039
|
+
if (!loaded.ok) return accountErr(loaded.error);
|
|
3040
|
+
return ok3(spaceFromRecord(spaceKey(spaceId), loaded.data.data));
|
|
3041
|
+
},
|
|
3042
|
+
register: async (space) => {
|
|
3043
|
+
await this.config.ensureAccountSpaceHosted?.();
|
|
3044
|
+
const kvResult = this.accountKV();
|
|
3045
|
+
if (!kvResult.ok) return kvResult;
|
|
3046
|
+
const stored = spaceRecordFromInput(space);
|
|
3047
|
+
const written = await kvResult.data.put(spaceKey(stored.space_id), stored);
|
|
3048
|
+
if (!written.ok) return accountErr(written.error);
|
|
3049
|
+
const registered = spaceFromRecord(spaceKey(stored.space_id), stored);
|
|
3050
|
+
await this.upsertSpaceIndexQuietly(registered);
|
|
3051
|
+
return ok3(registered);
|
|
3052
|
+
},
|
|
3053
|
+
syncAccessible: async () => {
|
|
3054
|
+
const listed = await this.config.getSpaces().list();
|
|
3055
|
+
if (!listed.ok) return accountErr(listed.error);
|
|
3056
|
+
const registered = [];
|
|
3057
|
+
for (const space of listed.data) {
|
|
3058
|
+
const result = await this.spaces.register(space);
|
|
3059
|
+
if (!result.ok) return result;
|
|
3060
|
+
registered.push(result.data);
|
|
3061
|
+
}
|
|
3062
|
+
return ok3(registered);
|
|
3063
|
+
},
|
|
3064
|
+
remove: async (spaceId) => {
|
|
3065
|
+
const kvResult = this.accountKV();
|
|
3066
|
+
if (!kvResult.ok) return kvResult;
|
|
3067
|
+
const removed = await kvResult.data.delete(spaceKey(spaceId));
|
|
3068
|
+
if (!removed.ok) return accountErr(removed.error);
|
|
3069
|
+
await this.deleteSpaceIndexQuietly(spaceId);
|
|
3070
|
+
return ok3(void 0);
|
|
3071
|
+
}
|
|
3072
|
+
};
|
|
3073
|
+
this.delegations = {
|
|
3074
|
+
list: async (options = {}) => {
|
|
3075
|
+
if (options.preferIndex) {
|
|
3076
|
+
const indexed = await this.index.delegations.list(options);
|
|
3077
|
+
if (indexed.ok && indexed.data.length > 0) return indexed;
|
|
3078
|
+
if (!indexed.ok && !isMissingIndexError(indexed.error)) return indexed;
|
|
3079
|
+
const live = await this.delegations.list({
|
|
3080
|
+
direction: options.direction,
|
|
3081
|
+
space: options.space
|
|
3082
|
+
});
|
|
3083
|
+
if (live.ok && options.refreshIndex !== false) {
|
|
3084
|
+
await this.replaceDelegationsIndexQuietly(live.data);
|
|
3085
|
+
}
|
|
3086
|
+
return live;
|
|
3087
|
+
}
|
|
3088
|
+
const spaces = await this.config.getSpaces().list();
|
|
3089
|
+
if (!spaces.ok) return accountErr(spaces.error);
|
|
3090
|
+
const targetSpaces = options.space ? spaces.data.filter((space) => space.id === options.space || space.name === options.space) : spaces.data;
|
|
3091
|
+
const delegations = [];
|
|
3092
|
+
for (const space of targetSpaces) {
|
|
3093
|
+
const scoped = this.config.getSpaces().get(space.id).delegations;
|
|
3094
|
+
if (options.direction !== "received") {
|
|
3095
|
+
const granted = await scoped.list();
|
|
3096
|
+
if (!granted.ok) return accountErr(granted.error);
|
|
3097
|
+
delegations.push(...granted.data.map((d) => mapDelegation(d, space, "granted")));
|
|
3098
|
+
}
|
|
3099
|
+
if (options.direction !== "granted") {
|
|
3100
|
+
const received = await scoped.listReceived();
|
|
3101
|
+
if (!received.ok) return accountErr(received.error);
|
|
3102
|
+
delegations.push(...received.data.map((d) => mapDelegation(d, space, "received")));
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
delegations.sort((a, b) => a.spaceId.localeCompare(b.spaceId) || a.cid.localeCompare(b.cid));
|
|
3106
|
+
return ok3(delegations);
|
|
3107
|
+
},
|
|
3108
|
+
revoke: async (options) => {
|
|
3109
|
+
const space = await this.resolveSpace(options.space);
|
|
3110
|
+
if (!space.ok) return space;
|
|
3111
|
+
const revoked = await this.config.getSpaces().get(space.data.id).delegations.revoke(options.cid);
|
|
3112
|
+
if (!revoked.ok) return accountErr(revoked.error);
|
|
3113
|
+
return ok3(void 0);
|
|
3114
|
+
}
|
|
3115
|
+
};
|
|
3116
|
+
this.index = {
|
|
3117
|
+
rebuild: async () => {
|
|
3118
|
+
const dbResult = this.accountDb();
|
|
3119
|
+
if (!dbResult.ok) return dbResult;
|
|
3120
|
+
const applications = await this.applications.list();
|
|
3121
|
+
if (!applications.ok) return applications;
|
|
3122
|
+
const spaces = await this.spaces.list();
|
|
3123
|
+
if (!spaces.ok) return spaces;
|
|
3124
|
+
const delegations = await this.delegations.list();
|
|
3125
|
+
if (!delegations.ok) return delegations;
|
|
3126
|
+
const syncedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3127
|
+
const schema = await this.ensureAccountIndex(dbResult.data);
|
|
3128
|
+
if (!schema.ok) return schema;
|
|
3129
|
+
const statements = [
|
|
3130
|
+
{ sql: "DELETE FROM applications" },
|
|
3131
|
+
{ sql: "DELETE FROM application_state" },
|
|
3132
|
+
{ sql: "DELETE FROM spaces" },
|
|
3133
|
+
{ sql: "DELETE FROM delegations" },
|
|
3134
|
+
{ sql: "DELETE FROM sync_state" },
|
|
3135
|
+
...applications.data.map((app) => ({
|
|
3136
|
+
sql: "INSERT INTO applications (app_id, name, description, updated_at, manifest_json) VALUES (?, ?, ?, ?, ?)",
|
|
3137
|
+
params: [
|
|
3138
|
+
app.appId,
|
|
3139
|
+
app.name ?? null,
|
|
3140
|
+
app.description ?? null,
|
|
3141
|
+
app.updatedAt ?? syncedAt,
|
|
3142
|
+
JSON.stringify(app.manifests)
|
|
3143
|
+
]
|
|
3144
|
+
})),
|
|
3145
|
+
...applications.data.map((app) => ({
|
|
3146
|
+
sql: "INSERT OR REPLACE INTO application_state (app_id, manifest_hash, indexed_at) VALUES (?, ?, ?)",
|
|
3147
|
+
params: [app.appId, app.manifestHash ?? hashJson(app.manifests), syncedAt]
|
|
3148
|
+
})),
|
|
3149
|
+
...spaces.data.map((space) => ({
|
|
3150
|
+
sql: "INSERT OR REPLACE INTO spaces (space_id, name, owner_did, type, permissions_json, status, registered_at, updated_at, expires_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
3151
|
+
params: [
|
|
3152
|
+
space.spaceId,
|
|
3153
|
+
space.name,
|
|
3154
|
+
space.ownerDid,
|
|
3155
|
+
space.type,
|
|
3156
|
+
JSON.stringify(space.permissions),
|
|
3157
|
+
space.status,
|
|
3158
|
+
space.registeredAt ?? syncedAt,
|
|
3159
|
+
space.updatedAt ?? syncedAt,
|
|
3160
|
+
space.expiresAt?.toISOString() ?? null
|
|
3161
|
+
]
|
|
3162
|
+
})),
|
|
3163
|
+
...delegations.data.map((delegation) => ({
|
|
3164
|
+
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
3165
|
+
params: [
|
|
3166
|
+
delegation.cid,
|
|
3167
|
+
delegation.direction,
|
|
3168
|
+
delegation.spaceId,
|
|
3169
|
+
delegation.spaceName ?? null,
|
|
3170
|
+
delegation.counterpartyDid,
|
|
3171
|
+
delegation.delegateDid,
|
|
3172
|
+
delegation.delegatorDid ?? null,
|
|
3173
|
+
delegation.path,
|
|
3174
|
+
JSON.stringify(delegation.actions),
|
|
3175
|
+
delegation.expiry.toISOString(),
|
|
3176
|
+
delegation.status,
|
|
3177
|
+
delegation.createdAt?.toISOString() ?? null,
|
|
3178
|
+
syncedAt
|
|
3179
|
+
]
|
|
3180
|
+
})),
|
|
3181
|
+
{
|
|
3182
|
+
sql: "INSERT INTO sync_state (source, synced_at, count) VALUES (?, ?, ?)",
|
|
3183
|
+
params: ["applications", syncedAt, applications.data.length]
|
|
3184
|
+
},
|
|
3185
|
+
{
|
|
3186
|
+
sql: "INSERT INTO sync_state (source, synced_at, count) VALUES (?, ?, ?)",
|
|
3187
|
+
params: ["spaces", syncedAt, spaces.data.length]
|
|
3188
|
+
},
|
|
3189
|
+
{
|
|
3190
|
+
sql: "INSERT INTO sync_state (source, synced_at, count) VALUES (?, ?, ?)",
|
|
3191
|
+
params: ["delegations", syncedAt, delegations.data.length]
|
|
3192
|
+
}
|
|
3193
|
+
];
|
|
3194
|
+
const rebuilt = await dbResult.data.batch(statements);
|
|
3195
|
+
if (!rebuilt.ok) return accountErr(rebuilt.error);
|
|
3196
|
+
return ok3({
|
|
3197
|
+
database: ACCOUNT_INDEX_DB,
|
|
3198
|
+
applications: applications.data.length,
|
|
3199
|
+
spaces: spaces.data.length,
|
|
3200
|
+
delegations: delegations.data.length,
|
|
3201
|
+
syncedAt
|
|
3202
|
+
});
|
|
3203
|
+
},
|
|
3204
|
+
applications: {
|
|
3205
|
+
list: async () => {
|
|
3206
|
+
const dbResult = this.accountDb();
|
|
3207
|
+
if (!dbResult.ok) return dbResult;
|
|
3208
|
+
const queried = await dbResult.data.query(
|
|
3209
|
+
"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"
|
|
3210
|
+
);
|
|
3211
|
+
if (!queried.ok) return accountErr(queried.error);
|
|
3212
|
+
return ok3(queried.data.rows.map(indexedApplicationFromRow));
|
|
3213
|
+
}
|
|
3214
|
+
},
|
|
3215
|
+
spaces: {
|
|
3216
|
+
list: async () => {
|
|
3217
|
+
const dbResult = this.accountDb();
|
|
3218
|
+
if (!dbResult.ok) return dbResult;
|
|
3219
|
+
const queried = await dbResult.data.query(
|
|
3220
|
+
"SELECT space_id, name, owner_did, type, permissions_json, status, registered_at, updated_at, expires_at FROM spaces ORDER BY name, space_id"
|
|
3221
|
+
);
|
|
3222
|
+
if (!queried.ok) return accountErr(queried.error);
|
|
3223
|
+
return ok3(queried.data.rows.map(indexedSpaceFromRow));
|
|
3224
|
+
}
|
|
3225
|
+
},
|
|
3226
|
+
delegations: {
|
|
3227
|
+
list: async (options = {}) => {
|
|
3228
|
+
const dbResult = this.accountDb();
|
|
3229
|
+
if (!dbResult.ok) return dbResult;
|
|
3230
|
+
const where = [];
|
|
3231
|
+
const params = [];
|
|
3232
|
+
if (options.direction && options.direction !== "all") {
|
|
3233
|
+
where.push("direction = ?");
|
|
3234
|
+
params.push(options.direction);
|
|
3235
|
+
}
|
|
3236
|
+
if (options.space) {
|
|
3237
|
+
where.push("(space_id = ? OR space_name = ?)");
|
|
3238
|
+
params.push(options.space, options.space);
|
|
3239
|
+
}
|
|
3240
|
+
const queried = await dbResult.data.query(
|
|
3241
|
+
`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`,
|
|
3242
|
+
params
|
|
3243
|
+
);
|
|
3244
|
+
if (!queried.ok) return accountErr(queried.error);
|
|
3245
|
+
return ok3(queried.data.rows.map(indexedDelegationFromRow));
|
|
3246
|
+
}
|
|
3247
|
+
},
|
|
3248
|
+
query: async (sql, params) => {
|
|
3249
|
+
const dbResult = this.accountDb();
|
|
3250
|
+
if (!dbResult.ok) return dbResult;
|
|
3251
|
+
const queried = await dbResult.data.query(sql, params);
|
|
3252
|
+
if (!queried.ok) return accountErr(queried.error);
|
|
3253
|
+
return ok3(queried.data);
|
|
3254
|
+
},
|
|
3255
|
+
status: async () => {
|
|
3256
|
+
const dbResult = this.accountDb();
|
|
3257
|
+
if (!dbResult.ok) return dbResult;
|
|
3258
|
+
const queried = await dbResult.data.query(
|
|
3259
|
+
"SELECT source, synced_at, count FROM sync_state ORDER BY source"
|
|
3260
|
+
);
|
|
3261
|
+
if (!queried.ok) {
|
|
3262
|
+
if (isMissingIndexError(queried.error)) {
|
|
3263
|
+
return ok3({ database: ACCOUNT_INDEX_DB, state: "missing", sources: [] });
|
|
3264
|
+
}
|
|
3265
|
+
return accountErr(queried.error);
|
|
3266
|
+
}
|
|
3267
|
+
return ok3({
|
|
3268
|
+
database: ACCOUNT_INDEX_DB,
|
|
3269
|
+
state: "ready",
|
|
3270
|
+
sources: queried.data.rows.map(([source, syncedAt, count]) => ({
|
|
3271
|
+
source,
|
|
3272
|
+
syncedAt,
|
|
3273
|
+
count
|
|
3274
|
+
}))
|
|
3275
|
+
});
|
|
3276
|
+
}
|
|
3277
|
+
};
|
|
3278
|
+
}
|
|
3279
|
+
async status() {
|
|
3280
|
+
const apps = await this.applications.list();
|
|
3281
|
+
if (!apps.ok) return apps;
|
|
3282
|
+
const delegations = await this.delegations.list();
|
|
3283
|
+
if (!delegations.ok) return delegations;
|
|
3284
|
+
const spaces = await this.spaces.list();
|
|
3285
|
+
if (!spaces.ok) return spaces;
|
|
3286
|
+
return ok3({
|
|
3287
|
+
did: this.config.getDid(),
|
|
3288
|
+
host: this.config.getHost(),
|
|
3289
|
+
primarySpaceId: this.config.getPrimarySpaceId(),
|
|
3290
|
+
accountSpaceId: this.config.getAccountSpaceId(),
|
|
3291
|
+
applications: apps.data.length,
|
|
3292
|
+
spaces: spaces.data.length,
|
|
3293
|
+
grantedDelegations: delegations.data.filter((d) => d.direction === "granted").length,
|
|
3294
|
+
receivedDelegations: delegations.data.filter((d) => d.direction === "received").length
|
|
3295
|
+
});
|
|
3296
|
+
}
|
|
3297
|
+
accountKV() {
|
|
3298
|
+
const accountSpaceId = this.config.getAccountSpaceId();
|
|
3299
|
+
if (!accountSpaceId) {
|
|
3300
|
+
return err3(
|
|
3301
|
+
serviceError3(
|
|
3302
|
+
"ACCOUNT_SPACE_UNAVAILABLE",
|
|
3303
|
+
"Account space is unavailable. Sign in with a wallet-backed profile first.",
|
|
3304
|
+
SERVICE_NAME2
|
|
3305
|
+
)
|
|
3306
|
+
);
|
|
3307
|
+
}
|
|
3308
|
+
return ok3(this.config.getSpaces().get(accountSpaceId).kv);
|
|
3309
|
+
}
|
|
3310
|
+
accountDb() {
|
|
3311
|
+
const db = this.config.getAccountDb?.();
|
|
3312
|
+
if (!db) {
|
|
3313
|
+
return err3(
|
|
3314
|
+
serviceError3(
|
|
3315
|
+
"ACCOUNT_INDEX_UNAVAILABLE",
|
|
3316
|
+
"Account index database is unavailable. Sign in with a wallet-backed profile first.",
|
|
3317
|
+
SERVICE_NAME2
|
|
3318
|
+
)
|
|
3319
|
+
);
|
|
3320
|
+
}
|
|
3321
|
+
return ok3(db);
|
|
3321
3322
|
}
|
|
3322
|
-
|
|
3323
|
-
|
|
3323
|
+
async indexHasApplicationHash(appId, manifestHash) {
|
|
3324
|
+
const dbResult = this.accountDb();
|
|
3325
|
+
if (!dbResult.ok) return false;
|
|
3326
|
+
const schema = await this.ensureAccountIndex(dbResult.data);
|
|
3327
|
+
if (!schema.ok) return false;
|
|
3328
|
+
const queried = await dbResult.data.query(
|
|
3329
|
+
"SELECT 1 FROM application_state WHERE app_id = ? AND manifest_hash = ? LIMIT 1",
|
|
3330
|
+
[appId, manifestHash]
|
|
3331
|
+
);
|
|
3332
|
+
return queried.ok && queried.data.rows.length > 0;
|
|
3333
|
+
}
|
|
3334
|
+
async upsertApplicationIndexQuietly(app) {
|
|
3335
|
+
await ignoreIndexFailure(() => this.upsertApplicationIndex(app));
|
|
3336
|
+
}
|
|
3337
|
+
async upsertApplicationIndex(app) {
|
|
3338
|
+
const dbResult = this.accountDb();
|
|
3339
|
+
if (!dbResult.ok) return ok3(void 0);
|
|
3340
|
+
const schema = await this.ensureAccountIndex(dbResult.data);
|
|
3341
|
+
if (!schema.ok) return schema;
|
|
3342
|
+
const updatedAt = app.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
3343
|
+
const manifestHash = app.manifestHash ?? hashJson(app.manifests);
|
|
3344
|
+
const written = await dbResult.data.batch([
|
|
3345
|
+
{
|
|
3346
|
+
sql: "INSERT OR REPLACE INTO applications (app_id, name, description, updated_at, manifest_json) VALUES (?, ?, ?, ?, ?)",
|
|
3347
|
+
params: [
|
|
3348
|
+
app.appId,
|
|
3349
|
+
app.name ?? null,
|
|
3350
|
+
app.description ?? null,
|
|
3351
|
+
updatedAt,
|
|
3352
|
+
JSON.stringify(app.manifests)
|
|
3353
|
+
]
|
|
3354
|
+
},
|
|
3355
|
+
{
|
|
3356
|
+
sql: "INSERT OR REPLACE INTO application_state (app_id, manifest_hash, indexed_at) VALUES (?, ?, ?)",
|
|
3357
|
+
params: [app.appId, manifestHash, updatedAt]
|
|
3358
|
+
}
|
|
3359
|
+
]);
|
|
3360
|
+
if (!written.ok) return accountErr(written.error);
|
|
3361
|
+
return ok3(void 0);
|
|
3324
3362
|
}
|
|
3325
|
-
|
|
3326
|
-
|
|
3363
|
+
async deleteApplicationIndexQuietly(appId) {
|
|
3364
|
+
await ignoreIndexFailure(() => this.deleteApplicationIndex(appId));
|
|
3365
|
+
}
|
|
3366
|
+
async deleteApplicationIndex(appId) {
|
|
3367
|
+
const dbResult = this.accountDb();
|
|
3368
|
+
if (!dbResult.ok) return ok3(void 0);
|
|
3369
|
+
const schema = await this.ensureAccountIndex(dbResult.data);
|
|
3370
|
+
if (!schema.ok) return schema;
|
|
3371
|
+
const deleted = await dbResult.data.batch([
|
|
3372
|
+
{ sql: "DELETE FROM applications WHERE app_id = ?", params: [appId] },
|
|
3373
|
+
{ sql: "DELETE FROM application_state WHERE app_id = ?", params: [appId] }
|
|
3374
|
+
]);
|
|
3375
|
+
if (!deleted.ok) return accountErr(deleted.error);
|
|
3376
|
+
return ok3(void 0);
|
|
3327
3377
|
}
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
`manifest.secrets.${name} must be true, a string action, an actions array, or an object`
|
|
3331
|
-
);
|
|
3378
|
+
async upsertSpaceIndexQuietly(space) {
|
|
3379
|
+
await ignoreIndexFailure(() => this.upsertSpaceIndex(space));
|
|
3332
3380
|
}
|
|
3333
|
-
|
|
3334
|
-
|
|
3381
|
+
async upsertSpaceIndex(space) {
|
|
3382
|
+
const dbResult = this.accountDb();
|
|
3383
|
+
if (!dbResult.ok) return ok3(void 0);
|
|
3384
|
+
const schema = await this.ensureAccountIndex(dbResult.data);
|
|
3385
|
+
if (!schema.ok) return schema;
|
|
3386
|
+
const updatedAt = space.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
3387
|
+
const written = await dbResult.data.batch([
|
|
3388
|
+
{
|
|
3389
|
+
sql: "INSERT OR REPLACE INTO spaces (space_id, name, owner_did, type, permissions_json, status, registered_at, updated_at, expires_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
3390
|
+
params: [
|
|
3391
|
+
space.spaceId,
|
|
3392
|
+
space.name,
|
|
3393
|
+
space.ownerDid,
|
|
3394
|
+
space.type,
|
|
3395
|
+
JSON.stringify(space.permissions),
|
|
3396
|
+
space.status,
|
|
3397
|
+
space.registeredAt ?? updatedAt,
|
|
3398
|
+
updatedAt,
|
|
3399
|
+
space.expiresAt?.toISOString() ?? null
|
|
3400
|
+
]
|
|
3401
|
+
}
|
|
3402
|
+
]);
|
|
3403
|
+
if (!written.ok) return accountErr(written.error);
|
|
3404
|
+
return ok3(void 0);
|
|
3335
3405
|
}
|
|
3336
|
-
|
|
3337
|
-
|
|
3406
|
+
async deleteSpaceIndexQuietly(spaceId) {
|
|
3407
|
+
await ignoreIndexFailure(() => this.deleteSpaceIndex(spaceId));
|
|
3408
|
+
}
|
|
3409
|
+
async deleteSpaceIndex(spaceId) {
|
|
3410
|
+
const dbResult = this.accountDb();
|
|
3411
|
+
if (!dbResult.ok) return ok3(void 0);
|
|
3412
|
+
const schema = await this.ensureAccountIndex(dbResult.data);
|
|
3413
|
+
if (!schema.ok) return schema;
|
|
3414
|
+
const deleted = await dbResult.data.batch([
|
|
3415
|
+
{ sql: "DELETE FROM spaces WHERE space_id = ?", params: [spaceId] }
|
|
3416
|
+
]);
|
|
3417
|
+
if (!deleted.ok) return accountErr(deleted.error);
|
|
3418
|
+
return ok3(void 0);
|
|
3338
3419
|
}
|
|
3339
|
-
|
|
3340
|
-
|
|
3420
|
+
async resolveSpace(space) {
|
|
3421
|
+
const listed = await this.config.getSpaces().list();
|
|
3422
|
+
if (!listed.ok) return accountErr(listed.error);
|
|
3423
|
+
const found = listed.data.find((candidate) => candidate.id === space || candidate.name === space);
|
|
3424
|
+
if (!found) {
|
|
3425
|
+
return err3(
|
|
3426
|
+
serviceError3("SPACE_NOT_FOUND", `No account space found for ${JSON.stringify(space)}`, SERVICE_NAME2)
|
|
3427
|
+
);
|
|
3428
|
+
}
|
|
3429
|
+
return ok3(found);
|
|
3430
|
+
}
|
|
3431
|
+
async replaceApplicationsIndexQuietly(applications) {
|
|
3432
|
+
await ignoreIndexFailure(async () => {
|
|
3433
|
+
const dbResult = this.accountDb();
|
|
3434
|
+
if (!dbResult.ok) return;
|
|
3435
|
+
const syncedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3436
|
+
const schema = await this.ensureAccountIndex(dbResult.data);
|
|
3437
|
+
if (!schema.ok) return;
|
|
3438
|
+
await dbResult.data.batch([
|
|
3439
|
+
{ sql: "DELETE FROM applications" },
|
|
3440
|
+
{ sql: "DELETE FROM application_state" },
|
|
3441
|
+
...applications.map((app) => ({
|
|
3442
|
+
sql: "INSERT OR REPLACE INTO applications (app_id, name, description, updated_at, manifest_json) VALUES (?, ?, ?, ?, ?)",
|
|
3443
|
+
params: [
|
|
3444
|
+
app.appId,
|
|
3445
|
+
app.name ?? null,
|
|
3446
|
+
app.description ?? null,
|
|
3447
|
+
app.updatedAt ?? syncedAt,
|
|
3448
|
+
JSON.stringify(app.manifests)
|
|
3449
|
+
]
|
|
3450
|
+
})),
|
|
3451
|
+
...applications.map((app) => ({
|
|
3452
|
+
sql: "INSERT OR REPLACE INTO application_state (app_id, manifest_hash, indexed_at) VALUES (?, ?, ?)",
|
|
3453
|
+
params: [app.appId, app.manifestHash ?? hashJson(app.manifests), syncedAt]
|
|
3454
|
+
})),
|
|
3455
|
+
{
|
|
3456
|
+
sql: "INSERT OR REPLACE INTO sync_state (source, synced_at, count) VALUES (?, ?, ?)",
|
|
3457
|
+
params: ["applications", syncedAt, applications.length]
|
|
3458
|
+
}
|
|
3459
|
+
]);
|
|
3460
|
+
});
|
|
3341
3461
|
}
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3462
|
+
async replaceSpacesIndexQuietly(spaces) {
|
|
3463
|
+
await ignoreIndexFailure(async () => {
|
|
3464
|
+
const dbResult = this.accountDb();
|
|
3465
|
+
if (!dbResult.ok) return;
|
|
3466
|
+
const syncedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3467
|
+
const schema = await this.ensureAccountIndex(dbResult.data);
|
|
3468
|
+
if (!schema.ok) return;
|
|
3469
|
+
await dbResult.data.batch([
|
|
3470
|
+
{ sql: "DELETE FROM spaces" },
|
|
3471
|
+
...spaces.map((space) => ({
|
|
3472
|
+
sql: "INSERT OR REPLACE INTO spaces (space_id, name, owner_did, type, permissions_json, status, registered_at, updated_at, expires_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
3473
|
+
params: [
|
|
3474
|
+
space.spaceId,
|
|
3475
|
+
space.name,
|
|
3476
|
+
space.ownerDid,
|
|
3477
|
+
space.type,
|
|
3478
|
+
JSON.stringify(space.permissions),
|
|
3479
|
+
space.status,
|
|
3480
|
+
space.registeredAt ?? syncedAt,
|
|
3481
|
+
space.updatedAt ?? syncedAt,
|
|
3482
|
+
space.expiresAt?.toISOString() ?? null
|
|
3483
|
+
]
|
|
3484
|
+
})),
|
|
3485
|
+
{
|
|
3486
|
+
sql: "INSERT OR REPLACE INTO sync_state (source, synced_at, count) VALUES (?, ?, ?)",
|
|
3487
|
+
params: ["spaces", syncedAt, spaces.length]
|
|
3488
|
+
}
|
|
3489
|
+
]);
|
|
3490
|
+
});
|
|
3349
3491
|
}
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3492
|
+
async replaceDelegationsIndexQuietly(delegations) {
|
|
3493
|
+
await ignoreIndexFailure(async () => {
|
|
3494
|
+
const dbResult = this.accountDb();
|
|
3495
|
+
if (!dbResult.ok) return;
|
|
3496
|
+
const syncedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3497
|
+
const schema = await this.ensureAccountIndex(dbResult.data);
|
|
3498
|
+
if (!schema.ok) return;
|
|
3499
|
+
await dbResult.data.batch([
|
|
3500
|
+
{ sql: "DELETE FROM delegations" },
|
|
3501
|
+
...delegations.map((delegation) => ({
|
|
3502
|
+
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
3503
|
+
params: [
|
|
3504
|
+
delegation.cid,
|
|
3505
|
+
delegation.direction,
|
|
3506
|
+
delegation.spaceId,
|
|
3507
|
+
delegation.spaceName ?? null,
|
|
3508
|
+
delegation.counterpartyDid,
|
|
3509
|
+
delegation.delegateDid,
|
|
3510
|
+
delegation.delegatorDid ?? null,
|
|
3511
|
+
delegation.path,
|
|
3512
|
+
JSON.stringify(delegation.actions),
|
|
3513
|
+
delegation.expiry.toISOString(),
|
|
3514
|
+
delegation.status,
|
|
3515
|
+
delegation.createdAt?.toISOString() ?? null,
|
|
3516
|
+
syncedAt
|
|
3517
|
+
]
|
|
3518
|
+
})),
|
|
3519
|
+
{
|
|
3520
|
+
sql: "INSERT OR REPLACE INTO sync_state (source, synced_at, count) VALUES (?, ?, ?)",
|
|
3521
|
+
params: ["delegations", syncedAt, delegations.length]
|
|
3522
|
+
}
|
|
3523
|
+
]);
|
|
3366
3524
|
});
|
|
3367
3525
|
}
|
|
3368
|
-
|
|
3526
|
+
async ensureAccountIndex(db) {
|
|
3527
|
+
const migrated = await db.migrations.apply({
|
|
3528
|
+
namespace: ACCOUNT_INDEX_NAMESPACE,
|
|
3529
|
+
migrations: [
|
|
3530
|
+
{
|
|
3531
|
+
id: "001_initial",
|
|
3532
|
+
sql: ACCOUNT_INDEX_SCHEMA
|
|
3533
|
+
}
|
|
3534
|
+
]
|
|
3535
|
+
});
|
|
3536
|
+
if (!migrated.ok) return accountErr(migrated.error);
|
|
3537
|
+
return ok3(void 0);
|
|
3538
|
+
}
|
|
3539
|
+
};
|
|
3540
|
+
var ACCOUNT_INDEX_SCHEMA = [
|
|
3541
|
+
`CREATE TABLE IF NOT EXISTS applications (
|
|
3542
|
+
app_id TEXT PRIMARY KEY,
|
|
3543
|
+
name TEXT,
|
|
3544
|
+
description TEXT,
|
|
3545
|
+
updated_at TEXT,
|
|
3546
|
+
manifest_json TEXT NOT NULL
|
|
3547
|
+
)`,
|
|
3548
|
+
`CREATE TABLE IF NOT EXISTS application_state (
|
|
3549
|
+
app_id TEXT PRIMARY KEY,
|
|
3550
|
+
manifest_hash TEXT NOT NULL,
|
|
3551
|
+
indexed_at TEXT NOT NULL
|
|
3552
|
+
)`,
|
|
3553
|
+
`CREATE TABLE IF NOT EXISTS spaces (
|
|
3554
|
+
space_id TEXT PRIMARY KEY,
|
|
3555
|
+
name TEXT NOT NULL,
|
|
3556
|
+
owner_did TEXT NOT NULL,
|
|
3557
|
+
type TEXT NOT NULL,
|
|
3558
|
+
permissions_json TEXT NOT NULL,
|
|
3559
|
+
status TEXT NOT NULL,
|
|
3560
|
+
registered_at TEXT,
|
|
3561
|
+
updated_at TEXT NOT NULL,
|
|
3562
|
+
expires_at TEXT
|
|
3563
|
+
)`,
|
|
3564
|
+
`CREATE TABLE IF NOT EXISTS delegations (
|
|
3565
|
+
cid TEXT PRIMARY KEY,
|
|
3566
|
+
direction TEXT NOT NULL,
|
|
3567
|
+
space_id TEXT NOT NULL,
|
|
3568
|
+
space_name TEXT,
|
|
3569
|
+
counterparty_did TEXT NOT NULL,
|
|
3570
|
+
delegate_did TEXT NOT NULL,
|
|
3571
|
+
delegator_did TEXT,
|
|
3572
|
+
path TEXT NOT NULL,
|
|
3573
|
+
actions_json TEXT NOT NULL,
|
|
3574
|
+
expiry TEXT NOT NULL,
|
|
3575
|
+
status TEXT NOT NULL,
|
|
3576
|
+
created_at TEXT,
|
|
3577
|
+
updated_at TEXT NOT NULL
|
|
3578
|
+
)`,
|
|
3579
|
+
`CREATE TABLE IF NOT EXISTS sync_state (
|
|
3580
|
+
source TEXT PRIMARY KEY,
|
|
3581
|
+
synced_at TEXT NOT NULL,
|
|
3582
|
+
count INTEGER NOT NULL
|
|
3583
|
+
)`,
|
|
3584
|
+
"CREATE INDEX IF NOT EXISTS idx_delegations_direction ON delegations(direction)",
|
|
3585
|
+
"CREATE INDEX IF NOT EXISTS idx_delegations_space ON delegations(space_id)",
|
|
3586
|
+
"CREATE INDEX IF NOT EXISTS idx_delegations_counterparty ON delegations(counterparty_did)",
|
|
3587
|
+
"CREATE INDEX IF NOT EXISTS idx_spaces_owner ON spaces(owner_did)",
|
|
3588
|
+
"CREATE INDEX IF NOT EXISTS idx_spaces_type ON spaces(type)"
|
|
3589
|
+
];
|
|
3590
|
+
function applicationKey(appId) {
|
|
3591
|
+
return `${ACCOUNT_REGISTRY_PATH}${appId}`;
|
|
3369
3592
|
}
|
|
3370
|
-
function
|
|
3371
|
-
|
|
3372
|
-
const resolvedPath = applyPrefix(prefix, entry.path, skipPrefixForEntry);
|
|
3373
|
-
const entryExpiryMs = entry.expiry !== void 0 ? parseExpiry(entry.expiry) : void 0;
|
|
3374
|
-
return expandPermissionEntry({
|
|
3375
|
-
...entry,
|
|
3376
|
-
space: entry.space ?? inheritedSpace,
|
|
3377
|
-
path: resolvedPath,
|
|
3378
|
-
skipPrefix: true
|
|
3379
|
-
}).map((expanded) => ({
|
|
3380
|
-
service: expanded.service,
|
|
3381
|
-
space: expanded.space ?? inheritedSpace,
|
|
3382
|
-
path: expanded.path,
|
|
3383
|
-
actions: expanded.actions,
|
|
3384
|
-
// Only populate `expiryMs` when the entry had its own expiry override.
|
|
3385
|
-
// When absent, callers use the parent (delegation or manifest) expiry
|
|
3386
|
-
// which is carried on ResolvedDelegate.expiryMs / ResolvedCapabilities.expiryMs.
|
|
3387
|
-
...entryExpiryMs !== void 0 ? { expiryMs: entryExpiryMs } : {},
|
|
3388
|
-
...entry.description !== void 0 ? { description: entry.description } : {}
|
|
3389
|
-
}));
|
|
3593
|
+
function appIdFromKey(key) {
|
|
3594
|
+
return key.startsWith(ACCOUNT_REGISTRY_PATH) ? key.slice(ACCOUNT_REGISTRY_PATH.length) : key;
|
|
3390
3595
|
}
|
|
3391
|
-
function
|
|
3392
|
-
const
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
}
|
|
3403
|
-
return [...byBase.entries()].map(([base, actions]) => ({
|
|
3404
|
-
...entry,
|
|
3405
|
-
service: "tinycloud.kv",
|
|
3406
|
-
path: vaultKVPath(base, entry.path),
|
|
3407
|
-
actions,
|
|
3408
|
-
skipPrefix: true
|
|
3409
|
-
}));
|
|
3596
|
+
function applicationFromRecord(key, record) {
|
|
3597
|
+
const manifests = Array.isArray(record.manifests) ? record.manifests : [];
|
|
3598
|
+
const first = manifests[0];
|
|
3599
|
+
return {
|
|
3600
|
+
appId: record.app_id ?? record.appId ?? first?.app_id ?? appIdFromKey(key),
|
|
3601
|
+
manifests,
|
|
3602
|
+
updatedAt: record.updated_at ?? record.updatedAt,
|
|
3603
|
+
name: first?.name,
|
|
3604
|
+
description: first?.description,
|
|
3605
|
+
manifestHash: record.manifest_hash ?? record.manifestHash ?? hashJson(manifests)
|
|
3606
|
+
};
|
|
3410
3607
|
}
|
|
3411
|
-
function
|
|
3412
|
-
const
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3608
|
+
function indexedApplicationFromRow(row) {
|
|
3609
|
+
const [appId, name, description, updatedAt, manifestJson, manifestHash] = row;
|
|
3610
|
+
return {
|
|
3611
|
+
appId,
|
|
3612
|
+
name: name ?? void 0,
|
|
3613
|
+
description: description ?? void 0,
|
|
3614
|
+
updatedAt: updatedAt ?? void 0,
|
|
3615
|
+
manifests: JSON.parse(manifestJson),
|
|
3616
|
+
manifestHash: manifestHash ?? void 0
|
|
3617
|
+
};
|
|
3618
|
+
}
|
|
3619
|
+
function spaceKey(spaceId) {
|
|
3620
|
+
return `${ACCOUNT_SPACES_PATH}${spaceId}`;
|
|
3621
|
+
}
|
|
3622
|
+
function spaceIdFromKey(key) {
|
|
3623
|
+
return key.startsWith(ACCOUNT_SPACES_PATH) ? key.slice(ACCOUNT_SPACES_PATH.length) : key;
|
|
3624
|
+
}
|
|
3625
|
+
function spaceRecordFromInput(space) {
|
|
3626
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3627
|
+
const accountSpace = "spaceId" in space ? space : {
|
|
3628
|
+
spaceId: space.id,
|
|
3629
|
+
name: space.name ?? space.id.split(":").pop() ?? space.id,
|
|
3630
|
+
ownerDid: space.owner ?? "",
|
|
3631
|
+
type: space.type ?? "discovered",
|
|
3632
|
+
permissions: space.permissions ?? [],
|
|
3633
|
+
status: "active",
|
|
3634
|
+
expiresAt: space.expiresAt
|
|
3635
|
+
};
|
|
3636
|
+
return {
|
|
3637
|
+
space_id: accountSpace.spaceId,
|
|
3638
|
+
name: accountSpace.name,
|
|
3639
|
+
owner_did: accountSpace.ownerDid,
|
|
3640
|
+
type: accountSpace.type,
|
|
3641
|
+
permissions: accountSpace.permissions,
|
|
3642
|
+
status: accountSpace.status,
|
|
3643
|
+
registered_at: accountSpace.registeredAt ?? now,
|
|
3644
|
+
updated_at: now,
|
|
3645
|
+
expires_at: accountSpace.expiresAt instanceof Date ? accountSpace.expiresAt.toISOString() : accountSpace.expiresAt
|
|
3646
|
+
};
|
|
3647
|
+
}
|
|
3648
|
+
function spaceFromRecord(key, record) {
|
|
3649
|
+
const expiresAt = record.expires_at ?? record.expiresAt;
|
|
3650
|
+
return {
|
|
3651
|
+
spaceId: record.space_id ?? record.spaceId ?? spaceIdFromKey(key),
|
|
3652
|
+
name: record.name ?? spaceIdFromKey(key).split(":").pop() ?? spaceIdFromKey(key),
|
|
3653
|
+
ownerDid: record.owner_did ?? record.ownerDid ?? record.owner ?? "",
|
|
3654
|
+
type: record.type ?? "discovered",
|
|
3655
|
+
permissions: Array.isArray(record.permissions) ? record.permissions : [],
|
|
3656
|
+
status: record.status ?? "active",
|
|
3657
|
+
registeredAt: record.registered_at ?? record.registeredAt,
|
|
3658
|
+
updatedAt: record.updated_at ?? record.updatedAt,
|
|
3659
|
+
expiresAt: expiresAt ? new Date(expiresAt) : void 0
|
|
3660
|
+
};
|
|
3661
|
+
}
|
|
3662
|
+
function indexedSpaceFromRow(row) {
|
|
3663
|
+
const [spaceId, name, ownerDid, type, permissionsJson, status, registeredAt, updatedAt, expiresAt] = row;
|
|
3664
|
+
return {
|
|
3665
|
+
spaceId,
|
|
3666
|
+
name,
|
|
3667
|
+
ownerDid,
|
|
3668
|
+
type,
|
|
3669
|
+
permissions: JSON.parse(permissionsJson),
|
|
3670
|
+
status,
|
|
3671
|
+
registeredAt: registeredAt ?? void 0,
|
|
3672
|
+
updatedAt,
|
|
3673
|
+
expiresAt: expiresAt ? new Date(expiresAt) : void 0
|
|
3674
|
+
};
|
|
3675
|
+
}
|
|
3676
|
+
function hashJson(value) {
|
|
3677
|
+
const input = stableJson(value);
|
|
3678
|
+
let hash = 0xcbf29ce484222325n;
|
|
3679
|
+
const prime = 0x100000001b3n;
|
|
3680
|
+
for (let index = 0; index < input.length; index += 1) {
|
|
3681
|
+
hash ^= BigInt(input.charCodeAt(index));
|
|
3682
|
+
hash = BigInt.asUintN(64, hash * prime);
|
|
3683
|
+
}
|
|
3684
|
+
return hash.toString(16).padStart(16, "0");
|
|
3685
|
+
}
|
|
3686
|
+
function stableJson(value) {
|
|
3687
|
+
if (value === null || typeof value !== "object") {
|
|
3688
|
+
return JSON.stringify(value);
|
|
3427
3689
|
}
|
|
3428
|
-
if (
|
|
3429
|
-
return {
|
|
3690
|
+
if (Array.isArray(value)) {
|
|
3691
|
+
return `[${value.map(stableJson).join(",")}]`;
|
|
3430
3692
|
}
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
);
|
|
3693
|
+
const object = value;
|
|
3694
|
+
return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${stableJson(object[key])}`).join(",")}}`;
|
|
3434
3695
|
}
|
|
3435
|
-
function
|
|
3436
|
-
|
|
3437
|
-
|
|
3696
|
+
function indexedDelegationFromRow(row) {
|
|
3697
|
+
const [
|
|
3698
|
+
cid,
|
|
3699
|
+
direction,
|
|
3700
|
+
spaceId,
|
|
3701
|
+
spaceName,
|
|
3702
|
+
counterpartyDid,
|
|
3703
|
+
delegateDid,
|
|
3704
|
+
delegatorDid,
|
|
3705
|
+
path,
|
|
3706
|
+
actionsJson,
|
|
3707
|
+
expiry,
|
|
3708
|
+
status,
|
|
3709
|
+
createdAt
|
|
3710
|
+
] = row;
|
|
3711
|
+
return {
|
|
3712
|
+
cid,
|
|
3713
|
+
direction,
|
|
3714
|
+
spaceId,
|
|
3715
|
+
spaceName: spaceName ?? void 0,
|
|
3716
|
+
counterpartyDid,
|
|
3717
|
+
delegateDid,
|
|
3718
|
+
delegatorDid: delegatorDid ?? void 0,
|
|
3719
|
+
path,
|
|
3720
|
+
actions: JSON.parse(actionsJson),
|
|
3721
|
+
expiry: new Date(expiry),
|
|
3722
|
+
status,
|
|
3723
|
+
createdAt: createdAt ? new Date(createdAt) : void 0
|
|
3724
|
+
};
|
|
3725
|
+
}
|
|
3726
|
+
function mapDelegation(delegation, space, direction) {
|
|
3727
|
+
return {
|
|
3728
|
+
cid: delegation.cid,
|
|
3729
|
+
direction,
|
|
3730
|
+
spaceId: delegation.spaceId || space.id,
|
|
3731
|
+
spaceName: space.name,
|
|
3732
|
+
counterpartyDid: direction === "granted" ? delegation.delegateDID : delegation.delegatorDID ?? delegation.delegateDID,
|
|
3733
|
+
delegateDid: delegation.delegateDID,
|
|
3734
|
+
delegatorDid: delegation.delegatorDID,
|
|
3735
|
+
path: delegation.path,
|
|
3736
|
+
actions: delegation.actions,
|
|
3737
|
+
expiry: delegation.expiry,
|
|
3738
|
+
status: delegation.isRevoked ? "revoked" : delegation.expiry.getTime() <= Date.now() ? "expired" : "active",
|
|
3739
|
+
createdAt: delegation.createdAt
|
|
3740
|
+
};
|
|
3741
|
+
}
|
|
3742
|
+
function accountErr(error) {
|
|
3743
|
+
return err3(serviceError3(error.code, error.message, SERVICE_NAME2, { cause: error.cause, meta: error.meta }));
|
|
3744
|
+
}
|
|
3745
|
+
function isMissingIndexError(error) {
|
|
3746
|
+
return /no such table:/i.test(error.message);
|
|
3747
|
+
}
|
|
3748
|
+
async function ignoreIndexFailure(task) {
|
|
3749
|
+
try {
|
|
3750
|
+
await task();
|
|
3751
|
+
} catch {
|
|
3438
3752
|
}
|
|
3439
|
-
|
|
3440
|
-
|
|
3753
|
+
}
|
|
3754
|
+
|
|
3755
|
+
// src/index.ts
|
|
3756
|
+
import {
|
|
3757
|
+
ServiceContext as ServiceContext2,
|
|
3758
|
+
KVService as KVService2,
|
|
3759
|
+
PrefixedKVService,
|
|
3760
|
+
ok as ok5,
|
|
3761
|
+
err as err5,
|
|
3762
|
+
serviceError as serviceError5,
|
|
3763
|
+
ErrorCodes as ErrorCodes2,
|
|
3764
|
+
defaultRetryPolicy as defaultRetryPolicy2,
|
|
3765
|
+
SQLService as SQLService2,
|
|
3766
|
+
DatabaseHandle,
|
|
3767
|
+
SQLAction,
|
|
3768
|
+
DuckDbService as DuckDbService2,
|
|
3769
|
+
DuckDbDatabaseHandle,
|
|
3770
|
+
DuckDbAction,
|
|
3771
|
+
HooksService as HooksService2,
|
|
3772
|
+
DataVaultService,
|
|
3773
|
+
VaultHeaders,
|
|
3774
|
+
VaultPublicSpaceKVActions,
|
|
3775
|
+
createVaultCrypto,
|
|
3776
|
+
SecretsService,
|
|
3777
|
+
SECRET_NAME_RE as SECRET_NAME_RE2,
|
|
3778
|
+
canonicalizeSecretScope,
|
|
3779
|
+
resolveSecretListPrefix,
|
|
3780
|
+
resolveSecretPath as resolveSecretPath2,
|
|
3781
|
+
EncryptionService,
|
|
3782
|
+
parseNetworkId as parseNetworkId2,
|
|
3783
|
+
buildNetworkId as buildNetworkId2,
|
|
3784
|
+
isNetworkId,
|
|
3785
|
+
networkDiscoveryKey,
|
|
3786
|
+
NetworkIdError,
|
|
3787
|
+
ENCRYPTION_NETWORK_URN_PREFIX,
|
|
3788
|
+
NETWORK_NAME_PATTERN,
|
|
3789
|
+
canonicalizeEncryptionJson,
|
|
3790
|
+
canonicalHashHex,
|
|
3791
|
+
hexEncode,
|
|
3792
|
+
hexDecode,
|
|
3793
|
+
base64Encode,
|
|
3794
|
+
base64Decode,
|
|
3795
|
+
utf8Encode,
|
|
3796
|
+
utf8Decode,
|
|
3797
|
+
encryptToNetwork,
|
|
3798
|
+
decryptEnvelopeWithKey,
|
|
3799
|
+
validateEnvelope,
|
|
3800
|
+
generateRandomReceiverKey,
|
|
3801
|
+
deriveSignedReceiverKey,
|
|
3802
|
+
buildCanonicalDecryptRequest,
|
|
3803
|
+
buildDecryptFacts,
|
|
3804
|
+
buildDecryptAttenuation,
|
|
3805
|
+
buildDecryptInvocation,
|
|
3806
|
+
checkDecryptInvocationInput,
|
|
3807
|
+
verifyDecryptResponse,
|
|
3808
|
+
canonicalSignedResponse,
|
|
3809
|
+
openWrappedKey,
|
|
3810
|
+
discoverNetwork,
|
|
3811
|
+
ensureNetworkUsableForDecrypt,
|
|
3812
|
+
DEFAULT_ENCRYPTION_ALG,
|
|
3813
|
+
ENVELOPE_VERSION,
|
|
3814
|
+
DEFAULT_KEY_VERSION,
|
|
3815
|
+
DECRYPT_FACT_TYPE,
|
|
3816
|
+
DECRYPT_RESULT_TYPE,
|
|
3817
|
+
DECRYPT_ACTION,
|
|
3818
|
+
ENCRYPTION_SERVICE,
|
|
3819
|
+
ENCRYPTION_SERVICE_SHORT,
|
|
3820
|
+
encryptionError
|
|
3821
|
+
} from "@tinycloud/sdk-services";
|
|
3822
|
+
|
|
3823
|
+
// src/space.ts
|
|
3824
|
+
async function fetchPeerId(host, spaceId) {
|
|
3825
|
+
const res = await fetch(
|
|
3826
|
+
`${host}/peer/generate/${encodeURIComponent(spaceId)}`
|
|
3827
|
+
);
|
|
3828
|
+
if (!res.ok) {
|
|
3829
|
+
const error = await res.text().catch(() => res.statusText);
|
|
3830
|
+
throw new Error(`Failed to get peer ID: ${res.status} - ${error}`);
|
|
3441
3831
|
}
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3832
|
+
return res.text();
|
|
3833
|
+
}
|
|
3834
|
+
async function submitHostDelegation(host, headers) {
|
|
3835
|
+
const res = await fetch(`${host}/delegate`, {
|
|
3836
|
+
method: "POST",
|
|
3837
|
+
headers
|
|
3838
|
+
});
|
|
3839
|
+
return {
|
|
3840
|
+
success: res.ok,
|
|
3841
|
+
status: res.status,
|
|
3842
|
+
error: res.ok ? void 0 : await res.text().catch(() => res.statusText)
|
|
3843
|
+
};
|
|
3844
|
+
}
|
|
3845
|
+
async function activateSessionWithHost(host, delegationHeader) {
|
|
3846
|
+
const res = await fetch(`${host}/delegate`, {
|
|
3847
|
+
method: "POST",
|
|
3848
|
+
headers: delegationHeader
|
|
3849
|
+
});
|
|
3850
|
+
if (res.ok) {
|
|
3851
|
+
try {
|
|
3852
|
+
const body = await res.json();
|
|
3853
|
+
return {
|
|
3854
|
+
success: true,
|
|
3855
|
+
status: res.status,
|
|
3856
|
+
activated: body.activated ?? [],
|
|
3857
|
+
skipped: body.skipped ?? []
|
|
3858
|
+
};
|
|
3859
|
+
} catch {
|
|
3860
|
+
return {
|
|
3861
|
+
success: true,
|
|
3862
|
+
status: res.status,
|
|
3863
|
+
activated: [],
|
|
3864
|
+
skipped: []
|
|
3865
|
+
};
|
|
3866
|
+
}
|
|
3446
3867
|
}
|
|
3447
|
-
return action;
|
|
3448
|
-
}
|
|
3449
|
-
function vaultKVPath(base, path) {
|
|
3450
|
-
const normalized = path.startsWith("/") ? path.slice(1) : path;
|
|
3451
|
-
return `${base}/${normalized}`;
|
|
3452
|
-
}
|
|
3453
|
-
function cloneResourceCapability(entry) {
|
|
3454
3868
|
return {
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
actions: [...entry.actions],
|
|
3459
|
-
...entry.expiryMs !== void 0 ? { expiryMs: entry.expiryMs } : {},
|
|
3460
|
-
...entry.description !== void 0 ? { description: entry.description } : {}
|
|
3869
|
+
success: false,
|
|
3870
|
+
status: res.status,
|
|
3871
|
+
error: await res.text().catch(() => res.statusText)
|
|
3461
3872
|
};
|
|
3462
3873
|
}
|
|
3463
|
-
|
|
3874
|
+
|
|
3875
|
+
// src/delegations/DelegationManager.ts
|
|
3876
|
+
var DelegationAction = {
|
|
3877
|
+
CREATE: "tinycloud.delegation/create",
|
|
3878
|
+
REVOKE: "tinycloud.delegation/revoke",
|
|
3879
|
+
LIST: "tinycloud.delegation/list",
|
|
3880
|
+
GET: "tinycloud.delegation/get",
|
|
3881
|
+
CHECK: "tinycloud.delegation/check"
|
|
3882
|
+
};
|
|
3883
|
+
function createError(code, message, cause, meta) {
|
|
3464
3884
|
return {
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
...entry.expiry !== void 0 ? { expiry: entry.expiry } : {},
|
|
3471
|
-
...entry.description !== void 0 ? { description: entry.description } : {}
|
|
3885
|
+
code,
|
|
3886
|
+
message,
|
|
3887
|
+
service: "delegation",
|
|
3888
|
+
cause,
|
|
3889
|
+
meta
|
|
3472
3890
|
};
|
|
3473
3891
|
}
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3892
|
+
var DelegationManager = class {
|
|
3893
|
+
/**
|
|
3894
|
+
* Creates a new DelegationManager instance.
|
|
3895
|
+
*
|
|
3896
|
+
* @param config - Configuration including hosts, session, and invoke function
|
|
3897
|
+
*/
|
|
3898
|
+
constructor(config) {
|
|
3899
|
+
this.hosts = config.hosts;
|
|
3900
|
+
this.session = config.session;
|
|
3901
|
+
this.invoke = config.invoke;
|
|
3902
|
+
this.fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);
|
|
3903
|
+
}
|
|
3904
|
+
/**
|
|
3905
|
+
* Updates the session (e.g., after re-authentication).
|
|
3906
|
+
*
|
|
3907
|
+
* @param session - New session to use for operations
|
|
3908
|
+
*/
|
|
3909
|
+
updateSession(session) {
|
|
3910
|
+
this.session = session;
|
|
3911
|
+
}
|
|
3912
|
+
/**
|
|
3913
|
+
* Gets the primary host URL.
|
|
3914
|
+
*/
|
|
3915
|
+
get host() {
|
|
3916
|
+
return this.hosts[0];
|
|
3917
|
+
}
|
|
3918
|
+
/**
|
|
3919
|
+
* Executes an invoke operation against the delegation API.
|
|
3920
|
+
*/
|
|
3921
|
+
async invokeOperation(path, action, body) {
|
|
3922
|
+
const headers = this.invoke(this.session, "delegation", path, action);
|
|
3923
|
+
return this.fetchFn(`${this.host}/invoke`, {
|
|
3924
|
+
method: "POST",
|
|
3925
|
+
headers,
|
|
3926
|
+
body
|
|
3927
|
+
});
|
|
3928
|
+
}
|
|
3929
|
+
/**
|
|
3930
|
+
* Creates a new delegation.
|
|
3931
|
+
*
|
|
3932
|
+
* Delegates specific permissions to another DID for a given path.
|
|
3933
|
+
* The delegatee can then use these permissions to access resources
|
|
3934
|
+
* within the specified scope.
|
|
3935
|
+
*
|
|
3936
|
+
* @param params - Parameters for the delegation
|
|
3937
|
+
* @returns Result containing the created Delegation or an error
|
|
3938
|
+
*
|
|
3939
|
+
* @example
|
|
3940
|
+
* ```typescript
|
|
3941
|
+
* const result = await manager.create({
|
|
3942
|
+
* delegateDID: bob.did,
|
|
3943
|
+
* path: "documents/shared/",
|
|
3944
|
+
* actions: ["tinycloud.kv/get", "tinycloud.kv/put"],
|
|
3945
|
+
* expiry: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days
|
|
3946
|
+
* });
|
|
3947
|
+
* ```
|
|
3948
|
+
*/
|
|
3949
|
+
async create(params) {
|
|
3950
|
+
if (!params.delegateDID) {
|
|
3951
|
+
return {
|
|
3952
|
+
ok: false,
|
|
3953
|
+
error: createError(
|
|
3954
|
+
DelegationErrorCodes.INVALID_INPUT,
|
|
3955
|
+
"delegateDID is required"
|
|
3956
|
+
)
|
|
3957
|
+
};
|
|
3482
3958
|
}
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3959
|
+
if (!params.path) {
|
|
3960
|
+
return {
|
|
3961
|
+
ok: false,
|
|
3962
|
+
error: createError(
|
|
3963
|
+
DelegationErrorCodes.INVALID_INPUT,
|
|
3964
|
+
"path is required"
|
|
3965
|
+
)
|
|
3966
|
+
};
|
|
3967
|
+
}
|
|
3968
|
+
if (!params.actions || params.actions.length === 0) {
|
|
3969
|
+
return {
|
|
3970
|
+
ok: false,
|
|
3971
|
+
error: createError(
|
|
3972
|
+
DelegationErrorCodes.INVALID_INPUT,
|
|
3973
|
+
"at least one action is required"
|
|
3974
|
+
)
|
|
3975
|
+
};
|
|
3976
|
+
}
|
|
3977
|
+
try {
|
|
3978
|
+
const body = JSON.stringify({
|
|
3979
|
+
delegateDID: params.delegateDID,
|
|
3980
|
+
path: params.path,
|
|
3981
|
+
actions: params.actions,
|
|
3982
|
+
expiry: params.expiry?.toISOString(),
|
|
3983
|
+
disableSubDelegation: params.disableSubDelegation ?? false,
|
|
3984
|
+
statement: params.statement
|
|
3985
|
+
});
|
|
3986
|
+
const response = await this.invokeOperation(
|
|
3987
|
+
params.path,
|
|
3988
|
+
DelegationAction.CREATE,
|
|
3989
|
+
body
|
|
3990
|
+
);
|
|
3991
|
+
if (!response.ok) {
|
|
3992
|
+
const errorText = await response.text();
|
|
3993
|
+
return {
|
|
3994
|
+
ok: false,
|
|
3995
|
+
error: createError(
|
|
3996
|
+
DelegationErrorCodes.CREATION_FAILED,
|
|
3997
|
+
`Failed to create delegation: ${response.status} - ${errorText}`,
|
|
3998
|
+
void 0,
|
|
3999
|
+
{ status: response.status, path: params.path }
|
|
4000
|
+
)
|
|
4001
|
+
};
|
|
4002
|
+
}
|
|
4003
|
+
const apiResponse = await response.json();
|
|
4004
|
+
const delegation = {
|
|
4005
|
+
cid: apiResponse.cid ?? "",
|
|
4006
|
+
delegateDID: params.delegateDID,
|
|
4007
|
+
spaceId: this.session.spaceId,
|
|
4008
|
+
path: params.path,
|
|
4009
|
+
actions: params.actions,
|
|
4010
|
+
expiry: params.expiry ?? new Date(Date.now() + EXPIRY.SHARE_MS),
|
|
4011
|
+
isRevoked: false,
|
|
4012
|
+
allowSubDelegation: !(params.disableSubDelegation ?? false),
|
|
4013
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
4014
|
+
};
|
|
4015
|
+
return { ok: true, data: delegation };
|
|
4016
|
+
} catch (error) {
|
|
4017
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
4018
|
+
return {
|
|
4019
|
+
ok: false,
|
|
4020
|
+
error: createError(
|
|
4021
|
+
DelegationErrorCodes.ABORTED,
|
|
4022
|
+
"Request aborted",
|
|
4023
|
+
error
|
|
4024
|
+
)
|
|
4025
|
+
};
|
|
4026
|
+
}
|
|
4027
|
+
return {
|
|
4028
|
+
ok: false,
|
|
4029
|
+
error: createError(
|
|
4030
|
+
DelegationErrorCodes.NETWORK_ERROR,
|
|
4031
|
+
`Network error during delegation creation: ${String(error)}`,
|
|
4032
|
+
error instanceof Error ? error : void 0
|
|
4033
|
+
)
|
|
4034
|
+
};
|
|
4035
|
+
}
|
|
4036
|
+
}
|
|
4037
|
+
/**
|
|
4038
|
+
* Revokes an existing delegation.
|
|
4039
|
+
*
|
|
4040
|
+
* Once revoked, the delegation can no longer be used to access resources.
|
|
4041
|
+
* This also invalidates any sub-delegations derived from this delegation.
|
|
4042
|
+
*
|
|
4043
|
+
* @param cid - The CID of the delegation to revoke
|
|
4044
|
+
* @returns Result indicating success or an error
|
|
4045
|
+
*
|
|
4046
|
+
* @example
|
|
4047
|
+
* ```typescript
|
|
4048
|
+
* const result = await manager.revoke("bafy...");
|
|
4049
|
+
* if (result.ok) {
|
|
4050
|
+
* console.log("Delegation revoked successfully");
|
|
4051
|
+
* }
|
|
4052
|
+
* ```
|
|
4053
|
+
*/
|
|
4054
|
+
async revoke(cid) {
|
|
4055
|
+
if (!cid) {
|
|
4056
|
+
return {
|
|
4057
|
+
ok: false,
|
|
4058
|
+
error: createError(
|
|
4059
|
+
DelegationErrorCodes.INVALID_INPUT,
|
|
4060
|
+
"cid is required"
|
|
4061
|
+
)
|
|
4062
|
+
};
|
|
4063
|
+
}
|
|
4064
|
+
try {
|
|
4065
|
+
const body = JSON.stringify({ cid });
|
|
4066
|
+
const response = await this.invokeOperation(
|
|
4067
|
+
cid,
|
|
4068
|
+
DelegationAction.REVOKE,
|
|
4069
|
+
body
|
|
4070
|
+
);
|
|
4071
|
+
if (!response.ok) {
|
|
4072
|
+
const errorText = await response.text();
|
|
4073
|
+
if (response.status === 404) {
|
|
4074
|
+
return {
|
|
4075
|
+
ok: false,
|
|
4076
|
+
error: createError(
|
|
4077
|
+
DelegationErrorCodes.NOT_FOUND,
|
|
4078
|
+
`Delegation not found: ${cid}`
|
|
4079
|
+
)
|
|
4080
|
+
};
|
|
4081
|
+
}
|
|
4082
|
+
return {
|
|
4083
|
+
ok: false,
|
|
4084
|
+
error: createError(
|
|
4085
|
+
DelegationErrorCodes.REVOCATION_FAILED,
|
|
4086
|
+
`Failed to revoke delegation: ${response.status} - ${errorText}`,
|
|
4087
|
+
void 0,
|
|
4088
|
+
{ status: response.status, cid }
|
|
4089
|
+
)
|
|
4090
|
+
};
|
|
3488
4091
|
}
|
|
4092
|
+
return { ok: true, data: void 0 };
|
|
4093
|
+
} catch (error) {
|
|
4094
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
4095
|
+
return {
|
|
4096
|
+
ok: false,
|
|
4097
|
+
error: createError(
|
|
4098
|
+
DelegationErrorCodes.ABORTED,
|
|
4099
|
+
"Request aborted",
|
|
4100
|
+
error
|
|
4101
|
+
)
|
|
4102
|
+
};
|
|
4103
|
+
}
|
|
4104
|
+
return {
|
|
4105
|
+
ok: false,
|
|
4106
|
+
error: createError(
|
|
4107
|
+
DelegationErrorCodes.NETWORK_ERROR,
|
|
4108
|
+
`Network error during delegation revocation: ${String(error)}`,
|
|
4109
|
+
error instanceof Error ? error : void 0
|
|
4110
|
+
)
|
|
4111
|
+
};
|
|
3489
4112
|
}
|
|
3490
|
-
if (existing.description === void 0 && resource.description !== void 0) {
|
|
3491
|
-
existing.description = resource.description;
|
|
3492
|
-
}
|
|
3493
|
-
}
|
|
3494
|
-
return [...byKey.values()];
|
|
3495
|
-
}
|
|
3496
|
-
function capabilitiesReadPermission(space) {
|
|
3497
|
-
return {
|
|
3498
|
-
service: "tinycloud.capabilities",
|
|
3499
|
-
space,
|
|
3500
|
-
path: "",
|
|
3501
|
-
actions: ["tinycloud.capabilities/read"]
|
|
3502
|
-
};
|
|
3503
|
-
}
|
|
3504
|
-
function withCapabilitiesReadForSpaces(resources) {
|
|
3505
|
-
if (resources.length === 0) {
|
|
3506
|
-
return [];
|
|
3507
|
-
}
|
|
3508
|
-
const spaces = new Set(
|
|
3509
|
-
resources.filter((resource) => resource.service !== ENCRYPTION_PERMISSION_SERVICE).map((resource) => resource.space)
|
|
3510
|
-
);
|
|
3511
|
-
return dedupeResources([
|
|
3512
|
-
...resources,
|
|
3513
|
-
...[...spaces].map(capabilitiesReadPermission)
|
|
3514
|
-
]);
|
|
3515
|
-
}
|
|
3516
|
-
function accountRegistryPermission() {
|
|
3517
|
-
return {
|
|
3518
|
-
service: "tinycloud.kv",
|
|
3519
|
-
space: ACCOUNT_REGISTRY_SPACE,
|
|
3520
|
-
path: ACCOUNT_REGISTRY_PATH,
|
|
3521
|
-
actions: ["tinycloud.kv/get", "tinycloud.kv/put", "tinycloud.kv/list"]
|
|
3522
|
-
};
|
|
3523
|
-
}
|
|
3524
|
-
function composeManifestRequest(inputs, options = {}) {
|
|
3525
|
-
if (!Array.isArray(inputs) || inputs.length === 0) {
|
|
3526
|
-
throw new ManifestValidationError(
|
|
3527
|
-
"composeManifestRequest requires at least one manifest"
|
|
3528
|
-
);
|
|
3529
|
-
}
|
|
3530
|
-
const includeAccountRegistryPermissions = options.includeAccountRegistryPermissions ?? true;
|
|
3531
|
-
const manifests = inputs.map(validateManifest);
|
|
3532
|
-
const resolved = manifests.map(resolveManifest);
|
|
3533
|
-
const resources = resolved.flatMap((entry) => entry.resources);
|
|
3534
|
-
const delegationTargets = resolved.flatMap(
|
|
3535
|
-
(entry) => entry.additionalDelegates.map((delegate) => ({
|
|
3536
|
-
...delegate,
|
|
3537
|
-
permissions: dedupeResources(delegate.permissions)
|
|
3538
|
-
}))
|
|
3539
|
-
);
|
|
3540
|
-
if (includeAccountRegistryPermissions) {
|
|
3541
|
-
resources.push(accountRegistryPermission());
|
|
3542
4113
|
}
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
4114
|
+
/**
|
|
4115
|
+
* Lists all delegations for the current session's space.
|
|
4116
|
+
*
|
|
4117
|
+
* Returns both delegations created by the current user (as delegator)
|
|
4118
|
+
* and delegations granted to the current user (as delegatee).
|
|
4119
|
+
*
|
|
4120
|
+
* @returns Result containing an array of Delegations or an error
|
|
4121
|
+
*
|
|
4122
|
+
* @example
|
|
4123
|
+
* ```typescript
|
|
4124
|
+
* const result = await manager.list();
|
|
4125
|
+
* if (result.ok) {
|
|
4126
|
+
* for (const delegation of result.data) {
|
|
4127
|
+
* console.log(`${delegation.cid}: ${delegation.path} -> ${delegation.delegateDID}`);
|
|
4128
|
+
* }
|
|
4129
|
+
* }
|
|
4130
|
+
* ```
|
|
4131
|
+
*/
|
|
4132
|
+
async list() {
|
|
4133
|
+
try {
|
|
4134
|
+
const response = await this.invokeOperation("", DelegationAction.LIST);
|
|
4135
|
+
if (!response.ok) {
|
|
4136
|
+
const errorText = await response.text();
|
|
4137
|
+
return {
|
|
4138
|
+
ok: false,
|
|
4139
|
+
error: createError(
|
|
4140
|
+
DelegationErrorCodes.NETWORK_ERROR,
|
|
4141
|
+
`Failed to list delegations: ${response.status} - ${errorText}`,
|
|
4142
|
+
void 0,
|
|
4143
|
+
{ status: response.status }
|
|
4144
|
+
)
|
|
4145
|
+
};
|
|
4146
|
+
}
|
|
4147
|
+
const data = await response.json();
|
|
4148
|
+
const delegations = data.map((item) => ({
|
|
4149
|
+
cid: item.cid,
|
|
4150
|
+
delegateDID: item.delegateDID,
|
|
4151
|
+
delegatorDID: item.delegatorDID,
|
|
4152
|
+
spaceId: item.spaceId,
|
|
4153
|
+
path: item.path,
|
|
4154
|
+
actions: item.actions,
|
|
4155
|
+
expiry: new Date(item.expiry),
|
|
4156
|
+
isRevoked: item.isRevoked,
|
|
4157
|
+
createdAt: item.createdAt ? new Date(item.createdAt) : void 0,
|
|
4158
|
+
parentCid: item.parentCid,
|
|
4159
|
+
allowSubDelegation: item.allowSubDelegation
|
|
4160
|
+
}));
|
|
4161
|
+
return { ok: true, data: delegations };
|
|
4162
|
+
} catch (error) {
|
|
4163
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
4164
|
+
return {
|
|
4165
|
+
ok: false,
|
|
4166
|
+
error: createError(
|
|
4167
|
+
DelegationErrorCodes.ABORTED,
|
|
4168
|
+
"Request aborted",
|
|
4169
|
+
error
|
|
4170
|
+
)
|
|
4171
|
+
};
|
|
4172
|
+
}
|
|
4173
|
+
return {
|
|
4174
|
+
ok: false,
|
|
4175
|
+
error: createError(
|
|
4176
|
+
DelegationErrorCodes.NETWORK_ERROR,
|
|
4177
|
+
`Network error during delegation list: ${String(error)}`,
|
|
4178
|
+
error instanceof Error ? error : void 0
|
|
4179
|
+
)
|
|
4180
|
+
};
|
|
3551
4181
|
}
|
|
3552
4182
|
}
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
}
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
if (
|
|
3575
|
-
|
|
3576
|
-
|
|
4183
|
+
/**
|
|
4184
|
+
* Gets the full delegation chain for a given delegation.
|
|
4185
|
+
*
|
|
4186
|
+
* Returns the chain of delegations from the root (original delegator)
|
|
4187
|
+
* to the specified delegation, including all intermediate sub-delegations.
|
|
4188
|
+
*
|
|
4189
|
+
* @param cid - The CID of the delegation to get the chain for
|
|
4190
|
+
* @returns Result containing the DelegationChain or an error
|
|
4191
|
+
*
|
|
4192
|
+
* @example
|
|
4193
|
+
* ```typescript
|
|
4194
|
+
* const result = await manager.getChain("bafy...");
|
|
4195
|
+
* if (result.ok) {
|
|
4196
|
+
* console.log("Chain length:", result.data.length);
|
|
4197
|
+
* for (const delegation of result.data) {
|
|
4198
|
+
* console.log(`- ${delegation.delegatorDID} -> ${delegation.delegateDID}`);
|
|
4199
|
+
* }
|
|
4200
|
+
* }
|
|
4201
|
+
* ```
|
|
4202
|
+
*/
|
|
4203
|
+
async getChain(cid) {
|
|
4204
|
+
if (!cid) {
|
|
4205
|
+
return {
|
|
4206
|
+
ok: false,
|
|
4207
|
+
error: createError(
|
|
4208
|
+
DelegationErrorCodes.INVALID_INPUT,
|
|
4209
|
+
"cid is required"
|
|
4210
|
+
)
|
|
4211
|
+
};
|
|
4212
|
+
}
|
|
4213
|
+
try {
|
|
4214
|
+
const body = JSON.stringify({ cid, includeChain: true });
|
|
4215
|
+
const response = await this.invokeOperation(
|
|
4216
|
+
cid,
|
|
4217
|
+
DelegationAction.GET,
|
|
4218
|
+
body
|
|
3577
4219
|
);
|
|
4220
|
+
if (!response.ok) {
|
|
4221
|
+
const errorText = await response.text();
|
|
4222
|
+
if (response.status === 404) {
|
|
4223
|
+
return {
|
|
4224
|
+
ok: false,
|
|
4225
|
+
error: createError(
|
|
4226
|
+
DelegationErrorCodes.NOT_FOUND,
|
|
4227
|
+
`Delegation not found: ${cid}`
|
|
4228
|
+
)
|
|
4229
|
+
};
|
|
4230
|
+
}
|
|
4231
|
+
return {
|
|
4232
|
+
ok: false,
|
|
4233
|
+
error: createError(
|
|
4234
|
+
DelegationErrorCodes.NETWORK_ERROR,
|
|
4235
|
+
`Failed to get delegation chain: ${response.status} - ${errorText}`,
|
|
4236
|
+
void 0,
|
|
4237
|
+
{ status: response.status, cid }
|
|
4238
|
+
)
|
|
4239
|
+
};
|
|
4240
|
+
}
|
|
4241
|
+
const data = await response.json();
|
|
4242
|
+
const chain = data.chain.map((item) => ({
|
|
4243
|
+
cid: item.cid,
|
|
4244
|
+
delegateDID: item.delegateDID,
|
|
4245
|
+
delegatorDID: item.delegatorDID,
|
|
4246
|
+
spaceId: item.spaceId,
|
|
4247
|
+
path: item.path,
|
|
4248
|
+
actions: item.actions,
|
|
4249
|
+
expiry: new Date(item.expiry),
|
|
4250
|
+
isRevoked: item.isRevoked,
|
|
4251
|
+
createdAt: item.createdAt ? new Date(item.createdAt) : void 0,
|
|
4252
|
+
parentCid: item.parentCid,
|
|
4253
|
+
allowSubDelegation: item.allowSubDelegation
|
|
4254
|
+
}));
|
|
4255
|
+
return { ok: true, data: chain };
|
|
4256
|
+
} catch (error) {
|
|
4257
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
4258
|
+
return {
|
|
4259
|
+
ok: false,
|
|
4260
|
+
error: createError(
|
|
4261
|
+
DelegationErrorCodes.ABORTED,
|
|
4262
|
+
"Request aborted",
|
|
4263
|
+
error
|
|
4264
|
+
)
|
|
4265
|
+
};
|
|
4266
|
+
}
|
|
4267
|
+
return {
|
|
4268
|
+
ok: false,
|
|
4269
|
+
error: createError(
|
|
4270
|
+
DelegationErrorCodes.NETWORK_ERROR,
|
|
4271
|
+
`Network error during chain retrieval: ${String(error)}`,
|
|
4272
|
+
error instanceof Error ? error : void 0
|
|
4273
|
+
)
|
|
4274
|
+
};
|
|
3578
4275
|
}
|
|
3579
|
-
|
|
3580
|
-
|
|
4276
|
+
}
|
|
4277
|
+
/**
|
|
4278
|
+
* Checks if the current session has permission for a given path and action.
|
|
4279
|
+
*
|
|
4280
|
+
* This can be used to verify permissions before attempting an operation,
|
|
4281
|
+
* or to implement custom access control logic.
|
|
4282
|
+
*
|
|
4283
|
+
* @param path - The resource path to check
|
|
4284
|
+
* @param action - The action to check (e.g., "tinycloud.kv/get")
|
|
4285
|
+
* @returns Result containing a boolean indicating permission or an error
|
|
4286
|
+
*
|
|
4287
|
+
* @example
|
|
4288
|
+
* ```typescript
|
|
4289
|
+
* const result = await manager.checkPermission("documents/private/", "tinycloud.kv/put");
|
|
4290
|
+
* if (result.ok && result.data) {
|
|
4291
|
+
* console.log("Permission granted");
|
|
4292
|
+
* } else {
|
|
4293
|
+
* console.log("Permission denied");
|
|
4294
|
+
* }
|
|
4295
|
+
* ```
|
|
4296
|
+
*/
|
|
4297
|
+
async checkPermission(path, action) {
|
|
4298
|
+
if (!path) {
|
|
4299
|
+
return {
|
|
4300
|
+
ok: false,
|
|
4301
|
+
error: createError(
|
|
4302
|
+
DelegationErrorCodes.INVALID_INPUT,
|
|
4303
|
+
"path is required"
|
|
4304
|
+
)
|
|
4305
|
+
};
|
|
3581
4306
|
}
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
4307
|
+
if (!action) {
|
|
4308
|
+
return {
|
|
4309
|
+
ok: false,
|
|
4310
|
+
error: createError(
|
|
4311
|
+
DelegationErrorCodes.INVALID_INPUT,
|
|
4312
|
+
"action is required"
|
|
4313
|
+
)
|
|
4314
|
+
};
|
|
4315
|
+
}
|
|
4316
|
+
try {
|
|
4317
|
+
const body = JSON.stringify({ path, action });
|
|
4318
|
+
const response = await this.invokeOperation(
|
|
4319
|
+
path,
|
|
4320
|
+
DelegationAction.CHECK,
|
|
4321
|
+
body
|
|
4322
|
+
);
|
|
4323
|
+
if (!response.ok) {
|
|
4324
|
+
if (response.status === 403) {
|
|
4325
|
+
return { ok: true, data: false };
|
|
3592
4326
|
}
|
|
4327
|
+
const errorText = await response.text();
|
|
4328
|
+
return {
|
|
4329
|
+
ok: false,
|
|
4330
|
+
error: createError(
|
|
4331
|
+
DelegationErrorCodes.NETWORK_ERROR,
|
|
4332
|
+
`Failed to check permission: ${response.status} - ${errorText}`,
|
|
4333
|
+
void 0,
|
|
4334
|
+
{ status: response.status, path, action }
|
|
4335
|
+
)
|
|
4336
|
+
};
|
|
3593
4337
|
}
|
|
4338
|
+
const data = await response.json();
|
|
4339
|
+
return { ok: true, data: data.allowed };
|
|
4340
|
+
} catch (error) {
|
|
4341
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
4342
|
+
return {
|
|
4343
|
+
ok: false,
|
|
4344
|
+
error: createError(
|
|
4345
|
+
DelegationErrorCodes.ABORTED,
|
|
4346
|
+
"Request aborted",
|
|
4347
|
+
error
|
|
4348
|
+
)
|
|
4349
|
+
};
|
|
4350
|
+
}
|
|
4351
|
+
return {
|
|
4352
|
+
ok: false,
|
|
4353
|
+
error: createError(
|
|
4354
|
+
DelegationErrorCodes.NETWORK_ERROR,
|
|
4355
|
+
`Network error during permission check: ${String(error)}`,
|
|
4356
|
+
error instanceof Error ? error : void 0
|
|
4357
|
+
)
|
|
4358
|
+
};
|
|
3594
4359
|
}
|
|
3595
4360
|
}
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
4361
|
+
};
|
|
4362
|
+
|
|
4363
|
+
// src/delegations/SharingService.schema.ts
|
|
4364
|
+
import { z as z5 } from "zod";
|
|
4365
|
+
var EncodedShareDataSchema = z5.object({
|
|
4366
|
+
/** Private key in JWK format (must include d parameter) */
|
|
4367
|
+
key: JWKSchema.refine(
|
|
4368
|
+
(jwk) => typeof jwk.d === "string" && jwk.d.length > 0,
|
|
4369
|
+
{ message: "JWK must include private key (d parameter)" }
|
|
4370
|
+
),
|
|
4371
|
+
/** DID of the key */
|
|
4372
|
+
keyDid: z5.string().min(1, "keyDid is required"),
|
|
4373
|
+
/** The delegation granting access */
|
|
4374
|
+
delegation: DelegationSchema,
|
|
4375
|
+
/** Resource path this link grants access to */
|
|
4376
|
+
path: z5.string().min(1, "path is required"),
|
|
4377
|
+
/** TinyCloud host URL */
|
|
4378
|
+
host: z5.string().url("host must be a valid URL"),
|
|
4379
|
+
/** Space ID */
|
|
4380
|
+
spaceId: z5.string().min(1, "spaceId is required"),
|
|
4381
|
+
/** Schema version (must be 1) */
|
|
4382
|
+
version: z5.literal(1)
|
|
4383
|
+
});
|
|
4384
|
+
var ReceiveOptionsSchema = z5.object({
|
|
4385
|
+
/**
|
|
4386
|
+
* Whether to automatically create a sub-delegation to the current session key.
|
|
4387
|
+
* Default: true
|
|
4388
|
+
*/
|
|
4389
|
+
autoSubdelegate: z5.boolean().optional(),
|
|
4390
|
+
/**
|
|
4391
|
+
* Whether to use the current session key for operations (requires autoSubdelegate).
|
|
4392
|
+
* Default: true
|
|
4393
|
+
*/
|
|
4394
|
+
useSessionKey: z5.boolean().optional(),
|
|
4395
|
+
/**
|
|
4396
|
+
* Ingestion options passed to CapabilityKeyRegistry.
|
|
4397
|
+
*/
|
|
4398
|
+
ingestOptions: IngestOptionsSchema.optional()
|
|
4399
|
+
});
|
|
4400
|
+
var SharingServiceConfigSchema = z5.object({
|
|
4401
|
+
/** TinyCloud host URLs */
|
|
4402
|
+
hosts: z5.array(z5.string().url()).min(1, "At least one host URL is required"),
|
|
4403
|
+
/**
|
|
4404
|
+
* Active session for authentication.
|
|
4405
|
+
* Required for generate(), optional for receive().
|
|
4406
|
+
*/
|
|
4407
|
+
session: z5.unknown().refine(
|
|
4408
|
+
(val) => val === void 0 || val !== null && typeof val === "object",
|
|
4409
|
+
{ message: "Expected a ServiceSession object or undefined" }
|
|
4410
|
+
).optional(),
|
|
4411
|
+
/** Platform-specific invoke function */
|
|
4412
|
+
invoke: z5.unknown().refine((val) => typeof val === "function", {
|
|
4413
|
+
message: "Expected an invoke function"
|
|
4414
|
+
}),
|
|
4415
|
+
/** Optional custom fetch implementation */
|
|
4416
|
+
fetch: z5.unknown().refine(
|
|
4417
|
+
(val) => val === void 0 || typeof val === "function",
|
|
4418
|
+
{ message: "Expected a fetch function or undefined" }
|
|
4419
|
+
).optional(),
|
|
4420
|
+
/** Key provider for cryptographic operations */
|
|
4421
|
+
keyProvider: KeyProviderSchema,
|
|
4422
|
+
/** Capability key registry for key/delegation management */
|
|
4423
|
+
registry: z5.unknown().refine(
|
|
4424
|
+
(val) => val !== null && typeof val === "object",
|
|
4425
|
+
{ message: "Expected an ICapabilityKeyRegistry object" }
|
|
4426
|
+
),
|
|
4427
|
+
/**
|
|
4428
|
+
* Delegation manager for creating delegations.
|
|
4429
|
+
* Required for generate(), optional for receive().
|
|
4430
|
+
*/
|
|
4431
|
+
delegationManager: z5.unknown().refine(
|
|
4432
|
+
(val) => val === void 0 || val !== null && typeof val === "object",
|
|
4433
|
+
{ message: "Expected a DelegationManager object or undefined" }
|
|
4434
|
+
).optional(),
|
|
4435
|
+
/** Factory for creating KV service instances */
|
|
4436
|
+
createKVService: z5.unknown().refine(
|
|
4437
|
+
(val) => typeof val === "function",
|
|
4438
|
+
{ message: "Expected a createKVService factory function" }
|
|
4439
|
+
),
|
|
4440
|
+
/** Base URL for sharing links (e.g., "https://share.myapp.com") */
|
|
4441
|
+
baseUrl: z5.string().optional(),
|
|
4442
|
+
/**
|
|
4443
|
+
* Custom delegation creation function.
|
|
4444
|
+
*/
|
|
4445
|
+
createDelegation: z5.unknown().refine((val) => val === void 0 || typeof val === "function", {
|
|
4446
|
+
message: "Expected a createDelegation function or undefined"
|
|
4447
|
+
}).optional(),
|
|
4448
|
+
/**
|
|
4449
|
+
* WASM function for client-side delegation creation.
|
|
4450
|
+
*/
|
|
4451
|
+
createDelegationWasm: z5.unknown().refine((val) => val === void 0 || typeof val === "function", {
|
|
4452
|
+
message: "Expected a createDelegationWasm function or undefined"
|
|
4453
|
+
}).optional(),
|
|
4454
|
+
/**
|
|
4455
|
+
* Path prefix for KV operations.
|
|
4456
|
+
*/
|
|
4457
|
+
pathPrefix: z5.string().optional(),
|
|
4458
|
+
/**
|
|
4459
|
+
* Session expiry time.
|
|
4460
|
+
*/
|
|
4461
|
+
sessionExpiry: z5.date().optional(),
|
|
4462
|
+
/**
|
|
4463
|
+
* Callback to create a DIRECT delegation from wallet to share key.
|
|
4464
|
+
* This is the preferred method for long-lived share links because it
|
|
4465
|
+
* bypasses the session delegation chain entirely.
|
|
4466
|
+
*/
|
|
4467
|
+
onRootDelegationNeeded: z5.unknown().refine((val) => val === void 0 || typeof val === "function", {
|
|
4468
|
+
message: "Expected an onRootDelegationNeeded function or undefined"
|
|
4469
|
+
}).optional()
|
|
4470
|
+
});
|
|
4471
|
+
function validateEncodedShareData(data) {
|
|
4472
|
+
const result = EncodedShareDataSchema.safeParse(data);
|
|
4473
|
+
if (!result.success) {
|
|
4474
|
+
return {
|
|
4475
|
+
ok: false,
|
|
4476
|
+
error: {
|
|
4477
|
+
code: DelegationErrorCodes.VALIDATION_ERROR,
|
|
4478
|
+
message: `Invalid share data: ${result.error.message}`,
|
|
4479
|
+
service: "delegation",
|
|
4480
|
+
meta: { issues: result.error.issues }
|
|
4481
|
+
}
|
|
4482
|
+
};
|
|
3620
4483
|
}
|
|
3621
|
-
return
|
|
4484
|
+
return { ok: true, data: result.data };
|
|
3622
4485
|
}
|
|
3623
4486
|
|
|
3624
4487
|
// src/delegations/SharingService.ts
|
|
@@ -3799,13 +4662,13 @@ var SharingService = class {
|
|
|
3799
4662
|
)
|
|
3800
4663
|
};
|
|
3801
4664
|
}
|
|
3802
|
-
} catch (
|
|
4665
|
+
} catch (err6) {
|
|
3803
4666
|
return {
|
|
3804
4667
|
ok: false,
|
|
3805
4668
|
error: createError2(
|
|
3806
4669
|
DelegationErrorCodes.CREATION_FAILED,
|
|
3807
|
-
`Failed to generate session key for share: ${
|
|
3808
|
-
|
|
4670
|
+
`Failed to generate session key for share: ${err6 instanceof Error ? err6.message : String(err6)}`,
|
|
4671
|
+
err6 instanceof Error ? err6 : void 0
|
|
3809
4672
|
)
|
|
3810
4673
|
};
|
|
3811
4674
|
}
|
|
@@ -3851,7 +4714,7 @@ var SharingService = class {
|
|
|
3851
4714
|
}
|
|
3852
4715
|
delegation = parsed;
|
|
3853
4716
|
}
|
|
3854
|
-
} catch (
|
|
4717
|
+
} catch (err6) {
|
|
3855
4718
|
const fallbackResult = await this.handleSessionExtensionFallback(requestedExpiry);
|
|
3856
4719
|
expiry = fallbackResult.expiry;
|
|
3857
4720
|
const delegationResult = await this.createSessionDelegation(plainDID, fullPath, actions, expiry);
|
|
@@ -4049,13 +4912,13 @@ var SharingService = class {
|
|
|
4049
4912
|
allowSubDelegation: true,
|
|
4050
4913
|
createdAt: /* @__PURE__ */ new Date()
|
|
4051
4914
|
};
|
|
4052
|
-
} catch (
|
|
4915
|
+
} catch (err6) {
|
|
4053
4916
|
return {
|
|
4054
4917
|
ok: false,
|
|
4055
4918
|
error: createError2(
|
|
4056
4919
|
DelegationErrorCodes.CREATION_FAILED,
|
|
4057
|
-
`Failed to create delegation via WASM: ${
|
|
4058
|
-
|
|
4920
|
+
`Failed to create delegation via WASM: ${err6 instanceof Error ? err6.message : String(err6)}`,
|
|
4921
|
+
err6 instanceof Error ? err6 : void 0
|
|
4059
4922
|
)
|
|
4060
4923
|
};
|
|
4061
4924
|
}
|
|
@@ -4136,8 +4999,8 @@ var SharingService = class {
|
|
|
4136
4999
|
let activeKey = keyInfo;
|
|
4137
5000
|
if (autoSubdelegate && useSessionKey && this.session) {
|
|
4138
5001
|
try {
|
|
4139
|
-
} catch (
|
|
4140
|
-
console.warn("Auto-subdelegation failed, using ingested key directly:",
|
|
5002
|
+
} catch (err6) {
|
|
5003
|
+
console.warn("Auto-subdelegation failed, using ingested key directly:", err6);
|
|
4141
5004
|
}
|
|
4142
5005
|
}
|
|
4143
5006
|
const authHeader = shareData.delegation.authHeader ?? `Bearer ${shareData.delegation.cid}`;
|
|
@@ -4235,26 +5098,26 @@ var SharingService = class {
|
|
|
4235
5098
|
let jsonString;
|
|
4236
5099
|
try {
|
|
4237
5100
|
jsonString = base64UrlDecode(base64Data);
|
|
4238
|
-
} catch (
|
|
5101
|
+
} catch (err6) {
|
|
4239
5102
|
return {
|
|
4240
5103
|
ok: false,
|
|
4241
5104
|
error: createError2(
|
|
4242
5105
|
DelegationErrorCodes.INVALID_TOKEN,
|
|
4243
|
-
`Failed to decode base64 data: ${
|
|
4244
|
-
|
|
5106
|
+
`Failed to decode base64 data: ${err6 instanceof Error ? err6.message : String(err6)}`,
|
|
5107
|
+
err6 instanceof Error ? err6 : void 0
|
|
4245
5108
|
)
|
|
4246
5109
|
};
|
|
4247
5110
|
}
|
|
4248
5111
|
let parsed;
|
|
4249
5112
|
try {
|
|
4250
5113
|
parsed = JSON.parse(jsonString);
|
|
4251
|
-
} catch (
|
|
5114
|
+
} catch (err6) {
|
|
4252
5115
|
return {
|
|
4253
5116
|
ok: false,
|
|
4254
5117
|
error: createError2(
|
|
4255
5118
|
DelegationErrorCodes.INVALID_TOKEN,
|
|
4256
|
-
`Failed to parse share data JSON: ${
|
|
4257
|
-
|
|
5119
|
+
`Failed to parse share data JSON: ${err6 instanceof Error ? err6.message : String(err6)}`,
|
|
5120
|
+
err6 instanceof Error ? err6 : void 0
|
|
4258
5121
|
)
|
|
4259
5122
|
};
|
|
4260
5123
|
}
|
|
@@ -4281,8 +5144,8 @@ function createSharingService(config) {
|
|
|
4281
5144
|
}
|
|
4282
5145
|
|
|
4283
5146
|
// src/authorization/CapabilityKeyRegistry.ts
|
|
4284
|
-
import { ok as
|
|
4285
|
-
var
|
|
5147
|
+
import { ok as ok4, err as err4, serviceError as serviceError4 } from "@tinycloud/sdk-services";
|
|
5148
|
+
var SERVICE_NAME3 = "capability-key-registry";
|
|
4286
5149
|
var CapabilityKeyRegistryErrorCodes = {
|
|
4287
5150
|
/** Key not found in registry */
|
|
4288
5151
|
KEY_NOT_FOUND: "KEY_NOT_FOUND",
|
|
@@ -4499,11 +5362,11 @@ var CapabilityKeyRegistry = class {
|
|
|
4499
5362
|
revokeDelegation(cid) {
|
|
4500
5363
|
const stored = this.store.byCid.get(cid);
|
|
4501
5364
|
if (!stored) {
|
|
4502
|
-
return
|
|
4503
|
-
|
|
5365
|
+
return err4(
|
|
5366
|
+
serviceError4(
|
|
4504
5367
|
CapabilityKeyRegistryErrorCodes.KEY_NOT_FOUND,
|
|
4505
5368
|
`Delegation not found: ${cid}`,
|
|
4506
|
-
|
|
5369
|
+
SERVICE_NAME3
|
|
4507
5370
|
)
|
|
4508
5371
|
);
|
|
4509
5372
|
}
|
|
@@ -4522,7 +5385,7 @@ var CapabilityKeyRegistry = class {
|
|
|
4522
5385
|
}
|
|
4523
5386
|
}
|
|
4524
5387
|
}
|
|
4525
|
-
return
|
|
5388
|
+
return ok4(void 0);
|
|
4526
5389
|
}
|
|
4527
5390
|
// ===========================================================================
|
|
4528
5391
|
// Search
|
|
@@ -4751,8 +5614,8 @@ async function checkNodeInfo(host, sdkProtocol, fetchFn = globalThis.fetch.bind(
|
|
|
4751
5614
|
response = await fetchFn(`${host}/info`, {
|
|
4752
5615
|
signal: AbortSignal.timeout(5e3)
|
|
4753
5616
|
});
|
|
4754
|
-
} catch (
|
|
4755
|
-
throw new VersionCheckError(host,
|
|
5617
|
+
} catch (err6) {
|
|
5618
|
+
throw new VersionCheckError(host, err6);
|
|
4756
5619
|
}
|
|
4757
5620
|
if (!response.ok) {
|
|
4758
5621
|
throw new VersionCheckError(host);
|
|
@@ -5284,6 +6147,7 @@ function parseRecapCapabilities(parseWasm, siwe) {
|
|
|
5284
6147
|
export {
|
|
5285
6148
|
ACCOUNT_REGISTRY_PATH,
|
|
5286
6149
|
ACCOUNT_REGISTRY_SPACE,
|
|
6150
|
+
AccountService,
|
|
5287
6151
|
AutoApproveSpaceCreationHandler,
|
|
5288
6152
|
CapabilityKeyRegistry,
|
|
5289
6153
|
CapabilityKeyRegistryErrorCodes,
|
|
@@ -5389,7 +6253,7 @@ export {
|
|
|
5389
6253
|
utf8Decode as encryptionUtf8Decode,
|
|
5390
6254
|
utf8Encode as encryptionUtf8Encode,
|
|
5391
6255
|
ensureNetworkUsableForDecrypt,
|
|
5392
|
-
|
|
6256
|
+
err5 as err,
|
|
5393
6257
|
expandActionShortNames,
|
|
5394
6258
|
expandPermissionEntries,
|
|
5395
6259
|
expandPermissionEntry,
|
|
@@ -5410,7 +6274,7 @@ export {
|
|
|
5410
6274
|
multiaddrToHttpUrl,
|
|
5411
6275
|
networkDiscoveryKey,
|
|
5412
6276
|
normalizeDefaults,
|
|
5413
|
-
|
|
6277
|
+
ok5 as ok,
|
|
5414
6278
|
openWrappedKey,
|
|
5415
6279
|
parseCanonicalNetworkId,
|
|
5416
6280
|
parseExpiry,
|
|
@@ -5428,7 +6292,7 @@ export {
|
|
|
5428
6292
|
resolveTinyCloudHosts,
|
|
5429
6293
|
resourceCapabilitiesToAbilitiesMap,
|
|
5430
6294
|
resourceCapabilitiesToSpaceAbilitiesMap,
|
|
5431
|
-
|
|
6295
|
+
serviceError5 as serviceError,
|
|
5432
6296
|
signLocationRecord,
|
|
5433
6297
|
submitHostDelegation,
|
|
5434
6298
|
validateClientSession,
|