@tinycloud/node-sdk 2.1.0-beta.0 → 2.1.0-beta.2

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.js CHANGED
@@ -203,7 +203,9 @@ import {
203
203
  submitHostDelegation,
204
204
  activateSessionWithHost,
205
205
  checkNodeInfo,
206
- AutoApproveSpaceCreationHandler
206
+ AutoApproveSpaceCreationHandler,
207
+ manifestAbilitiesUnion,
208
+ resolveManifest
207
209
  } from "@tinycloud/sdk-core";
208
210
 
209
211
  // src/authorization/strategies.ts
@@ -272,8 +274,25 @@ var NodeUserAuthorization = class {
272
274
  this.enablePublicSpace = config.enablePublicSpace ?? true;
273
275
  this.nonce = config.nonce;
274
276
  this.siweConfig = config.siweConfig;
277
+ this._manifest = config.manifest;
275
278
  this.sessionManager = this.wasm.createSessionManager();
276
279
  }
280
+ /**
281
+ * Return the manifest currently driving sign-in behavior, or
282
+ * `undefined` if none is set. Used by TinyCloudWeb/TinyCloudNode
283
+ * internals to surface the manifest for requestPermissions flows
284
+ * without forcing the caller to track it separately.
285
+ */
286
+ get manifest() {
287
+ return this._manifest;
288
+ }
289
+ /**
290
+ * Install or replace the stored manifest. Takes effect on the next
291
+ * `signIn()` call — the current session (if any) is not touched.
292
+ */
293
+ setManifest(manifest) {
294
+ this._manifest = manifest;
295
+ }
277
296
  /**
278
297
  * The current active session (web-core compatible).
279
298
  */
@@ -290,6 +309,39 @@ var NodeUserAuthorization = class {
290
309
  get nodeFeatures() {
291
310
  return this._nodeFeatures;
292
311
  }
312
+ /**
313
+ * Compute the `abilities` map the WASM `prepareSession` call should
314
+ * see at sign-in time.
315
+ *
316
+ * When a manifest is installed, we resolve it and union together:
317
+ * - the app's own `resources` (what it needs at runtime)
318
+ * - every `additionalDelegates[*].permissions` list (what it will
319
+ * re-delegate to other DIDs post sign-in)
320
+ *
321
+ * into the short-service / path / full-URN-actions shape the WASM
322
+ * layer expects. This is the key invariant that lets
323
+ * {@link TinyCloudNode.delegateTo} issue manifest-declared
324
+ * delegations via the session key (no wallet prompt): the session's
325
+ * own recap already covers every action those delegations need.
326
+ *
327
+ * When no manifest is installed, we fall back to the
328
+ * {@link defaultActions} table so existing callers see no change.
329
+ *
330
+ * This is a pure function of `this._manifest` + `this.defaultActions`
331
+ * — the manifest resolution performs no I/O and throws a
332
+ * {@link ManifestValidationError} on structural problems (missing
333
+ * id/name, unparseable expiry, etc), which will surface at sign-in
334
+ * rather than being silently swallowed.
335
+ *
336
+ * @internal
337
+ */
338
+ resolveSignInAbilities() {
339
+ if (this._manifest === void 0) {
340
+ return this.defaultActions;
341
+ }
342
+ const resolved = resolveManifest(this._manifest);
343
+ return manifestAbilitiesUnion(resolved);
344
+ }
293
345
  /**
294
346
  * Build SIWE overrides from the top-level nonce and siweConfig.
295
347
  * - Top-level `nonce` is seeded first so `siweConfig.nonce` wins if both are set.
@@ -493,7 +545,7 @@ var NodeUserAuthorization = class {
493
545
  const now = /* @__PURE__ */ new Date();
494
546
  const expirationTime = new Date(now.getTime() + this.sessionExpirationMs);
495
547
  const prepared = this.wasm.prepareSession({
496
- abilities: this.defaultActions,
548
+ abilities: this.resolveSignInAbilities(),
497
549
  address,
498
550
  chainId,
499
551
  domain: this.domain,
@@ -636,7 +688,7 @@ var NodeUserAuthorization = class {
636
688
  const now = /* @__PURE__ */ new Date();
637
689
  const expirationTime = new Date(now.getTime() + this.sessionExpirationMs);
638
690
  const prepared = this.wasm.prepareSession({
639
- abilities: this.defaultActions,
691
+ abilities: this.resolveSignInAbilities(),
640
692
  address,
641
693
  chainId,
642
694
  domain: this.domain,
@@ -820,7 +872,13 @@ import {
820
872
  CapabilityKeyRegistry,
821
873
  SharingService,
822
874
  UnsupportedFeatureError,
823
- makePublicSpaceId
875
+ makePublicSpaceId,
876
+ PermissionNotInManifestError,
877
+ SessionExpiredError,
878
+ expandActionShortNames,
879
+ isCapabilitySubset,
880
+ parseRecapCapabilities,
881
+ SERVICE_LONG_TO_SHORT
824
882
  } from "@tinycloud/sdk-core";
825
883
 
826
884
  // src/DelegatedAccess.ts
@@ -982,9 +1040,72 @@ function createWasmKeyProvider(sessionManager) {
982
1040
  return new WasmKeyProvider({ sessionManager });
983
1041
  }
984
1042
 
1043
+ // src/delegateToHelpers.ts
1044
+ import {
1045
+ parseExpiry,
1046
+ SiweMessage
1047
+ } from "@tinycloud/sdk-core";
1048
+ function legacyParamsToPermissionEntries(actions, path, spaceIdOverride) {
1049
+ const byService = /* @__PURE__ */ new Map();
1050
+ for (const a of actions) {
1051
+ const slashIdx = a.indexOf("/");
1052
+ if (slashIdx === -1) {
1053
+ continue;
1054
+ }
1055
+ const service = a.slice(0, slashIdx);
1056
+ if (!service.startsWith("tinycloud.")) {
1057
+ continue;
1058
+ }
1059
+ const list = byService.get(service);
1060
+ if (list === void 0) {
1061
+ byService.set(service, [a]);
1062
+ } else {
1063
+ list.push(a);
1064
+ }
1065
+ }
1066
+ const space = spaceIdOverride ?? "default";
1067
+ const entries = [];
1068
+ for (const [service, actionList] of byService) {
1069
+ entries.push({
1070
+ service,
1071
+ space,
1072
+ path,
1073
+ actions: actionList
1074
+ });
1075
+ }
1076
+ return entries;
1077
+ }
1078
+ function resolveExpiryMs(expiry) {
1079
+ if (expiry === void 0) {
1080
+ return 60 * 60 * 1e3;
1081
+ }
1082
+ if (typeof expiry === "number") {
1083
+ if (!Number.isFinite(expiry) || expiry <= 0) {
1084
+ throw new Error(
1085
+ `delegateTo expiry must be a positive finite number (got ${expiry})`
1086
+ );
1087
+ }
1088
+ return expiry;
1089
+ }
1090
+ return parseExpiry(expiry);
1091
+ }
1092
+ function extractSiweExpiration(siwe) {
1093
+ const parsed = new SiweMessage(siwe);
1094
+ if (parsed.expirationTime === void 0 || parsed.expirationTime === null) {
1095
+ return void 0;
1096
+ }
1097
+ const d = new Date(parsed.expirationTime);
1098
+ if (Number.isNaN(d.getTime())) {
1099
+ throw new Error(
1100
+ `Session SIWE has unparseable expirationTime: ${parsed.expirationTime}`
1101
+ );
1102
+ }
1103
+ return d;
1104
+ }
1105
+
985
1106
  // src/TinyCloudNode.ts
986
1107
  var DEFAULT_HOST = "https://node.tinycloud.xyz";
987
- var TinyCloudNode = class _TinyCloudNode {
1108
+ var _TinyCloudNode = class _TinyCloudNode {
988
1109
  /**
989
1110
  * Create a new TinyCloudNode instance.
990
1111
  *
@@ -1108,12 +1229,35 @@ var TinyCloudNode = class _TinyCloudNode {
1108
1229
  enablePublicSpace: config.enablePublicSpace ?? true,
1109
1230
  spaceCreationHandler: config.spaceCreationHandler,
1110
1231
  nonce: config.nonce,
1111
- siweConfig: config.siweConfig
1232
+ siweConfig: config.siweConfig,
1233
+ manifest: config.manifest
1112
1234
  });
1113
1235
  this.tc = new TinyCloud(this.auth, {
1114
1236
  invokeAny: this.wasmBindings.invokeAny
1115
1237
  });
1116
1238
  }
1239
+ /**
1240
+ * Install or replace the manifest that drives the SIWE recap at
1241
+ * sign-in. Takes effect on the next `signIn()` call — the current
1242
+ * session (if any) is not touched. Wire this up from a higher
1243
+ * layer (e.g. TinyCloudWeb.setManifest) so the manifest is kept
1244
+ * in sync across the stack.
1245
+ */
1246
+ setManifest(manifest) {
1247
+ if (!this.auth) {
1248
+ throw new Error(
1249
+ "setManifest requires wallet mode. Provide a signer or privateKey in the TinyCloudNode config."
1250
+ );
1251
+ }
1252
+ this.auth.setManifest(manifest);
1253
+ }
1254
+ /**
1255
+ * Return the manifest currently installed on the auth handler,
1256
+ * or `undefined` if none is set.
1257
+ */
1258
+ get manifest() {
1259
+ return this.auth?.manifest;
1260
+ }
1117
1261
  /**
1118
1262
  * Get the primary identity DID for this user.
1119
1263
  * - If wallet connected and signed in: returns PKH DID (did:pkh:eip155:{chainId}:{address})
@@ -1620,7 +1764,19 @@ var TinyCloudNode = class _TinyCloudNode {
1620
1764
  }
1621
1765
  /**
1622
1766
  * Wrapper for the WASM createDelegation function.
1623
- * Adapts the WASM interface to what SharingService expects.
1767
+ *
1768
+ * The WASM call now takes a multi-resource `abilities` map
1769
+ * (matching `prepareSession`'s shape) and emits ONE UCAN that
1770
+ * covers every `(service, path, actions)` entry. We mirror the raw
1771
+ * result back through `CreateDelegationWasmResult`, converting the
1772
+ * seconds-since-epoch `expiry` to a Date and normalizing the
1773
+ * `delegateDid` → `delegateDID` case.
1774
+ *
1775
+ * Both SharingService (single-entry) and
1776
+ * {@link TinyCloudNode.delegateTo} (multi-entry) drive this through
1777
+ * the same code path so there's exactly one place that touches the
1778
+ * WASM boundary.
1779
+ *
1624
1780
  * @internal
1625
1781
  */
1626
1782
  createDelegationWrapper(params) {
@@ -1635,18 +1791,19 @@ var TinyCloudNode = class _TinyCloudNode {
1635
1791
  wasmSession,
1636
1792
  params.delegateDID,
1637
1793
  params.spaceId,
1638
- params.path,
1639
- params.actions,
1794
+ params.abilities,
1640
1795
  params.expirationSecs,
1641
1796
  params.notBeforeSecs
1642
1797
  );
1643
1798
  return {
1644
1799
  delegation: result.delegation,
1645
1800
  cid: result.cid,
1646
- delegateDID: result.delegateDid,
1647
- path: result.path,
1648
- actions: result.actions,
1649
- expiry: new Date(result.expiry * 1e3)
1801
+ // Rust serde `rename_all = "camelCase"` emits `delegateDid`
1802
+ // (lowercase d); the TypeScript interface uses `delegateDID`
1803
+ // (historical, matches Delegation.delegateDID). Normalize here.
1804
+ delegateDID: result.delegateDid ?? result.delegateDID,
1805
+ expiry: new Date(result.expiry * 1e3),
1806
+ resources: result.resources
1650
1807
  };
1651
1808
  }
1652
1809
  /**
@@ -2118,6 +2275,209 @@ var TinyCloudNode = class _TinyCloudNode {
2118
2275
  async checkPermission(path, action) {
2119
2276
  return this.delegationManager.checkPermission(path, action);
2120
2277
  }
2278
+ /**
2279
+ * Issue a delegation using the capability-chain flow.
2280
+ *
2281
+ * When every requested permission is a subset of the current
2282
+ * session's recap, the delegation is signed by the session key via
2283
+ * WASM — no wallet prompt. When at least one is NOT derivable, a
2284
+ * {@link PermissionNotInManifestError} is raised (carrying the
2285
+ * missing entries) so the caller can trigger an escalation flow
2286
+ * (e.g. `TinyCloudWeb.requestPermissions`). Passing
2287
+ * `forceWalletSign: true` bypasses the derivability check and
2288
+ * always uses the wallet-signed SIWE path — used by the legacy
2289
+ * `createDelegation` fallback and by callers that want explicit
2290
+ * wallet confirmation.
2291
+ *
2292
+ * Multi-entry delegations are now emitted as **one** signed UCAN:
2293
+ * the underlying WASM `createDelegation` takes a full
2294
+ * `HashMap<Service, HashMap<Path, Vec<Ability>>>` abilities map
2295
+ * and produces a single attenuation carrying every
2296
+ * `(service, path, actions)` entry. The returned
2297
+ * {@link DelegateToResult.delegation} is that single blob, and
2298
+ * apps can POST it to their backend exactly like a single-entry
2299
+ * delegation (the server verifies all granted resources from one
2300
+ * UCAN).
2301
+ *
2302
+ * For single-entry requests the `PortableDelegation.path` and
2303
+ * `.actions` fields mirror the one granted entry. For
2304
+ * multi-entry requests they mirror the **first** entry (stable
2305
+ * lexicographic order from the Rust side); consumers that need
2306
+ * the full picture read `PortableDelegation.resources`.
2307
+ *
2308
+ * @throws {@link SessionExpiredError} when there is no session or
2309
+ * the current session has expired (or will within the 60s
2310
+ * safety margin).
2311
+ * @throws {@link PermissionNotInManifestError} when any requested
2312
+ * entry is not a subset of the granted session capabilities and
2313
+ * `forceWalletSign` is not set.
2314
+ */
2315
+ async delegateTo(did, permissions, options) {
2316
+ const session = this.auth?.tinyCloudSession;
2317
+ if (!session) {
2318
+ throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
2319
+ }
2320
+ const sessionExpiry = extractSiweExpiration(session.siwe);
2321
+ if (sessionExpiry !== void 0) {
2322
+ const now2 = Date.now();
2323
+ const marginMs = _TinyCloudNode.SESSION_EXPIRY_SAFETY_MARGIN_MS;
2324
+ if (sessionExpiry.getTime() <= now2 + marginMs) {
2325
+ throw new SessionExpiredError(sessionExpiry);
2326
+ }
2327
+ }
2328
+ if (!Array.isArray(permissions) || permissions.length === 0) {
2329
+ throw new Error(
2330
+ "delegateTo requires a non-empty permissions array"
2331
+ );
2332
+ }
2333
+ const expandedEntries = permissions.map((entry) => ({
2334
+ ...entry,
2335
+ actions: expandActionShortNames(entry.service, entry.actions)
2336
+ }));
2337
+ const now = /* @__PURE__ */ new Date();
2338
+ const expiryMs = resolveExpiryMs(options?.expiry);
2339
+ const expirationTime = new Date(now.getTime() + expiryMs);
2340
+ let effectiveExpiration = expirationTime;
2341
+ if (sessionExpiry !== void 0 && sessionExpiry < expirationTime) {
2342
+ effectiveExpiration = sessionExpiry;
2343
+ }
2344
+ if (options?.forceWalletSign) {
2345
+ if (expandedEntries.length > 1) {
2346
+ throw new Error(
2347
+ "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."
2348
+ );
2349
+ }
2350
+ const delegation2 = await this.createDelegationLegacyWalletPath(
2351
+ did,
2352
+ expandedEntries[0],
2353
+ effectiveExpiration
2354
+ );
2355
+ return { delegation: delegation2, prompted: true };
2356
+ }
2357
+ const granted = parseRecapCapabilities(
2358
+ (siwe) => this.wasmBindings.parseRecapFromSiwe(siwe),
2359
+ session.siwe
2360
+ );
2361
+ const { subset, missing } = isCapabilitySubset(expandedEntries, granted);
2362
+ if (!subset) {
2363
+ throw new PermissionNotInManifestError(missing, granted);
2364
+ }
2365
+ const delegation = await this.createDelegationViaWasmPath(
2366
+ did,
2367
+ expandedEntries,
2368
+ effectiveExpiration,
2369
+ session
2370
+ );
2371
+ return { delegation, prompted: false };
2372
+ }
2373
+ /**
2374
+ * Issue a delegation via the session-key UCAN WASM path.
2375
+ *
2376
+ * The caller has already verified every entry is derivable from
2377
+ * the current session; we build one multi-resource abilities map
2378
+ * and emit one signed UCAN covering them all.
2379
+ *
2380
+ * All entries must share the same target space (the UCAN is
2381
+ * scoped to a single space). If they don't, this throws — mixing
2382
+ * spaces in a single delegation is not supported by the underlying
2383
+ * Rust create_delegation call and the resulting UCAN would be
2384
+ * under-specified.
2385
+ *
2386
+ * @internal
2387
+ */
2388
+ async createDelegationViaWasmPath(did, entries, expirationTime, session) {
2389
+ if (entries.length === 0) {
2390
+ throw new Error(
2391
+ "createDelegationViaWasmPath requires a non-empty entries array"
2392
+ );
2393
+ }
2394
+ const resolvedSpaces = /* @__PURE__ */ new Set();
2395
+ for (const entry of entries) {
2396
+ const spaceId2 = entry.space === "default" ? session.spaceId : entry.space;
2397
+ resolvedSpaces.add(spaceId2);
2398
+ }
2399
+ if (resolvedSpaces.size !== 1) {
2400
+ throw new Error(
2401
+ `delegateTo: all permission entries must target the same space, got ${resolvedSpaces.size}: ${JSON.stringify([...resolvedSpaces])}`
2402
+ );
2403
+ }
2404
+ const spaceId = [...resolvedSpaces][0];
2405
+ const abilities = {};
2406
+ for (const entry of entries) {
2407
+ const shortService = SERVICE_LONG_TO_SHORT[entry.service];
2408
+ if (shortService === void 0) {
2409
+ throw new Error(
2410
+ `delegateTo: unknown service '${entry.service}' \u2014 no short-form mapping`
2411
+ );
2412
+ }
2413
+ if (abilities[shortService] === void 0) {
2414
+ abilities[shortService] = {};
2415
+ }
2416
+ const pathsMap = abilities[shortService];
2417
+ const existing = pathsMap[entry.path];
2418
+ if (existing === void 0) {
2419
+ pathsMap[entry.path] = [...entry.actions];
2420
+ } else {
2421
+ const seen = new Set(existing);
2422
+ for (const action of entry.actions) {
2423
+ if (!seen.has(action)) {
2424
+ existing.push(action);
2425
+ seen.add(action);
2426
+ }
2427
+ }
2428
+ }
2429
+ }
2430
+ const serviceSession = {
2431
+ delegationHeader: session.delegationHeader,
2432
+ delegationCid: session.delegationCid,
2433
+ jwk: session.jwk,
2434
+ spaceId,
2435
+ verificationMethod: session.verificationMethod
2436
+ };
2437
+ const expirationSecs = Math.floor(expirationTime.getTime() / 1e3);
2438
+ const result = this.createDelegationWrapper({
2439
+ session: serviceSession,
2440
+ delegateDID: did,
2441
+ spaceId,
2442
+ abilities,
2443
+ expirationSecs
2444
+ });
2445
+ const primary = result.resources[0];
2446
+ return {
2447
+ cid: result.cid,
2448
+ delegationHeader: { Authorization: `Bearer ${result.delegation}` },
2449
+ spaceId,
2450
+ path: primary.path,
2451
+ actions: primary.actions,
2452
+ resources: result.resources,
2453
+ disableSubDelegation: false,
2454
+ expiry: result.expiry,
2455
+ delegateDID: did,
2456
+ ownerAddress: session.address,
2457
+ chainId: session.chainId,
2458
+ host: this.config.host
2459
+ };
2460
+ }
2461
+ /**
2462
+ * Issue a delegation via the legacy wallet-signed SIWE path for a single
2463
+ * {@link PermissionEntry}. Shares the implementation with the public
2464
+ * `createDelegation` method via {@link createDelegationWalletPath} so
2465
+ * both entry points hit exactly the same SIWE / signer / public-space
2466
+ * logic without mutual recursion.
2467
+ *
2468
+ * @internal
2469
+ */
2470
+ async createDelegationLegacyWalletPath(delegateDID, entry, expirationTime) {
2471
+ const spaceIdOverride = entry.space === "default" ? void 0 : entry.space;
2472
+ return this.createDelegationWalletPath({
2473
+ path: entry.path,
2474
+ actions: entry.actions,
2475
+ delegateDID,
2476
+ includePublicSpace: true,
2477
+ expiryMs: Math.max(0, expirationTime.getTime() - Date.now()),
2478
+ spaceIdOverride
2479
+ });
2480
+ }
2121
2481
  /**
2122
2482
  * Create a delegation from this user to another user.
2123
2483
  *
@@ -2128,6 +2488,49 @@ var TinyCloudNode = class _TinyCloudNode {
2128
2488
  * @returns A portable delegation that can be sent to the recipient
2129
2489
  */
2130
2490
  async createDelegation(params) {
2491
+ if (!this.signer) {
2492
+ throw new Error("Cannot createDelegation() in session-only mode. Requires wallet mode.");
2493
+ }
2494
+ if (!this.auth?.tinyCloudSession) {
2495
+ throw new Error("Not signed in. Call signIn() first.");
2496
+ }
2497
+ let resolvedDelegateDID = params.delegateDID;
2498
+ if (resolvedDelegateDID.endsWith(".eth") && this.config.ensResolver) {
2499
+ const address = await this.config.ensResolver.resolveAddress(resolvedDelegateDID);
2500
+ if (!address) throw new Error(`Could not resolve ENS name: ${resolvedDelegateDID}`);
2501
+ resolvedDelegateDID = `did:pkh:eip155:1:${address}`;
2502
+ }
2503
+ const entries = legacyParamsToPermissionEntries(
2504
+ params.actions,
2505
+ params.path,
2506
+ params.spaceIdOverride
2507
+ );
2508
+ try {
2509
+ const result = await this.delegateTo(
2510
+ resolvedDelegateDID,
2511
+ entries,
2512
+ params.expiryMs !== void 0 ? { expiry: params.expiryMs } : void 0
2513
+ );
2514
+ return result.delegation;
2515
+ } catch (err) {
2516
+ if (err instanceof PermissionNotInManifestError) {
2517
+ } else {
2518
+ throw err;
2519
+ }
2520
+ }
2521
+ return this.createDelegationWalletPath({
2522
+ ...params,
2523
+ delegateDID: resolvedDelegateDID
2524
+ });
2525
+ }
2526
+ /**
2527
+ * Legacy wallet-signed SIWE delegation path. Lifted from the original
2528
+ * `createDelegation` body verbatim so both the legacy public method and
2529
+ * `delegateTo({ forceWalletSign: true })` hit the same code.
2530
+ *
2531
+ * @internal
2532
+ */
2533
+ async createDelegationWalletPath(params) {
2131
2534
  if (!this.signer) {
2132
2535
  throw new Error("Cannot createDelegation() in session-only mode. Requires wallet mode.");
2133
2536
  }
@@ -2135,11 +2538,6 @@ var TinyCloudNode = class _TinyCloudNode {
2135
2538
  if (!session) {
2136
2539
  throw new Error("Not signed in. Call signIn() first.");
2137
2540
  }
2138
- if (params.delegateDID.endsWith(".eth") && this.config.ensResolver) {
2139
- const address = await this.config.ensResolver.resolveAddress(params.delegateDID);
2140
- if (!address) throw new Error(`Could not resolve ENS name: ${params.delegateDID}`);
2141
- params = { ...params, delegateDID: `did:pkh:eip155:1:${address}` };
2142
- }
2143
2541
  const abilities = {};
2144
2542
  const kvActions = params.actions.filter((a) => a.startsWith("tinycloud.kv/"));
2145
2543
  const sqlActions = params.actions.filter((a) => a.startsWith("tinycloud.sql/"));
@@ -2427,6 +2825,31 @@ var TinyCloudNode = class _TinyCloudNode {
2427
2825
  };
2428
2826
  }
2429
2827
  };
2828
+ // ===========================================================================
2829
+ // Capability-chain delegation (spec: .claude/specs/capability-chain.md)
2830
+ // ===========================================================================
2831
+ /**
2832
+ * Safety margin before the session's own expiry at which {@link delegateTo}
2833
+ * will refuse to issue a derived delegation. Prevents issuing sub-delegations
2834
+ * that would be invalid by the time the recipient used them. Spec: 60 seconds.
2835
+ *
2836
+ * @internal
2837
+ */
2838
+ _TinyCloudNode.SESSION_EXPIRY_SAFETY_MARGIN_MS = 6e4;
2839
+ var TinyCloudNode = _TinyCloudNode;
2840
+
2841
+ // src/core.ts
2842
+ import {
2843
+ PermissionNotInManifestError as PermissionNotInManifestError2,
2844
+ SessionExpiredError as SessionExpiredError2,
2845
+ ManifestValidationError,
2846
+ resolveManifest as resolveManifest2,
2847
+ validateManifest,
2848
+ loadManifest,
2849
+ isCapabilitySubset as isCapabilitySubset2,
2850
+ expandActionShortNames as expandActionShortNames2,
2851
+ parseExpiry as parseExpiry2
2852
+ } from "@tinycloud/sdk-core";
2430
2853
 
2431
2854
  // src/delegation.ts
2432
2855
  function serializeDelegation(delegation) {
@@ -2490,13 +2913,16 @@ export {
2490
2913
  DuckDbService3 as DuckDbService,
2491
2914
  FileSessionStorage,
2492
2915
  KVService3 as KVService,
2916
+ ManifestValidationError,
2493
2917
  MemorySessionStorage,
2494
2918
  NodeUserAuthorization,
2919
+ PermissionNotInManifestError2 as PermissionNotInManifestError,
2495
2920
  PrefixedKVService,
2496
2921
  ProtocolMismatchError,
2497
2922
  SQLAction,
2498
2923
  SQLService3 as SQLService,
2499
2924
  ServiceContext3 as ServiceContext,
2925
+ SessionExpiredError2 as SessionExpiredError,
2500
2926
  SharingService2 as SharingService,
2501
2927
  SilentNotificationHandler2 as SilentNotificationHandler,
2502
2928
  Space,
@@ -2519,8 +2945,14 @@ export {
2519
2945
  defaultSignStrategy,
2520
2946
  defaultSpaceCreationHandler,
2521
2947
  deserializeDelegation,
2948
+ expandActionShortNames2 as expandActionShortNames,
2949
+ isCapabilitySubset2 as isCapabilitySubset,
2950
+ loadManifest,
2522
2951
  makePublicSpaceId2 as makePublicSpaceId,
2952
+ parseExpiry2 as parseExpiry,
2523
2953
  parseSpaceUri,
2524
- serializeDelegation
2954
+ resolveManifest2 as resolveManifest,
2955
+ serializeDelegation,
2956
+ validateManifest
2525
2957
  };
2526
2958
  //# sourceMappingURL=core.js.map