@tinycloud/node-sdk 2.1.0-beta.1 → 2.1.0-beta.3
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/core.cjs +205 -62
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +162 -23
- package/dist/core.d.ts +162 -23
- package/dist/core.js +212 -66
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +203 -60
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +210 -64
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/core.cjs
CHANGED
|
@@ -339,8 +339,25 @@ var NodeUserAuthorization = class {
|
|
|
339
339
|
this.enablePublicSpace = config.enablePublicSpace ?? true;
|
|
340
340
|
this.nonce = config.nonce;
|
|
341
341
|
this.siweConfig = config.siweConfig;
|
|
342
|
+
this._manifest = config.manifest;
|
|
342
343
|
this.sessionManager = this.wasm.createSessionManager();
|
|
343
344
|
}
|
|
345
|
+
/**
|
|
346
|
+
* Return the manifest currently driving sign-in behavior, or
|
|
347
|
+
* `undefined` if none is set. Used by TinyCloudWeb/TinyCloudNode
|
|
348
|
+
* internals to surface the manifest for requestPermissions flows
|
|
349
|
+
* without forcing the caller to track it separately.
|
|
350
|
+
*/
|
|
351
|
+
get manifest() {
|
|
352
|
+
return this._manifest;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Install or replace the stored manifest. Takes effect on the next
|
|
356
|
+
* `signIn()` call — the current session (if any) is not touched.
|
|
357
|
+
*/
|
|
358
|
+
setManifest(manifest) {
|
|
359
|
+
this._manifest = manifest;
|
|
360
|
+
}
|
|
344
361
|
/**
|
|
345
362
|
* The current active session (web-core compatible).
|
|
346
363
|
*/
|
|
@@ -357,6 +374,39 @@ var NodeUserAuthorization = class {
|
|
|
357
374
|
get nodeFeatures() {
|
|
358
375
|
return this._nodeFeatures;
|
|
359
376
|
}
|
|
377
|
+
/**
|
|
378
|
+
* Compute the `abilities` map the WASM `prepareSession` call should
|
|
379
|
+
* see at sign-in time.
|
|
380
|
+
*
|
|
381
|
+
* When a manifest is installed, we resolve it and union together:
|
|
382
|
+
* - the app's own `resources` (what it needs at runtime)
|
|
383
|
+
* - every `additionalDelegates[*].permissions` list (what it will
|
|
384
|
+
* re-delegate to other DIDs post sign-in)
|
|
385
|
+
*
|
|
386
|
+
* into the short-service / path / full-URN-actions shape the WASM
|
|
387
|
+
* layer expects. This is the key invariant that lets
|
|
388
|
+
* {@link TinyCloudNode.delegateTo} issue manifest-declared
|
|
389
|
+
* delegations via the session key (no wallet prompt): the session's
|
|
390
|
+
* own recap already covers every action those delegations need.
|
|
391
|
+
*
|
|
392
|
+
* When no manifest is installed, we fall back to the
|
|
393
|
+
* {@link defaultActions} table so existing callers see no change.
|
|
394
|
+
*
|
|
395
|
+
* This is a pure function of `this._manifest` + `this.defaultActions`
|
|
396
|
+
* — the manifest resolution performs no I/O and throws a
|
|
397
|
+
* {@link ManifestValidationError} on structural problems (missing
|
|
398
|
+
* id/name, unparseable expiry, etc), which will surface at sign-in
|
|
399
|
+
* rather than being silently swallowed.
|
|
400
|
+
*
|
|
401
|
+
* @internal
|
|
402
|
+
*/
|
|
403
|
+
resolveSignInAbilities() {
|
|
404
|
+
if (this._manifest === void 0) {
|
|
405
|
+
return this.defaultActions;
|
|
406
|
+
}
|
|
407
|
+
const resolved = (0, import_sdk_core2.resolveManifest)(this._manifest);
|
|
408
|
+
return (0, import_sdk_core2.manifestAbilitiesUnion)(resolved);
|
|
409
|
+
}
|
|
360
410
|
/**
|
|
361
411
|
* Build SIWE overrides from the top-level nonce and siweConfig.
|
|
362
412
|
* - Top-level `nonce` is seeded first so `siweConfig.nonce` wins if both are set.
|
|
@@ -560,7 +610,7 @@ var NodeUserAuthorization = class {
|
|
|
560
610
|
const now = /* @__PURE__ */ new Date();
|
|
561
611
|
const expirationTime = new Date(now.getTime() + this.sessionExpirationMs);
|
|
562
612
|
const prepared = this.wasm.prepareSession({
|
|
563
|
-
abilities: this.
|
|
613
|
+
abilities: this.resolveSignInAbilities(),
|
|
564
614
|
address,
|
|
565
615
|
chainId,
|
|
566
616
|
domain: this.domain,
|
|
@@ -703,7 +753,7 @@ var NodeUserAuthorization = class {
|
|
|
703
753
|
const now = /* @__PURE__ */ new Date();
|
|
704
754
|
const expirationTime = new Date(now.getTime() + this.sessionExpirationMs);
|
|
705
755
|
const prepared = this.wasm.prepareSession({
|
|
706
|
-
abilities: this.
|
|
756
|
+
abilities: this.resolveSignInAbilities(),
|
|
707
757
|
address,
|
|
708
758
|
chainId,
|
|
709
759
|
domain: this.domain,
|
|
@@ -1212,12 +1262,35 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1212
1262
|
enablePublicSpace: config.enablePublicSpace ?? true,
|
|
1213
1263
|
spaceCreationHandler: config.spaceCreationHandler,
|
|
1214
1264
|
nonce: config.nonce,
|
|
1215
|
-
siweConfig: config.siweConfig
|
|
1265
|
+
siweConfig: config.siweConfig,
|
|
1266
|
+
manifest: config.manifest
|
|
1216
1267
|
});
|
|
1217
1268
|
this.tc = new import_sdk_core5.TinyCloud(this.auth, {
|
|
1218
1269
|
invokeAny: this.wasmBindings.invokeAny
|
|
1219
1270
|
});
|
|
1220
1271
|
}
|
|
1272
|
+
/**
|
|
1273
|
+
* Install or replace the manifest that drives the SIWE recap at
|
|
1274
|
+
* sign-in. Takes effect on the next `signIn()` call — the current
|
|
1275
|
+
* session (if any) is not touched. Wire this up from a higher
|
|
1276
|
+
* layer (e.g. TinyCloudWeb.setManifest) so the manifest is kept
|
|
1277
|
+
* in sync across the stack.
|
|
1278
|
+
*/
|
|
1279
|
+
setManifest(manifest) {
|
|
1280
|
+
if (!this.auth) {
|
|
1281
|
+
throw new Error(
|
|
1282
|
+
"setManifest requires wallet mode. Provide a signer or privateKey in the TinyCloudNode config."
|
|
1283
|
+
);
|
|
1284
|
+
}
|
|
1285
|
+
this.auth.setManifest(manifest);
|
|
1286
|
+
}
|
|
1287
|
+
/**
|
|
1288
|
+
* Return the manifest currently installed on the auth handler,
|
|
1289
|
+
* or `undefined` if none is set.
|
|
1290
|
+
*/
|
|
1291
|
+
get manifest() {
|
|
1292
|
+
return this.auth?.manifest;
|
|
1293
|
+
}
|
|
1221
1294
|
/**
|
|
1222
1295
|
* Get the primary identity DID for this user.
|
|
1223
1296
|
* - If wallet connected and signed in: returns PKH DID (did:pkh:eip155:{chainId}:{address})
|
|
@@ -1724,7 +1797,19 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1724
1797
|
}
|
|
1725
1798
|
/**
|
|
1726
1799
|
* Wrapper for the WASM createDelegation function.
|
|
1727
|
-
*
|
|
1800
|
+
*
|
|
1801
|
+
* The WASM call now takes a multi-resource `abilities` map
|
|
1802
|
+
* (matching `prepareSession`'s shape) and emits ONE UCAN that
|
|
1803
|
+
* covers every `(service, path, actions)` entry. We mirror the raw
|
|
1804
|
+
* result back through `CreateDelegationWasmResult`, converting the
|
|
1805
|
+
* seconds-since-epoch `expiry` to a Date and normalizing the
|
|
1806
|
+
* `delegateDid` → `delegateDID` case.
|
|
1807
|
+
*
|
|
1808
|
+
* Both SharingService (single-entry) and
|
|
1809
|
+
* {@link TinyCloudNode.delegateTo} (multi-entry) drive this through
|
|
1810
|
+
* the same code path so there's exactly one place that touches the
|
|
1811
|
+
* WASM boundary.
|
|
1812
|
+
*
|
|
1728
1813
|
* @internal
|
|
1729
1814
|
*/
|
|
1730
1815
|
createDelegationWrapper(params) {
|
|
@@ -1739,18 +1824,19 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1739
1824
|
wasmSession,
|
|
1740
1825
|
params.delegateDID,
|
|
1741
1826
|
params.spaceId,
|
|
1742
|
-
params.
|
|
1743
|
-
params.actions,
|
|
1827
|
+
params.abilities,
|
|
1744
1828
|
params.expirationSecs,
|
|
1745
1829
|
params.notBeforeSecs
|
|
1746
1830
|
);
|
|
1747
1831
|
return {
|
|
1748
1832
|
delegation: result.delegation,
|
|
1749
1833
|
cid: result.cid,
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1834
|
+
// Rust serde `rename_all = "camelCase"` emits `delegateDid`
|
|
1835
|
+
// (lowercase d); the TypeScript interface uses `delegateDID`
|
|
1836
|
+
// (historical, matches Delegation.delegateDID). Normalize here.
|
|
1837
|
+
delegateDID: result.delegateDid ?? result.delegateDID,
|
|
1838
|
+
expiry: new Date(result.expiry * 1e3),
|
|
1839
|
+
resources: result.resources
|
|
1754
1840
|
};
|
|
1755
1841
|
}
|
|
1756
1842
|
/**
|
|
@@ -2225,24 +2311,38 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2225
2311
|
/**
|
|
2226
2312
|
* Issue a delegation using the capability-chain flow.
|
|
2227
2313
|
*
|
|
2228
|
-
* When
|
|
2229
|
-
* recap, the delegation is signed by the session key via
|
|
2230
|
-
* prompt. When
|
|
2231
|
-
*
|
|
2232
|
-
*
|
|
2233
|
-
*
|
|
2234
|
-
*
|
|
2235
|
-
*
|
|
2236
|
-
*
|
|
2237
|
-
*
|
|
2238
|
-
*
|
|
2239
|
-
*
|
|
2240
|
-
* underlying `
|
|
2241
|
-
*
|
|
2242
|
-
*
|
|
2243
|
-
*
|
|
2244
|
-
*
|
|
2245
|
-
*
|
|
2314
|
+
* When every requested permission is a subset of the current
|
|
2315
|
+
* session's recap, the delegation is signed by the session key via
|
|
2316
|
+
* WASM — no wallet prompt. When at least one is NOT derivable, a
|
|
2317
|
+
* {@link PermissionNotInManifestError} is raised (carrying the
|
|
2318
|
+
* missing entries) so the caller can trigger an escalation flow
|
|
2319
|
+
* (e.g. `TinyCloudWeb.requestPermissions`). Passing
|
|
2320
|
+
* `forceWalletSign: true` bypasses the derivability check and
|
|
2321
|
+
* always uses the wallet-signed SIWE path — used by the legacy
|
|
2322
|
+
* `createDelegation` fallback and by callers that want explicit
|
|
2323
|
+
* wallet confirmation.
|
|
2324
|
+
*
|
|
2325
|
+
* Multi-entry delegations are now emitted as **one** signed UCAN:
|
|
2326
|
+
* the underlying WASM `createDelegation` takes a full
|
|
2327
|
+
* `HashMap<Service, HashMap<Path, Vec<Ability>>>` abilities map
|
|
2328
|
+
* and produces a single attenuation carrying every
|
|
2329
|
+
* `(service, path, actions)` entry. The returned
|
|
2330
|
+
* {@link DelegateToResult.delegation} is that single blob, and
|
|
2331
|
+
* apps can POST it to their backend exactly like a single-entry
|
|
2332
|
+
* delegation (the server verifies all granted resources from one
|
|
2333
|
+
* UCAN).
|
|
2334
|
+
*
|
|
2335
|
+
* For single-entry requests the `PortableDelegation.path` and
|
|
2336
|
+
* `.actions` fields mirror the one granted entry. For
|
|
2337
|
+
* multi-entry requests they mirror the **first** entry (stable
|
|
2338
|
+
* lexicographic order from the Rust side); consumers that need
|
|
2339
|
+
* the full picture read `PortableDelegation.resources`.
|
|
2340
|
+
*
|
|
2341
|
+
* @throws {@link SessionExpiredError} when there is no session or
|
|
2342
|
+
* the current session has expired (or will within the 60s
|
|
2343
|
+
* safety margin).
|
|
2344
|
+
* @throws {@link PermissionNotInManifestError} when any requested
|
|
2345
|
+
* entry is not a subset of the granted session capabilities and
|
|
2246
2346
|
* `forceWalletSign` is not set.
|
|
2247
2347
|
*/
|
|
2248
2348
|
async delegateTo(did, permissions, options) {
|
|
@@ -2263,16 +2363,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2263
2363
|
"delegateTo requires a non-empty permissions array"
|
|
2264
2364
|
);
|
|
2265
2365
|
}
|
|
2266
|
-
|
|
2267
|
-
throw new Error(
|
|
2268
|
-
"delegateTo currently supports one permission entry per call. Call delegateTo multiple times for multi-resource delegation."
|
|
2269
|
-
);
|
|
2270
|
-
}
|
|
2271
|
-
const entry = permissions[0];
|
|
2272
|
-
const expandedEntry = {
|
|
2366
|
+
const expandedEntries = permissions.map((entry) => ({
|
|
2273
2367
|
...entry,
|
|
2274
2368
|
actions: (0, import_sdk_core5.expandActionShortNames)(entry.service, entry.actions)
|
|
2275
|
-
};
|
|
2369
|
+
}));
|
|
2276
2370
|
const now = /* @__PURE__ */ new Date();
|
|
2277
2371
|
const expiryMs = resolveExpiryMs(options?.expiry);
|
|
2278
2372
|
const expirationTime = new Date(now.getTime() + expiryMs);
|
|
@@ -2281,9 +2375,14 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2281
2375
|
effectiveExpiration = sessionExpiry;
|
|
2282
2376
|
}
|
|
2283
2377
|
if (options?.forceWalletSign) {
|
|
2378
|
+
if (expandedEntries.length > 1) {
|
|
2379
|
+
throw new Error(
|
|
2380
|
+
"delegateTo with forceWalletSign=true supports at most one PermissionEntry. Multi-entry requests must go through the session-key UCAN path (drop forceWalletSign) or the legacy createDelegation method."
|
|
2381
|
+
);
|
|
2382
|
+
}
|
|
2284
2383
|
const delegation2 = await this.createDelegationLegacyWalletPath(
|
|
2285
2384
|
did,
|
|
2286
|
-
|
|
2385
|
+
expandedEntries[0],
|
|
2287
2386
|
effectiveExpiration
|
|
2288
2387
|
);
|
|
2289
2388
|
return { delegation: delegation2, prompted: true };
|
|
@@ -2292,14 +2391,13 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2292
2391
|
(siwe) => this.wasmBindings.parseRecapFromSiwe(siwe),
|
|
2293
2392
|
session.siwe
|
|
2294
2393
|
);
|
|
2295
|
-
const
|
|
2296
|
-
const { subset, missing } = (0, import_sdk_core5.isCapabilitySubset)(requested, granted);
|
|
2394
|
+
const { subset, missing } = (0, import_sdk_core5.isCapabilitySubset)(expandedEntries, granted);
|
|
2297
2395
|
if (!subset) {
|
|
2298
2396
|
throw new import_sdk_core5.PermissionNotInManifestError(missing, granted);
|
|
2299
2397
|
}
|
|
2300
2398
|
const delegation = await this.createDelegationViaWasmPath(
|
|
2301
2399
|
did,
|
|
2302
|
-
|
|
2400
|
+
expandedEntries,
|
|
2303
2401
|
effectiveExpiration,
|
|
2304
2402
|
session
|
|
2305
2403
|
);
|
|
@@ -2308,14 +2406,60 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2308
2406
|
/**
|
|
2309
2407
|
* Issue a delegation via the session-key UCAN WASM path.
|
|
2310
2408
|
*
|
|
2311
|
-
* The caller has already verified
|
|
2312
|
-
* current session; we
|
|
2313
|
-
*
|
|
2409
|
+
* The caller has already verified every entry is derivable from
|
|
2410
|
+
* the current session; we build one multi-resource abilities map
|
|
2411
|
+
* and emit one signed UCAN covering them all.
|
|
2412
|
+
*
|
|
2413
|
+
* All entries must share the same target space (the UCAN is
|
|
2414
|
+
* scoped to a single space). If they don't, this throws — mixing
|
|
2415
|
+
* spaces in a single delegation is not supported by the underlying
|
|
2416
|
+
* Rust create_delegation call and the resulting UCAN would be
|
|
2417
|
+
* under-specified.
|
|
2314
2418
|
*
|
|
2315
2419
|
* @internal
|
|
2316
2420
|
*/
|
|
2317
|
-
async createDelegationViaWasmPath(did,
|
|
2318
|
-
|
|
2421
|
+
async createDelegationViaWasmPath(did, entries, expirationTime, session) {
|
|
2422
|
+
if (entries.length === 0) {
|
|
2423
|
+
throw new Error(
|
|
2424
|
+
"createDelegationViaWasmPath requires a non-empty entries array"
|
|
2425
|
+
);
|
|
2426
|
+
}
|
|
2427
|
+
const resolvedSpaces = /* @__PURE__ */ new Set();
|
|
2428
|
+
for (const entry of entries) {
|
|
2429
|
+
const spaceId2 = entry.space === "default" ? session.spaceId : entry.space;
|
|
2430
|
+
resolvedSpaces.add(spaceId2);
|
|
2431
|
+
}
|
|
2432
|
+
if (resolvedSpaces.size !== 1) {
|
|
2433
|
+
throw new Error(
|
|
2434
|
+
`delegateTo: all permission entries must target the same space, got ${resolvedSpaces.size}: ${JSON.stringify([...resolvedSpaces])}`
|
|
2435
|
+
);
|
|
2436
|
+
}
|
|
2437
|
+
const spaceId = [...resolvedSpaces][0];
|
|
2438
|
+
const abilities = {};
|
|
2439
|
+
for (const entry of entries) {
|
|
2440
|
+
const shortService = import_sdk_core5.SERVICE_LONG_TO_SHORT[entry.service];
|
|
2441
|
+
if (shortService === void 0) {
|
|
2442
|
+
throw new Error(
|
|
2443
|
+
`delegateTo: unknown service '${entry.service}' \u2014 no short-form mapping`
|
|
2444
|
+
);
|
|
2445
|
+
}
|
|
2446
|
+
if (abilities[shortService] === void 0) {
|
|
2447
|
+
abilities[shortService] = {};
|
|
2448
|
+
}
|
|
2449
|
+
const pathsMap = abilities[shortService];
|
|
2450
|
+
const existing = pathsMap[entry.path];
|
|
2451
|
+
if (existing === void 0) {
|
|
2452
|
+
pathsMap[entry.path] = [...entry.actions];
|
|
2453
|
+
} else {
|
|
2454
|
+
const seen = new Set(existing);
|
|
2455
|
+
for (const action of entry.actions) {
|
|
2456
|
+
if (!seen.has(action)) {
|
|
2457
|
+
existing.push(action);
|
|
2458
|
+
seen.add(action);
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2319
2463
|
const serviceSession = {
|
|
2320
2464
|
delegationHeader: session.delegationHeader,
|
|
2321
2465
|
delegationCid: session.delegationCid,
|
|
@@ -2328,16 +2472,17 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2328
2472
|
session: serviceSession,
|
|
2329
2473
|
delegateDID: did,
|
|
2330
2474
|
spaceId,
|
|
2331
|
-
|
|
2332
|
-
actions: entry.actions,
|
|
2475
|
+
abilities,
|
|
2333
2476
|
expirationSecs
|
|
2334
2477
|
});
|
|
2478
|
+
const primary = result.resources[0];
|
|
2335
2479
|
return {
|
|
2336
2480
|
cid: result.cid,
|
|
2337
2481
|
delegationHeader: { Authorization: `Bearer ${result.delegation}` },
|
|
2338
2482
|
spaceId,
|
|
2339
|
-
path:
|
|
2340
|
-
actions:
|
|
2483
|
+
path: primary.path,
|
|
2484
|
+
actions: primary.actions,
|
|
2485
|
+
resources: result.resources,
|
|
2341
2486
|
disableSubDelegation: false,
|
|
2342
2487
|
expiry: result.expiry,
|
|
2343
2488
|
delegateDID: did,
|
|
@@ -2393,19 +2538,17 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2393
2538
|
params.path,
|
|
2394
2539
|
params.spaceIdOverride
|
|
2395
2540
|
);
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
throw err;
|
|
2408
|
-
}
|
|
2541
|
+
try {
|
|
2542
|
+
const result = await this.delegateTo(
|
|
2543
|
+
resolvedDelegateDID,
|
|
2544
|
+
entries,
|
|
2545
|
+
params.expiryMs !== void 0 ? { expiry: params.expiryMs } : void 0
|
|
2546
|
+
);
|
|
2547
|
+
return result.delegation;
|
|
2548
|
+
} catch (err) {
|
|
2549
|
+
if (err instanceof import_sdk_core5.PermissionNotInManifestError) {
|
|
2550
|
+
} else {
|
|
2551
|
+
throw err;
|
|
2409
2552
|
}
|
|
2410
2553
|
}
|
|
2411
2554
|
return this.createDelegationWalletPath({
|