@tinycloud/sdk-core 2.4.0-beta.1 → 2.4.0-beta.6
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 +1724 -1360
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +141 -59
- package/dist/index.d.ts +141 -59
- package/dist/index.js +1576 -1209
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2151,1017 +2151,292 @@ var TinyCloud = class _TinyCloud {
|
|
|
2151
2151
|
}
|
|
2152
2152
|
};
|
|
2153
2153
|
|
|
2154
|
-
// src/
|
|
2154
|
+
// src/account/AccountService.ts
|
|
2155
2155
|
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
|
|
2156
|
+
err as err3,
|
|
2157
|
+
ok as ok3,
|
|
2158
|
+
serviceError as serviceError3
|
|
2220
2159
|
} from "@tinycloud/sdk-services";
|
|
2221
2160
|
|
|
2222
|
-
// src/
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
)
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
throw new Error(`Failed to get peer ID: ${res.status} - ${error}`);
|
|
2161
|
+
// src/manifest.ts
|
|
2162
|
+
import ms from "ms";
|
|
2163
|
+
import { resolveSecretPath, SECRET_NAME_RE } from "@tinycloud/sdk-services";
|
|
2164
|
+
var ManifestValidationError = class extends Error {
|
|
2165
|
+
constructor(message) {
|
|
2166
|
+
super(`Manifest validation failed: ${message}`);
|
|
2167
|
+
this.name = "ManifestValidationError";
|
|
2230
2168
|
}
|
|
2231
|
-
|
|
2169
|
+
};
|
|
2170
|
+
var DEFAULT_EXPIRY = "30d";
|
|
2171
|
+
var DEFAULT_DEFAULTS = true;
|
|
2172
|
+
var DEFAULT_MANIFEST_VERSION = 1;
|
|
2173
|
+
var DEFAULT_MANIFEST_SPACE = "applications";
|
|
2174
|
+
var ACCOUNT_REGISTRY_SPACE = "account";
|
|
2175
|
+
var ACCOUNT_REGISTRY_PATH = "applications/";
|
|
2176
|
+
var SECRETS_SPACE = "secrets";
|
|
2177
|
+
var VAULT_PERMISSION_SERVICE = "tinycloud.vault";
|
|
2178
|
+
var SERVICE_SHORT_TO_LONG = Object.freeze({
|
|
2179
|
+
kv: "tinycloud.kv",
|
|
2180
|
+
sql: "tinycloud.sql",
|
|
2181
|
+
duckdb: "tinycloud.duckdb",
|
|
2182
|
+
capabilities: "tinycloud.capabilities",
|
|
2183
|
+
hooks: "tinycloud.hooks",
|
|
2184
|
+
encryption: "tinycloud.encryption"
|
|
2185
|
+
});
|
|
2186
|
+
var ENCRYPTION_PERMISSION_SERVICE = "tinycloud.encryption";
|
|
2187
|
+
var ENCRYPTION_MANIFEST_SPACE = "encryption";
|
|
2188
|
+
var SERVICE_LONG_TO_SHORT = Object.freeze(
|
|
2189
|
+
Object.fromEntries(
|
|
2190
|
+
Object.entries(SERVICE_SHORT_TO_LONG).map(([s, l]) => [l, s])
|
|
2191
|
+
)
|
|
2192
|
+
);
|
|
2193
|
+
var DEFAULT_STANDARD_ENTRIES = [
|
|
2194
|
+
{
|
|
2195
|
+
service: "tinycloud.kv",
|
|
2196
|
+
space: DEFAULT_MANIFEST_SPACE,
|
|
2197
|
+
path: "/",
|
|
2198
|
+
actions: ["get", "put", "del", "list", "metadata"]
|
|
2199
|
+
},
|
|
2200
|
+
{
|
|
2201
|
+
service: "tinycloud.sql",
|
|
2202
|
+
space: DEFAULT_MANIFEST_SPACE,
|
|
2203
|
+
path: "/",
|
|
2204
|
+
actions: ["read", "write"]
|
|
2205
|
+
}
|
|
2206
|
+
];
|
|
2207
|
+
var DEFAULT_ADMIN_ENTRIES = [
|
|
2208
|
+
{
|
|
2209
|
+
service: "tinycloud.kv",
|
|
2210
|
+
space: DEFAULT_MANIFEST_SPACE,
|
|
2211
|
+
path: "/",
|
|
2212
|
+
actions: ["get", "put", "del", "list", "metadata"]
|
|
2213
|
+
},
|
|
2214
|
+
{
|
|
2215
|
+
service: "tinycloud.sql",
|
|
2216
|
+
space: DEFAULT_MANIFEST_SPACE,
|
|
2217
|
+
path: "/",
|
|
2218
|
+
actions: ["read", "write", "ddl"]
|
|
2219
|
+
}
|
|
2220
|
+
];
|
|
2221
|
+
var DEFAULT_ALL_ENTRIES = [
|
|
2222
|
+
{
|
|
2223
|
+
service: "tinycloud.kv",
|
|
2224
|
+
space: DEFAULT_MANIFEST_SPACE,
|
|
2225
|
+
path: "/",
|
|
2226
|
+
actions: ["get", "put", "del", "list", "metadata"]
|
|
2227
|
+
},
|
|
2228
|
+
{
|
|
2229
|
+
service: "tinycloud.sql",
|
|
2230
|
+
space: DEFAULT_MANIFEST_SPACE,
|
|
2231
|
+
path: "/",
|
|
2232
|
+
actions: ["read", "write", "ddl"]
|
|
2233
|
+
},
|
|
2234
|
+
{
|
|
2235
|
+
service: "tinycloud.duckdb",
|
|
2236
|
+
space: DEFAULT_MANIFEST_SPACE,
|
|
2237
|
+
path: "/",
|
|
2238
|
+
actions: ["read", "write"]
|
|
2239
|
+
}
|
|
2240
|
+
];
|
|
2241
|
+
function parseExpiry(duration) {
|
|
2242
|
+
if (typeof duration !== "string" || duration.length === 0) {
|
|
2243
|
+
throw new ManifestValidationError(
|
|
2244
|
+
`expiry must be a non-empty duration string (got ${JSON.stringify(duration)})`
|
|
2245
|
+
);
|
|
2246
|
+
}
|
|
2247
|
+
const parsed = ms(duration);
|
|
2248
|
+
if (typeof parsed !== "number" || !Number.isFinite(parsed) || parsed <= 0) {
|
|
2249
|
+
throw new ManifestValidationError(
|
|
2250
|
+
`invalid expiry duration: ${JSON.stringify(duration)}`
|
|
2251
|
+
);
|
|
2252
|
+
}
|
|
2253
|
+
return parsed;
|
|
2232
2254
|
}
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2255
|
+
function expandActionShortNames(service, actions) {
|
|
2256
|
+
return actions.map((a) => {
|
|
2257
|
+
if (a.includes("/")) {
|
|
2258
|
+
return a;
|
|
2259
|
+
}
|
|
2260
|
+
return `${service}/${a}`;
|
|
2237
2261
|
});
|
|
2238
|
-
return {
|
|
2239
|
-
success: res.ok,
|
|
2240
|
-
status: res.status,
|
|
2241
|
-
error: res.ok ? void 0 : await res.text().catch(() => res.statusText)
|
|
2242
|
-
};
|
|
2243
2262
|
}
|
|
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
|
-
}
|
|
2263
|
+
function expandPermissionEntry(entry) {
|
|
2264
|
+
if (entry.service === ENCRYPTION_PERMISSION_SERVICE) {
|
|
2265
|
+
return expandEncryptionPermissionEntry(entry);
|
|
2266
2266
|
}
|
|
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
|
-
};
|
|
2267
|
+
if (entry.service !== VAULT_PERMISSION_SERVICE) {
|
|
2268
|
+
return [
|
|
2269
|
+
{
|
|
2270
|
+
...entry,
|
|
2271
|
+
actions: expandActionShortNames(entry.service, entry.actions)
|
|
2272
|
+
}
|
|
2273
|
+
];
|
|
2274
|
+
}
|
|
2275
|
+
return expandVaultPermissionEntry(entry);
|
|
2290
2276
|
}
|
|
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);
|
|
2277
|
+
function expandEncryptionPermissionEntry(entry) {
|
|
2278
|
+
if (typeof entry.path !== "string" || !entry.path.startsWith("urn:tinycloud:encryption:")) {
|
|
2279
|
+
throw new ManifestValidationError(
|
|
2280
|
+
`tinycloud.encryption entries require path to be a networkId URN (got ${JSON.stringify(entry.path)})`
|
|
2281
|
+
);
|
|
2302
2282
|
}
|
|
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
|
-
};
|
|
2283
|
+
const normalizedActions = [];
|
|
2284
|
+
for (const action of entry.actions) {
|
|
2285
|
+
if (action === "decrypt" || action === "tinycloud.encryption/decrypt") {
|
|
2286
|
+
normalizedActions.push("tinycloud.encryption/decrypt");
|
|
2287
|
+
continue;
|
|
2357
2288
|
}
|
|
2358
|
-
if (
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
error: createError(
|
|
2362
|
-
DelegationErrorCodes.INVALID_INPUT,
|
|
2363
|
-
"path is required"
|
|
2364
|
-
)
|
|
2365
|
-
};
|
|
2289
|
+
if (action === "network.create" || action === "tinycloud.encryption/network.create") {
|
|
2290
|
+
normalizedActions.push("tinycloud.encryption/network.create");
|
|
2291
|
+
continue;
|
|
2366
2292
|
}
|
|
2367
|
-
if (
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
error: createError(
|
|
2371
|
-
DelegationErrorCodes.INVALID_INPUT,
|
|
2372
|
-
"at least one action is required"
|
|
2373
|
-
)
|
|
2374
|
-
};
|
|
2293
|
+
if (action === "network.revoke" || action === "tinycloud.encryption/network.revoke") {
|
|
2294
|
+
normalizedActions.push("tinycloud.encryption/network.revoke");
|
|
2295
|
+
continue;
|
|
2375
2296
|
}
|
|
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
|
|
2297
|
+
if (action.includes("/")) {
|
|
2298
|
+
throw new ManifestValidationError(
|
|
2299
|
+
`unknown encryption action ${JSON.stringify(action)}; expected decrypt, network.create, or network.revoke`
|
|
2389
2300
|
);
|
|
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
2301
|
}
|
|
2302
|
+
throw new ManifestValidationError(
|
|
2303
|
+
`unknown encryption action ${JSON.stringify(action)}; expected decrypt, network.create, or network.revoke`
|
|
2304
|
+
);
|
|
2435
2305
|
}
|
|
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
|
-
};
|
|
2306
|
+
const dedupedActions = [];
|
|
2307
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2308
|
+
for (const a of normalizedActions) {
|
|
2309
|
+
if (!seen.has(a)) {
|
|
2310
|
+
dedupedActions.push(a);
|
|
2311
|
+
seen.add(a);
|
|
2462
2312
|
}
|
|
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
|
-
};
|
|
2313
|
+
}
|
|
2314
|
+
return [
|
|
2315
|
+
{
|
|
2316
|
+
service: ENCRYPTION_PERMISSION_SERVICE,
|
|
2317
|
+
space: ENCRYPTION_MANIFEST_SPACE,
|
|
2318
|
+
path: entry.path,
|
|
2319
|
+
actions: dedupedActions,
|
|
2320
|
+
skipPrefix: true,
|
|
2321
|
+
...entry.expiry !== void 0 ? { expiry: entry.expiry } : {},
|
|
2322
|
+
...entry.description !== void 0 ? { description: entry.description } : {}
|
|
2511
2323
|
}
|
|
2324
|
+
];
|
|
2325
|
+
}
|
|
2326
|
+
function expandPermissionEntries(entries) {
|
|
2327
|
+
return entries.flatMap(expandPermissionEntry);
|
|
2328
|
+
}
|
|
2329
|
+
function applyPrefix(prefix, path, skipPrefix) {
|
|
2330
|
+
if (skipPrefix) {
|
|
2331
|
+
return path;
|
|
2512
2332
|
}
|
|
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
|
-
};
|
|
2333
|
+
if (prefix === "") {
|
|
2334
|
+
return path;
|
|
2335
|
+
}
|
|
2336
|
+
if (path.startsWith("/")) {
|
|
2337
|
+
return `${prefix}${path}`;
|
|
2338
|
+
}
|
|
2339
|
+
return `${prefix}/${path}`;
|
|
2340
|
+
}
|
|
2341
|
+
async function loadManifest(url) {
|
|
2342
|
+
const fetchFn = globalThis.fetch;
|
|
2343
|
+
if (typeof fetchFn !== "function") {
|
|
2344
|
+
throw new ManifestValidationError(
|
|
2345
|
+
"loadManifest requires a global fetch; pass the manifest object directly on runtimes without fetch"
|
|
2346
|
+
);
|
|
2347
|
+
}
|
|
2348
|
+
const res = await fetchFn(url);
|
|
2349
|
+
if (!res.ok) {
|
|
2350
|
+
throw new ManifestValidationError(
|
|
2351
|
+
`failed to fetch manifest from ${url}: HTTP ${res.status}`
|
|
2352
|
+
);
|
|
2353
|
+
}
|
|
2354
|
+
const json = await res.json();
|
|
2355
|
+
return validateManifest(json);
|
|
2356
|
+
}
|
|
2357
|
+
function validateManifest(input) {
|
|
2358
|
+
if (input === null || typeof input !== "object") {
|
|
2359
|
+
throw new ManifestValidationError("manifest must be an object");
|
|
2360
|
+
}
|
|
2361
|
+
const m = input;
|
|
2362
|
+
if (m.manifest_version !== void 0 && m.manifest_version !== DEFAULT_MANIFEST_VERSION) {
|
|
2363
|
+
throw new ManifestValidationError(
|
|
2364
|
+
`manifest.manifest_version must be ${DEFAULT_MANIFEST_VERSION}`
|
|
2365
|
+
);
|
|
2366
|
+
}
|
|
2367
|
+
if (typeof m.app_id !== "string" || m.app_id.length === 0) {
|
|
2368
|
+
throw new ManifestValidationError(
|
|
2369
|
+
"manifest.app_id is required and must be a non-empty string"
|
|
2370
|
+
);
|
|
2371
|
+
}
|
|
2372
|
+
if (typeof m.name !== "string" || m.name.length === 0) {
|
|
2373
|
+
throw new ManifestValidationError(
|
|
2374
|
+
"manifest.name is required and must be a non-empty string"
|
|
2375
|
+
);
|
|
2376
|
+
}
|
|
2377
|
+
if (m.did !== void 0 && (typeof m.did !== "string" || m.did.length === 0)) {
|
|
2378
|
+
throw new ManifestValidationError(
|
|
2379
|
+
"manifest.did must be a non-empty DID string"
|
|
2380
|
+
);
|
|
2381
|
+
}
|
|
2382
|
+
if (m.space !== void 0 && (typeof m.space !== "string" || m.space.length === 0)) {
|
|
2383
|
+
throw new ManifestValidationError(
|
|
2384
|
+
"manifest.space must be a non-empty string"
|
|
2385
|
+
);
|
|
2386
|
+
}
|
|
2387
|
+
if (m.expiry !== void 0) {
|
|
2388
|
+
parseExpiry(m.expiry);
|
|
2389
|
+
}
|
|
2390
|
+
if (m.permissions !== void 0) {
|
|
2391
|
+
if (!Array.isArray(m.permissions)) {
|
|
2392
|
+
throw new ManifestValidationError(
|
|
2393
|
+
"manifest.permissions must be an array"
|
|
2394
|
+
);
|
|
2580
2395
|
}
|
|
2396
|
+
m.permissions.forEach(
|
|
2397
|
+
(p, i) => validatePermissionEntry(p, `permissions[${i}]`)
|
|
2398
|
+
);
|
|
2581
2399
|
}
|
|
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
|
-
};
|
|
2400
|
+
if (m.secrets !== void 0) {
|
|
2401
|
+
validateManifestSecrets(m.secrets);
|
|
2402
|
+
}
|
|
2403
|
+
return m;
|
|
2404
|
+
}
|
|
2405
|
+
function validateManifestSecrets(secrets) {
|
|
2406
|
+
if (secrets === null || typeof secrets !== "object" || Array.isArray(secrets)) {
|
|
2407
|
+
throw new ManifestValidationError("manifest.secrets must be an object");
|
|
2408
|
+
}
|
|
2409
|
+
for (const [name, spec] of Object.entries(secrets)) {
|
|
2410
|
+
if (!SECRET_NAME_RE.test(name)) {
|
|
2411
|
+
throw new ManifestValidationError(
|
|
2412
|
+
`manifest.secrets.${name} must match ${SECRET_NAME_RE.source}`
|
|
2413
|
+
);
|
|
2611
2414
|
}
|
|
2612
2415
|
try {
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
DelegationAction.GET,
|
|
2617
|
-
body
|
|
2416
|
+
resolveSecretPath(
|
|
2417
|
+
secretNameFromSpec(name, spec),
|
|
2418
|
+
{ scope: secretScopeFromSpec(spec) }
|
|
2618
2419
|
);
|
|
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
2420
|
} catch (error) {
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2421
|
+
throw new ManifestValidationError(
|
|
2422
|
+
`manifest.secrets.${name}: ${error instanceof Error ? error.message : String(error)}`
|
|
2423
|
+
);
|
|
2424
|
+
}
|
|
2425
|
+
const actions = secretActionsFromSpec(name, spec);
|
|
2426
|
+
if (actions.length === 0) {
|
|
2427
|
+
throw new ManifestValidationError(
|
|
2428
|
+
`manifest.secrets.${name} actions must be non-empty`
|
|
2429
|
+
);
|
|
2430
|
+
}
|
|
2431
|
+
for (const action of actions) {
|
|
2432
|
+
if (typeof action !== "string" || action.length === 0) {
|
|
2433
|
+
throw new ManifestValidationError(
|
|
2434
|
+
`manifest.secrets.${name} actions must be non-empty strings`
|
|
2435
|
+
);
|
|
2665
2436
|
}
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
DelegationErrorCodes.NETWORK_ERROR,
|
|
2670
|
-
`Network error during chain retrieval: ${String(error)}`,
|
|
2671
|
-
error instanceof Error ? error : void 0
|
|
2672
|
-
)
|
|
2673
|
-
};
|
|
2674
|
-
}
|
|
2675
|
-
}
|
|
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
|
-
}
|
|
2885
|
-
|
|
2886
|
-
// src/manifest.ts
|
|
2887
|
-
import ms from "ms";
|
|
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"]
|
|
2930
|
-
}
|
|
2931
|
-
];
|
|
2932
|
-
var DEFAULT_ADMIN_ENTRIES = [
|
|
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"]
|
|
2944
|
-
}
|
|
2945
|
-
];
|
|
2946
|
-
var DEFAULT_ALL_ENTRIES = [
|
|
2947
|
-
{
|
|
2948
|
-
service: "tinycloud.kv",
|
|
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"]
|
|
2964
|
-
}
|
|
2965
|
-
];
|
|
2966
|
-
function parseExpiry(duration) {
|
|
2967
|
-
if (typeof duration !== "string" || duration.length === 0) {
|
|
2968
|
-
throw new ManifestValidationError(
|
|
2969
|
-
`expiry must be a non-empty duration string (got ${JSON.stringify(duration)})`
|
|
2970
|
-
);
|
|
2971
|
-
}
|
|
2972
|
-
const parsed = ms(duration);
|
|
2973
|
-
if (typeof parsed !== "number" || !Number.isFinite(parsed) || parsed <= 0) {
|
|
2974
|
-
throw new ManifestValidationError(
|
|
2975
|
-
`invalid expiry duration: ${JSON.stringify(duration)}`
|
|
2976
|
-
);
|
|
2977
|
-
}
|
|
2978
|
-
return parsed;
|
|
2979
|
-
}
|
|
2980
|
-
function expandActionShortNames(service, actions) {
|
|
2981
|
-
return actions.map((a) => {
|
|
2982
|
-
if (a.includes("/")) {
|
|
2983
|
-
return a;
|
|
2984
|
-
}
|
|
2985
|
-
return `${service}/${a}`;
|
|
2986
|
-
});
|
|
2987
|
-
}
|
|
2988
|
-
function expandPermissionEntry(entry) {
|
|
2989
|
-
if (entry.service === ENCRYPTION_PERMISSION_SERVICE) {
|
|
2990
|
-
return expandEncryptionPermissionEntry(entry);
|
|
2991
|
-
}
|
|
2992
|
-
if (entry.service !== VAULT_PERMISSION_SERVICE) {
|
|
2993
|
-
return [
|
|
2994
|
-
{
|
|
2995
|
-
...entry,
|
|
2996
|
-
actions: expandActionShortNames(entry.service, entry.actions)
|
|
2997
|
-
}
|
|
2998
|
-
];
|
|
2999
|
-
}
|
|
3000
|
-
return expandVaultPermissionEntry(entry);
|
|
3001
|
-
}
|
|
3002
|
-
function expandEncryptionPermissionEntry(entry) {
|
|
3003
|
-
if (typeof entry.path !== "string" || !entry.path.startsWith("urn:tinycloud:encryption:")) {
|
|
3004
|
-
throw new ManifestValidationError(
|
|
3005
|
-
`tinycloud.encryption entries require path to be a networkId URN (got ${JSON.stringify(entry.path)})`
|
|
3006
|
-
);
|
|
3007
|
-
}
|
|
3008
|
-
const normalizedActions = [];
|
|
3009
|
-
for (const action of entry.actions) {
|
|
3010
|
-
if (action === "decrypt" || action === "tinycloud.encryption/decrypt") {
|
|
3011
|
-
normalizedActions.push("tinycloud.encryption/decrypt");
|
|
3012
|
-
continue;
|
|
3013
|
-
}
|
|
3014
|
-
if (action === "network.create" || action === "tinycloud.encryption/network.create") {
|
|
3015
|
-
normalizedActions.push("tinycloud.encryption/network.create");
|
|
3016
|
-
continue;
|
|
3017
|
-
}
|
|
3018
|
-
if (action === "network.revoke" || action === "tinycloud.encryption/network.revoke") {
|
|
3019
|
-
normalizedActions.push("tinycloud.encryption/network.revoke");
|
|
3020
|
-
continue;
|
|
3021
|
-
}
|
|
3022
|
-
if (action.includes("/")) {
|
|
3023
|
-
throw new ManifestValidationError(
|
|
3024
|
-
`unknown encryption action ${JSON.stringify(action)}; expected decrypt, network.create, or network.revoke`
|
|
3025
|
-
);
|
|
3026
|
-
}
|
|
3027
|
-
throw new ManifestValidationError(
|
|
3028
|
-
`unknown encryption action ${JSON.stringify(action)}; expected decrypt, network.create, or network.revoke`
|
|
3029
|
-
);
|
|
3030
|
-
}
|
|
3031
|
-
const dedupedActions = [];
|
|
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);
|
|
3053
|
-
}
|
|
3054
|
-
function applyPrefix(prefix, path, skipPrefix) {
|
|
3055
|
-
if (skipPrefix) {
|
|
3056
|
-
return path;
|
|
3057
|
-
}
|
|
3058
|
-
if (prefix === "") {
|
|
3059
|
-
return path;
|
|
3060
|
-
}
|
|
3061
|
-
if (path.startsWith("/")) {
|
|
3062
|
-
return `${prefix}${path}`;
|
|
3063
|
-
}
|
|
3064
|
-
return `${prefix}/${path}`;
|
|
3065
|
-
}
|
|
3066
|
-
async function loadManifest(url) {
|
|
3067
|
-
const fetchFn = globalThis.fetch;
|
|
3068
|
-
if (typeof fetchFn !== "function") {
|
|
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
|
-
);
|
|
3078
|
-
}
|
|
3079
|
-
const json = await res.json();
|
|
3080
|
-
return validateManifest(json);
|
|
3081
|
-
}
|
|
3082
|
-
function validateManifest(input) {
|
|
3083
|
-
if (input === null || typeof input !== "object") {
|
|
3084
|
-
throw new ManifestValidationError("manifest must be an object");
|
|
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
|
-
);
|
|
3096
|
-
}
|
|
3097
|
-
if (typeof m.name !== "string" || m.name.length === 0) {
|
|
3098
|
-
throw new ManifestValidationError(
|
|
3099
|
-
"manifest.name is required and must be a non-empty string"
|
|
3100
|
-
);
|
|
3101
|
-
}
|
|
3102
|
-
if (m.did !== void 0 && (typeof m.did !== "string" || m.did.length === 0)) {
|
|
3103
|
-
throw new ManifestValidationError(
|
|
3104
|
-
"manifest.did must be a non-empty DID string"
|
|
3105
|
-
);
|
|
3106
|
-
}
|
|
3107
|
-
if (m.space !== void 0 && (typeof m.space !== "string" || m.space.length === 0)) {
|
|
3108
|
-
throw new ManifestValidationError(
|
|
3109
|
-
"manifest.space must be a non-empty string"
|
|
3110
|
-
);
|
|
3111
|
-
}
|
|
3112
|
-
if (m.expiry !== void 0) {
|
|
3113
|
-
parseExpiry(m.expiry);
|
|
3114
|
-
}
|
|
3115
|
-
if (m.permissions !== void 0) {
|
|
3116
|
-
if (!Array.isArray(m.permissions)) {
|
|
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
|
-
);
|
|
3124
|
-
}
|
|
3125
|
-
if (m.secrets !== void 0) {
|
|
3126
|
-
validateManifestSecrets(m.secrets);
|
|
3127
|
-
}
|
|
3128
|
-
return m;
|
|
3129
|
-
}
|
|
3130
|
-
function validateManifestSecrets(secrets) {
|
|
3131
|
-
if (secrets === null || typeof secrets !== "object" || Array.isArray(secrets)) {
|
|
3132
|
-
throw new ManifestValidationError("manifest.secrets must be an object");
|
|
3133
|
-
}
|
|
3134
|
-
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
|
-
const actions = secretActionsFromSpec(name, spec);
|
|
3151
|
-
if (actions.length === 0) {
|
|
3152
|
-
throw new ManifestValidationError(
|
|
3153
|
-
`manifest.secrets.${name} actions must be non-empty`
|
|
3154
|
-
);
|
|
3155
|
-
}
|
|
3156
|
-
for (const action of actions) {
|
|
3157
|
-
if (typeof action !== "string" || action.length === 0) {
|
|
3158
|
-
throw new ManifestValidationError(
|
|
3159
|
-
`manifest.secrets.${name} actions must be non-empty strings`
|
|
3160
|
-
);
|
|
3161
|
-
}
|
|
3162
|
-
}
|
|
3163
|
-
if (spec !== null && typeof spec === "object" && !Array.isArray(spec) && spec.expiry !== void 0) {
|
|
3164
|
-
parseExpiry(spec.expiry);
|
|
2437
|
+
}
|
|
2438
|
+
if (spec !== null && typeof spec === "object" && !Array.isArray(spec) && spec.expiry !== void 0) {
|
|
2439
|
+
parseExpiry(spec.expiry);
|
|
3165
2440
|
}
|
|
3166
2441
|
}
|
|
3167
2442
|
}
|
|
@@ -3388,237 +2663,1328 @@ function resolveEntry(entry, prefix, _inheritedExpiryMs, inheritedSpace) {
|
|
|
3388
2663
|
...entry.description !== void 0 ? { description: entry.description } : {}
|
|
3389
2664
|
}));
|
|
3390
2665
|
}
|
|
3391
|
-
function expandVaultPermissionEntry(entry) {
|
|
3392
|
-
const byBase = /* @__PURE__ */ new Map();
|
|
3393
|
-
for (const action of entry.actions) {
|
|
3394
|
-
const expansion = vaultActionExpansion(action);
|
|
3395
|
-
for (const base of expansion.bases) {
|
|
3396
|
-
const actions = byBase.get(base) ?? [];
|
|
3397
|
-
if (!actions.includes(expansion.action)) {
|
|
3398
|
-
actions.push(expansion.action);
|
|
2666
|
+
function expandVaultPermissionEntry(entry) {
|
|
2667
|
+
const byBase = /* @__PURE__ */ new Map();
|
|
2668
|
+
for (const action of entry.actions) {
|
|
2669
|
+
const expansion = vaultActionExpansion(action);
|
|
2670
|
+
for (const base of expansion.bases) {
|
|
2671
|
+
const actions = byBase.get(base) ?? [];
|
|
2672
|
+
if (!actions.includes(expansion.action)) {
|
|
2673
|
+
actions.push(expansion.action);
|
|
2674
|
+
}
|
|
2675
|
+
byBase.set(base, actions);
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2678
|
+
return [...byBase.entries()].map(([base, actions]) => ({
|
|
2679
|
+
...entry,
|
|
2680
|
+
service: "tinycloud.kv",
|
|
2681
|
+
path: vaultKVPath(base, entry.path),
|
|
2682
|
+
actions,
|
|
2683
|
+
skipPrefix: true
|
|
2684
|
+
}));
|
|
2685
|
+
}
|
|
2686
|
+
function vaultActionExpansion(action) {
|
|
2687
|
+
const normalized = normalizeVaultAction(action);
|
|
2688
|
+
if (normalized === "read" || normalized === "get") {
|
|
2689
|
+
return { bases: ["vault"], action: "tinycloud.kv/get" };
|
|
2690
|
+
}
|
|
2691
|
+
if (normalized === "write" || normalized === "put") {
|
|
2692
|
+
return { bases: ["vault"], action: "tinycloud.kv/put" };
|
|
2693
|
+
}
|
|
2694
|
+
if (normalized === "delete" || normalized === "del") {
|
|
2695
|
+
return { bases: ["vault"], action: "tinycloud.kv/del" };
|
|
2696
|
+
}
|
|
2697
|
+
if (normalized === "list") {
|
|
2698
|
+
return { bases: ["vault"], action: "tinycloud.kv/list" };
|
|
2699
|
+
}
|
|
2700
|
+
if (normalized === "head") {
|
|
2701
|
+
return { bases: ["vault"], action: "tinycloud.kv/get" };
|
|
2702
|
+
}
|
|
2703
|
+
if (normalized === "metadata") {
|
|
2704
|
+
return { bases: ["vault"], action: "tinycloud.kv/metadata" };
|
|
2705
|
+
}
|
|
2706
|
+
throw new ManifestValidationError(
|
|
2707
|
+
`unknown vault action ${JSON.stringify(action)}; expected read, write, delete, get, put, del, list, head, or metadata`
|
|
2708
|
+
);
|
|
2709
|
+
}
|
|
2710
|
+
function normalizeVaultAction(action) {
|
|
2711
|
+
if (action.startsWith(`${VAULT_PERMISSION_SERVICE}/`)) {
|
|
2712
|
+
return action.slice(`${VAULT_PERMISSION_SERVICE}/`.length);
|
|
2713
|
+
}
|
|
2714
|
+
if (action.startsWith("tinycloud.kv/")) {
|
|
2715
|
+
return action.slice("tinycloud.kv/".length);
|
|
2716
|
+
}
|
|
2717
|
+
if (action.includes("/")) {
|
|
2718
|
+
throw new ManifestValidationError(
|
|
2719
|
+
`unknown vault action ${JSON.stringify(action)}; expected a tinycloud.vault or tinycloud.kv action`
|
|
2720
|
+
);
|
|
2721
|
+
}
|
|
2722
|
+
return action;
|
|
2723
|
+
}
|
|
2724
|
+
function vaultKVPath(base, path) {
|
|
2725
|
+
const normalized = path.startsWith("/") ? path.slice(1) : path;
|
|
2726
|
+
return `${base}/${normalized}`;
|
|
2727
|
+
}
|
|
2728
|
+
function cloneResourceCapability(entry) {
|
|
2729
|
+
return {
|
|
2730
|
+
service: entry.service,
|
|
2731
|
+
space: entry.space,
|
|
2732
|
+
path: entry.path,
|
|
2733
|
+
actions: [...entry.actions],
|
|
2734
|
+
...entry.expiryMs !== void 0 ? { expiryMs: entry.expiryMs } : {},
|
|
2735
|
+
...entry.description !== void 0 ? { description: entry.description } : {}
|
|
2736
|
+
};
|
|
2737
|
+
}
|
|
2738
|
+
function clonePermissionEntry(entry) {
|
|
2739
|
+
return {
|
|
2740
|
+
service: entry.service,
|
|
2741
|
+
...entry.space !== void 0 ? { space: entry.space } : {},
|
|
2742
|
+
path: entry.path,
|
|
2743
|
+
actions: [...entry.actions],
|
|
2744
|
+
...entry.skipPrefix !== void 0 ? { skipPrefix: entry.skipPrefix } : {},
|
|
2745
|
+
...entry.expiry !== void 0 ? { expiry: entry.expiry } : {},
|
|
2746
|
+
...entry.description !== void 0 ? { description: entry.description } : {}
|
|
2747
|
+
};
|
|
2748
|
+
}
|
|
2749
|
+
function dedupeResources(resources) {
|
|
2750
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
2751
|
+
for (const resource of resources) {
|
|
2752
|
+
const key = `${resource.service}\0${resource.space}\0${resource.path}\0${resource.expiryMs ?? ""}`;
|
|
2753
|
+
const existing = byKey.get(key);
|
|
2754
|
+
if (existing === void 0) {
|
|
2755
|
+
byKey.set(key, cloneResourceCapability(resource));
|
|
2756
|
+
continue;
|
|
2757
|
+
}
|
|
2758
|
+
const seen = new Set(existing.actions);
|
|
2759
|
+
for (const action of resource.actions) {
|
|
2760
|
+
if (!seen.has(action)) {
|
|
2761
|
+
existing.actions.push(action);
|
|
2762
|
+
seen.add(action);
|
|
2763
|
+
}
|
|
2764
|
+
}
|
|
2765
|
+
if (existing.description === void 0 && resource.description !== void 0) {
|
|
2766
|
+
existing.description = resource.description;
|
|
2767
|
+
}
|
|
2768
|
+
}
|
|
2769
|
+
return [...byKey.values()];
|
|
2770
|
+
}
|
|
2771
|
+
function capabilitiesReadPermission(space) {
|
|
2772
|
+
return {
|
|
2773
|
+
service: "tinycloud.capabilities",
|
|
2774
|
+
space,
|
|
2775
|
+
path: "",
|
|
2776
|
+
actions: ["tinycloud.capabilities/read"]
|
|
2777
|
+
};
|
|
2778
|
+
}
|
|
2779
|
+
function withCapabilitiesReadForSpaces(resources) {
|
|
2780
|
+
if (resources.length === 0) {
|
|
2781
|
+
return [];
|
|
2782
|
+
}
|
|
2783
|
+
const spaces = new Set(
|
|
2784
|
+
resources.filter((resource) => resource.service !== ENCRYPTION_PERMISSION_SERVICE).map((resource) => resource.space)
|
|
2785
|
+
);
|
|
2786
|
+
return dedupeResources([
|
|
2787
|
+
...resources,
|
|
2788
|
+
...[...spaces].map(capabilitiesReadPermission)
|
|
2789
|
+
]);
|
|
2790
|
+
}
|
|
2791
|
+
function accountRegistryPermission() {
|
|
2792
|
+
return {
|
|
2793
|
+
service: "tinycloud.kv",
|
|
2794
|
+
space: ACCOUNT_REGISTRY_SPACE,
|
|
2795
|
+
path: ACCOUNT_REGISTRY_PATH,
|
|
2796
|
+
actions: ["tinycloud.kv/get", "tinycloud.kv/put", "tinycloud.kv/list"]
|
|
2797
|
+
};
|
|
2798
|
+
}
|
|
2799
|
+
function composeManifestRequest(inputs, options = {}) {
|
|
2800
|
+
if (!Array.isArray(inputs) || inputs.length === 0) {
|
|
2801
|
+
throw new ManifestValidationError(
|
|
2802
|
+
"composeManifestRequest requires at least one manifest"
|
|
2803
|
+
);
|
|
2804
|
+
}
|
|
2805
|
+
const includeAccountRegistryPermissions = options.includeAccountRegistryPermissions ?? true;
|
|
2806
|
+
const manifests = inputs.map(validateManifest);
|
|
2807
|
+
const resolved = manifests.map(resolveManifest);
|
|
2808
|
+
const resources = resolved.flatMap((entry) => entry.resources);
|
|
2809
|
+
const delegationTargets = resolved.flatMap(
|
|
2810
|
+
(entry) => entry.additionalDelegates.map((delegate) => ({
|
|
2811
|
+
...delegate,
|
|
2812
|
+
permissions: dedupeResources(delegate.permissions)
|
|
2813
|
+
}))
|
|
2814
|
+
);
|
|
2815
|
+
if (includeAccountRegistryPermissions) {
|
|
2816
|
+
resources.push(accountRegistryPermission());
|
|
2817
|
+
}
|
|
2818
|
+
const resourcesWithImplicitCapabilities = withCapabilitiesReadForSpaces(resources);
|
|
2819
|
+
const manifestsByAppId = /* @__PURE__ */ new Map();
|
|
2820
|
+
for (const manifest of manifests) {
|
|
2821
|
+
const current = manifestsByAppId.get(manifest.app_id);
|
|
2822
|
+
if (current === void 0) {
|
|
2823
|
+
manifestsByAppId.set(manifest.app_id, [manifest]);
|
|
2824
|
+
} else {
|
|
2825
|
+
current.push(manifest);
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
const registryRecords = includeAccountRegistryPermissions ? [...manifestsByAppId.entries()].map(([app_id, appManifests]) => ({
|
|
2829
|
+
key: `${ACCOUNT_REGISTRY_PATH}${app_id}`,
|
|
2830
|
+
app_id,
|
|
2831
|
+
manifests: appManifests.map((manifest) => ({
|
|
2832
|
+
...manifest,
|
|
2833
|
+
permissions: manifest.permissions?.map(clonePermissionEntry)
|
|
2834
|
+
}))
|
|
2835
|
+
})) : [];
|
|
2836
|
+
return {
|
|
2837
|
+
manifests,
|
|
2838
|
+
resources: resourcesWithImplicitCapabilities,
|
|
2839
|
+
delegationTargets,
|
|
2840
|
+
registryRecords,
|
|
2841
|
+
expiryMs: Math.max(...resolved.map((entry) => entry.expiryMs)),
|
|
2842
|
+
includePublicSpace: resolved.some((entry) => entry.includePublicSpace)
|
|
2843
|
+
};
|
|
2844
|
+
}
|
|
2845
|
+
function resourceCapabilitiesToAbilitiesMap(resources) {
|
|
2846
|
+
const out = {};
|
|
2847
|
+
for (const r of resources) {
|
|
2848
|
+
const shortService = SERVICE_LONG_TO_SHORT[r.service];
|
|
2849
|
+
if (shortService === void 0) {
|
|
2850
|
+
throw new ManifestValidationError(
|
|
2851
|
+
`unknown service '${r.service}' \u2014 no short-form mapping. Known services: ${Object.keys(SERVICE_LONG_TO_SHORT).join(", ")}`
|
|
2852
|
+
);
|
|
2853
|
+
}
|
|
2854
|
+
if (out[shortService] === void 0) {
|
|
2855
|
+
out[shortService] = {};
|
|
2856
|
+
}
|
|
2857
|
+
const pathsMap = out[shortService];
|
|
2858
|
+
const existing = pathsMap[r.path];
|
|
2859
|
+
if (existing === void 0) {
|
|
2860
|
+
pathsMap[r.path] = [...r.actions];
|
|
2861
|
+
} else {
|
|
2862
|
+
const seen = new Set(existing);
|
|
2863
|
+
for (const action of r.actions) {
|
|
2864
|
+
if (!seen.has(action)) {
|
|
2865
|
+
existing.push(action);
|
|
2866
|
+
seen.add(action);
|
|
2867
|
+
}
|
|
3399
2868
|
}
|
|
3400
|
-
byBase.set(base, actions);
|
|
3401
2869
|
}
|
|
3402
2870
|
}
|
|
3403
|
-
return
|
|
3404
|
-
...entry,
|
|
3405
|
-
service: "tinycloud.kv",
|
|
3406
|
-
path: vaultKVPath(base, entry.path),
|
|
3407
|
-
actions,
|
|
3408
|
-
skipPrefix: true
|
|
3409
|
-
}));
|
|
2871
|
+
return out;
|
|
3410
2872
|
}
|
|
3411
|
-
function
|
|
3412
|
-
const
|
|
3413
|
-
|
|
3414
|
-
|
|
2873
|
+
function resourceCapabilitiesToSpaceAbilitiesMap(resources) {
|
|
2874
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
2875
|
+
for (const resource of resources) {
|
|
2876
|
+
const entries = grouped.get(resource.space);
|
|
2877
|
+
if (entries === void 0) {
|
|
2878
|
+
grouped.set(resource.space, [resource]);
|
|
2879
|
+
} else {
|
|
2880
|
+
entries.push(resource);
|
|
2881
|
+
}
|
|
3415
2882
|
}
|
|
3416
|
-
|
|
3417
|
-
|
|
2883
|
+
const out = {};
|
|
2884
|
+
for (const [space, entries] of grouped.entries()) {
|
|
2885
|
+
out[space] = resourceCapabilitiesToAbilitiesMap(entries);
|
|
3418
2886
|
}
|
|
3419
|
-
|
|
3420
|
-
|
|
2887
|
+
return out;
|
|
2888
|
+
}
|
|
2889
|
+
function manifestAbilitiesUnion(resolved) {
|
|
2890
|
+
const all = [...resolved.resources];
|
|
2891
|
+
for (const delegate of resolved.additionalDelegates) {
|
|
2892
|
+
for (const perm of delegate.permissions) {
|
|
2893
|
+
all.push(perm);
|
|
2894
|
+
}
|
|
3421
2895
|
}
|
|
3422
|
-
|
|
3423
|
-
|
|
2896
|
+
return resourceCapabilitiesToAbilitiesMap(all);
|
|
2897
|
+
}
|
|
2898
|
+
|
|
2899
|
+
// src/account/AccountService.ts
|
|
2900
|
+
var SERVICE_NAME2 = "account";
|
|
2901
|
+
var ACCOUNT_INDEX_DB = "account";
|
|
2902
|
+
var AccountService = class {
|
|
2903
|
+
constructor(config) {
|
|
2904
|
+
this.config = config;
|
|
2905
|
+
this.applications = {
|
|
2906
|
+
list: async () => {
|
|
2907
|
+
const kvResult = this.accountKV();
|
|
2908
|
+
if (!kvResult.ok) return kvResult;
|
|
2909
|
+
const listed = await kvResult.data.list({ prefix: ACCOUNT_REGISTRY_PATH });
|
|
2910
|
+
if (!listed.ok) return accountErr(listed.error);
|
|
2911
|
+
const applications = [];
|
|
2912
|
+
for (const key of listed.data.keys) {
|
|
2913
|
+
const loaded = await kvResult.data.get(key);
|
|
2914
|
+
if (!loaded.ok) return accountErr(loaded.error);
|
|
2915
|
+
applications.push(applicationFromRecord(key, loaded.data.data));
|
|
2916
|
+
}
|
|
2917
|
+
applications.sort((a, b) => a.appId.localeCompare(b.appId));
|
|
2918
|
+
return ok3(applications);
|
|
2919
|
+
},
|
|
2920
|
+
get: async (appId) => {
|
|
2921
|
+
const kvResult = this.accountKV();
|
|
2922
|
+
if (!kvResult.ok) return kvResult;
|
|
2923
|
+
const key = applicationKey(appId);
|
|
2924
|
+
const loaded = await kvResult.data.get(key);
|
|
2925
|
+
if (!loaded.ok) return accountErr(loaded.error);
|
|
2926
|
+
return ok3(applicationFromRecord(key, loaded.data.data));
|
|
2927
|
+
},
|
|
2928
|
+
register: async (manifest) => {
|
|
2929
|
+
const manifests = Array.isArray(manifest) ? manifest : [manifest];
|
|
2930
|
+
const request = composeManifestRequest(manifests);
|
|
2931
|
+
if (request.registryRecords.length === 0) {
|
|
2932
|
+
return err3(
|
|
2933
|
+
serviceError3(
|
|
2934
|
+
"INVALID_MANIFEST",
|
|
2935
|
+
"Manifest did not produce an account application registry record",
|
|
2936
|
+
SERVICE_NAME2
|
|
2937
|
+
)
|
|
2938
|
+
);
|
|
2939
|
+
}
|
|
2940
|
+
await this.config.ensureAccountSpaceHosted?.();
|
|
2941
|
+
const kvResult = this.accountKV();
|
|
2942
|
+
if (!kvResult.ok) return kvResult;
|
|
2943
|
+
let registered;
|
|
2944
|
+
for (const record of request.registryRecords) {
|
|
2945
|
+
const stored = {
|
|
2946
|
+
app_id: record.app_id,
|
|
2947
|
+
manifests: record.manifests,
|
|
2948
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
2949
|
+
};
|
|
2950
|
+
const written = await kvResult.data.put(record.key, stored);
|
|
2951
|
+
if (!written.ok) return accountErr(written.error);
|
|
2952
|
+
registered = applicationFromRecord(record.key, stored);
|
|
2953
|
+
}
|
|
2954
|
+
return ok3(registered);
|
|
2955
|
+
},
|
|
2956
|
+
remove: async (appId) => {
|
|
2957
|
+
const kvResult = this.accountKV();
|
|
2958
|
+
if (!kvResult.ok) return kvResult;
|
|
2959
|
+
const removed = await kvResult.data.delete(applicationKey(appId));
|
|
2960
|
+
if (!removed.ok) return accountErr(removed.error);
|
|
2961
|
+
return ok3(void 0);
|
|
2962
|
+
}
|
|
2963
|
+
};
|
|
2964
|
+
this.delegations = {
|
|
2965
|
+
list: async (options = {}) => {
|
|
2966
|
+
const spaces = await this.config.getSpaces().list();
|
|
2967
|
+
if (!spaces.ok) return accountErr(spaces.error);
|
|
2968
|
+
const targetSpaces = options.space ? spaces.data.filter((space) => space.id === options.space || space.name === options.space) : spaces.data;
|
|
2969
|
+
const delegations = [];
|
|
2970
|
+
for (const space of targetSpaces) {
|
|
2971
|
+
const scoped = this.config.getSpaces().get(space.id).delegations;
|
|
2972
|
+
if (options.direction !== "received") {
|
|
2973
|
+
const granted = await scoped.list();
|
|
2974
|
+
if (!granted.ok) return accountErr(granted.error);
|
|
2975
|
+
delegations.push(...granted.data.map((d) => mapDelegation(d, space, "granted")));
|
|
2976
|
+
}
|
|
2977
|
+
if (options.direction !== "granted") {
|
|
2978
|
+
const received = await scoped.listReceived();
|
|
2979
|
+
if (!received.ok) return accountErr(received.error);
|
|
2980
|
+
delegations.push(...received.data.map((d) => mapDelegation(d, space, "received")));
|
|
2981
|
+
}
|
|
2982
|
+
}
|
|
2983
|
+
delegations.sort((a, b) => a.spaceId.localeCompare(b.spaceId) || a.cid.localeCompare(b.cid));
|
|
2984
|
+
return ok3(delegations);
|
|
2985
|
+
},
|
|
2986
|
+
revoke: async (options) => {
|
|
2987
|
+
const space = await this.resolveSpace(options.space);
|
|
2988
|
+
if (!space.ok) return space;
|
|
2989
|
+
const revoked = await this.config.getSpaces().get(space.data.id).delegations.revoke(options.cid);
|
|
2990
|
+
if (!revoked.ok) return accountErr(revoked.error);
|
|
2991
|
+
return ok3(void 0);
|
|
2992
|
+
}
|
|
2993
|
+
};
|
|
2994
|
+
this.index = {
|
|
2995
|
+
rebuild: async () => {
|
|
2996
|
+
const dbResult = this.accountDb();
|
|
2997
|
+
if (!dbResult.ok) return dbResult;
|
|
2998
|
+
const applications = await this.applications.list();
|
|
2999
|
+
if (!applications.ok) return applications;
|
|
3000
|
+
const delegations = await this.delegations.list();
|
|
3001
|
+
if (!delegations.ok) return delegations;
|
|
3002
|
+
const syncedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3003
|
+
const statements = [
|
|
3004
|
+
...ACCOUNT_INDEX_SCHEMA.map((sql) => ({ sql })),
|
|
3005
|
+
{ sql: "DELETE FROM applications" },
|
|
3006
|
+
{ sql: "DELETE FROM delegations" },
|
|
3007
|
+
{ sql: "DELETE FROM sync_state" },
|
|
3008
|
+
...applications.data.map((app) => ({
|
|
3009
|
+
sql: "INSERT INTO applications (app_id, name, description, updated_at, manifest_json) VALUES (?, ?, ?, ?, ?)",
|
|
3010
|
+
params: [
|
|
3011
|
+
app.appId,
|
|
3012
|
+
app.name ?? null,
|
|
3013
|
+
app.description ?? null,
|
|
3014
|
+
app.updatedAt ?? syncedAt,
|
|
3015
|
+
JSON.stringify(app.manifests)
|
|
3016
|
+
]
|
|
3017
|
+
})),
|
|
3018
|
+
...delegations.data.map((delegation) => ({
|
|
3019
|
+
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
3020
|
+
params: [
|
|
3021
|
+
delegation.cid,
|
|
3022
|
+
delegation.direction,
|
|
3023
|
+
delegation.spaceId,
|
|
3024
|
+
delegation.spaceName ?? null,
|
|
3025
|
+
delegation.counterpartyDid,
|
|
3026
|
+
delegation.delegateDid,
|
|
3027
|
+
delegation.delegatorDid ?? null,
|
|
3028
|
+
delegation.path,
|
|
3029
|
+
JSON.stringify(delegation.actions),
|
|
3030
|
+
delegation.expiry.toISOString(),
|
|
3031
|
+
delegation.status,
|
|
3032
|
+
delegation.createdAt?.toISOString() ?? null,
|
|
3033
|
+
syncedAt
|
|
3034
|
+
]
|
|
3035
|
+
})),
|
|
3036
|
+
{
|
|
3037
|
+
sql: "INSERT INTO sync_state (source, synced_at, count) VALUES (?, ?, ?)",
|
|
3038
|
+
params: ["applications", syncedAt, applications.data.length]
|
|
3039
|
+
},
|
|
3040
|
+
{
|
|
3041
|
+
sql: "INSERT INTO sync_state (source, synced_at, count) VALUES (?, ?, ?)",
|
|
3042
|
+
params: ["delegations", syncedAt, delegations.data.length]
|
|
3043
|
+
}
|
|
3044
|
+
];
|
|
3045
|
+
const rebuilt = await dbResult.data.batch(statements);
|
|
3046
|
+
if (!rebuilt.ok) return accountErr(rebuilt.error);
|
|
3047
|
+
return ok3({
|
|
3048
|
+
database: ACCOUNT_INDEX_DB,
|
|
3049
|
+
applications: applications.data.length,
|
|
3050
|
+
delegations: delegations.data.length,
|
|
3051
|
+
syncedAt
|
|
3052
|
+
});
|
|
3053
|
+
},
|
|
3054
|
+
applications: {
|
|
3055
|
+
list: async () => {
|
|
3056
|
+
const dbResult = this.accountDb();
|
|
3057
|
+
if (!dbResult.ok) return dbResult;
|
|
3058
|
+
const queried = await dbResult.data.query(
|
|
3059
|
+
"SELECT app_id, name, description, updated_at, manifest_json FROM applications ORDER BY app_id"
|
|
3060
|
+
);
|
|
3061
|
+
if (!queried.ok) return accountErr(queried.error);
|
|
3062
|
+
return ok3(queried.data.rows.map(indexedApplicationFromRow));
|
|
3063
|
+
}
|
|
3064
|
+
},
|
|
3065
|
+
delegations: {
|
|
3066
|
+
list: async (options = {}) => {
|
|
3067
|
+
const dbResult = this.accountDb();
|
|
3068
|
+
if (!dbResult.ok) return dbResult;
|
|
3069
|
+
const where = [];
|
|
3070
|
+
const params = [];
|
|
3071
|
+
if (options.direction && options.direction !== "all") {
|
|
3072
|
+
where.push("direction = ?");
|
|
3073
|
+
params.push(options.direction);
|
|
3074
|
+
}
|
|
3075
|
+
if (options.space) {
|
|
3076
|
+
where.push("(space_id = ? OR space_name = ?)");
|
|
3077
|
+
params.push(options.space, options.space);
|
|
3078
|
+
}
|
|
3079
|
+
const queried = await dbResult.data.query(
|
|
3080
|
+
`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`,
|
|
3081
|
+
params
|
|
3082
|
+
);
|
|
3083
|
+
if (!queried.ok) return accountErr(queried.error);
|
|
3084
|
+
return ok3(queried.data.rows.map(indexedDelegationFromRow));
|
|
3085
|
+
}
|
|
3086
|
+
},
|
|
3087
|
+
query: async (sql, params) => {
|
|
3088
|
+
const dbResult = this.accountDb();
|
|
3089
|
+
if (!dbResult.ok) return dbResult;
|
|
3090
|
+
const queried = await dbResult.data.query(sql, params);
|
|
3091
|
+
if (!queried.ok) return accountErr(queried.error);
|
|
3092
|
+
return ok3(queried.data);
|
|
3093
|
+
}
|
|
3094
|
+
};
|
|
3424
3095
|
}
|
|
3425
|
-
|
|
3426
|
-
|
|
3096
|
+
async status() {
|
|
3097
|
+
const apps = await this.applications.list();
|
|
3098
|
+
if (!apps.ok) return apps;
|
|
3099
|
+
const delegations = await this.delegations.list();
|
|
3100
|
+
if (!delegations.ok) return delegations;
|
|
3101
|
+
return ok3({
|
|
3102
|
+
did: this.config.getDid(),
|
|
3103
|
+
host: this.config.getHost(),
|
|
3104
|
+
primarySpaceId: this.config.getPrimarySpaceId(),
|
|
3105
|
+
accountSpaceId: this.config.getAccountSpaceId(),
|
|
3106
|
+
applications: apps.data.length,
|
|
3107
|
+
grantedDelegations: delegations.data.filter((d) => d.direction === "granted").length,
|
|
3108
|
+
receivedDelegations: delegations.data.filter((d) => d.direction === "received").length
|
|
3109
|
+
});
|
|
3427
3110
|
}
|
|
3428
|
-
|
|
3429
|
-
|
|
3111
|
+
accountKV() {
|
|
3112
|
+
const accountSpaceId = this.config.getAccountSpaceId();
|
|
3113
|
+
if (!accountSpaceId) {
|
|
3114
|
+
return err3(
|
|
3115
|
+
serviceError3(
|
|
3116
|
+
"ACCOUNT_SPACE_UNAVAILABLE",
|
|
3117
|
+
"Account space is unavailable. Sign in with a wallet-backed profile first.",
|
|
3118
|
+
SERVICE_NAME2
|
|
3119
|
+
)
|
|
3120
|
+
);
|
|
3121
|
+
}
|
|
3122
|
+
return ok3(this.config.getSpaces().get(accountSpaceId).kv);
|
|
3430
3123
|
}
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3124
|
+
accountDb() {
|
|
3125
|
+
const db = this.config.getAccountDb?.();
|
|
3126
|
+
if (!db) {
|
|
3127
|
+
return err3(
|
|
3128
|
+
serviceError3(
|
|
3129
|
+
"ACCOUNT_INDEX_UNAVAILABLE",
|
|
3130
|
+
"Account index database is unavailable. Sign in with a wallet-backed profile first.",
|
|
3131
|
+
SERVICE_NAME2
|
|
3132
|
+
)
|
|
3133
|
+
);
|
|
3134
|
+
}
|
|
3135
|
+
return ok3(db);
|
|
3438
3136
|
}
|
|
3439
|
-
|
|
3440
|
-
|
|
3137
|
+
async resolveSpace(space) {
|
|
3138
|
+
const listed = await this.config.getSpaces().list();
|
|
3139
|
+
if (!listed.ok) return accountErr(listed.error);
|
|
3140
|
+
const found = listed.data.find((candidate) => candidate.id === space || candidate.name === space);
|
|
3141
|
+
if (!found) {
|
|
3142
|
+
return err3(
|
|
3143
|
+
serviceError3("SPACE_NOT_FOUND", `No account space found for ${JSON.stringify(space)}`, SERVICE_NAME2)
|
|
3144
|
+
);
|
|
3145
|
+
}
|
|
3146
|
+
return ok3(found);
|
|
3441
3147
|
}
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3148
|
+
};
|
|
3149
|
+
var ACCOUNT_INDEX_SCHEMA = [
|
|
3150
|
+
`CREATE TABLE IF NOT EXISTS applications (
|
|
3151
|
+
app_id TEXT PRIMARY KEY,
|
|
3152
|
+
name TEXT,
|
|
3153
|
+
description TEXT,
|
|
3154
|
+
updated_at TEXT,
|
|
3155
|
+
manifest_json TEXT NOT NULL
|
|
3156
|
+
)`,
|
|
3157
|
+
`CREATE TABLE IF NOT EXISTS delegations (
|
|
3158
|
+
cid TEXT PRIMARY KEY,
|
|
3159
|
+
direction TEXT NOT NULL,
|
|
3160
|
+
space_id TEXT NOT NULL,
|
|
3161
|
+
space_name TEXT,
|
|
3162
|
+
counterparty_did TEXT NOT NULL,
|
|
3163
|
+
delegate_did TEXT NOT NULL,
|
|
3164
|
+
delegator_did TEXT,
|
|
3165
|
+
path TEXT NOT NULL,
|
|
3166
|
+
actions_json TEXT NOT NULL,
|
|
3167
|
+
expiry TEXT NOT NULL,
|
|
3168
|
+
status TEXT NOT NULL,
|
|
3169
|
+
created_at TEXT,
|
|
3170
|
+
updated_at TEXT NOT NULL
|
|
3171
|
+
)`,
|
|
3172
|
+
`CREATE TABLE IF NOT EXISTS sync_state (
|
|
3173
|
+
source TEXT PRIMARY KEY,
|
|
3174
|
+
synced_at TEXT NOT NULL,
|
|
3175
|
+
count INTEGER NOT NULL
|
|
3176
|
+
)`,
|
|
3177
|
+
"CREATE INDEX IF NOT EXISTS idx_delegations_direction ON delegations(direction)",
|
|
3178
|
+
"CREATE INDEX IF NOT EXISTS idx_delegations_space ON delegations(space_id)",
|
|
3179
|
+
"CREATE INDEX IF NOT EXISTS idx_delegations_counterparty ON delegations(counterparty_did)"
|
|
3180
|
+
];
|
|
3181
|
+
function applicationKey(appId) {
|
|
3182
|
+
return `${ACCOUNT_REGISTRY_PATH}${appId}`;
|
|
3183
|
+
}
|
|
3184
|
+
function appIdFromKey(key) {
|
|
3185
|
+
return key.startsWith(ACCOUNT_REGISTRY_PATH) ? key.slice(ACCOUNT_REGISTRY_PATH.length) : key;
|
|
3186
|
+
}
|
|
3187
|
+
function applicationFromRecord(key, record) {
|
|
3188
|
+
const manifests = Array.isArray(record.manifests) ? record.manifests : [];
|
|
3189
|
+
const first = manifests[0];
|
|
3190
|
+
return {
|
|
3191
|
+
appId: record.app_id ?? record.appId ?? first?.app_id ?? appIdFromKey(key),
|
|
3192
|
+
manifests,
|
|
3193
|
+
updatedAt: record.updated_at ?? record.updatedAt,
|
|
3194
|
+
name: first?.name,
|
|
3195
|
+
description: first?.description
|
|
3196
|
+
};
|
|
3197
|
+
}
|
|
3198
|
+
function indexedApplicationFromRow(row) {
|
|
3199
|
+
const [appId, name, description, updatedAt, manifestJson] = row;
|
|
3200
|
+
return {
|
|
3201
|
+
appId,
|
|
3202
|
+
name: name ?? void 0,
|
|
3203
|
+
description: description ?? void 0,
|
|
3204
|
+
updatedAt: updatedAt ?? void 0,
|
|
3205
|
+
manifests: JSON.parse(manifestJson)
|
|
3206
|
+
};
|
|
3207
|
+
}
|
|
3208
|
+
function indexedDelegationFromRow(row) {
|
|
3209
|
+
const [
|
|
3210
|
+
cid,
|
|
3211
|
+
direction,
|
|
3212
|
+
spaceId,
|
|
3213
|
+
spaceName,
|
|
3214
|
+
counterpartyDid,
|
|
3215
|
+
delegateDid,
|
|
3216
|
+
delegatorDid,
|
|
3217
|
+
path,
|
|
3218
|
+
actionsJson,
|
|
3219
|
+
expiry,
|
|
3220
|
+
status,
|
|
3221
|
+
createdAt
|
|
3222
|
+
] = row;
|
|
3223
|
+
return {
|
|
3224
|
+
cid,
|
|
3225
|
+
direction,
|
|
3226
|
+
spaceId,
|
|
3227
|
+
spaceName: spaceName ?? void 0,
|
|
3228
|
+
counterpartyDid,
|
|
3229
|
+
delegateDid,
|
|
3230
|
+
delegatorDid: delegatorDid ?? void 0,
|
|
3231
|
+
path,
|
|
3232
|
+
actions: JSON.parse(actionsJson),
|
|
3233
|
+
expiry: new Date(expiry),
|
|
3234
|
+
status,
|
|
3235
|
+
createdAt: createdAt ? new Date(createdAt) : void 0
|
|
3236
|
+
};
|
|
3237
|
+
}
|
|
3238
|
+
function mapDelegation(delegation, space, direction) {
|
|
3239
|
+
return {
|
|
3240
|
+
cid: delegation.cid,
|
|
3241
|
+
direction,
|
|
3242
|
+
spaceId: delegation.spaceId || space.id,
|
|
3243
|
+
spaceName: space.name,
|
|
3244
|
+
counterpartyDid: direction === "granted" ? delegation.delegateDID : delegation.delegatorDID ?? delegation.delegateDID,
|
|
3245
|
+
delegateDid: delegation.delegateDID,
|
|
3246
|
+
delegatorDid: delegation.delegatorDID,
|
|
3247
|
+
path: delegation.path,
|
|
3248
|
+
actions: delegation.actions,
|
|
3249
|
+
expiry: delegation.expiry,
|
|
3250
|
+
status: delegation.isRevoked ? "revoked" : delegation.expiry.getTime() <= Date.now() ? "expired" : "active",
|
|
3251
|
+
createdAt: delegation.createdAt
|
|
3252
|
+
};
|
|
3253
|
+
}
|
|
3254
|
+
function accountErr(error) {
|
|
3255
|
+
return err3(serviceError3(error.code, error.message, SERVICE_NAME2, { cause: error.cause, meta: error.meta }));
|
|
3256
|
+
}
|
|
3257
|
+
|
|
3258
|
+
// src/index.ts
|
|
3259
|
+
import {
|
|
3260
|
+
ServiceContext as ServiceContext2,
|
|
3261
|
+
KVService as KVService2,
|
|
3262
|
+
PrefixedKVService,
|
|
3263
|
+
ok as ok5,
|
|
3264
|
+
err as err5,
|
|
3265
|
+
serviceError as serviceError5,
|
|
3266
|
+
ErrorCodes as ErrorCodes2,
|
|
3267
|
+
defaultRetryPolicy as defaultRetryPolicy2,
|
|
3268
|
+
SQLService as SQLService2,
|
|
3269
|
+
DatabaseHandle,
|
|
3270
|
+
SQLAction,
|
|
3271
|
+
DuckDbService as DuckDbService2,
|
|
3272
|
+
DuckDbDatabaseHandle,
|
|
3273
|
+
DuckDbAction,
|
|
3274
|
+
HooksService as HooksService2,
|
|
3275
|
+
DataVaultService,
|
|
3276
|
+
VaultHeaders,
|
|
3277
|
+
VaultPublicSpaceKVActions,
|
|
3278
|
+
createVaultCrypto,
|
|
3279
|
+
SecretsService,
|
|
3280
|
+
SECRET_NAME_RE as SECRET_NAME_RE2,
|
|
3281
|
+
canonicalizeSecretScope,
|
|
3282
|
+
resolveSecretListPrefix,
|
|
3283
|
+
resolveSecretPath as resolveSecretPath2,
|
|
3284
|
+
EncryptionService,
|
|
3285
|
+
parseNetworkId as parseNetworkId2,
|
|
3286
|
+
buildNetworkId as buildNetworkId2,
|
|
3287
|
+
isNetworkId,
|
|
3288
|
+
networkDiscoveryKey,
|
|
3289
|
+
NetworkIdError,
|
|
3290
|
+
ENCRYPTION_NETWORK_URN_PREFIX,
|
|
3291
|
+
NETWORK_NAME_PATTERN,
|
|
3292
|
+
canonicalizeEncryptionJson,
|
|
3293
|
+
canonicalHashHex,
|
|
3294
|
+
hexEncode,
|
|
3295
|
+
hexDecode,
|
|
3296
|
+
base64Encode,
|
|
3297
|
+
base64Decode,
|
|
3298
|
+
utf8Encode,
|
|
3299
|
+
utf8Decode,
|
|
3300
|
+
encryptToNetwork,
|
|
3301
|
+
decryptEnvelopeWithKey,
|
|
3302
|
+
validateEnvelope,
|
|
3303
|
+
generateRandomReceiverKey,
|
|
3304
|
+
deriveSignedReceiverKey,
|
|
3305
|
+
buildCanonicalDecryptRequest,
|
|
3306
|
+
buildDecryptFacts,
|
|
3307
|
+
buildDecryptAttenuation,
|
|
3308
|
+
buildDecryptInvocation,
|
|
3309
|
+
checkDecryptInvocationInput,
|
|
3310
|
+
verifyDecryptResponse,
|
|
3311
|
+
canonicalSignedResponse,
|
|
3312
|
+
openWrappedKey,
|
|
3313
|
+
discoverNetwork,
|
|
3314
|
+
ensureNetworkUsableForDecrypt,
|
|
3315
|
+
DEFAULT_ENCRYPTION_ALG,
|
|
3316
|
+
ENVELOPE_VERSION,
|
|
3317
|
+
DEFAULT_KEY_VERSION,
|
|
3318
|
+
DECRYPT_FACT_TYPE,
|
|
3319
|
+
DECRYPT_RESULT_TYPE,
|
|
3320
|
+
DECRYPT_ACTION,
|
|
3321
|
+
ENCRYPTION_SERVICE,
|
|
3322
|
+
ENCRYPTION_SERVICE_SHORT,
|
|
3323
|
+
encryptionError
|
|
3324
|
+
} from "@tinycloud/sdk-services";
|
|
3325
|
+
|
|
3326
|
+
// src/space.ts
|
|
3327
|
+
async function fetchPeerId(host, spaceId) {
|
|
3328
|
+
const res = await fetch(
|
|
3329
|
+
`${host}/peer/generate/${encodeURIComponent(spaceId)}`
|
|
3330
|
+
);
|
|
3331
|
+
if (!res.ok) {
|
|
3332
|
+
const error = await res.text().catch(() => res.statusText);
|
|
3333
|
+
throw new Error(`Failed to get peer ID: ${res.status} - ${error}`);
|
|
3334
|
+
}
|
|
3335
|
+
return res.text();
|
|
3336
|
+
}
|
|
3337
|
+
async function submitHostDelegation(host, headers) {
|
|
3338
|
+
const res = await fetch(`${host}/delegate`, {
|
|
3339
|
+
method: "POST",
|
|
3340
|
+
headers
|
|
3341
|
+
});
|
|
3342
|
+
return {
|
|
3343
|
+
success: res.ok,
|
|
3344
|
+
status: res.status,
|
|
3345
|
+
error: res.ok ? void 0 : await res.text().catch(() => res.statusText)
|
|
3346
|
+
};
|
|
3347
|
+
}
|
|
3348
|
+
async function activateSessionWithHost(host, delegationHeader) {
|
|
3349
|
+
const res = await fetch(`${host}/delegate`, {
|
|
3350
|
+
method: "POST",
|
|
3351
|
+
headers: delegationHeader
|
|
3352
|
+
});
|
|
3353
|
+
if (res.ok) {
|
|
3354
|
+
try {
|
|
3355
|
+
const body = await res.json();
|
|
3356
|
+
return {
|
|
3357
|
+
success: true,
|
|
3358
|
+
status: res.status,
|
|
3359
|
+
activated: body.activated ?? [],
|
|
3360
|
+
skipped: body.skipped ?? []
|
|
3361
|
+
};
|
|
3362
|
+
} catch {
|
|
3363
|
+
return {
|
|
3364
|
+
success: true,
|
|
3365
|
+
status: res.status,
|
|
3366
|
+
activated: [],
|
|
3367
|
+
skipped: []
|
|
3368
|
+
};
|
|
3369
|
+
}
|
|
3446
3370
|
}
|
|
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
3371
|
return {
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
actions: [...entry.actions],
|
|
3459
|
-
...entry.expiryMs !== void 0 ? { expiryMs: entry.expiryMs } : {},
|
|
3460
|
-
...entry.description !== void 0 ? { description: entry.description } : {}
|
|
3372
|
+
success: false,
|
|
3373
|
+
status: res.status,
|
|
3374
|
+
error: await res.text().catch(() => res.statusText)
|
|
3461
3375
|
};
|
|
3462
3376
|
}
|
|
3463
|
-
|
|
3377
|
+
|
|
3378
|
+
// src/delegations/DelegationManager.ts
|
|
3379
|
+
var DelegationAction = {
|
|
3380
|
+
CREATE: "tinycloud.delegation/create",
|
|
3381
|
+
REVOKE: "tinycloud.delegation/revoke",
|
|
3382
|
+
LIST: "tinycloud.delegation/list",
|
|
3383
|
+
GET: "tinycloud.delegation/get",
|
|
3384
|
+
CHECK: "tinycloud.delegation/check"
|
|
3385
|
+
};
|
|
3386
|
+
function createError(code, message, cause, meta) {
|
|
3464
3387
|
return {
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
...entry.expiry !== void 0 ? { expiry: entry.expiry } : {},
|
|
3471
|
-
...entry.description !== void 0 ? { description: entry.description } : {}
|
|
3388
|
+
code,
|
|
3389
|
+
message,
|
|
3390
|
+
service: "delegation",
|
|
3391
|
+
cause,
|
|
3392
|
+
meta
|
|
3472
3393
|
};
|
|
3473
3394
|
}
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3395
|
+
var DelegationManager = class {
|
|
3396
|
+
/**
|
|
3397
|
+
* Creates a new DelegationManager instance.
|
|
3398
|
+
*
|
|
3399
|
+
* @param config - Configuration including hosts, session, and invoke function
|
|
3400
|
+
*/
|
|
3401
|
+
constructor(config) {
|
|
3402
|
+
this.hosts = config.hosts;
|
|
3403
|
+
this.session = config.session;
|
|
3404
|
+
this.invoke = config.invoke;
|
|
3405
|
+
this.fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);
|
|
3406
|
+
}
|
|
3407
|
+
/**
|
|
3408
|
+
* Updates the session (e.g., after re-authentication).
|
|
3409
|
+
*
|
|
3410
|
+
* @param session - New session to use for operations
|
|
3411
|
+
*/
|
|
3412
|
+
updateSession(session) {
|
|
3413
|
+
this.session = session;
|
|
3414
|
+
}
|
|
3415
|
+
/**
|
|
3416
|
+
* Gets the primary host URL.
|
|
3417
|
+
*/
|
|
3418
|
+
get host() {
|
|
3419
|
+
return this.hosts[0];
|
|
3420
|
+
}
|
|
3421
|
+
/**
|
|
3422
|
+
* Executes an invoke operation against the delegation API.
|
|
3423
|
+
*/
|
|
3424
|
+
async invokeOperation(path, action, body) {
|
|
3425
|
+
const headers = this.invoke(this.session, "delegation", path, action);
|
|
3426
|
+
return this.fetchFn(`${this.host}/invoke`, {
|
|
3427
|
+
method: "POST",
|
|
3428
|
+
headers,
|
|
3429
|
+
body
|
|
3430
|
+
});
|
|
3431
|
+
}
|
|
3432
|
+
/**
|
|
3433
|
+
* Creates a new delegation.
|
|
3434
|
+
*
|
|
3435
|
+
* Delegates specific permissions to another DID for a given path.
|
|
3436
|
+
* The delegatee can then use these permissions to access resources
|
|
3437
|
+
* within the specified scope.
|
|
3438
|
+
*
|
|
3439
|
+
* @param params - Parameters for the delegation
|
|
3440
|
+
* @returns Result containing the created Delegation or an error
|
|
3441
|
+
*
|
|
3442
|
+
* @example
|
|
3443
|
+
* ```typescript
|
|
3444
|
+
* const result = await manager.create({
|
|
3445
|
+
* delegateDID: bob.did,
|
|
3446
|
+
* path: "documents/shared/",
|
|
3447
|
+
* actions: ["tinycloud.kv/get", "tinycloud.kv/put"],
|
|
3448
|
+
* expiry: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days
|
|
3449
|
+
* });
|
|
3450
|
+
* ```
|
|
3451
|
+
*/
|
|
3452
|
+
async create(params) {
|
|
3453
|
+
if (!params.delegateDID) {
|
|
3454
|
+
return {
|
|
3455
|
+
ok: false,
|
|
3456
|
+
error: createError(
|
|
3457
|
+
DelegationErrorCodes.INVALID_INPUT,
|
|
3458
|
+
"delegateDID is required"
|
|
3459
|
+
)
|
|
3460
|
+
};
|
|
3482
3461
|
}
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3462
|
+
if (!params.path) {
|
|
3463
|
+
return {
|
|
3464
|
+
ok: false,
|
|
3465
|
+
error: createError(
|
|
3466
|
+
DelegationErrorCodes.INVALID_INPUT,
|
|
3467
|
+
"path is required"
|
|
3468
|
+
)
|
|
3469
|
+
};
|
|
3470
|
+
}
|
|
3471
|
+
if (!params.actions || params.actions.length === 0) {
|
|
3472
|
+
return {
|
|
3473
|
+
ok: false,
|
|
3474
|
+
error: createError(
|
|
3475
|
+
DelegationErrorCodes.INVALID_INPUT,
|
|
3476
|
+
"at least one action is required"
|
|
3477
|
+
)
|
|
3478
|
+
};
|
|
3479
|
+
}
|
|
3480
|
+
try {
|
|
3481
|
+
const body = JSON.stringify({
|
|
3482
|
+
delegateDID: params.delegateDID,
|
|
3483
|
+
path: params.path,
|
|
3484
|
+
actions: params.actions,
|
|
3485
|
+
expiry: params.expiry?.toISOString(),
|
|
3486
|
+
disableSubDelegation: params.disableSubDelegation ?? false,
|
|
3487
|
+
statement: params.statement
|
|
3488
|
+
});
|
|
3489
|
+
const response = await this.invokeOperation(
|
|
3490
|
+
params.path,
|
|
3491
|
+
DelegationAction.CREATE,
|
|
3492
|
+
body
|
|
3493
|
+
);
|
|
3494
|
+
if (!response.ok) {
|
|
3495
|
+
const errorText = await response.text();
|
|
3496
|
+
return {
|
|
3497
|
+
ok: false,
|
|
3498
|
+
error: createError(
|
|
3499
|
+
DelegationErrorCodes.CREATION_FAILED,
|
|
3500
|
+
`Failed to create delegation: ${response.status} - ${errorText}`,
|
|
3501
|
+
void 0,
|
|
3502
|
+
{ status: response.status, path: params.path }
|
|
3503
|
+
)
|
|
3504
|
+
};
|
|
3505
|
+
}
|
|
3506
|
+
const apiResponse = await response.json();
|
|
3507
|
+
const delegation = {
|
|
3508
|
+
cid: apiResponse.cid ?? "",
|
|
3509
|
+
delegateDID: params.delegateDID,
|
|
3510
|
+
spaceId: this.session.spaceId,
|
|
3511
|
+
path: params.path,
|
|
3512
|
+
actions: params.actions,
|
|
3513
|
+
expiry: params.expiry ?? new Date(Date.now() + EXPIRY.SHARE_MS),
|
|
3514
|
+
isRevoked: false,
|
|
3515
|
+
allowSubDelegation: !(params.disableSubDelegation ?? false),
|
|
3516
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
3517
|
+
};
|
|
3518
|
+
return { ok: true, data: delegation };
|
|
3519
|
+
} catch (error) {
|
|
3520
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
3521
|
+
return {
|
|
3522
|
+
ok: false,
|
|
3523
|
+
error: createError(
|
|
3524
|
+
DelegationErrorCodes.ABORTED,
|
|
3525
|
+
"Request aborted",
|
|
3526
|
+
error
|
|
3527
|
+
)
|
|
3528
|
+
};
|
|
3529
|
+
}
|
|
3530
|
+
return {
|
|
3531
|
+
ok: false,
|
|
3532
|
+
error: createError(
|
|
3533
|
+
DelegationErrorCodes.NETWORK_ERROR,
|
|
3534
|
+
`Network error during delegation creation: ${String(error)}`,
|
|
3535
|
+
error instanceof Error ? error : void 0
|
|
3536
|
+
)
|
|
3537
|
+
};
|
|
3538
|
+
}
|
|
3539
|
+
}
|
|
3540
|
+
/**
|
|
3541
|
+
* Revokes an existing delegation.
|
|
3542
|
+
*
|
|
3543
|
+
* Once revoked, the delegation can no longer be used to access resources.
|
|
3544
|
+
* This also invalidates any sub-delegations derived from this delegation.
|
|
3545
|
+
*
|
|
3546
|
+
* @param cid - The CID of the delegation to revoke
|
|
3547
|
+
* @returns Result indicating success or an error
|
|
3548
|
+
*
|
|
3549
|
+
* @example
|
|
3550
|
+
* ```typescript
|
|
3551
|
+
* const result = await manager.revoke("bafy...");
|
|
3552
|
+
* if (result.ok) {
|
|
3553
|
+
* console.log("Delegation revoked successfully");
|
|
3554
|
+
* }
|
|
3555
|
+
* ```
|
|
3556
|
+
*/
|
|
3557
|
+
async revoke(cid) {
|
|
3558
|
+
if (!cid) {
|
|
3559
|
+
return {
|
|
3560
|
+
ok: false,
|
|
3561
|
+
error: createError(
|
|
3562
|
+
DelegationErrorCodes.INVALID_INPUT,
|
|
3563
|
+
"cid is required"
|
|
3564
|
+
)
|
|
3565
|
+
};
|
|
3566
|
+
}
|
|
3567
|
+
try {
|
|
3568
|
+
const body = JSON.stringify({ cid });
|
|
3569
|
+
const response = await this.invokeOperation(
|
|
3570
|
+
cid,
|
|
3571
|
+
DelegationAction.REVOKE,
|
|
3572
|
+
body
|
|
3573
|
+
);
|
|
3574
|
+
if (!response.ok) {
|
|
3575
|
+
const errorText = await response.text();
|
|
3576
|
+
if (response.status === 404) {
|
|
3577
|
+
return {
|
|
3578
|
+
ok: false,
|
|
3579
|
+
error: createError(
|
|
3580
|
+
DelegationErrorCodes.NOT_FOUND,
|
|
3581
|
+
`Delegation not found: ${cid}`
|
|
3582
|
+
)
|
|
3583
|
+
};
|
|
3584
|
+
}
|
|
3585
|
+
return {
|
|
3586
|
+
ok: false,
|
|
3587
|
+
error: createError(
|
|
3588
|
+
DelegationErrorCodes.REVOCATION_FAILED,
|
|
3589
|
+
`Failed to revoke delegation: ${response.status} - ${errorText}`,
|
|
3590
|
+
void 0,
|
|
3591
|
+
{ status: response.status, cid }
|
|
3592
|
+
)
|
|
3593
|
+
};
|
|
3488
3594
|
}
|
|
3595
|
+
return { ok: true, data: void 0 };
|
|
3596
|
+
} catch (error) {
|
|
3597
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
3598
|
+
return {
|
|
3599
|
+
ok: false,
|
|
3600
|
+
error: createError(
|
|
3601
|
+
DelegationErrorCodes.ABORTED,
|
|
3602
|
+
"Request aborted",
|
|
3603
|
+
error
|
|
3604
|
+
)
|
|
3605
|
+
};
|
|
3606
|
+
}
|
|
3607
|
+
return {
|
|
3608
|
+
ok: false,
|
|
3609
|
+
error: createError(
|
|
3610
|
+
DelegationErrorCodes.NETWORK_ERROR,
|
|
3611
|
+
`Network error during delegation revocation: ${String(error)}`,
|
|
3612
|
+
error instanceof Error ? error : void 0
|
|
3613
|
+
)
|
|
3614
|
+
};
|
|
3489
3615
|
}
|
|
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
3616
|
}
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3617
|
+
/**
|
|
3618
|
+
* Lists all delegations for the current session's space.
|
|
3619
|
+
*
|
|
3620
|
+
* Returns both delegations created by the current user (as delegator)
|
|
3621
|
+
* and delegations granted to the current user (as delegatee).
|
|
3622
|
+
*
|
|
3623
|
+
* @returns Result containing an array of Delegations or an error
|
|
3624
|
+
*
|
|
3625
|
+
* @example
|
|
3626
|
+
* ```typescript
|
|
3627
|
+
* const result = await manager.list();
|
|
3628
|
+
* if (result.ok) {
|
|
3629
|
+
* for (const delegation of result.data) {
|
|
3630
|
+
* console.log(`${delegation.cid}: ${delegation.path} -> ${delegation.delegateDID}`);
|
|
3631
|
+
* }
|
|
3632
|
+
* }
|
|
3633
|
+
* ```
|
|
3634
|
+
*/
|
|
3635
|
+
async list() {
|
|
3636
|
+
try {
|
|
3637
|
+
const response = await this.invokeOperation("", DelegationAction.LIST);
|
|
3638
|
+
if (!response.ok) {
|
|
3639
|
+
const errorText = await response.text();
|
|
3640
|
+
return {
|
|
3641
|
+
ok: false,
|
|
3642
|
+
error: createError(
|
|
3643
|
+
DelegationErrorCodes.NETWORK_ERROR,
|
|
3644
|
+
`Failed to list delegations: ${response.status} - ${errorText}`,
|
|
3645
|
+
void 0,
|
|
3646
|
+
{ status: response.status }
|
|
3647
|
+
)
|
|
3648
|
+
};
|
|
3649
|
+
}
|
|
3650
|
+
const data = await response.json();
|
|
3651
|
+
const delegations = data.map((item) => ({
|
|
3652
|
+
cid: item.cid,
|
|
3653
|
+
delegateDID: item.delegateDID,
|
|
3654
|
+
delegatorDID: item.delegatorDID,
|
|
3655
|
+
spaceId: item.spaceId,
|
|
3656
|
+
path: item.path,
|
|
3657
|
+
actions: item.actions,
|
|
3658
|
+
expiry: new Date(item.expiry),
|
|
3659
|
+
isRevoked: item.isRevoked,
|
|
3660
|
+
createdAt: item.createdAt ? new Date(item.createdAt) : void 0,
|
|
3661
|
+
parentCid: item.parentCid,
|
|
3662
|
+
allowSubDelegation: item.allowSubDelegation
|
|
3663
|
+
}));
|
|
3664
|
+
return { ok: true, data: delegations };
|
|
3665
|
+
} catch (error) {
|
|
3666
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
3667
|
+
return {
|
|
3668
|
+
ok: false,
|
|
3669
|
+
error: createError(
|
|
3670
|
+
DelegationErrorCodes.ABORTED,
|
|
3671
|
+
"Request aborted",
|
|
3672
|
+
error
|
|
3673
|
+
)
|
|
3674
|
+
};
|
|
3675
|
+
}
|
|
3676
|
+
return {
|
|
3677
|
+
ok: false,
|
|
3678
|
+
error: createError(
|
|
3679
|
+
DelegationErrorCodes.NETWORK_ERROR,
|
|
3680
|
+
`Network error during delegation list: ${String(error)}`,
|
|
3681
|
+
error instanceof Error ? error : void 0
|
|
3682
|
+
)
|
|
3683
|
+
};
|
|
3551
3684
|
}
|
|
3552
3685
|
}
|
|
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
|
-
|
|
3686
|
+
/**
|
|
3687
|
+
* Gets the full delegation chain for a given delegation.
|
|
3688
|
+
*
|
|
3689
|
+
* Returns the chain of delegations from the root (original delegator)
|
|
3690
|
+
* to the specified delegation, including all intermediate sub-delegations.
|
|
3691
|
+
*
|
|
3692
|
+
* @param cid - The CID of the delegation to get the chain for
|
|
3693
|
+
* @returns Result containing the DelegationChain or an error
|
|
3694
|
+
*
|
|
3695
|
+
* @example
|
|
3696
|
+
* ```typescript
|
|
3697
|
+
* const result = await manager.getChain("bafy...");
|
|
3698
|
+
* if (result.ok) {
|
|
3699
|
+
* console.log("Chain length:", result.data.length);
|
|
3700
|
+
* for (const delegation of result.data) {
|
|
3701
|
+
* console.log(`- ${delegation.delegatorDID} -> ${delegation.delegateDID}`);
|
|
3702
|
+
* }
|
|
3703
|
+
* }
|
|
3704
|
+
* ```
|
|
3705
|
+
*/
|
|
3706
|
+
async getChain(cid) {
|
|
3707
|
+
if (!cid) {
|
|
3708
|
+
return {
|
|
3709
|
+
ok: false,
|
|
3710
|
+
error: createError(
|
|
3711
|
+
DelegationErrorCodes.INVALID_INPUT,
|
|
3712
|
+
"cid is required"
|
|
3713
|
+
)
|
|
3714
|
+
};
|
|
3715
|
+
}
|
|
3716
|
+
try {
|
|
3717
|
+
const body = JSON.stringify({ cid, includeChain: true });
|
|
3718
|
+
const response = await this.invokeOperation(
|
|
3719
|
+
cid,
|
|
3720
|
+
DelegationAction.GET,
|
|
3721
|
+
body
|
|
3577
3722
|
);
|
|
3723
|
+
if (!response.ok) {
|
|
3724
|
+
const errorText = await response.text();
|
|
3725
|
+
if (response.status === 404) {
|
|
3726
|
+
return {
|
|
3727
|
+
ok: false,
|
|
3728
|
+
error: createError(
|
|
3729
|
+
DelegationErrorCodes.NOT_FOUND,
|
|
3730
|
+
`Delegation not found: ${cid}`
|
|
3731
|
+
)
|
|
3732
|
+
};
|
|
3733
|
+
}
|
|
3734
|
+
return {
|
|
3735
|
+
ok: false,
|
|
3736
|
+
error: createError(
|
|
3737
|
+
DelegationErrorCodes.NETWORK_ERROR,
|
|
3738
|
+
`Failed to get delegation chain: ${response.status} - ${errorText}`,
|
|
3739
|
+
void 0,
|
|
3740
|
+
{ status: response.status, cid }
|
|
3741
|
+
)
|
|
3742
|
+
};
|
|
3743
|
+
}
|
|
3744
|
+
const data = await response.json();
|
|
3745
|
+
const chain = data.chain.map((item) => ({
|
|
3746
|
+
cid: item.cid,
|
|
3747
|
+
delegateDID: item.delegateDID,
|
|
3748
|
+
delegatorDID: item.delegatorDID,
|
|
3749
|
+
spaceId: item.spaceId,
|
|
3750
|
+
path: item.path,
|
|
3751
|
+
actions: item.actions,
|
|
3752
|
+
expiry: new Date(item.expiry),
|
|
3753
|
+
isRevoked: item.isRevoked,
|
|
3754
|
+
createdAt: item.createdAt ? new Date(item.createdAt) : void 0,
|
|
3755
|
+
parentCid: item.parentCid,
|
|
3756
|
+
allowSubDelegation: item.allowSubDelegation
|
|
3757
|
+
}));
|
|
3758
|
+
return { ok: true, data: chain };
|
|
3759
|
+
} catch (error) {
|
|
3760
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
3761
|
+
return {
|
|
3762
|
+
ok: false,
|
|
3763
|
+
error: createError(
|
|
3764
|
+
DelegationErrorCodes.ABORTED,
|
|
3765
|
+
"Request aborted",
|
|
3766
|
+
error
|
|
3767
|
+
)
|
|
3768
|
+
};
|
|
3769
|
+
}
|
|
3770
|
+
return {
|
|
3771
|
+
ok: false,
|
|
3772
|
+
error: createError(
|
|
3773
|
+
DelegationErrorCodes.NETWORK_ERROR,
|
|
3774
|
+
`Network error during chain retrieval: ${String(error)}`,
|
|
3775
|
+
error instanceof Error ? error : void 0
|
|
3776
|
+
)
|
|
3777
|
+
};
|
|
3578
3778
|
}
|
|
3579
|
-
|
|
3580
|
-
|
|
3779
|
+
}
|
|
3780
|
+
/**
|
|
3781
|
+
* Checks if the current session has permission for a given path and action.
|
|
3782
|
+
*
|
|
3783
|
+
* This can be used to verify permissions before attempting an operation,
|
|
3784
|
+
* or to implement custom access control logic.
|
|
3785
|
+
*
|
|
3786
|
+
* @param path - The resource path to check
|
|
3787
|
+
* @param action - The action to check (e.g., "tinycloud.kv/get")
|
|
3788
|
+
* @returns Result containing a boolean indicating permission or an error
|
|
3789
|
+
*
|
|
3790
|
+
* @example
|
|
3791
|
+
* ```typescript
|
|
3792
|
+
* const result = await manager.checkPermission("documents/private/", "tinycloud.kv/put");
|
|
3793
|
+
* if (result.ok && result.data) {
|
|
3794
|
+
* console.log("Permission granted");
|
|
3795
|
+
* } else {
|
|
3796
|
+
* console.log("Permission denied");
|
|
3797
|
+
* }
|
|
3798
|
+
* ```
|
|
3799
|
+
*/
|
|
3800
|
+
async checkPermission(path, action) {
|
|
3801
|
+
if (!path) {
|
|
3802
|
+
return {
|
|
3803
|
+
ok: false,
|
|
3804
|
+
error: createError(
|
|
3805
|
+
DelegationErrorCodes.INVALID_INPUT,
|
|
3806
|
+
"path is required"
|
|
3807
|
+
)
|
|
3808
|
+
};
|
|
3581
3809
|
}
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3810
|
+
if (!action) {
|
|
3811
|
+
return {
|
|
3812
|
+
ok: false,
|
|
3813
|
+
error: createError(
|
|
3814
|
+
DelegationErrorCodes.INVALID_INPUT,
|
|
3815
|
+
"action is required"
|
|
3816
|
+
)
|
|
3817
|
+
};
|
|
3818
|
+
}
|
|
3819
|
+
try {
|
|
3820
|
+
const body = JSON.stringify({ path, action });
|
|
3821
|
+
const response = await this.invokeOperation(
|
|
3822
|
+
path,
|
|
3823
|
+
DelegationAction.CHECK,
|
|
3824
|
+
body
|
|
3825
|
+
);
|
|
3826
|
+
if (!response.ok) {
|
|
3827
|
+
if (response.status === 403) {
|
|
3828
|
+
return { ok: true, data: false };
|
|
3592
3829
|
}
|
|
3830
|
+
const errorText = await response.text();
|
|
3831
|
+
return {
|
|
3832
|
+
ok: false,
|
|
3833
|
+
error: createError(
|
|
3834
|
+
DelegationErrorCodes.NETWORK_ERROR,
|
|
3835
|
+
`Failed to check permission: ${response.status} - ${errorText}`,
|
|
3836
|
+
void 0,
|
|
3837
|
+
{ status: response.status, path, action }
|
|
3838
|
+
)
|
|
3839
|
+
};
|
|
3593
3840
|
}
|
|
3841
|
+
const data = await response.json();
|
|
3842
|
+
return { ok: true, data: data.allowed };
|
|
3843
|
+
} catch (error) {
|
|
3844
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
3845
|
+
return {
|
|
3846
|
+
ok: false,
|
|
3847
|
+
error: createError(
|
|
3848
|
+
DelegationErrorCodes.ABORTED,
|
|
3849
|
+
"Request aborted",
|
|
3850
|
+
error
|
|
3851
|
+
)
|
|
3852
|
+
};
|
|
3853
|
+
}
|
|
3854
|
+
return {
|
|
3855
|
+
ok: false,
|
|
3856
|
+
error: createError(
|
|
3857
|
+
DelegationErrorCodes.NETWORK_ERROR,
|
|
3858
|
+
`Network error during permission check: ${String(error)}`,
|
|
3859
|
+
error instanceof Error ? error : void 0
|
|
3860
|
+
)
|
|
3861
|
+
};
|
|
3594
3862
|
}
|
|
3595
3863
|
}
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3864
|
+
};
|
|
3865
|
+
|
|
3866
|
+
// src/delegations/SharingService.schema.ts
|
|
3867
|
+
import { z as z5 } from "zod";
|
|
3868
|
+
var EncodedShareDataSchema = z5.object({
|
|
3869
|
+
/** Private key in JWK format (must include d parameter) */
|
|
3870
|
+
key: JWKSchema.refine(
|
|
3871
|
+
(jwk) => typeof jwk.d === "string" && jwk.d.length > 0,
|
|
3872
|
+
{ message: "JWK must include private key (d parameter)" }
|
|
3873
|
+
),
|
|
3874
|
+
/** DID of the key */
|
|
3875
|
+
keyDid: z5.string().min(1, "keyDid is required"),
|
|
3876
|
+
/** The delegation granting access */
|
|
3877
|
+
delegation: DelegationSchema,
|
|
3878
|
+
/** Resource path this link grants access to */
|
|
3879
|
+
path: z5.string().min(1, "path is required"),
|
|
3880
|
+
/** TinyCloud host URL */
|
|
3881
|
+
host: z5.string().url("host must be a valid URL"),
|
|
3882
|
+
/** Space ID */
|
|
3883
|
+
spaceId: z5.string().min(1, "spaceId is required"),
|
|
3884
|
+
/** Schema version (must be 1) */
|
|
3885
|
+
version: z5.literal(1)
|
|
3886
|
+
});
|
|
3887
|
+
var ReceiveOptionsSchema = z5.object({
|
|
3888
|
+
/**
|
|
3889
|
+
* Whether to automatically create a sub-delegation to the current session key.
|
|
3890
|
+
* Default: true
|
|
3891
|
+
*/
|
|
3892
|
+
autoSubdelegate: z5.boolean().optional(),
|
|
3893
|
+
/**
|
|
3894
|
+
* Whether to use the current session key for operations (requires autoSubdelegate).
|
|
3895
|
+
* Default: true
|
|
3896
|
+
*/
|
|
3897
|
+
useSessionKey: z5.boolean().optional(),
|
|
3898
|
+
/**
|
|
3899
|
+
* Ingestion options passed to CapabilityKeyRegistry.
|
|
3900
|
+
*/
|
|
3901
|
+
ingestOptions: IngestOptionsSchema.optional()
|
|
3902
|
+
});
|
|
3903
|
+
var SharingServiceConfigSchema = z5.object({
|
|
3904
|
+
/** TinyCloud host URLs */
|
|
3905
|
+
hosts: z5.array(z5.string().url()).min(1, "At least one host URL is required"),
|
|
3906
|
+
/**
|
|
3907
|
+
* Active session for authentication.
|
|
3908
|
+
* Required for generate(), optional for receive().
|
|
3909
|
+
*/
|
|
3910
|
+
session: z5.unknown().refine(
|
|
3911
|
+
(val) => val === void 0 || val !== null && typeof val === "object",
|
|
3912
|
+
{ message: "Expected a ServiceSession object or undefined" }
|
|
3913
|
+
).optional(),
|
|
3914
|
+
/** Platform-specific invoke function */
|
|
3915
|
+
invoke: z5.unknown().refine((val) => typeof val === "function", {
|
|
3916
|
+
message: "Expected an invoke function"
|
|
3917
|
+
}),
|
|
3918
|
+
/** Optional custom fetch implementation */
|
|
3919
|
+
fetch: z5.unknown().refine(
|
|
3920
|
+
(val) => val === void 0 || typeof val === "function",
|
|
3921
|
+
{ message: "Expected a fetch function or undefined" }
|
|
3922
|
+
).optional(),
|
|
3923
|
+
/** Key provider for cryptographic operations */
|
|
3924
|
+
keyProvider: KeyProviderSchema,
|
|
3925
|
+
/** Capability key registry for key/delegation management */
|
|
3926
|
+
registry: z5.unknown().refine(
|
|
3927
|
+
(val) => val !== null && typeof val === "object",
|
|
3928
|
+
{ message: "Expected an ICapabilityKeyRegistry object" }
|
|
3929
|
+
),
|
|
3930
|
+
/**
|
|
3931
|
+
* Delegation manager for creating delegations.
|
|
3932
|
+
* Required for generate(), optional for receive().
|
|
3933
|
+
*/
|
|
3934
|
+
delegationManager: z5.unknown().refine(
|
|
3935
|
+
(val) => val === void 0 || val !== null && typeof val === "object",
|
|
3936
|
+
{ message: "Expected a DelegationManager object or undefined" }
|
|
3937
|
+
).optional(),
|
|
3938
|
+
/** Factory for creating KV service instances */
|
|
3939
|
+
createKVService: z5.unknown().refine(
|
|
3940
|
+
(val) => typeof val === "function",
|
|
3941
|
+
{ message: "Expected a createKVService factory function" }
|
|
3942
|
+
),
|
|
3943
|
+
/** Base URL for sharing links (e.g., "https://share.myapp.com") */
|
|
3944
|
+
baseUrl: z5.string().optional(),
|
|
3945
|
+
/**
|
|
3946
|
+
* Custom delegation creation function.
|
|
3947
|
+
*/
|
|
3948
|
+
createDelegation: z5.unknown().refine((val) => val === void 0 || typeof val === "function", {
|
|
3949
|
+
message: "Expected a createDelegation function or undefined"
|
|
3950
|
+
}).optional(),
|
|
3951
|
+
/**
|
|
3952
|
+
* WASM function for client-side delegation creation.
|
|
3953
|
+
*/
|
|
3954
|
+
createDelegationWasm: z5.unknown().refine((val) => val === void 0 || typeof val === "function", {
|
|
3955
|
+
message: "Expected a createDelegationWasm function or undefined"
|
|
3956
|
+
}).optional(),
|
|
3957
|
+
/**
|
|
3958
|
+
* Path prefix for KV operations.
|
|
3959
|
+
*/
|
|
3960
|
+
pathPrefix: z5.string().optional(),
|
|
3961
|
+
/**
|
|
3962
|
+
* Session expiry time.
|
|
3963
|
+
*/
|
|
3964
|
+
sessionExpiry: z5.date().optional(),
|
|
3965
|
+
/**
|
|
3966
|
+
* Callback to create a DIRECT delegation from wallet to share key.
|
|
3967
|
+
* This is the preferred method for long-lived share links because it
|
|
3968
|
+
* bypasses the session delegation chain entirely.
|
|
3969
|
+
*/
|
|
3970
|
+
onRootDelegationNeeded: z5.unknown().refine((val) => val === void 0 || typeof val === "function", {
|
|
3971
|
+
message: "Expected an onRootDelegationNeeded function or undefined"
|
|
3972
|
+
}).optional()
|
|
3973
|
+
});
|
|
3974
|
+
function validateEncodedShareData(data) {
|
|
3975
|
+
const result = EncodedShareDataSchema.safeParse(data);
|
|
3976
|
+
if (!result.success) {
|
|
3977
|
+
return {
|
|
3978
|
+
ok: false,
|
|
3979
|
+
error: {
|
|
3980
|
+
code: DelegationErrorCodes.VALIDATION_ERROR,
|
|
3981
|
+
message: `Invalid share data: ${result.error.message}`,
|
|
3982
|
+
service: "delegation",
|
|
3983
|
+
meta: { issues: result.error.issues }
|
|
3984
|
+
}
|
|
3985
|
+
};
|
|
3620
3986
|
}
|
|
3621
|
-
return
|
|
3987
|
+
return { ok: true, data: result.data };
|
|
3622
3988
|
}
|
|
3623
3989
|
|
|
3624
3990
|
// src/delegations/SharingService.ts
|
|
@@ -3799,13 +4165,13 @@ var SharingService = class {
|
|
|
3799
4165
|
)
|
|
3800
4166
|
};
|
|
3801
4167
|
}
|
|
3802
|
-
} catch (
|
|
4168
|
+
} catch (err6) {
|
|
3803
4169
|
return {
|
|
3804
4170
|
ok: false,
|
|
3805
4171
|
error: createError2(
|
|
3806
4172
|
DelegationErrorCodes.CREATION_FAILED,
|
|
3807
|
-
`Failed to generate session key for share: ${
|
|
3808
|
-
|
|
4173
|
+
`Failed to generate session key for share: ${err6 instanceof Error ? err6.message : String(err6)}`,
|
|
4174
|
+
err6 instanceof Error ? err6 : void 0
|
|
3809
4175
|
)
|
|
3810
4176
|
};
|
|
3811
4177
|
}
|
|
@@ -3851,7 +4217,7 @@ var SharingService = class {
|
|
|
3851
4217
|
}
|
|
3852
4218
|
delegation = parsed;
|
|
3853
4219
|
}
|
|
3854
|
-
} catch (
|
|
4220
|
+
} catch (err6) {
|
|
3855
4221
|
const fallbackResult = await this.handleSessionExtensionFallback(requestedExpiry);
|
|
3856
4222
|
expiry = fallbackResult.expiry;
|
|
3857
4223
|
const delegationResult = await this.createSessionDelegation(plainDID, fullPath, actions, expiry);
|
|
@@ -4049,13 +4415,13 @@ var SharingService = class {
|
|
|
4049
4415
|
allowSubDelegation: true,
|
|
4050
4416
|
createdAt: /* @__PURE__ */ new Date()
|
|
4051
4417
|
};
|
|
4052
|
-
} catch (
|
|
4418
|
+
} catch (err6) {
|
|
4053
4419
|
return {
|
|
4054
4420
|
ok: false,
|
|
4055
4421
|
error: createError2(
|
|
4056
4422
|
DelegationErrorCodes.CREATION_FAILED,
|
|
4057
|
-
`Failed to create delegation via WASM: ${
|
|
4058
|
-
|
|
4423
|
+
`Failed to create delegation via WASM: ${err6 instanceof Error ? err6.message : String(err6)}`,
|
|
4424
|
+
err6 instanceof Error ? err6 : void 0
|
|
4059
4425
|
)
|
|
4060
4426
|
};
|
|
4061
4427
|
}
|
|
@@ -4136,8 +4502,8 @@ var SharingService = class {
|
|
|
4136
4502
|
let activeKey = keyInfo;
|
|
4137
4503
|
if (autoSubdelegate && useSessionKey && this.session) {
|
|
4138
4504
|
try {
|
|
4139
|
-
} catch (
|
|
4140
|
-
console.warn("Auto-subdelegation failed, using ingested key directly:",
|
|
4505
|
+
} catch (err6) {
|
|
4506
|
+
console.warn("Auto-subdelegation failed, using ingested key directly:", err6);
|
|
4141
4507
|
}
|
|
4142
4508
|
}
|
|
4143
4509
|
const authHeader = shareData.delegation.authHeader ?? `Bearer ${shareData.delegation.cid}`;
|
|
@@ -4235,26 +4601,26 @@ var SharingService = class {
|
|
|
4235
4601
|
let jsonString;
|
|
4236
4602
|
try {
|
|
4237
4603
|
jsonString = base64UrlDecode(base64Data);
|
|
4238
|
-
} catch (
|
|
4604
|
+
} catch (err6) {
|
|
4239
4605
|
return {
|
|
4240
4606
|
ok: false,
|
|
4241
4607
|
error: createError2(
|
|
4242
4608
|
DelegationErrorCodes.INVALID_TOKEN,
|
|
4243
|
-
`Failed to decode base64 data: ${
|
|
4244
|
-
|
|
4609
|
+
`Failed to decode base64 data: ${err6 instanceof Error ? err6.message : String(err6)}`,
|
|
4610
|
+
err6 instanceof Error ? err6 : void 0
|
|
4245
4611
|
)
|
|
4246
4612
|
};
|
|
4247
4613
|
}
|
|
4248
4614
|
let parsed;
|
|
4249
4615
|
try {
|
|
4250
4616
|
parsed = JSON.parse(jsonString);
|
|
4251
|
-
} catch (
|
|
4617
|
+
} catch (err6) {
|
|
4252
4618
|
return {
|
|
4253
4619
|
ok: false,
|
|
4254
4620
|
error: createError2(
|
|
4255
4621
|
DelegationErrorCodes.INVALID_TOKEN,
|
|
4256
|
-
`Failed to parse share data JSON: ${
|
|
4257
|
-
|
|
4622
|
+
`Failed to parse share data JSON: ${err6 instanceof Error ? err6.message : String(err6)}`,
|
|
4623
|
+
err6 instanceof Error ? err6 : void 0
|
|
4258
4624
|
)
|
|
4259
4625
|
};
|
|
4260
4626
|
}
|
|
@@ -4281,8 +4647,8 @@ function createSharingService(config) {
|
|
|
4281
4647
|
}
|
|
4282
4648
|
|
|
4283
4649
|
// src/authorization/CapabilityKeyRegistry.ts
|
|
4284
|
-
import { ok as
|
|
4285
|
-
var
|
|
4650
|
+
import { ok as ok4, err as err4, serviceError as serviceError4 } from "@tinycloud/sdk-services";
|
|
4651
|
+
var SERVICE_NAME3 = "capability-key-registry";
|
|
4286
4652
|
var CapabilityKeyRegistryErrorCodes = {
|
|
4287
4653
|
/** Key not found in registry */
|
|
4288
4654
|
KEY_NOT_FOUND: "KEY_NOT_FOUND",
|
|
@@ -4499,11 +4865,11 @@ var CapabilityKeyRegistry = class {
|
|
|
4499
4865
|
revokeDelegation(cid) {
|
|
4500
4866
|
const stored = this.store.byCid.get(cid);
|
|
4501
4867
|
if (!stored) {
|
|
4502
|
-
return
|
|
4503
|
-
|
|
4868
|
+
return err4(
|
|
4869
|
+
serviceError4(
|
|
4504
4870
|
CapabilityKeyRegistryErrorCodes.KEY_NOT_FOUND,
|
|
4505
4871
|
`Delegation not found: ${cid}`,
|
|
4506
|
-
|
|
4872
|
+
SERVICE_NAME3
|
|
4507
4873
|
)
|
|
4508
4874
|
);
|
|
4509
4875
|
}
|
|
@@ -4522,7 +4888,7 @@ var CapabilityKeyRegistry = class {
|
|
|
4522
4888
|
}
|
|
4523
4889
|
}
|
|
4524
4890
|
}
|
|
4525
|
-
return
|
|
4891
|
+
return ok4(void 0);
|
|
4526
4892
|
}
|
|
4527
4893
|
// ===========================================================================
|
|
4528
4894
|
// Search
|
|
@@ -4751,8 +5117,8 @@ async function checkNodeInfo(host, sdkProtocol, fetchFn = globalThis.fetch.bind(
|
|
|
4751
5117
|
response = await fetchFn(`${host}/info`, {
|
|
4752
5118
|
signal: AbortSignal.timeout(5e3)
|
|
4753
5119
|
});
|
|
4754
|
-
} catch (
|
|
4755
|
-
throw new VersionCheckError(host,
|
|
5120
|
+
} catch (err6) {
|
|
5121
|
+
throw new VersionCheckError(host, err6);
|
|
4756
5122
|
}
|
|
4757
5123
|
if (!response.ok) {
|
|
4758
5124
|
throw new VersionCheckError(host);
|
|
@@ -5284,6 +5650,7 @@ function parseRecapCapabilities(parseWasm, siwe) {
|
|
|
5284
5650
|
export {
|
|
5285
5651
|
ACCOUNT_REGISTRY_PATH,
|
|
5286
5652
|
ACCOUNT_REGISTRY_SPACE,
|
|
5653
|
+
AccountService,
|
|
5287
5654
|
AutoApproveSpaceCreationHandler,
|
|
5288
5655
|
CapabilityKeyRegistry,
|
|
5289
5656
|
CapabilityKeyRegistryErrorCodes,
|
|
@@ -5389,7 +5756,7 @@ export {
|
|
|
5389
5756
|
utf8Decode as encryptionUtf8Decode,
|
|
5390
5757
|
utf8Encode as encryptionUtf8Encode,
|
|
5391
5758
|
ensureNetworkUsableForDecrypt,
|
|
5392
|
-
|
|
5759
|
+
err5 as err,
|
|
5393
5760
|
expandActionShortNames,
|
|
5394
5761
|
expandPermissionEntries,
|
|
5395
5762
|
expandPermissionEntry,
|
|
@@ -5410,7 +5777,7 @@ export {
|
|
|
5410
5777
|
multiaddrToHttpUrl,
|
|
5411
5778
|
networkDiscoveryKey,
|
|
5412
5779
|
normalizeDefaults,
|
|
5413
|
-
|
|
5780
|
+
ok5 as ok,
|
|
5414
5781
|
openWrappedKey,
|
|
5415
5782
|
parseCanonicalNetworkId,
|
|
5416
5783
|
parseExpiry,
|
|
@@ -5428,7 +5795,7 @@ export {
|
|
|
5428
5795
|
resolveTinyCloudHosts,
|
|
5429
5796
|
resourceCapabilitiesToAbilitiesMap,
|
|
5430
5797
|
resourceCapabilitiesToSpaceAbilitiesMap,
|
|
5431
|
-
|
|
5798
|
+
serviceError5 as serviceError,
|
|
5432
5799
|
signLocationRecord,
|
|
5433
5800
|
submitHostDelegation,
|
|
5434
5801
|
validateClientSession,
|