@tinycloud/sdk-core 2.1.0-beta.5 → 2.2.0-beta.0

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
@@ -2666,6 +2666,10 @@ var ManifestValidationError = class extends Error {
2666
2666
  };
2667
2667
  var DEFAULT_EXPIRY = "30d";
2668
2668
  var DEFAULT_DEFAULTS = true;
2669
+ var DEFAULT_MANIFEST_VERSION = 1;
2670
+ var DEFAULT_MANIFEST_SPACE = "applications";
2671
+ var ACCOUNT_REGISTRY_SPACE = "account";
2672
+ var ACCOUNT_REGISTRY_PATH = "applications/";
2669
2673
  var SERVICE_SHORT_TO_LONG = Object.freeze({
2670
2674
  kv: "tinycloud.kv",
2671
2675
  sql: "tinycloud.sql",
@@ -2681,19 +2685,19 @@ var SERVICE_LONG_TO_SHORT = Object.freeze(
2681
2685
  var DEFAULT_STANDARD_ENTRIES = [
2682
2686
  {
2683
2687
  service: "tinycloud.kv",
2684
- space: "default",
2688
+ space: DEFAULT_MANIFEST_SPACE,
2685
2689
  path: "/",
2686
2690
  actions: ["get", "put", "del", "list", "metadata"]
2687
2691
  },
2688
2692
  {
2689
2693
  service: "tinycloud.sql",
2690
- space: "default",
2694
+ space: DEFAULT_MANIFEST_SPACE,
2691
2695
  path: "/",
2692
2696
  actions: ["read", "write"]
2693
2697
  },
2694
2698
  {
2695
2699
  service: "tinycloud.capabilities",
2696
- space: "default",
2700
+ space: DEFAULT_MANIFEST_SPACE,
2697
2701
  path: "/",
2698
2702
  actions: ["read"]
2699
2703
  }
@@ -2701,19 +2705,19 @@ var DEFAULT_STANDARD_ENTRIES = [
2701
2705
  var DEFAULT_ADMIN_ENTRIES = [
2702
2706
  {
2703
2707
  service: "tinycloud.kv",
2704
- space: "default",
2708
+ space: DEFAULT_MANIFEST_SPACE,
2705
2709
  path: "/",
2706
2710
  actions: ["get", "put", "del", "list", "metadata"]
2707
2711
  },
2708
2712
  {
2709
2713
  service: "tinycloud.sql",
2710
- space: "default",
2714
+ space: DEFAULT_MANIFEST_SPACE,
2711
2715
  path: "/",
2712
2716
  actions: ["read", "write", "ddl"]
2713
2717
  },
2714
2718
  {
2715
2719
  service: "tinycloud.capabilities",
2716
- space: "default",
2720
+ space: DEFAULT_MANIFEST_SPACE,
2717
2721
  path: "/",
2718
2722
  actions: ["read", "admin"]
2719
2723
  }
@@ -2721,25 +2725,25 @@ var DEFAULT_ADMIN_ENTRIES = [
2721
2725
  var DEFAULT_ALL_ENTRIES = [
2722
2726
  {
2723
2727
  service: "tinycloud.kv",
2724
- space: "default",
2728
+ space: DEFAULT_MANIFEST_SPACE,
2725
2729
  path: "/",
2726
2730
  actions: ["get", "put", "del", "list", "metadata"]
2727
2731
  },
2728
2732
  {
2729
2733
  service: "tinycloud.sql",
2730
- space: "default",
2734
+ space: DEFAULT_MANIFEST_SPACE,
2731
2735
  path: "/",
2732
2736
  actions: ["read", "write", "ddl"]
2733
2737
  },
2734
2738
  {
2735
2739
  service: "tinycloud.duckdb",
2736
- space: "default",
2740
+ space: DEFAULT_MANIFEST_SPACE,
2737
2741
  path: "/",
2738
2742
  actions: ["read", "write"]
2739
2743
  },
2740
2744
  {
2741
2745
  service: "tinycloud.capabilities",
2742
- space: "default",
2746
+ space: DEFAULT_MANIFEST_SPACE,
2743
2747
  path: "/",
2744
2748
  actions: ["read", "admin"]
2745
2749
  }
@@ -2801,12 +2805,23 @@ function validateManifest(input) {
2801
2805
  throw new ManifestValidationError("manifest must be an object");
2802
2806
  }
2803
2807
  const m = input;
2804
- if (typeof m.id !== "string" || m.id.length === 0) {
2805
- throw new ManifestValidationError("manifest.id is required and must be a non-empty string");
2808
+ if (m.manifest_version !== void 0 && m.manifest_version !== DEFAULT_MANIFEST_VERSION) {
2809
+ throw new ManifestValidationError(
2810
+ `manifest.manifest_version must be ${DEFAULT_MANIFEST_VERSION}`
2811
+ );
2812
+ }
2813
+ if (typeof m.app_id !== "string" || m.app_id.length === 0) {
2814
+ throw new ManifestValidationError("manifest.app_id is required and must be a non-empty string");
2806
2815
  }
2807
2816
  if (typeof m.name !== "string" || m.name.length === 0) {
2808
2817
  throw new ManifestValidationError("manifest.name is required and must be a non-empty string");
2809
2818
  }
2819
+ if (m.did !== void 0 && (typeof m.did !== "string" || m.did.length === 0)) {
2820
+ throw new ManifestValidationError("manifest.did must be a non-empty DID string");
2821
+ }
2822
+ if (m.space !== void 0 && (typeof m.space !== "string" || m.space.length === 0)) {
2823
+ throw new ManifestValidationError("manifest.space must be a non-empty string");
2824
+ }
2810
2825
  if (m.expiry !== void 0) {
2811
2826
  parseExpiry(m.expiry);
2812
2827
  }
@@ -2818,29 +2833,6 @@ function validateManifest(input) {
2818
2833
  (p, i) => validatePermissionEntry(p, `permissions[${i}]`)
2819
2834
  );
2820
2835
  }
2821
- if (m.delegations !== void 0) {
2822
- if (!Array.isArray(m.delegations)) {
2823
- throw new ManifestValidationError("manifest.delegations must be an array");
2824
- }
2825
- m.delegations.forEach((d, i) => {
2826
- if (typeof d?.to !== "string" || d.to.length === 0) {
2827
- throw new ManifestValidationError(
2828
- `delegations[${i}].to is required and must be a non-empty DID string`
2829
- );
2830
- }
2831
- if (d.expiry !== void 0) {
2832
- parseExpiry(d.expiry);
2833
- }
2834
- if (!Array.isArray(d.permissions)) {
2835
- throw new ManifestValidationError(
2836
- `delegations[${i}].permissions must be an array`
2837
- );
2838
- }
2839
- d.permissions.forEach(
2840
- (p, j) => validatePermissionEntry(p, `delegations[${i}].permissions[${j}]`)
2841
- );
2842
- });
2843
- }
2844
2836
  return m;
2845
2837
  }
2846
2838
  function validatePermissionEntry(p, path) {
@@ -2851,8 +2843,8 @@ function validatePermissionEntry(p, path) {
2851
2843
  if (typeof entry.service !== "string" || entry.service.length === 0) {
2852
2844
  throw new ManifestValidationError(`${path}.service is required`);
2853
2845
  }
2854
- if (typeof entry.space !== "string" || entry.space.length === 0) {
2855
- throw new ManifestValidationError(`${path}.space is required`);
2846
+ if (entry.space !== void 0 && (typeof entry.space !== "string" || entry.space.length === 0)) {
2847
+ throw new ManifestValidationError(`${path}.space must be a non-empty string`);
2856
2848
  }
2857
2849
  if (typeof entry.path !== "string") {
2858
2850
  throw new ManifestValidationError(
@@ -2898,7 +2890,8 @@ function defaultEntriesForTier(tier) {
2898
2890
  }
2899
2891
  function resolveManifest(input) {
2900
2892
  const manifest = validateManifest(input);
2901
- const prefix = manifest.prefix !== void 0 ? manifest.prefix : manifest.id;
2893
+ const prefix = manifest.prefix !== void 0 ? manifest.prefix : manifest.app_id;
2894
+ const space = manifest.space ?? DEFAULT_MANIFEST_SPACE;
2902
2895
  const expiryMs = parseExpiry(manifest.expiry ?? DEFAULT_EXPIRY);
2903
2896
  const includePublicSpace = manifest.includePublicSpace ?? true;
2904
2897
  const tier = normalizeDefaults(manifest.defaults);
@@ -2906,29 +2899,27 @@ function resolveManifest(input) {
2906
2899
  const explicitEntries = manifest.permissions ?? [];
2907
2900
  const allEntries = [...defaultEntries, ...explicitEntries];
2908
2901
  const resources = allEntries.map(
2909
- (entry) => resolveEntry(entry, prefix, expiryMs)
2902
+ (entry) => resolveEntry(entry, prefix, expiryMs, space)
2910
2903
  );
2911
- const additionalDelegates = (manifest.delegations ?? []).map((d) => ({
2912
- did: d.to,
2913
- name: d.name,
2914
- expiryMs: parseExpiry(d.expiry ?? manifest.expiry ?? DEFAULT_EXPIRY),
2915
- permissions: d.permissions.map(
2916
- (entry) => resolveEntry(
2917
- entry,
2918
- prefix,
2919
- parseExpiry(d.expiry ?? manifest.expiry ?? DEFAULT_EXPIRY)
2920
- )
2921
- )
2922
- }));
2904
+ const additionalDelegates = manifest.did === void 0 ? [] : [
2905
+ {
2906
+ did: manifest.did,
2907
+ name: manifest.name,
2908
+ expiryMs,
2909
+ permissions: resources.map(cloneResourceCapability)
2910
+ }
2911
+ ];
2923
2912
  return {
2924
- id: manifest.id,
2913
+ app_id: manifest.app_id,
2914
+ ...manifest.did !== void 0 ? { did: manifest.did } : {},
2915
+ space,
2925
2916
  resources,
2926
2917
  expiryMs,
2927
2918
  includePublicSpace,
2928
2919
  additionalDelegates
2929
2920
  };
2930
2921
  }
2931
- function resolveEntry(entry, prefix, _inheritedExpiryMs) {
2922
+ function resolveEntry(entry, prefix, _inheritedExpiryMs, inheritedSpace) {
2932
2923
  const resolvedPath = applyPrefix(
2933
2924
  prefix,
2934
2925
  entry.path,
@@ -2938,7 +2929,7 @@ function resolveEntry(entry, prefix, _inheritedExpiryMs) {
2938
2929
  const entryExpiryMs = entry.expiry !== void 0 ? parseExpiry(entry.expiry) : void 0;
2939
2930
  return {
2940
2931
  service: entry.service,
2941
- space: entry.space,
2932
+ space: entry.space ?? inheritedSpace,
2942
2933
  path: resolvedPath,
2943
2934
  actions: resolvedActions,
2944
2935
  // Only populate `expiryMs` when the entry had its own expiry override.
@@ -2947,6 +2938,101 @@ function resolveEntry(entry, prefix, _inheritedExpiryMs) {
2947
2938
  ...entryExpiryMs !== void 0 ? { expiryMs: entryExpiryMs } : {}
2948
2939
  };
2949
2940
  }
2941
+ function cloneResourceCapability(entry) {
2942
+ return {
2943
+ service: entry.service,
2944
+ space: entry.space,
2945
+ path: entry.path,
2946
+ actions: [...entry.actions],
2947
+ ...entry.expiryMs !== void 0 ? { expiryMs: entry.expiryMs } : {}
2948
+ };
2949
+ }
2950
+ function clonePermissionEntry(entry) {
2951
+ return {
2952
+ service: entry.service,
2953
+ ...entry.space !== void 0 ? { space: entry.space } : {},
2954
+ path: entry.path,
2955
+ actions: [...entry.actions],
2956
+ ...entry.skipPrefix !== void 0 ? { skipPrefix: entry.skipPrefix } : {},
2957
+ ...entry.expiry !== void 0 ? { expiry: entry.expiry } : {}
2958
+ };
2959
+ }
2960
+ function dedupeResources(resources) {
2961
+ const byKey = /* @__PURE__ */ new Map();
2962
+ for (const resource of resources) {
2963
+ const key = `${resource.service}\0${resource.space}\0${resource.path}\0${resource.expiryMs ?? ""}`;
2964
+ const existing = byKey.get(key);
2965
+ if (existing === void 0) {
2966
+ byKey.set(key, cloneResourceCapability(resource));
2967
+ continue;
2968
+ }
2969
+ const seen = new Set(existing.actions);
2970
+ for (const action of resource.actions) {
2971
+ if (!seen.has(action)) {
2972
+ existing.actions.push(action);
2973
+ seen.add(action);
2974
+ }
2975
+ }
2976
+ }
2977
+ return [...byKey.values()];
2978
+ }
2979
+ function accountRegistryPermission() {
2980
+ return {
2981
+ service: "tinycloud.kv",
2982
+ space: ACCOUNT_REGISTRY_SPACE,
2983
+ path: ACCOUNT_REGISTRY_PATH,
2984
+ actions: [
2985
+ "tinycloud.kv/get",
2986
+ "tinycloud.kv/put",
2987
+ "tinycloud.kv/list"
2988
+ ]
2989
+ };
2990
+ }
2991
+ function composeManifestRequest(inputs, options = {}) {
2992
+ if (!Array.isArray(inputs) || inputs.length === 0) {
2993
+ throw new ManifestValidationError(
2994
+ "composeManifestRequest requires at least one manifest"
2995
+ );
2996
+ }
2997
+ const includeAccountRegistryPermissions = options.includeAccountRegistryPermissions ?? true;
2998
+ const manifests = inputs.map(validateManifest);
2999
+ const resolved = manifests.map(resolveManifest);
3000
+ const resources = resolved.flatMap((entry) => entry.resources);
3001
+ const delegationTargets = resolved.flatMap(
3002
+ (entry) => entry.additionalDelegates.map((delegate) => ({
3003
+ ...delegate,
3004
+ permissions: dedupeResources(delegate.permissions)
3005
+ }))
3006
+ );
3007
+ if (includeAccountRegistryPermissions) {
3008
+ resources.push(accountRegistryPermission());
3009
+ }
3010
+ const manifestsByAppId = /* @__PURE__ */ new Map();
3011
+ for (const manifest of manifests) {
3012
+ const current = manifestsByAppId.get(manifest.app_id);
3013
+ if (current === void 0) {
3014
+ manifestsByAppId.set(manifest.app_id, [manifest]);
3015
+ } else {
3016
+ current.push(manifest);
3017
+ }
3018
+ }
3019
+ const registryRecords = includeAccountRegistryPermissions ? [...manifestsByAppId.entries()].map(([app_id, appManifests]) => ({
3020
+ key: `${ACCOUNT_REGISTRY_PATH}${app_id}`,
3021
+ app_id,
3022
+ manifests: appManifests.map((manifest) => ({
3023
+ ...manifest,
3024
+ permissions: manifest.permissions?.map(clonePermissionEntry)
3025
+ }))
3026
+ })) : [];
3027
+ return {
3028
+ manifests,
3029
+ resources: dedupeResources(resources),
3030
+ delegationTargets,
3031
+ registryRecords,
3032
+ expiryMs: Math.max(...resolved.map((entry) => entry.expiryMs)),
3033
+ includePublicSpace: resolved.some((entry) => entry.includePublicSpace)
3034
+ };
3035
+ }
2950
3036
  function resourceCapabilitiesToAbilitiesMap(resources) {
2951
3037
  const out = {};
2952
3038
  for (const r of resources) {
@@ -2975,6 +3061,22 @@ function resourceCapabilitiesToAbilitiesMap(resources) {
2975
3061
  }
2976
3062
  return out;
2977
3063
  }
3064
+ function resourceCapabilitiesToSpaceAbilitiesMap(resources) {
3065
+ const grouped = /* @__PURE__ */ new Map();
3066
+ for (const resource of resources) {
3067
+ const entries = grouped.get(resource.space);
3068
+ if (entries === void 0) {
3069
+ grouped.set(resource.space, [resource]);
3070
+ } else {
3071
+ entries.push(resource);
3072
+ }
3073
+ }
3074
+ const out = {};
3075
+ for (const [space, entries] of grouped.entries()) {
3076
+ out[space] = resourceCapabilitiesToAbilitiesMap(entries);
3077
+ }
3078
+ return out;
3079
+ }
2978
3080
  function manifestAbilitiesUnion(resolved) {
2979
3081
  const all = [...resolved.resources];
2980
3082
  for (const delegate of resolved.additionalDelegates) {
@@ -4179,7 +4281,7 @@ function canonicalizeEntryMatches(requested, granted) {
4179
4281
  if (requested.service !== granted.service) {
4180
4282
  return false;
4181
4283
  }
4182
- if (normalizeSpace(requested.space) !== normalizeSpace(granted.space)) {
4284
+ if (normalizeSpace(requested.space ?? DEFAULT_MANIFEST_SPACE) !== normalizeSpace(granted.space ?? DEFAULT_MANIFEST_SPACE)) {
4183
4285
  return false;
4184
4286
  }
4185
4287
  if (!pathContains(granted.path, requested.path)) {
@@ -4210,7 +4312,7 @@ function pathContains(grantedPath, requestedPath) {
4210
4312
  function cloneEntry(entry) {
4211
4313
  return {
4212
4314
  service: entry.service,
4213
- space: entry.space,
4315
+ ...entry.space !== void 0 ? { space: entry.space } : {},
4214
4316
  path: entry.path,
4215
4317
  actions: [...entry.actions],
4216
4318
  ...entry.skipPrefix !== void 0 ? { skipPrefix: entry.skipPrefix } : {},
@@ -4240,7 +4342,9 @@ function parseRecapCapabilities(parseWasm, siwe) {
4240
4342
  };
4241
4343
  });
4242
4344
  normalized.sort((a, b) => {
4243
- if (a.space !== b.space) return a.space < b.space ? -1 : 1;
4345
+ const aSpace = a.space ?? DEFAULT_MANIFEST_SPACE;
4346
+ const bSpace = b.space ?? DEFAULT_MANIFEST_SPACE;
4347
+ if (aSpace !== bSpace) return aSpace < bSpace ? -1 : 1;
4244
4348
  if (a.service !== b.service) return a.service < b.service ? -1 : 1;
4245
4349
  if (a.path !== b.path) return a.path < b.path ? -1 : 1;
4246
4350
  return 0;
@@ -4248,12 +4352,16 @@ function parseRecapCapabilities(parseWasm, siwe) {
4248
4352
  return normalized;
4249
4353
  }
4250
4354
  export {
4355
+ ACCOUNT_REGISTRY_PATH,
4356
+ ACCOUNT_REGISTRY_SPACE,
4251
4357
  AutoApproveSpaceCreationHandler,
4252
4358
  CapabilityKeyRegistry,
4253
4359
  CapabilityKeyRegistryErrorCodes,
4254
4360
  ClientSessionSchema,
4255
4361
  DEFAULT_DEFAULTS,
4256
4362
  DEFAULT_EXPIRY,
4363
+ DEFAULT_MANIFEST_SPACE,
4364
+ DEFAULT_MANIFEST_VERSION,
4257
4365
  DataVaultService,
4258
4366
  DatabaseHandle,
4259
4367
  DelegationErrorCodes,
@@ -4291,6 +4399,7 @@ export {
4291
4399
  applyPrefix,
4292
4400
  buildSpaceUri,
4293
4401
  checkNodeInfo,
4402
+ composeManifestRequest,
4294
4403
  createCapabilityKeyRegistry,
4295
4404
  createSharingService,
4296
4405
  createSpaceService,
@@ -4312,6 +4421,7 @@ export {
4312
4421
  parseSpaceUri,
4313
4422
  resolveManifest,
4314
4423
  resourceCapabilitiesToAbilitiesMap,
4424
+ resourceCapabilitiesToSpaceAbilitiesMap,
4315
4425
  serviceError4 as serviceError,
4316
4426
  submitHostDelegation,
4317
4427
  validateClientSession,