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

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 CHANGED
@@ -18284,22 +18284,6 @@ function secretPermissionEntries(name, action) {
18284
18284
  function isSecretsSpace(space) {
18285
18285
  return space === SECRETS_SPACE || space.endsWith(`:${SECRETS_SPACE}`);
18286
18286
  }
18287
- function composeEscalatedManifest(manifest, additional) {
18288
- if (Array.isArray(manifest)) {
18289
- const [primary, ...rest] = manifest;
18290
- return [
18291
- {
18292
- ...primary,
18293
- permissions: [...primary.permissions ?? [], ...additional]
18294
- },
18295
- ...rest
18296
- ];
18297
- }
18298
- return {
18299
- ...manifest,
18300
- permissions: [...manifest.permissions ?? [], ...additional]
18301
- };
18302
- }
18303
18287
  var NodeSecretsService = class {
18304
18288
  constructor(config) {
18305
18289
  this.config = config;
@@ -18312,10 +18296,11 @@ var NodeSecretsService = class {
18312
18296
  return this.service.isUnlocked;
18313
18297
  }
18314
18298
  async unlock(signer) {
18315
- if (signer !== void 0) {
18316
- this.unlockSigner = signer;
18299
+ const effectiveSigner = signer ?? this.config.getUnlockSigner?.();
18300
+ if (effectiveSigner !== void 0) {
18301
+ this.unlockSigner = effectiveSigner;
18317
18302
  }
18318
- const result = await this.service.unlock(signer);
18303
+ const result = await this.service.unlock(effectiveSigner);
18319
18304
  if (result.ok) {
18320
18305
  this.shouldRestoreUnlock = true;
18321
18306
  }
@@ -18360,21 +18345,8 @@ var NodeSecretsService = class {
18360
18345
  `Cannot autosign ${actionUrn(action)} for ${name}; TinyCloudNode needs wallet mode with a signer or privateKey.`
18361
18346
  );
18362
18347
  }
18363
- const manifest = this.config.getManifest();
18364
- if (manifest === void 0) {
18365
- return secretsError(
18366
- import_sdk_core4.ErrorCodes.PERMISSION_DENIED,
18367
- `Cannot autosign ${actionUrn(action)} for ${name}; set a manifest before mutating secrets.`
18368
- );
18369
- }
18370
18348
  try {
18371
- this.config.setManifest(
18372
- composeEscalatedManifest(
18373
- manifest,
18374
- secretPermissionEntries(name, action)
18375
- )
18376
- );
18377
- await this.config.signIn();
18349
+ await this.config.grantPermissions(secretPermissionEntries(name, action));
18378
18350
  return this.restoreUnlockAfterEscalation();
18379
18351
  } catch (error) {
18380
18352
  return secretsError(
@@ -18439,6 +18411,30 @@ var _TinyCloudNode = class _TinyCloudNode {
18439
18411
  this.auth = null;
18440
18412
  this.tc = null;
18441
18413
  this._chainId = 1;
18414
+ this.runtimePermissionGrants = [];
18415
+ this.invokeWithRuntimePermissions = (session, service, path, action, facts) => {
18416
+ return this.wasmBindings.invoke(
18417
+ this.selectInvocationSession(session, service, path, action),
18418
+ service,
18419
+ path,
18420
+ action,
18421
+ facts
18422
+ );
18423
+ };
18424
+ this.invokeAnyWithRuntimePermissions = (session, entries, facts) => {
18425
+ if (!this.wasmBindings.invokeAny) {
18426
+ throw new Error("WASM binding does not support invokeAny");
18427
+ }
18428
+ const grant = this.findGrantForOperations(
18429
+ entries.map((entry) => ({
18430
+ spaceId: entry.spaceId,
18431
+ service: this.invocationServiceName(entry.service),
18432
+ path: entry.path,
18433
+ action: entry.action
18434
+ }))
18435
+ );
18436
+ return this.wasmBindings.invokeAny(grant?.session ?? session, entries, facts);
18437
+ };
18442
18438
  this.explicitHost = config.host;
18443
18439
  this.config = {
18444
18440
  ...config,
@@ -18474,7 +18470,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18474
18470
  this._sharingService = new import_sdk_core5.SharingService({
18475
18471
  hosts: [this.config.host],
18476
18472
  // session: undefined - not needed for receive()
18477
- invoke: this.wasmBindings.invoke,
18473
+ invoke: this.invokeWithRuntimePermissions,
18478
18474
  fetch: globalThis.fetch.bind(globalThis),
18479
18475
  keyProvider: this._keyProvider,
18480
18476
  registry: this._capabilityRegistry,
@@ -18542,7 +18538,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18542
18538
  includeAccountRegistryPermissions: config.includeAccountRegistryPermissions
18543
18539
  });
18544
18540
  this.tc = new import_sdk_core5.TinyCloud(this.auth, {
18545
- invokeAny: this.wasmBindings.invokeAny
18541
+ invokeAny: this.invokeAnyWithRuntimePermissions
18546
18542
  });
18547
18543
  }
18548
18544
  syncResolvedHostFromAuth() {
@@ -18667,6 +18663,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18667
18663
  this._secrets = void 0;
18668
18664
  this._spaceService = void 0;
18669
18665
  this._serviceContext = void 0;
18666
+ this.runtimePermissionGrants = [];
18670
18667
  await this.tc.signIn(options);
18671
18668
  this.syncResolvedHostFromAuth();
18672
18669
  this.initializeServices();
@@ -18756,6 +18753,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18756
18753
  this._secrets = void 0;
18757
18754
  this._spaceService = void 0;
18758
18755
  this._serviceContext = void 0;
18756
+ this.runtimePermissionGrants = [];
18759
18757
  if (sessionData.address) {
18760
18758
  this._address = sessionData.address;
18761
18759
  }
@@ -18763,8 +18761,8 @@ var _TinyCloudNode = class _TinyCloudNode {
18763
18761
  this._chainId = sessionData.chainId;
18764
18762
  }
18765
18763
  this._serviceContext = new import_sdk_core5.ServiceContext({
18766
- invoke: this.wasmBindings.invoke,
18767
- invokeAny: this.wasmBindings.invokeAny,
18764
+ invoke: this.invokeWithRuntimePermissions,
18765
+ invokeAny: this.invokeAnyWithRuntimePermissions,
18768
18766
  fetch: globalThis.fetch.bind(globalThis),
18769
18767
  hosts: [this.config.host]
18770
18768
  });
@@ -18850,7 +18848,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18850
18848
  includeAccountRegistryPermissions: this.config.includeAccountRegistryPermissions
18851
18849
  });
18852
18850
  this.tc = new import_sdk_core5.TinyCloud(this.auth, {
18853
- invokeAny: this.wasmBindings.invokeAny
18851
+ invokeAny: this.invokeAnyWithRuntimePermissions
18854
18852
  });
18855
18853
  this.config.prefix = prefix;
18856
18854
  }
@@ -18894,7 +18892,7 @@ var _TinyCloudNode = class _TinyCloudNode {
18894
18892
  includeAccountRegistryPermissions: this.config.includeAccountRegistryPermissions
18895
18893
  });
18896
18894
  this.tc = new import_sdk_core5.TinyCloud(this.auth, {
18897
- invokeAny: this.wasmBindings.invokeAny
18895
+ invokeAny: this.invokeAnyWithRuntimePermissions
18898
18896
  });
18899
18897
  this.config.prefix = prefix;
18900
18898
  }
@@ -18907,10 +18905,10 @@ var _TinyCloudNode = class _TinyCloudNode {
18907
18905
  if (!session) {
18908
18906
  return;
18909
18907
  }
18910
- this.tc.initializeServices(this.wasmBindings.invoke, [this.config.host]);
18908
+ this.tc.initializeServices(this.invokeWithRuntimePermissions, [this.config.host]);
18911
18909
  this._serviceContext = new import_sdk_core5.ServiceContext({
18912
- invoke: this.wasmBindings.invoke,
18913
- invokeAny: this.wasmBindings.invokeAny,
18910
+ invoke: this.invokeWithRuntimePermissions,
18911
+ invokeAny: this.invokeAnyWithRuntimePermissions,
18914
18912
  fetch: globalThis.fetch.bind(globalThis),
18915
18913
  hosts: [this.config.host]
18916
18914
  });
@@ -19080,7 +19078,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19080
19078
  this._delegationManager = new import_sdk_core5.DelegationManager({
19081
19079
  hosts: [this.config.host],
19082
19080
  session: serviceSession,
19083
- invoke: this.wasmBindings.invoke,
19081
+ invoke: this.invokeWithRuntimePermissions,
19084
19082
  fetch: globalThis.fetch.bind(globalThis)
19085
19083
  });
19086
19084
  this._spaceService = new import_sdk_core5.SpaceService({
@@ -19347,9 +19345,9 @@ var _TinyCloudNode = class _TinyCloudNode {
19347
19345
  this._secrets = new NodeSecretsService({
19348
19346
  getService: () => this.getBaseSecrets(),
19349
19347
  getManifest: () => this.manifest,
19350
- setManifest: (manifest) => this.setManifest(manifest),
19351
- signIn: () => this.signIn(),
19352
- canEscalate: () => this.signer !== void 0 && this.tc !== void 0
19348
+ grantPermissions: (additional) => this.grantRuntimePermissions(additional),
19349
+ canEscalate: () => this.signer !== void 0 && this.tc !== void 0,
19350
+ getUnlockSigner: () => this.signer ?? void 0
19353
19351
  });
19354
19352
  }
19355
19353
  return this._secrets;
@@ -19433,6 +19431,171 @@ var _TinyCloudNode = class _TinyCloudNode {
19433
19431
  }
19434
19432
  };
19435
19433
  }
19434
+ /**
19435
+ * Check whether the current session or an approved runtime delegation covers
19436
+ * every requested permission.
19437
+ */
19438
+ hasRuntimePermissions(permissions) {
19439
+ const session = this.auth?.tinyCloudSession;
19440
+ if (!session || !Array.isArray(permissions) || permissions.length === 0) {
19441
+ return false;
19442
+ }
19443
+ const expanded = this.expandPermissionEntries(permissions);
19444
+ if (this.sessionCoversPermissionEntries(session, expanded)) {
19445
+ return true;
19446
+ }
19447
+ return this.findRuntimeGrantsForPermissionEntries(expanded, session).length > 0;
19448
+ }
19449
+ /**
19450
+ * Return installed runtime permission delegations. When `permissions` is
19451
+ * provided, only delegations currently covering those permissions are
19452
+ * returned. Base-session manifest permissions are not represented here.
19453
+ */
19454
+ getRuntimePermissionDelegations(permissions) {
19455
+ this.pruneExpiredRuntimePermissionGrants();
19456
+ if (permissions === void 0) {
19457
+ return this.runtimePermissionGrants.map((grant) => grant.delegation);
19458
+ }
19459
+ const session = this.auth?.tinyCloudSession;
19460
+ if (!session || !Array.isArray(permissions) || permissions.length === 0) {
19461
+ return [];
19462
+ }
19463
+ const expanded = this.expandPermissionEntries(permissions);
19464
+ return this.findRuntimeGrantsForPermissionEntries(expanded, session).map(
19465
+ (grant) => grant.delegation
19466
+ );
19467
+ }
19468
+ /**
19469
+ * Install a portable runtime permission delegation into this SDK instance so
19470
+ * matching service calls and downstream `delegateTo()` calls can use it.
19471
+ */
19472
+ async useRuntimeDelegation(delegation) {
19473
+ const session = this.auth?.tinyCloudSession;
19474
+ if (!session) {
19475
+ throw new import_sdk_core5.SessionExpiredError(/* @__PURE__ */ new Date(0));
19476
+ }
19477
+ if (delegation.expiry.getTime() <= Date.now()) {
19478
+ throw new import_sdk_core5.SessionExpiredError(delegation.expiry);
19479
+ }
19480
+ const expectedDids = /* @__PURE__ */ new Set([session.verificationMethod, this.sessionDid]);
19481
+ if (!expectedDids.has(delegation.delegateDID)) {
19482
+ throw new Error(
19483
+ `Runtime delegation targets ${delegation.delegateDID} but this session key is ${session.verificationMethod}.`
19484
+ );
19485
+ }
19486
+ const targetHost = delegation.host ?? this.config.host;
19487
+ const activateResult = await (0, import_sdk_core5.activateSessionWithHost)(
19488
+ targetHost,
19489
+ delegation.delegationHeader
19490
+ );
19491
+ if (!activateResult.success) {
19492
+ throw new Error(
19493
+ `Failed to activate runtime permission delegation: ${activateResult.error}`
19494
+ );
19495
+ }
19496
+ this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
19497
+ (grant) => grant.delegation.cid !== delegation.cid
19498
+ );
19499
+ this.runtimePermissionGrants.push(
19500
+ this.runtimeGrantFromDelegation(delegation, session)
19501
+ );
19502
+ }
19503
+ /**
19504
+ * Store additional permissions as narrow delegations to the current session
19505
+ * key. Future service invocations automatically use a stored delegation when
19506
+ * its `(space, service, path, action)` covers the request.
19507
+ */
19508
+ async grantRuntimePermissions(permissions, options) {
19509
+ if (!Array.isArray(permissions) || permissions.length === 0) {
19510
+ throw new Error("grantRuntimePermissions requires a non-empty permissions array");
19511
+ }
19512
+ const session = this.auth?.tinyCloudSession;
19513
+ if (!session) {
19514
+ throw new import_sdk_core5.SessionExpiredError(/* @__PURE__ */ new Date(0));
19515
+ }
19516
+ const sessionExpiry = extractSiweExpiration(session.siwe);
19517
+ if (sessionExpiry !== void 0) {
19518
+ const marginMs = _TinyCloudNode.SESSION_EXPIRY_SAFETY_MARGIN_MS;
19519
+ if (sessionExpiry.getTime() <= Date.now() + marginMs) {
19520
+ throw new import_sdk_core5.SessionExpiredError(sessionExpiry);
19521
+ }
19522
+ }
19523
+ const expanded = this.expandPermissionEntries(permissions);
19524
+ if (this.sessionCoversPermissionEntries(session, expanded)) {
19525
+ return [];
19526
+ }
19527
+ const existingGrants = this.findRuntimeGrantsForPermissionEntries(expanded, session);
19528
+ if (existingGrants.length > 0) {
19529
+ return existingGrants.map((grant) => grant.delegation);
19530
+ }
19531
+ if (!this.signer) {
19532
+ throw new Error(
19533
+ "grantRuntimePermissions requires wallet mode with a signer or privateKey."
19534
+ );
19535
+ }
19536
+ const bySpace = /* @__PURE__ */ new Map();
19537
+ for (const entry of expanded) {
19538
+ const spaceId = this.resolvePermissionSpace(entry.space, session);
19539
+ const current = bySpace.get(spaceId) ?? [];
19540
+ current.push(entry);
19541
+ bySpace.set(spaceId, current);
19542
+ }
19543
+ const now = /* @__PURE__ */ new Date();
19544
+ const requestedExpiryMs = resolveExpiryMs(options?.expiry);
19545
+ let expiresAt = new Date(now.getTime() + requestedExpiryMs);
19546
+ if (sessionExpiry !== void 0 && sessionExpiry < expiresAt) {
19547
+ expiresAt = sessionExpiry;
19548
+ }
19549
+ const delegations = [];
19550
+ for (const [spaceId, entries] of bySpace) {
19551
+ const abilities = this.permissionsToAbilities(entries);
19552
+ const prepared = this.wasmBindings.prepareSession({
19553
+ abilities,
19554
+ address: this.wasmBindings.ensureEip55(session.address),
19555
+ chainId: session.chainId,
19556
+ domain: this.siweDomain,
19557
+ issuedAt: now.toISOString(),
19558
+ expirationTime: expiresAt.toISOString(),
19559
+ spaceId,
19560
+ jwk: session.jwk
19561
+ });
19562
+ const signature2 = await this.signer.signMessage(prepared.siwe);
19563
+ const delegatedSession = this.wasmBindings.completeSessionSetup({
19564
+ ...prepared,
19565
+ signature: signature2
19566
+ });
19567
+ const activateResult = await (0, import_sdk_core5.activateSessionWithHost)(
19568
+ this.config.host,
19569
+ delegatedSession.delegationHeader
19570
+ );
19571
+ if (!activateResult.success) {
19572
+ throw new Error(
19573
+ `Failed to activate runtime permission delegation: ${activateResult.error}`
19574
+ );
19575
+ }
19576
+ const delegation = this.runtimeDelegationFromSession(
19577
+ delegatedSession,
19578
+ entries,
19579
+ spaceId,
19580
+ session,
19581
+ expiresAt
19582
+ );
19583
+ this.runtimePermissionGrants.push({
19584
+ session: {
19585
+ delegationHeader: delegatedSession.delegationHeader,
19586
+ delegationCid: delegatedSession.delegationCid,
19587
+ spaceId,
19588
+ verificationMethod: session.verificationMethod,
19589
+ jwk: session.jwk
19590
+ },
19591
+ delegation,
19592
+ operations: this.permissionOperations(entries, spaceId),
19593
+ expiresAt
19594
+ });
19595
+ delegations.push(delegation);
19596
+ }
19597
+ return delegations;
19598
+ }
19436
19599
  /**
19437
19600
  * Get the DelegationManager for delegation CRUD operations.
19438
19601
  *
@@ -19621,7 +19784,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19621
19784
  if (this._serviceContext) {
19622
19785
  const publicKV = new import_sdk_core5.KVService({ prefix: "" });
19623
19786
  const publicContext = new import_sdk_core5.ServiceContext({
19624
- invoke: this.wasmBindings.invoke,
19787
+ invoke: this.invokeWithRuntimePermissions,
19625
19788
  fetch: this._serviceContext.fetch,
19626
19789
  hosts: this._serviceContext.hosts
19627
19790
  });
@@ -19709,8 +19872,9 @@ var _TinyCloudNode = class _TinyCloudNode {
19709
19872
  * Issue a delegation using the capability-chain flow.
19710
19873
  *
19711
19874
  * When every requested permission is a subset of the current
19712
- * session's recap, the delegation is signed by the session key via
19713
- * WASM no wallet prompt. When at least one is NOT derivable, a
19875
+ * session's recap, or of one installed runtime permission delegation,
19876
+ * the delegation is signed by the session key via WASM no wallet
19877
+ * prompt. When at least one is NOT derivable, a
19714
19878
  * {@link PermissionNotInManifestError} is raised (carrying the
19715
19879
  * missing entries) so the caller can trigger an escalation flow
19716
19880
  * (e.g. `TinyCloudWeb.requestPermissions`). Passing
@@ -19790,6 +19954,23 @@ var _TinyCloudNode = class _TinyCloudNode {
19790
19954
  );
19791
19955
  const { subset, missing } = (0, import_sdk_core5.isCapabilitySubset)(expandedEntries, granted);
19792
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 import_sdk_core5.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
+ }
19793
19974
  throw new import_sdk_core5.PermissionNotInManifestError(missing, granted);
19794
19975
  }
19795
19976
  const delegation = await this.createDelegationViaWasmPath(
@@ -19934,6 +20115,41 @@ var _TinyCloudNode = class _TinyCloudNode {
19934
20115
  host: this.config.host
19935
20116
  };
19936
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 (0, import_sdk_core5.activateSessionWithHost)(
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
+ }
19937
20153
  resolvePermissionSpace(space, session) {
19938
20154
  if (space === void 0) {
19939
20155
  return this.wasmBindings.makeSpaceId(
@@ -19950,6 +20166,223 @@ var _TinyCloudNode = class _TinyCloudNode {
19950
20166
  }
19951
20167
  return this.wasmBindings.makeSpaceId(session.address, session.chainId, space);
19952
20168
  }
20169
+ expandPermissionEntries(permissions) {
20170
+ return permissions.map((entry) => ({
20171
+ ...entry,
20172
+ actions: (0, import_sdk_core5.expandActionShortNames)(entry.service, entry.actions)
20173
+ }));
20174
+ }
20175
+ shortServiceName(service) {
20176
+ const short = import_sdk_core5.SERVICE_LONG_TO_SHORT[service];
20177
+ if (short === void 0) {
20178
+ throw new Error(
20179
+ `unknown service '${service}' \u2014 no short-form mapping`
20180
+ );
20181
+ }
20182
+ return short;
20183
+ }
20184
+ permissionsToAbilities(entries) {
20185
+ const abilities = {};
20186
+ for (const entry of entries) {
20187
+ const service = this.shortServiceName(entry.service);
20188
+ abilities[service] ?? (abilities[service] = {});
20189
+ const existing = abilities[service][entry.path] ?? [];
20190
+ const seen = new Set(existing);
20191
+ for (const action of entry.actions) {
20192
+ if (!seen.has(action)) {
20193
+ existing.push(action);
20194
+ seen.add(action);
20195
+ }
20196
+ }
20197
+ abilities[service][entry.path] = existing;
20198
+ }
20199
+ return abilities;
20200
+ }
20201
+ permissionOperations(entries, spaceId) {
20202
+ return entries.flatMap((entry) => {
20203
+ const service = this.shortServiceName(entry.service);
20204
+ return entry.actions.map((action) => ({
20205
+ spaceId,
20206
+ service,
20207
+ path: entry.path,
20208
+ action
20209
+ }));
20210
+ });
20211
+ }
20212
+ sessionCoversPermissionEntries(session, entries) {
20213
+ try {
20214
+ const granted = (0, import_sdk_core5.parseRecapCapabilities)(
20215
+ (siwe) => this.wasmBindings.parseRecapFromSiwe(siwe),
20216
+ session.siwe
20217
+ );
20218
+ return (0, import_sdk_core5.isCapabilitySubset)(entries, granted).subset;
20219
+ } catch {
20220
+ return false;
20221
+ }
20222
+ }
20223
+ permissionEntriesToOperations(entries, session) {
20224
+ return entries.flatMap((entry) => {
20225
+ const spaceId = this.resolvePermissionSpace(entry.space, session);
20226
+ const service = this.shortServiceName(entry.service);
20227
+ return entry.actions.map((action) => ({
20228
+ spaceId,
20229
+ service,
20230
+ path: entry.path,
20231
+ action
20232
+ }));
20233
+ });
20234
+ }
20235
+ findRuntimeGrantsForPermissionEntries(entries, session) {
20236
+ const grants = [];
20237
+ const operations = this.permissionEntriesToOperations(entries, session);
20238
+ if (operations.length === 0) {
20239
+ return grants;
20240
+ }
20241
+ for (const operation of operations) {
20242
+ const grant = this.findGrantForOperation(operation);
20243
+ if (!grant) {
20244
+ return [];
20245
+ }
20246
+ if (!grants.includes(grant)) {
20247
+ grants.push(grant);
20248
+ }
20249
+ }
20250
+ return grants;
20251
+ }
20252
+ runtimeDelegationFromSession(delegatedSession, entries, spaceId, session, expiresAt) {
20253
+ const resources = this.delegatedResourcesForEntries(entries, spaceId);
20254
+ const primary = resources[0];
20255
+ return {
20256
+ cid: delegatedSession.delegationCid,
20257
+ delegationHeader: delegatedSession.delegationHeader,
20258
+ spaceId,
20259
+ path: primary.path,
20260
+ actions: primary.actions,
20261
+ resources,
20262
+ disableSubDelegation: false,
20263
+ expiry: expiresAt,
20264
+ delegateDID: session.verificationMethod,
20265
+ ownerAddress: session.address,
20266
+ chainId: session.chainId,
20267
+ host: this.config.host
20268
+ };
20269
+ }
20270
+ runtimeGrantFromDelegation(delegation, session) {
20271
+ const operations = this.operationsFromDelegation(delegation);
20272
+ return {
20273
+ session: {
20274
+ delegationHeader: delegation.delegationHeader,
20275
+ delegationCid: delegation.cid,
20276
+ spaceId: delegation.spaceId,
20277
+ verificationMethod: session.verificationMethod,
20278
+ jwk: session.jwk
20279
+ },
20280
+ delegation,
20281
+ operations,
20282
+ expiresAt: delegation.expiry
20283
+ };
20284
+ }
20285
+ delegatedResourcesForEntries(entries, spaceId) {
20286
+ return entries.map((entry) => ({
20287
+ service: this.shortServiceName(entry.service),
20288
+ space: spaceId,
20289
+ path: entry.path,
20290
+ actions: [...entry.actions]
20291
+ }));
20292
+ }
20293
+ operationsFromDelegation(delegation) {
20294
+ const resources = delegation.resources !== void 0 && delegation.resources.length > 0 ? delegation.resources : this.flatDelegationResources(delegation);
20295
+ return resources.flatMap(
20296
+ (resource) => resource.actions.map((action) => ({
20297
+ spaceId: resource.space,
20298
+ service: this.invocationServiceName(resource.service),
20299
+ path: resource.path,
20300
+ action
20301
+ }))
20302
+ );
20303
+ }
20304
+ flatDelegationResources(delegation) {
20305
+ const byService = /* @__PURE__ */ new Map();
20306
+ for (const action of delegation.actions) {
20307
+ const service = this.shortServiceName(action.split("/")[0]);
20308
+ const actions = byService.get(service) ?? [];
20309
+ actions.push(action);
20310
+ byService.set(service, actions);
20311
+ }
20312
+ return [...byService.entries()].map(([service, actions]) => ({
20313
+ service,
20314
+ space: delegation.spaceId,
20315
+ path: delegation.path,
20316
+ actions
20317
+ }));
20318
+ }
20319
+ selectInvocationSession(fallback, service, path, action) {
20320
+ const grant = this.findGrantForOperation({
20321
+ spaceId: fallback.spaceId,
20322
+ service: this.invocationServiceName(service),
20323
+ path,
20324
+ action
20325
+ });
20326
+ return grant?.session ?? fallback;
20327
+ }
20328
+ findGrantForOperations(operations) {
20329
+ if (operations.length === 0) {
20330
+ return void 0;
20331
+ }
20332
+ this.pruneExpiredRuntimePermissionGrants();
20333
+ return this.runtimePermissionGrants.find((grant) => {
20334
+ return operations.every(
20335
+ (operation) => grant.operations.some(
20336
+ (granted) => this.operationCovers(granted, operation)
20337
+ )
20338
+ );
20339
+ });
20340
+ }
20341
+ findGrantForOperation(operation) {
20342
+ return this.findGrantForOperations([operation]);
20343
+ }
20344
+ pruneExpiredRuntimePermissionGrants() {
20345
+ const now = Date.now();
20346
+ this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
20347
+ (grant) => grant.expiresAt.getTime() > now
20348
+ );
20349
+ }
20350
+ operationCovers(granted, requested) {
20351
+ return granted.spaceId === requested.spaceId && granted.service === requested.service && this.actionContains(granted.action, requested.action) && this.pathContains(granted.path, requested.path);
20352
+ }
20353
+ actionContains(grantedAction, requestedAction) {
20354
+ if (grantedAction === requestedAction) {
20355
+ return true;
20356
+ }
20357
+ if (grantedAction.endsWith("/*")) {
20358
+ const prefix = grantedAction.slice(0, -2);
20359
+ return requestedAction.startsWith(`${prefix}/`);
20360
+ }
20361
+ return false;
20362
+ }
20363
+ invocationServiceName(service) {
20364
+ return service.startsWith("tinycloud.") ? this.shortServiceName(service) : service;
20365
+ }
20366
+ pathContains(grantedPath, requestedPath) {
20367
+ if (grantedPath === "" || grantedPath === "/") {
20368
+ return true;
20369
+ }
20370
+ if (grantedPath.endsWith("/**")) {
20371
+ return requestedPath.startsWith(grantedPath.slice(0, -3));
20372
+ }
20373
+ if (grantedPath.endsWith("/*")) {
20374
+ const prefix = grantedPath.slice(0, -2);
20375
+ if (!requestedPath.startsWith(prefix)) {
20376
+ return false;
20377
+ }
20378
+ const remainder = requestedPath.slice(prefix.length);
20379
+ return !remainder.includes("/") || remainder === "/";
20380
+ }
20381
+ if (grantedPath.endsWith("/")) {
20382
+ return requestedPath.startsWith(grantedPath);
20383
+ }
20384
+ return grantedPath === requestedPath;
20385
+ }
19953
20386
  /**
19954
20387
  * Issue a delegation via the legacy wallet-signed SIWE path for a single
19955
20388
  * {@link PermissionEntry}. Shares the implementation with the public