@tinycloud/node-sdk 2.2.0-beta.7 → 2.2.0-beta.9

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.js CHANGED
@@ -17172,7 +17172,7 @@ import {
17172
17172
  ACCOUNT_REGISTRY_SPACE,
17173
17173
  PermissionNotInManifestError,
17174
17174
  SessionExpiredError,
17175
- expandActionShortNames,
17175
+ expandPermissionEntries as expandPermissionEntriesCore,
17176
17176
  isCapabilitySubset,
17177
17177
  parseRecapCapabilities,
17178
17178
  SERVICE_LONG_TO_SHORT
@@ -18264,49 +18264,29 @@ function secretsError(code, message, cause) {
18264
18264
  }
18265
18265
  };
18266
18266
  }
18267
- function actionUrn(action) {
18268
- return `tinycloud.kv/${action}`;
18267
+ function displayActionUrn(action) {
18268
+ return action === "put" ? "tinycloud.vault/write" : "tinycloud.vault/delete";
18269
18269
  }
18270
- function secretResourcePath(base2, name) {
18271
- return `${base2}/${SECRET_PREFIX}${name}`;
18270
+ function kvActionUrn(action) {
18271
+ return `tinycloud.kv/${action}`;
18272
18272
  }
18273
18273
  function secretPermissionEntries(name, action) {
18274
18274
  return [
18275
18275
  {
18276
- service: "tinycloud.kv",
18277
- space: SECRETS_SPACE,
18278
- path: secretResourcePath("keys", name),
18279
- actions: [action],
18280
- skipPrefix: true
18281
- },
18282
- {
18283
- service: "tinycloud.kv",
18276
+ service: "tinycloud.vault",
18284
18277
  space: SECRETS_SPACE,
18285
- path: secretResourcePath("vault", name),
18286
- actions: [action],
18278
+ path: `${SECRET_PREFIX}${name}`,
18279
+ actions: [action === "put" ? "write" : "delete"],
18287
18280
  skipPrefix: true
18288
18281
  }
18289
18282
  ];
18290
18283
  }
18284
+ function secretResourcePath(base2, name) {
18285
+ return `${base2}/${SECRET_PREFIX}${name}`;
18286
+ }
18291
18287
  function isSecretsSpace(space) {
18292
18288
  return space === SECRETS_SPACE || space.endsWith(`:${SECRETS_SPACE}`);
18293
18289
  }
18294
- function composeEscalatedManifest(manifest, additional) {
18295
- if (Array.isArray(manifest)) {
18296
- const [primary, ...rest] = manifest;
18297
- return [
18298
- {
18299
- ...primary,
18300
- permissions: [...primary.permissions ?? [], ...additional]
18301
- },
18302
- ...rest
18303
- ];
18304
- }
18305
- return {
18306
- ...manifest,
18307
- permissions: [...manifest.permissions ?? [], ...additional]
18308
- };
18309
- }
18310
18290
  var NodeSecretsService = class {
18311
18291
  constructor(config) {
18312
18292
  this.config = config;
@@ -18319,10 +18299,11 @@ var NodeSecretsService = class {
18319
18299
  return this.service.isUnlocked;
18320
18300
  }
18321
18301
  async unlock(signer) {
18322
- if (signer !== void 0) {
18323
- this.unlockSigner = signer;
18302
+ const effectiveSigner = signer ?? this.config.getUnlockSigner?.();
18303
+ if (effectiveSigner !== void 0) {
18304
+ this.unlockSigner = effectiveSigner;
18324
18305
  }
18325
- const result = await this.service.unlock(signer);
18306
+ const result = await this.service.unlock(effectiveSigner);
18326
18307
  if (result.ok) {
18327
18308
  this.shouldRestoreUnlock = true;
18328
18309
  }
@@ -18364,29 +18345,16 @@ var NodeSecretsService = class {
18364
18345
  if (!this.config.canEscalate()) {
18365
18346
  return secretsError(
18366
18347
  ErrorCodes.PERMISSION_DENIED,
18367
- `Cannot autosign ${actionUrn(action)} for ${name}; TinyCloudNode needs wallet mode with a signer or privateKey.`
18368
- );
18369
- }
18370
- const manifest = this.config.getManifest();
18371
- if (manifest === void 0) {
18372
- return secretsError(
18373
- ErrorCodes.PERMISSION_DENIED,
18374
- `Cannot autosign ${actionUrn(action)} for ${name}; set a manifest before mutating secrets.`
18348
+ `Cannot autosign ${displayActionUrn(action)} for ${name}; TinyCloudNode needs wallet mode with a signer or privateKey.`
18375
18349
  );
18376
18350
  }
18377
18351
  try {
18378
- this.config.setManifest(
18379
- composeEscalatedManifest(
18380
- manifest,
18381
- secretPermissionEntries(name, action)
18382
- )
18383
- );
18384
- await this.config.signIn();
18352
+ await this.config.grantPermissions(secretPermissionEntries(name, action));
18385
18353
  return this.restoreUnlockAfterEscalation();
18386
18354
  } catch (error) {
18387
18355
  return secretsError(
18388
18356
  ErrorCodes.PERMISSION_DENIED,
18389
- error instanceof Error ? error.message : `Autosign escalation for ${actionUrn(action)} on ${name} failed.`,
18357
+ error instanceof Error ? error.message : `Autosign escalation for ${displayActionUrn(action)} on ${name} failed.`,
18390
18358
  error instanceof Error ? error : void 0
18391
18359
  );
18392
18360
  }
@@ -18403,7 +18371,7 @@ var NodeSecretsService = class {
18403
18371
  return false;
18404
18372
  }
18405
18373
  const manifests = Array.isArray(manifest) ? manifest : [manifest];
18406
- const requiredAction = actionUrn(action);
18374
+ const requiredAction = kvActionUrn(action);
18407
18375
  return manifests.some((entry) => {
18408
18376
  const resolved = resolveManifest(entry);
18409
18377
  return ["keys", "vault"].every(
@@ -18446,6 +18414,30 @@ var _TinyCloudNode = class _TinyCloudNode {
18446
18414
  this.auth = null;
18447
18415
  this.tc = null;
18448
18416
  this._chainId = 1;
18417
+ this.runtimePermissionGrants = [];
18418
+ this.invokeWithRuntimePermissions = (session, service, path, action, facts) => {
18419
+ return this.wasmBindings.invoke(
18420
+ this.selectInvocationSession(session, service, path, action),
18421
+ service,
18422
+ path,
18423
+ action,
18424
+ facts
18425
+ );
18426
+ };
18427
+ this.invokeAnyWithRuntimePermissions = (session, entries, facts) => {
18428
+ if (!this.wasmBindings.invokeAny) {
18429
+ throw new Error("WASM binding does not support invokeAny");
18430
+ }
18431
+ const grant = this.findGrantForOperations(
18432
+ entries.map((entry) => ({
18433
+ spaceId: entry.spaceId,
18434
+ service: this.invocationServiceName(entry.service),
18435
+ path: entry.path,
18436
+ action: entry.action
18437
+ }))
18438
+ );
18439
+ return this.wasmBindings.invokeAny(grant?.session ?? session, entries, facts);
18440
+ };
18449
18441
  this.explicitHost = config.host;
18450
18442
  this.config = {
18451
18443
  ...config,
@@ -18481,7 +18473,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18481
18473
  this._sharingService = new SharingService({
18482
18474
  hosts: [this.config.host],
18483
18475
  // session: undefined - not needed for receive()
18484
- invoke: this.wasmBindings.invoke,
18476
+ invoke: this.invokeWithRuntimePermissions,
18485
18477
  fetch: globalThis.fetch.bind(globalThis),
18486
18478
  keyProvider: this._keyProvider,
18487
18479
  registry: this._capabilityRegistry,
@@ -18549,7 +18541,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18549
18541
  includeAccountRegistryPermissions: config.includeAccountRegistryPermissions
18550
18542
  });
18551
18543
  this.tc = new TinyCloud(this.auth, {
18552
- invokeAny: this.wasmBindings.invokeAny
18544
+ invokeAny: this.invokeAnyWithRuntimePermissions
18553
18545
  });
18554
18546
  }
18555
18547
  syncResolvedHostFromAuth() {
@@ -18674,6 +18666,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18674
18666
  this._secrets = void 0;
18675
18667
  this._spaceService = void 0;
18676
18668
  this._serviceContext = void 0;
18669
+ this.runtimePermissionGrants = [];
18677
18670
  await this.tc.signIn(options);
18678
18671
  this.syncResolvedHostFromAuth();
18679
18672
  this.initializeServices();
@@ -18763,6 +18756,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18763
18756
  this._secrets = void 0;
18764
18757
  this._spaceService = void 0;
18765
18758
  this._serviceContext = void 0;
18759
+ this.runtimePermissionGrants = [];
18766
18760
  if (sessionData.address) {
18767
18761
  this._address = sessionData.address;
18768
18762
  }
@@ -18770,8 +18764,8 @@ var _TinyCloudNode = class _TinyCloudNode {
18770
18764
  this._chainId = sessionData.chainId;
18771
18765
  }
18772
18766
  this._serviceContext = new ServiceContext2({
18773
- invoke: this.wasmBindings.invoke,
18774
- invokeAny: this.wasmBindings.invokeAny,
18767
+ invoke: this.invokeWithRuntimePermissions,
18768
+ invokeAny: this.invokeAnyWithRuntimePermissions,
18775
18769
  fetch: globalThis.fetch.bind(globalThis),
18776
18770
  hosts: [this.config.host]
18777
18771
  });
@@ -18857,7 +18851,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18857
18851
  includeAccountRegistryPermissions: this.config.includeAccountRegistryPermissions
18858
18852
  });
18859
18853
  this.tc = new TinyCloud(this.auth, {
18860
- invokeAny: this.wasmBindings.invokeAny
18854
+ invokeAny: this.invokeAnyWithRuntimePermissions
18861
18855
  });
18862
18856
  this.config.prefix = prefix;
18863
18857
  }
@@ -18901,7 +18895,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18901
18895
  includeAccountRegistryPermissions: this.config.includeAccountRegistryPermissions
18902
18896
  });
18903
18897
  this.tc = new TinyCloud(this.auth, {
18904
- invokeAny: this.wasmBindings.invokeAny
18898
+ invokeAny: this.invokeAnyWithRuntimePermissions
18905
18899
  });
18906
18900
  this.config.prefix = prefix;
18907
18901
  }
@@ -18914,10 +18908,10 @@ var _TinyCloudNode = class _TinyCloudNode {
18914
18908
  if (!session) {
18915
18909
  return;
18916
18910
  }
18917
- this.tc.initializeServices(this.wasmBindings.invoke, [this.config.host]);
18911
+ this.tc.initializeServices(this.invokeWithRuntimePermissions, [this.config.host]);
18918
18912
  this._serviceContext = new ServiceContext2({
18919
- invoke: this.wasmBindings.invoke,
18920
- invokeAny: this.wasmBindings.invokeAny,
18913
+ invoke: this.invokeWithRuntimePermissions,
18914
+ invokeAny: this.invokeAnyWithRuntimePermissions,
18921
18915
  fetch: globalThis.fetch.bind(globalThis),
18922
18916
  hosts: [this.config.host]
18923
18917
  });
@@ -19087,7 +19081,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19087
19081
  this._delegationManager = new DelegationManager({
19088
19082
  hosts: [this.config.host],
19089
19083
  session: serviceSession,
19090
- invoke: this.wasmBindings.invoke,
19084
+ invoke: this.invokeWithRuntimePermissions,
19091
19085
  fetch: globalThis.fetch.bind(globalThis)
19092
19086
  });
19093
19087
  this._spaceService = new SpaceService({
@@ -19354,9 +19348,9 @@ var _TinyCloudNode = class _TinyCloudNode {
19354
19348
  this._secrets = new NodeSecretsService({
19355
19349
  getService: () => this.getBaseSecrets(),
19356
19350
  getManifest: () => this.manifest,
19357
- setManifest: (manifest) => this.setManifest(manifest),
19358
- signIn: () => this.signIn(),
19359
- canEscalate: () => this.signer !== void 0 && this.tc !== void 0
19351
+ grantPermissions: (additional) => this.grantRuntimePermissions(additional),
19352
+ canEscalate: () => this.signer !== void 0 && this.tc !== void 0,
19353
+ getUnlockSigner: () => this.signer ?? void 0
19360
19354
  });
19361
19355
  }
19362
19356
  return this._secrets;
@@ -19440,6 +19434,171 @@ var _TinyCloudNode = class _TinyCloudNode {
19440
19434
  }
19441
19435
  };
19442
19436
  }
19437
+ /**
19438
+ * Check whether the current session or an approved runtime delegation covers
19439
+ * every requested permission.
19440
+ */
19441
+ hasRuntimePermissions(permissions) {
19442
+ const session = this.auth?.tinyCloudSession;
19443
+ if (!session || !Array.isArray(permissions) || permissions.length === 0) {
19444
+ return false;
19445
+ }
19446
+ const expanded = this.expandPermissionEntries(permissions);
19447
+ if (this.sessionCoversPermissionEntries(session, expanded)) {
19448
+ return true;
19449
+ }
19450
+ return this.findRuntimeGrantsForPermissionEntries(expanded, session).length > 0;
19451
+ }
19452
+ /**
19453
+ * Return installed runtime permission delegations. When `permissions` is
19454
+ * provided, only delegations currently covering those permissions are
19455
+ * returned. Base-session manifest permissions are not represented here.
19456
+ */
19457
+ getRuntimePermissionDelegations(permissions) {
19458
+ this.pruneExpiredRuntimePermissionGrants();
19459
+ if (permissions === void 0) {
19460
+ return this.runtimePermissionGrants.map((grant) => grant.delegation);
19461
+ }
19462
+ const session = this.auth?.tinyCloudSession;
19463
+ if (!session || !Array.isArray(permissions) || permissions.length === 0) {
19464
+ return [];
19465
+ }
19466
+ const expanded = this.expandPermissionEntries(permissions);
19467
+ return this.findRuntimeGrantsForPermissionEntries(expanded, session).map(
19468
+ (grant) => grant.delegation
19469
+ );
19470
+ }
19471
+ /**
19472
+ * Install a portable runtime permission delegation into this SDK instance so
19473
+ * matching service calls and downstream `delegateTo()` calls can use it.
19474
+ */
19475
+ async useRuntimeDelegation(delegation) {
19476
+ const session = this.auth?.tinyCloudSession;
19477
+ if (!session) {
19478
+ throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
19479
+ }
19480
+ if (delegation.expiry.getTime() <= Date.now()) {
19481
+ throw new SessionExpiredError(delegation.expiry);
19482
+ }
19483
+ const expectedDids = /* @__PURE__ */ new Set([session.verificationMethod, this.sessionDid]);
19484
+ if (!expectedDids.has(delegation.delegateDID)) {
19485
+ throw new Error(
19486
+ `Runtime delegation targets ${delegation.delegateDID} but this session key is ${session.verificationMethod}.`
19487
+ );
19488
+ }
19489
+ const targetHost = delegation.host ?? this.config.host;
19490
+ const activateResult = await activateSessionWithHost2(
19491
+ targetHost,
19492
+ delegation.delegationHeader
19493
+ );
19494
+ if (!activateResult.success) {
19495
+ throw new Error(
19496
+ `Failed to activate runtime permission delegation: ${activateResult.error}`
19497
+ );
19498
+ }
19499
+ this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
19500
+ (grant) => grant.delegation.cid !== delegation.cid
19501
+ );
19502
+ this.runtimePermissionGrants.push(
19503
+ this.runtimeGrantFromDelegation(delegation, session)
19504
+ );
19505
+ }
19506
+ /**
19507
+ * Store additional permissions as narrow delegations to the current session
19508
+ * key. Future service invocations automatically use a stored delegation when
19509
+ * its `(space, service, path, action)` covers the request.
19510
+ */
19511
+ async grantRuntimePermissions(permissions, options) {
19512
+ if (!Array.isArray(permissions) || permissions.length === 0) {
19513
+ throw new Error("grantRuntimePermissions requires a non-empty permissions array");
19514
+ }
19515
+ const session = this.auth?.tinyCloudSession;
19516
+ if (!session) {
19517
+ throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
19518
+ }
19519
+ const sessionExpiry = extractSiweExpiration(session.siwe);
19520
+ if (sessionExpiry !== void 0) {
19521
+ const marginMs = _TinyCloudNode.SESSION_EXPIRY_SAFETY_MARGIN_MS;
19522
+ if (sessionExpiry.getTime() <= Date.now() + marginMs) {
19523
+ throw new SessionExpiredError(sessionExpiry);
19524
+ }
19525
+ }
19526
+ const expanded = this.expandPermissionEntries(permissions);
19527
+ if (this.sessionCoversPermissionEntries(session, expanded)) {
19528
+ return [];
19529
+ }
19530
+ const existingGrants = this.findRuntimeGrantsForPermissionEntries(expanded, session);
19531
+ if (existingGrants.length > 0) {
19532
+ return existingGrants.map((grant) => grant.delegation);
19533
+ }
19534
+ if (!this.signer) {
19535
+ throw new Error(
19536
+ "grantRuntimePermissions requires wallet mode with a signer or privateKey."
19537
+ );
19538
+ }
19539
+ const bySpace = /* @__PURE__ */ new Map();
19540
+ for (const entry of expanded) {
19541
+ const spaceId = this.resolvePermissionSpace(entry.space, session);
19542
+ const current = bySpace.get(spaceId) ?? [];
19543
+ current.push(entry);
19544
+ bySpace.set(spaceId, current);
19545
+ }
19546
+ const now = /* @__PURE__ */ new Date();
19547
+ const requestedExpiryMs = resolveExpiryMs(options?.expiry);
19548
+ let expiresAt = new Date(now.getTime() + requestedExpiryMs);
19549
+ if (sessionExpiry !== void 0 && sessionExpiry < expiresAt) {
19550
+ expiresAt = sessionExpiry;
19551
+ }
19552
+ const delegations = [];
19553
+ for (const [spaceId, entries] of bySpace) {
19554
+ const abilities = this.permissionsToAbilities(entries);
19555
+ const prepared = this.wasmBindings.prepareSession({
19556
+ abilities,
19557
+ address: this.wasmBindings.ensureEip55(session.address),
19558
+ chainId: session.chainId,
19559
+ domain: this.siweDomain,
19560
+ issuedAt: now.toISOString(),
19561
+ expirationTime: expiresAt.toISOString(),
19562
+ spaceId,
19563
+ jwk: session.jwk
19564
+ });
19565
+ const signature2 = await this.signer.signMessage(prepared.siwe);
19566
+ const delegatedSession = this.wasmBindings.completeSessionSetup({
19567
+ ...prepared,
19568
+ signature: signature2
19569
+ });
19570
+ const activateResult = await activateSessionWithHost2(
19571
+ this.config.host,
19572
+ delegatedSession.delegationHeader
19573
+ );
19574
+ if (!activateResult.success) {
19575
+ throw new Error(
19576
+ `Failed to activate runtime permission delegation: ${activateResult.error}`
19577
+ );
19578
+ }
19579
+ const delegation = this.runtimeDelegationFromSession(
19580
+ delegatedSession,
19581
+ entries,
19582
+ spaceId,
19583
+ session,
19584
+ expiresAt
19585
+ );
19586
+ this.runtimePermissionGrants.push({
19587
+ session: {
19588
+ delegationHeader: delegatedSession.delegationHeader,
19589
+ delegationCid: delegatedSession.delegationCid,
19590
+ spaceId,
19591
+ verificationMethod: session.verificationMethod,
19592
+ jwk: session.jwk
19593
+ },
19594
+ delegation,
19595
+ operations: this.permissionOperations(entries, spaceId),
19596
+ expiresAt
19597
+ });
19598
+ delegations.push(delegation);
19599
+ }
19600
+ return delegations;
19601
+ }
19443
19602
  /**
19444
19603
  * Get the DelegationManager for delegation CRUD operations.
19445
19604
  *
@@ -19628,7 +19787,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19628
19787
  if (this._serviceContext) {
19629
19788
  const publicKV = new KVService2({ prefix: "" });
19630
19789
  const publicContext = new ServiceContext2({
19631
- invoke: this.wasmBindings.invoke,
19790
+ invoke: this.invokeWithRuntimePermissions,
19632
19791
  fetch: this._serviceContext.fetch,
19633
19792
  hosts: this._serviceContext.hosts
19634
19793
  });
@@ -19716,8 +19875,9 @@ var _TinyCloudNode = class _TinyCloudNode {
19716
19875
  * Issue a delegation using the capability-chain flow.
19717
19876
  *
19718
19877
  * When every requested permission is a subset of the current
19719
- * session's recap, the delegation is signed by the session key via
19720
- * WASM no wallet prompt. When at least one is NOT derivable, a
19878
+ * session's recap, or of one installed runtime permission delegation,
19879
+ * the delegation is signed by the session key via WASM no wallet
19880
+ * prompt. When at least one is NOT derivable, a
19721
19881
  * {@link PermissionNotInManifestError} is raised (carrying the
19722
19882
  * missing entries) so the caller can trigger an escalation flow
19723
19883
  * (e.g. `TinyCloudWeb.requestPermissions`). Passing
@@ -19767,10 +19927,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19767
19927
  "delegateTo requires a non-empty permissions array"
19768
19928
  );
19769
19929
  }
19770
- const expandedEntries = permissions.map((entry) => ({
19771
- ...entry,
19772
- actions: expandActionShortNames(entry.service, entry.actions)
19773
- }));
19930
+ const expandedEntries = this.expandPermissionEntries(permissions);
19774
19931
  const now = /* @__PURE__ */ new Date();
19775
19932
  const expiryMs = resolveExpiryMs(options?.expiry);
19776
19933
  const expirationTime = new Date(now.getTime() + expiryMs);
@@ -19797,6 +19954,23 @@ var _TinyCloudNode = class _TinyCloudNode {
19797
19954
  );
19798
19955
  const { subset, missing } = isCapabilitySubset(expandedEntries, granted);
19799
19956
  if (!subset) {
19957
+ const runtimeGrant = this.findGrantForOperations(
19958
+ this.permissionEntriesToOperations(expandedEntries, session)
19959
+ );
19960
+ if (runtimeGrant) {
19961
+ const marginMs = _TinyCloudNode.SESSION_EXPIRY_SAFETY_MARGIN_MS;
19962
+ if (runtimeGrant.expiresAt.getTime() <= Date.now() + marginMs) {
19963
+ throw new SessionExpiredError(runtimeGrant.expiresAt);
19964
+ }
19965
+ const runtimeExpiration = runtimeGrant.expiresAt < effectiveExpiration ? runtimeGrant.expiresAt : effectiveExpiration;
19966
+ const delegation2 = await this.createDelegationViaRuntimeGrant(
19967
+ did,
19968
+ expandedEntries,
19969
+ runtimeExpiration,
19970
+ runtimeGrant
19971
+ );
19972
+ return { delegation: delegation2, prompted: false };
19973
+ }
19800
19974
  throw new PermissionNotInManifestError(missing, granted);
19801
19975
  }
19802
19976
  const delegation = await this.createDelegationViaWasmPath(
@@ -19941,6 +20115,41 @@ var _TinyCloudNode = class _TinyCloudNode {
19941
20115
  host: this.config.host
19942
20116
  };
19943
20117
  }
20118
+ async createDelegationViaRuntimeGrant(did, entries, expirationTime, grant) {
20119
+ const result = this.createDelegationWrapper({
20120
+ session: grant.session,
20121
+ delegateDID: did,
20122
+ spaceId: grant.session.spaceId,
20123
+ abilities: this.permissionsToAbilities(entries),
20124
+ expirationSecs: Math.floor(expirationTime.getTime() / 1e3)
20125
+ });
20126
+ const primary = result.resources[0];
20127
+ const delegationHeader = { Authorization: result.delegation };
20128
+ const targetHost = grant.delegation.host ?? this.config.host;
20129
+ const activateResult = await activateSessionWithHost2(
20130
+ targetHost,
20131
+ delegationHeader
20132
+ );
20133
+ if (!activateResult.success) {
20134
+ throw new Error(
20135
+ `Failed to activate delegation with host: ${activateResult.error}`
20136
+ );
20137
+ }
20138
+ return {
20139
+ cid: result.cid,
20140
+ delegationHeader,
20141
+ spaceId: grant.session.spaceId,
20142
+ path: primary.path,
20143
+ actions: primary.actions,
20144
+ resources: result.resources,
20145
+ disableSubDelegation: false,
20146
+ expiry: result.expiry,
20147
+ delegateDID: did,
20148
+ ownerAddress: grant.delegation.ownerAddress,
20149
+ chainId: grant.delegation.chainId,
20150
+ host: targetHost
20151
+ };
20152
+ }
19944
20153
  resolvePermissionSpace(space, session) {
19945
20154
  if (space === void 0) {
19946
20155
  return this.wasmBindings.makeSpaceId(
@@ -19957,6 +20166,220 @@ var _TinyCloudNode = class _TinyCloudNode {
19957
20166
  }
19958
20167
  return this.wasmBindings.makeSpaceId(session.address, session.chainId, space);
19959
20168
  }
20169
+ expandPermissionEntries(permissions) {
20170
+ return expandPermissionEntriesCore(permissions);
20171
+ }
20172
+ shortServiceName(service) {
20173
+ const short = SERVICE_LONG_TO_SHORT[service];
20174
+ if (short === void 0) {
20175
+ throw new Error(
20176
+ `unknown service '${service}' \u2014 no short-form mapping`
20177
+ );
20178
+ }
20179
+ return short;
20180
+ }
20181
+ permissionsToAbilities(entries) {
20182
+ const abilities = {};
20183
+ for (const entry of entries) {
20184
+ const service = this.shortServiceName(entry.service);
20185
+ abilities[service] ?? (abilities[service] = {});
20186
+ const existing = abilities[service][entry.path] ?? [];
20187
+ const seen = new Set(existing);
20188
+ for (const action of entry.actions) {
20189
+ if (!seen.has(action)) {
20190
+ existing.push(action);
20191
+ seen.add(action);
20192
+ }
20193
+ }
20194
+ abilities[service][entry.path] = existing;
20195
+ }
20196
+ return abilities;
20197
+ }
20198
+ permissionOperations(entries, spaceId) {
20199
+ return entries.flatMap((entry) => {
20200
+ const service = this.shortServiceName(entry.service);
20201
+ return entry.actions.map((action) => ({
20202
+ spaceId,
20203
+ service,
20204
+ path: entry.path,
20205
+ action
20206
+ }));
20207
+ });
20208
+ }
20209
+ sessionCoversPermissionEntries(session, entries) {
20210
+ try {
20211
+ const granted = parseRecapCapabilities(
20212
+ (siwe) => this.wasmBindings.parseRecapFromSiwe(siwe),
20213
+ session.siwe
20214
+ );
20215
+ return isCapabilitySubset(entries, granted).subset;
20216
+ } catch {
20217
+ return false;
20218
+ }
20219
+ }
20220
+ permissionEntriesToOperations(entries, session) {
20221
+ return entries.flatMap((entry) => {
20222
+ const spaceId = this.resolvePermissionSpace(entry.space, session);
20223
+ const service = this.shortServiceName(entry.service);
20224
+ return entry.actions.map((action) => ({
20225
+ spaceId,
20226
+ service,
20227
+ path: entry.path,
20228
+ action
20229
+ }));
20230
+ });
20231
+ }
20232
+ findRuntimeGrantsForPermissionEntries(entries, session) {
20233
+ const grants = [];
20234
+ const operations = this.permissionEntriesToOperations(entries, session);
20235
+ if (operations.length === 0) {
20236
+ return grants;
20237
+ }
20238
+ for (const operation of operations) {
20239
+ const grant = this.findGrantForOperation(operation);
20240
+ if (!grant) {
20241
+ return [];
20242
+ }
20243
+ if (!grants.includes(grant)) {
20244
+ grants.push(grant);
20245
+ }
20246
+ }
20247
+ return grants;
20248
+ }
20249
+ runtimeDelegationFromSession(delegatedSession, entries, spaceId, session, expiresAt) {
20250
+ const resources = this.delegatedResourcesForEntries(entries, spaceId);
20251
+ const primary = resources[0];
20252
+ return {
20253
+ cid: delegatedSession.delegationCid,
20254
+ delegationHeader: delegatedSession.delegationHeader,
20255
+ spaceId,
20256
+ path: primary.path,
20257
+ actions: primary.actions,
20258
+ resources,
20259
+ disableSubDelegation: false,
20260
+ expiry: expiresAt,
20261
+ delegateDID: session.verificationMethod,
20262
+ ownerAddress: session.address,
20263
+ chainId: session.chainId,
20264
+ host: this.config.host
20265
+ };
20266
+ }
20267
+ runtimeGrantFromDelegation(delegation, session) {
20268
+ const operations = this.operationsFromDelegation(delegation);
20269
+ return {
20270
+ session: {
20271
+ delegationHeader: delegation.delegationHeader,
20272
+ delegationCid: delegation.cid,
20273
+ spaceId: delegation.spaceId,
20274
+ verificationMethod: session.verificationMethod,
20275
+ jwk: session.jwk
20276
+ },
20277
+ delegation,
20278
+ operations,
20279
+ expiresAt: delegation.expiry
20280
+ };
20281
+ }
20282
+ delegatedResourcesForEntries(entries, spaceId) {
20283
+ return entries.map((entry) => ({
20284
+ service: this.shortServiceName(entry.service),
20285
+ space: spaceId,
20286
+ path: entry.path,
20287
+ actions: [...entry.actions]
20288
+ }));
20289
+ }
20290
+ operationsFromDelegation(delegation) {
20291
+ const resources = delegation.resources !== void 0 && delegation.resources.length > 0 ? delegation.resources : this.flatDelegationResources(delegation);
20292
+ return resources.flatMap(
20293
+ (resource) => resource.actions.map((action) => ({
20294
+ spaceId: resource.space,
20295
+ service: this.invocationServiceName(resource.service),
20296
+ path: resource.path,
20297
+ action
20298
+ }))
20299
+ );
20300
+ }
20301
+ flatDelegationResources(delegation) {
20302
+ const byService = /* @__PURE__ */ new Map();
20303
+ for (const action of delegation.actions) {
20304
+ const service = this.shortServiceName(action.split("/")[0]);
20305
+ const actions = byService.get(service) ?? [];
20306
+ actions.push(action);
20307
+ byService.set(service, actions);
20308
+ }
20309
+ return [...byService.entries()].map(([service, actions]) => ({
20310
+ service,
20311
+ space: delegation.spaceId,
20312
+ path: delegation.path,
20313
+ actions
20314
+ }));
20315
+ }
20316
+ selectInvocationSession(fallback, service, path, action) {
20317
+ const grant = this.findGrantForOperation({
20318
+ spaceId: fallback.spaceId,
20319
+ service: this.invocationServiceName(service),
20320
+ path,
20321
+ action
20322
+ });
20323
+ return grant?.session ?? fallback;
20324
+ }
20325
+ findGrantForOperations(operations) {
20326
+ if (operations.length === 0) {
20327
+ return void 0;
20328
+ }
20329
+ this.pruneExpiredRuntimePermissionGrants();
20330
+ return this.runtimePermissionGrants.find((grant) => {
20331
+ return operations.every(
20332
+ (operation) => grant.operations.some(
20333
+ (granted) => this.operationCovers(granted, operation)
20334
+ )
20335
+ );
20336
+ });
20337
+ }
20338
+ findGrantForOperation(operation) {
20339
+ return this.findGrantForOperations([operation]);
20340
+ }
20341
+ pruneExpiredRuntimePermissionGrants() {
20342
+ const now = Date.now();
20343
+ this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
20344
+ (grant) => grant.expiresAt.getTime() > now
20345
+ );
20346
+ }
20347
+ operationCovers(granted, requested) {
20348
+ return granted.spaceId === requested.spaceId && granted.service === requested.service && this.actionContains(granted.action, requested.action) && this.pathContains(granted.path, requested.path);
20349
+ }
20350
+ actionContains(grantedAction, requestedAction) {
20351
+ if (grantedAction === requestedAction) {
20352
+ return true;
20353
+ }
20354
+ if (grantedAction.endsWith("/*")) {
20355
+ const prefix = grantedAction.slice(0, -2);
20356
+ return requestedAction.startsWith(`${prefix}/`);
20357
+ }
20358
+ return false;
20359
+ }
20360
+ invocationServiceName(service) {
20361
+ return service.startsWith("tinycloud.") ? this.shortServiceName(service) : service;
20362
+ }
20363
+ pathContains(grantedPath, requestedPath) {
20364
+ if (grantedPath === "" || grantedPath === "/") {
20365
+ return true;
20366
+ }
20367
+ if (grantedPath.endsWith("/**")) {
20368
+ return requestedPath.startsWith(grantedPath.slice(0, -3));
20369
+ }
20370
+ if (grantedPath.endsWith("/*")) {
20371
+ const prefix = grantedPath.slice(0, -2);
20372
+ if (!requestedPath.startsWith(prefix)) {
20373
+ return false;
20374
+ }
20375
+ const remainder = requestedPath.slice(prefix.length);
20376
+ return !remainder.includes("/") || remainder === "/";
20377
+ }
20378
+ if (grantedPath.endsWith("/")) {
20379
+ return requestedPath.startsWith(grantedPath);
20380
+ }
20381
+ return grantedPath === requestedPath;
20382
+ }
19960
20383
  /**
19961
20384
  * Issue a delegation via the legacy wallet-signed SIWE path for a single
19962
20385
  * {@link PermissionEntry}. Shares the implementation with the public
@@ -20478,6 +20901,7 @@ import {
20478
20901
  ACCOUNT_REGISTRY_SPACE as ACCOUNT_REGISTRY_SPACE2,
20479
20902
  DEFAULT_MANIFEST_SPACE as DEFAULT_MANIFEST_SPACE2,
20480
20903
  DEFAULT_MANIFEST_VERSION,
20904
+ VAULT_PERMISSION_SERVICE,
20481
20905
  PermissionNotInManifestError as PermissionNotInManifestError2,
20482
20906
  SessionExpiredError as SessionExpiredError2,
20483
20907
  ManifestValidationError,
@@ -20486,7 +20910,9 @@ import {
20486
20910
  validateManifest,
20487
20911
  loadManifest,
20488
20912
  isCapabilitySubset as isCapabilitySubset2,
20489
- expandActionShortNames as expandActionShortNames2,
20913
+ expandActionShortNames,
20914
+ expandPermissionEntries,
20915
+ expandPermissionEntry,
20490
20916
  parseExpiry as parseExpiry2,
20491
20917
  resourceCapabilitiesToSpaceAbilitiesMap as resourceCapabilitiesToSpaceAbilitiesMap2
20492
20918
  } from "@tinycloud/sdk-core";
@@ -20590,6 +21016,7 @@ export {
20590
21016
  TinyCloud2 as TinyCloud,
20591
21017
  TinyCloudNode,
20592
21018
  UnsupportedFeatureError2 as UnsupportedFeatureError,
21019
+ VAULT_PERMISSION_SERVICE,
20593
21020
  VaultHeaders,
20594
21021
  VaultPublicSpaceKVActions,
20595
21022
  VersionCheckError,
@@ -20605,7 +21032,9 @@ export {
20605
21032
  defaultSignStrategy,
20606
21033
  defaultSpaceCreationHandler,
20607
21034
  deserializeDelegation,
20608
- expandActionShortNames2 as expandActionShortNames,
21035
+ expandActionShortNames,
21036
+ expandPermissionEntries,
21037
+ expandPermissionEntry,
20609
21038
  isCapabilitySubset2 as isCapabilitySubset,
20610
21039
  loadManifest,
20611
21040
  makePublicSpaceId2 as makePublicSpaceId,