@tinycloud/sdk-core 2.1.0 → 2.2.0-beta.1

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
@@ -30,12 +30,16 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ ACCOUNT_REGISTRY_PATH: () => ACCOUNT_REGISTRY_PATH,
34
+ ACCOUNT_REGISTRY_SPACE: () => ACCOUNT_REGISTRY_SPACE,
33
35
  AutoApproveSpaceCreationHandler: () => AutoApproveSpaceCreationHandler,
34
36
  CapabilityKeyRegistry: () => CapabilityKeyRegistry,
35
37
  CapabilityKeyRegistryErrorCodes: () => CapabilityKeyRegistryErrorCodes,
36
38
  ClientSessionSchema: () => ClientSessionSchema,
37
39
  DEFAULT_DEFAULTS: () => DEFAULT_DEFAULTS,
38
40
  DEFAULT_EXPIRY: () => DEFAULT_EXPIRY,
41
+ DEFAULT_MANIFEST_SPACE: () => DEFAULT_MANIFEST_SPACE,
42
+ DEFAULT_MANIFEST_VERSION: () => DEFAULT_MANIFEST_VERSION,
39
43
  DataVaultService: () => import_sdk_services4.DataVaultService,
40
44
  DatabaseHandle: () => import_sdk_services4.DatabaseHandle,
41
45
  DelegationErrorCodes: () => DelegationErrorCodes,
@@ -73,6 +77,7 @@ __export(index_exports, {
73
77
  applyPrefix: () => applyPrefix,
74
78
  buildSpaceUri: () => buildSpaceUri,
75
79
  checkNodeInfo: () => checkNodeInfo,
80
+ composeManifestRequest: () => composeManifestRequest,
76
81
  createCapabilityKeyRegistry: () => createCapabilityKeyRegistry,
77
82
  createSharingService: () => createSharingService,
78
83
  createSpaceService: () => createSpaceService,
@@ -94,6 +99,7 @@ __export(index_exports, {
94
99
  parseSpaceUri: () => parseSpaceUri,
95
100
  resolveManifest: () => resolveManifest,
96
101
  resourceCapabilitiesToAbilitiesMap: () => resourceCapabilitiesToAbilitiesMap,
102
+ resourceCapabilitiesToSpaceAbilitiesMap: () => resourceCapabilitiesToSpaceAbilitiesMap,
97
103
  serviceError: () => import_sdk_services4.serviceError,
98
104
  submitHostDelegation: () => submitHostDelegation,
99
105
  validateClientSession: () => validateClientSession,
@@ -2740,6 +2746,10 @@ var ManifestValidationError = class extends Error {
2740
2746
  };
2741
2747
  var DEFAULT_EXPIRY = "30d";
2742
2748
  var DEFAULT_DEFAULTS = true;
2749
+ var DEFAULT_MANIFEST_VERSION = 1;
2750
+ var DEFAULT_MANIFEST_SPACE = "applications";
2751
+ var ACCOUNT_REGISTRY_SPACE = "account";
2752
+ var ACCOUNT_REGISTRY_PATH = "applications/";
2743
2753
  var SERVICE_SHORT_TO_LONG = Object.freeze({
2744
2754
  kv: "tinycloud.kv",
2745
2755
  sql: "tinycloud.sql",
@@ -2755,19 +2765,19 @@ var SERVICE_LONG_TO_SHORT = Object.freeze(
2755
2765
  var DEFAULT_STANDARD_ENTRIES = [
2756
2766
  {
2757
2767
  service: "tinycloud.kv",
2758
- space: "default",
2768
+ space: DEFAULT_MANIFEST_SPACE,
2759
2769
  path: "/",
2760
2770
  actions: ["get", "put", "del", "list", "metadata"]
2761
2771
  },
2762
2772
  {
2763
2773
  service: "tinycloud.sql",
2764
- space: "default",
2774
+ space: DEFAULT_MANIFEST_SPACE,
2765
2775
  path: "/",
2766
2776
  actions: ["read", "write"]
2767
2777
  },
2768
2778
  {
2769
2779
  service: "tinycloud.capabilities",
2770
- space: "default",
2780
+ space: DEFAULT_MANIFEST_SPACE,
2771
2781
  path: "/",
2772
2782
  actions: ["read"]
2773
2783
  }
@@ -2775,19 +2785,19 @@ var DEFAULT_STANDARD_ENTRIES = [
2775
2785
  var DEFAULT_ADMIN_ENTRIES = [
2776
2786
  {
2777
2787
  service: "tinycloud.kv",
2778
- space: "default",
2788
+ space: DEFAULT_MANIFEST_SPACE,
2779
2789
  path: "/",
2780
2790
  actions: ["get", "put", "del", "list", "metadata"]
2781
2791
  },
2782
2792
  {
2783
2793
  service: "tinycloud.sql",
2784
- space: "default",
2794
+ space: DEFAULT_MANIFEST_SPACE,
2785
2795
  path: "/",
2786
2796
  actions: ["read", "write", "ddl"]
2787
2797
  },
2788
2798
  {
2789
2799
  service: "tinycloud.capabilities",
2790
- space: "default",
2800
+ space: DEFAULT_MANIFEST_SPACE,
2791
2801
  path: "/",
2792
2802
  actions: ["read", "admin"]
2793
2803
  }
@@ -2795,25 +2805,25 @@ var DEFAULT_ADMIN_ENTRIES = [
2795
2805
  var DEFAULT_ALL_ENTRIES = [
2796
2806
  {
2797
2807
  service: "tinycloud.kv",
2798
- space: "default",
2808
+ space: DEFAULT_MANIFEST_SPACE,
2799
2809
  path: "/",
2800
2810
  actions: ["get", "put", "del", "list", "metadata"]
2801
2811
  },
2802
2812
  {
2803
2813
  service: "tinycloud.sql",
2804
- space: "default",
2814
+ space: DEFAULT_MANIFEST_SPACE,
2805
2815
  path: "/",
2806
2816
  actions: ["read", "write", "ddl"]
2807
2817
  },
2808
2818
  {
2809
2819
  service: "tinycloud.duckdb",
2810
- space: "default",
2820
+ space: DEFAULT_MANIFEST_SPACE,
2811
2821
  path: "/",
2812
2822
  actions: ["read", "write"]
2813
2823
  },
2814
2824
  {
2815
2825
  service: "tinycloud.capabilities",
2816
- space: "default",
2826
+ space: DEFAULT_MANIFEST_SPACE,
2817
2827
  path: "/",
2818
2828
  actions: ["read", "admin"]
2819
2829
  }
@@ -2824,9 +2834,7 @@ function parseExpiry(duration) {
2824
2834
  `expiry must be a non-empty duration string (got ${JSON.stringify(duration)})`
2825
2835
  );
2826
2836
  }
2827
- const parsed = (0, import_ms.default)(
2828
- duration
2829
- );
2837
+ const parsed = (0, import_ms.default)(duration);
2830
2838
  if (typeof parsed !== "number" || !Number.isFinite(parsed) || parsed <= 0) {
2831
2839
  throw new ManifestValidationError(
2832
2840
  `invalid expiry duration: ${JSON.stringify(duration)}`
@@ -2875,46 +2883,44 @@ function validateManifest(input) {
2875
2883
  throw new ManifestValidationError("manifest must be an object");
2876
2884
  }
2877
2885
  const m = input;
2878
- if (typeof m.id !== "string" || m.id.length === 0) {
2879
- throw new ManifestValidationError("manifest.id is required and must be a non-empty string");
2886
+ if (m.manifest_version !== void 0 && m.manifest_version !== DEFAULT_MANIFEST_VERSION) {
2887
+ throw new ManifestValidationError(
2888
+ `manifest.manifest_version must be ${DEFAULT_MANIFEST_VERSION}`
2889
+ );
2890
+ }
2891
+ if (typeof m.app_id !== "string" || m.app_id.length === 0) {
2892
+ throw new ManifestValidationError(
2893
+ "manifest.app_id is required and must be a non-empty string"
2894
+ );
2880
2895
  }
2881
2896
  if (typeof m.name !== "string" || m.name.length === 0) {
2882
- throw new ManifestValidationError("manifest.name is required and must be a non-empty string");
2897
+ throw new ManifestValidationError(
2898
+ "manifest.name is required and must be a non-empty string"
2899
+ );
2900
+ }
2901
+ if (m.did !== void 0 && (typeof m.did !== "string" || m.did.length === 0)) {
2902
+ throw new ManifestValidationError(
2903
+ "manifest.did must be a non-empty DID string"
2904
+ );
2905
+ }
2906
+ if (m.space !== void 0 && (typeof m.space !== "string" || m.space.length === 0)) {
2907
+ throw new ManifestValidationError(
2908
+ "manifest.space must be a non-empty string"
2909
+ );
2883
2910
  }
2884
2911
  if (m.expiry !== void 0) {
2885
2912
  parseExpiry(m.expiry);
2886
2913
  }
2887
2914
  if (m.permissions !== void 0) {
2888
2915
  if (!Array.isArray(m.permissions)) {
2889
- throw new ManifestValidationError("manifest.permissions must be an array");
2916
+ throw new ManifestValidationError(
2917
+ "manifest.permissions must be an array"
2918
+ );
2890
2919
  }
2891
2920
  m.permissions.forEach(
2892
2921
  (p, i) => validatePermissionEntry(p, `permissions[${i}]`)
2893
2922
  );
2894
2923
  }
2895
- if (m.delegations !== void 0) {
2896
- if (!Array.isArray(m.delegations)) {
2897
- throw new ManifestValidationError("manifest.delegations must be an array");
2898
- }
2899
- m.delegations.forEach((d, i) => {
2900
- if (typeof d?.to !== "string" || d.to.length === 0) {
2901
- throw new ManifestValidationError(
2902
- `delegations[${i}].to is required and must be a non-empty DID string`
2903
- );
2904
- }
2905
- if (d.expiry !== void 0) {
2906
- parseExpiry(d.expiry);
2907
- }
2908
- if (!Array.isArray(d.permissions)) {
2909
- throw new ManifestValidationError(
2910
- `delegations[${i}].permissions must be an array`
2911
- );
2912
- }
2913
- d.permissions.forEach(
2914
- (p, j) => validatePermissionEntry(p, `delegations[${i}].permissions[${j}]`)
2915
- );
2916
- });
2917
- }
2918
2924
  return m;
2919
2925
  }
2920
2926
  function validatePermissionEntry(p, path) {
@@ -2925,8 +2931,10 @@ function validatePermissionEntry(p, path) {
2925
2931
  if (typeof entry.service !== "string" || entry.service.length === 0) {
2926
2932
  throw new ManifestValidationError(`${path}.service is required`);
2927
2933
  }
2928
- if (typeof entry.space !== "string" || entry.space.length === 0) {
2929
- throw new ManifestValidationError(`${path}.space is required`);
2934
+ if (entry.space !== void 0 && (typeof entry.space !== "string" || entry.space.length === 0)) {
2935
+ throw new ManifestValidationError(
2936
+ `${path}.space must be a non-empty string`
2937
+ );
2930
2938
  }
2931
2939
  if (typeof entry.path !== "string") {
2932
2940
  throw new ManifestValidationError(
@@ -2972,7 +2980,8 @@ function defaultEntriesForTier(tier) {
2972
2980
  }
2973
2981
  function resolveManifest(input) {
2974
2982
  const manifest = validateManifest(input);
2975
- const prefix = manifest.prefix !== void 0 ? manifest.prefix : manifest.id;
2983
+ const prefix = manifest.prefix !== void 0 ? manifest.prefix : manifest.app_id;
2984
+ const space = manifest.space ?? DEFAULT_MANIFEST_SPACE;
2976
2985
  const expiryMs = parseExpiry(manifest.expiry ?? DEFAULT_EXPIRY);
2977
2986
  const includePublicSpace = manifest.includePublicSpace ?? true;
2978
2987
  const tier = normalizeDefaults(manifest.defaults);
@@ -2980,29 +2989,27 @@ function resolveManifest(input) {
2980
2989
  const explicitEntries = manifest.permissions ?? [];
2981
2990
  const allEntries = [...defaultEntries, ...explicitEntries];
2982
2991
  const resources = allEntries.map(
2983
- (entry) => resolveEntry(entry, prefix, expiryMs)
2992
+ (entry) => resolveEntry(entry, prefix, expiryMs, space)
2984
2993
  );
2985
- const additionalDelegates = (manifest.delegations ?? []).map((d) => ({
2986
- did: d.to,
2987
- name: d.name,
2988
- expiryMs: parseExpiry(d.expiry ?? manifest.expiry ?? DEFAULT_EXPIRY),
2989
- permissions: d.permissions.map(
2990
- (entry) => resolveEntry(
2991
- entry,
2992
- prefix,
2993
- parseExpiry(d.expiry ?? manifest.expiry ?? DEFAULT_EXPIRY)
2994
- )
2995
- )
2996
- }));
2994
+ const additionalDelegates = manifest.did === void 0 ? [] : [
2995
+ {
2996
+ did: manifest.did,
2997
+ name: manifest.name,
2998
+ expiryMs,
2999
+ permissions: resources.map(cloneResourceCapability)
3000
+ }
3001
+ ];
2997
3002
  return {
2998
- id: manifest.id,
3003
+ app_id: manifest.app_id,
3004
+ ...manifest.did !== void 0 ? { did: manifest.did } : {},
3005
+ space,
2999
3006
  resources,
3000
3007
  expiryMs,
3001
3008
  includePublicSpace,
3002
3009
  additionalDelegates
3003
3010
  };
3004
3011
  }
3005
- function resolveEntry(entry, prefix, _inheritedExpiryMs) {
3012
+ function resolveEntry(entry, prefix, _inheritedExpiryMs, inheritedSpace) {
3006
3013
  const resolvedPath = applyPrefix(
3007
3014
  prefix,
3008
3015
  entry.path,
@@ -3012,13 +3019,110 @@ function resolveEntry(entry, prefix, _inheritedExpiryMs) {
3012
3019
  const entryExpiryMs = entry.expiry !== void 0 ? parseExpiry(entry.expiry) : void 0;
3013
3020
  return {
3014
3021
  service: entry.service,
3015
- space: entry.space,
3022
+ space: entry.space ?? inheritedSpace,
3016
3023
  path: resolvedPath,
3017
3024
  actions: resolvedActions,
3018
3025
  // Only populate `expiryMs` when the entry had its own expiry override.
3019
3026
  // When absent, callers use the parent (delegation or manifest) expiry
3020
3027
  // which is carried on ResolvedDelegate.expiryMs / ResolvedCapabilities.expiryMs.
3021
- ...entryExpiryMs !== void 0 ? { expiryMs: entryExpiryMs } : {}
3028
+ ...entryExpiryMs !== void 0 ? { expiryMs: entryExpiryMs } : {},
3029
+ ...entry.description !== void 0 ? { description: entry.description } : {}
3030
+ };
3031
+ }
3032
+ function cloneResourceCapability(entry) {
3033
+ return {
3034
+ service: entry.service,
3035
+ space: entry.space,
3036
+ path: entry.path,
3037
+ actions: [...entry.actions],
3038
+ ...entry.expiryMs !== void 0 ? { expiryMs: entry.expiryMs } : {},
3039
+ ...entry.description !== void 0 ? { description: entry.description } : {}
3040
+ };
3041
+ }
3042
+ function clonePermissionEntry(entry) {
3043
+ return {
3044
+ service: entry.service,
3045
+ ...entry.space !== void 0 ? { space: entry.space } : {},
3046
+ path: entry.path,
3047
+ actions: [...entry.actions],
3048
+ ...entry.skipPrefix !== void 0 ? { skipPrefix: entry.skipPrefix } : {},
3049
+ ...entry.expiry !== void 0 ? { expiry: entry.expiry } : {},
3050
+ ...entry.description !== void 0 ? { description: entry.description } : {}
3051
+ };
3052
+ }
3053
+ function dedupeResources(resources) {
3054
+ const byKey = /* @__PURE__ */ new Map();
3055
+ for (const resource of resources) {
3056
+ const key = `${resource.service}\0${resource.space}\0${resource.path}\0${resource.expiryMs ?? ""}`;
3057
+ const existing = byKey.get(key);
3058
+ if (existing === void 0) {
3059
+ byKey.set(key, cloneResourceCapability(resource));
3060
+ continue;
3061
+ }
3062
+ const seen = new Set(existing.actions);
3063
+ for (const action of resource.actions) {
3064
+ if (!seen.has(action)) {
3065
+ existing.actions.push(action);
3066
+ seen.add(action);
3067
+ }
3068
+ }
3069
+ if (existing.description === void 0 && resource.description !== void 0) {
3070
+ existing.description = resource.description;
3071
+ }
3072
+ }
3073
+ return [...byKey.values()];
3074
+ }
3075
+ function accountRegistryPermission() {
3076
+ return {
3077
+ service: "tinycloud.kv",
3078
+ space: ACCOUNT_REGISTRY_SPACE,
3079
+ path: ACCOUNT_REGISTRY_PATH,
3080
+ actions: ["tinycloud.kv/get", "tinycloud.kv/put", "tinycloud.kv/list"]
3081
+ };
3082
+ }
3083
+ function composeManifestRequest(inputs, options = {}) {
3084
+ if (!Array.isArray(inputs) || inputs.length === 0) {
3085
+ throw new ManifestValidationError(
3086
+ "composeManifestRequest requires at least one manifest"
3087
+ );
3088
+ }
3089
+ const includeAccountRegistryPermissions = options.includeAccountRegistryPermissions ?? true;
3090
+ const manifests = inputs.map(validateManifest);
3091
+ const resolved = manifests.map(resolveManifest);
3092
+ const resources = resolved.flatMap((entry) => entry.resources);
3093
+ const delegationTargets = resolved.flatMap(
3094
+ (entry) => entry.additionalDelegates.map((delegate) => ({
3095
+ ...delegate,
3096
+ permissions: dedupeResources(delegate.permissions)
3097
+ }))
3098
+ );
3099
+ if (includeAccountRegistryPermissions) {
3100
+ resources.push(accountRegistryPermission());
3101
+ }
3102
+ const manifestsByAppId = /* @__PURE__ */ new Map();
3103
+ for (const manifest of manifests) {
3104
+ const current = manifestsByAppId.get(manifest.app_id);
3105
+ if (current === void 0) {
3106
+ manifestsByAppId.set(manifest.app_id, [manifest]);
3107
+ } else {
3108
+ current.push(manifest);
3109
+ }
3110
+ }
3111
+ const registryRecords = includeAccountRegistryPermissions ? [...manifestsByAppId.entries()].map(([app_id, appManifests]) => ({
3112
+ key: `${ACCOUNT_REGISTRY_PATH}${app_id}`,
3113
+ app_id,
3114
+ manifests: appManifests.map((manifest) => ({
3115
+ ...manifest,
3116
+ permissions: manifest.permissions?.map(clonePermissionEntry)
3117
+ }))
3118
+ })) : [];
3119
+ return {
3120
+ manifests,
3121
+ resources: dedupeResources(resources),
3122
+ delegationTargets,
3123
+ registryRecords,
3124
+ expiryMs: Math.max(...resolved.map((entry) => entry.expiryMs)),
3125
+ includePublicSpace: resolved.some((entry) => entry.includePublicSpace)
3022
3126
  };
3023
3127
  }
3024
3128
  function resourceCapabilitiesToAbilitiesMap(resources) {
@@ -3049,6 +3153,22 @@ function resourceCapabilitiesToAbilitiesMap(resources) {
3049
3153
  }
3050
3154
  return out;
3051
3155
  }
3156
+ function resourceCapabilitiesToSpaceAbilitiesMap(resources) {
3157
+ const grouped = /* @__PURE__ */ new Map();
3158
+ for (const resource of resources) {
3159
+ const entries = grouped.get(resource.space);
3160
+ if (entries === void 0) {
3161
+ grouped.set(resource.space, [resource]);
3162
+ } else {
3163
+ entries.push(resource);
3164
+ }
3165
+ }
3166
+ const out = {};
3167
+ for (const [space, entries] of grouped.entries()) {
3168
+ out[space] = resourceCapabilitiesToAbilitiesMap(entries);
3169
+ }
3170
+ return out;
3171
+ }
3052
3172
  function manifestAbilitiesUnion(resolved) {
3053
3173
  const all = [...resolved.resources];
3054
3174
  for (const delegate of resolved.additionalDelegates) {
@@ -4253,7 +4373,7 @@ function canonicalizeEntryMatches(requested, granted) {
4253
4373
  if (requested.service !== granted.service) {
4254
4374
  return false;
4255
4375
  }
4256
- if (normalizeSpace(requested.space) !== normalizeSpace(granted.space)) {
4376
+ if (normalizeSpace(requested.space ?? DEFAULT_MANIFEST_SPACE) !== normalizeSpace(granted.space ?? DEFAULT_MANIFEST_SPACE)) {
4257
4377
  return false;
4258
4378
  }
4259
4379
  if (!pathContains(granted.path, requested.path)) {
@@ -4284,7 +4404,7 @@ function pathContains(grantedPath, requestedPath) {
4284
4404
  function cloneEntry(entry) {
4285
4405
  return {
4286
4406
  service: entry.service,
4287
- space: entry.space,
4407
+ ...entry.space !== void 0 ? { space: entry.space } : {},
4288
4408
  path: entry.path,
4289
4409
  actions: [...entry.actions],
4290
4410
  ...entry.skipPrefix !== void 0 ? { skipPrefix: entry.skipPrefix } : {},
@@ -4314,7 +4434,9 @@ function parseRecapCapabilities(parseWasm, siwe) {
4314
4434
  };
4315
4435
  });
4316
4436
  normalized.sort((a, b) => {
4317
- if (a.space !== b.space) return a.space < b.space ? -1 : 1;
4437
+ const aSpace = a.space ?? DEFAULT_MANIFEST_SPACE;
4438
+ const bSpace = b.space ?? DEFAULT_MANIFEST_SPACE;
4439
+ if (aSpace !== bSpace) return aSpace < bSpace ? -1 : 1;
4318
4440
  if (a.service !== b.service) return a.service < b.service ? -1 : 1;
4319
4441
  if (a.path !== b.path) return a.path < b.path ? -1 : 1;
4320
4442
  return 0;
@@ -4323,12 +4445,16 @@ function parseRecapCapabilities(parseWasm, siwe) {
4323
4445
  }
4324
4446
  // Annotate the CommonJS export names for ESM import in node:
4325
4447
  0 && (module.exports = {
4448
+ ACCOUNT_REGISTRY_PATH,
4449
+ ACCOUNT_REGISTRY_SPACE,
4326
4450
  AutoApproveSpaceCreationHandler,
4327
4451
  CapabilityKeyRegistry,
4328
4452
  CapabilityKeyRegistryErrorCodes,
4329
4453
  ClientSessionSchema,
4330
4454
  DEFAULT_DEFAULTS,
4331
4455
  DEFAULT_EXPIRY,
4456
+ DEFAULT_MANIFEST_SPACE,
4457
+ DEFAULT_MANIFEST_VERSION,
4332
4458
  DataVaultService,
4333
4459
  DatabaseHandle,
4334
4460
  DelegationErrorCodes,
@@ -4366,6 +4492,7 @@ function parseRecapCapabilities(parseWasm, siwe) {
4366
4492
  applyPrefix,
4367
4493
  buildSpaceUri,
4368
4494
  checkNodeInfo,
4495
+ composeManifestRequest,
4369
4496
  createCapabilityKeyRegistry,
4370
4497
  createSharingService,
4371
4498
  createSpaceService,
@@ -4387,6 +4514,7 @@ function parseRecapCapabilities(parseWasm, siwe) {
4387
4514
  parseSpaceUri,
4388
4515
  resolveManifest,
4389
4516
  resourceCapabilitiesToAbilitiesMap,
4517
+ resourceCapabilitiesToSpaceAbilitiesMap,
4390
4518
  serviceError,
4391
4519
  submitHostDelegation,
4392
4520
  validateClientSession,