@tinycloud/sdk-core 2.1.0-beta.0 → 2.1.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
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
@@ -24,6 +34,8 @@ __export(index_exports, {
24
34
  CapabilityKeyRegistry: () => CapabilityKeyRegistry,
25
35
  CapabilityKeyRegistryErrorCodes: () => CapabilityKeyRegistryErrorCodes,
26
36
  ClientSessionSchema: () => ClientSessionSchema,
37
+ DEFAULT_DEFAULTS: () => DEFAULT_DEFAULTS,
38
+ DEFAULT_EXPIRY: () => DEFAULT_EXPIRY,
27
39
  DataVaultService: () => import_sdk_services4.DataVaultService,
28
40
  DatabaseHandle: () => import_sdk_services4.DatabaseHandle,
29
41
  DelegationErrorCodes: () => DelegationErrorCodes,
@@ -35,11 +47,16 @@ __export(index_exports, {
35
47
  ErrorCodes: () => import_sdk_services4.ErrorCodes,
36
48
  HooksService: () => import_sdk_services4.HooksService,
37
49
  KVService: () => import_sdk_services4.KVService,
50
+ ManifestValidationError: () => ManifestValidationError,
51
+ PermissionNotInManifestError: () => PermissionNotInManifestError,
38
52
  PrefixedKVService: () => import_sdk_services4.PrefixedKVService,
39
53
  ProtocolMismatchError: () => ProtocolMismatchError,
54
+ SERVICE_LONG_TO_SHORT: () => SERVICE_LONG_TO_SHORT,
55
+ SERVICE_SHORT_TO_LONG: () => SERVICE_SHORT_TO_LONG,
40
56
  SQLAction: () => import_sdk_services4.SQLAction,
41
57
  SQLService: () => import_sdk_services4.SQLService,
42
58
  ServiceContext: () => import_sdk_services4.ServiceContext,
59
+ SessionExpiredError: () => SessionExpiredError,
43
60
  SharingService: () => SharingService,
44
61
  SilentNotificationHandler: () => SilentNotificationHandler,
45
62
  SiweConfigSchema: () => SiweConfigSchema,
@@ -53,6 +70,7 @@ __export(index_exports, {
53
70
  VaultPublicSpaceKVActions: () => import_sdk_services4.VaultPublicSpaceKVActions,
54
71
  VersionCheckError: () => VersionCheckError,
55
72
  activateSessionWithHost: () => activateSessionWithHost,
73
+ applyPrefix: () => applyPrefix,
56
74
  buildSpaceUri: () => buildSpaceUri,
57
75
  checkNodeInfo: () => checkNodeInfo,
58
76
  createCapabilityKeyRegistry: () => createCapabilityKeyRegistry,
@@ -63,13 +81,21 @@ __export(index_exports, {
63
81
  defaultSignStrategy: () => defaultSignStrategy,
64
82
  defaultSpaceCreationHandler: () => defaultSpaceCreationHandler,
65
83
  err: () => import_sdk_services4.err,
84
+ expandActionShortNames: () => expandActionShortNames,
66
85
  fetchPeerId: () => fetchPeerId,
86
+ isCapabilitySubset: () => isCapabilitySubset,
87
+ loadManifest: () => loadManifest,
67
88
  makePublicSpaceId: () => makePublicSpaceId,
89
+ normalizeDefaults: () => normalizeDefaults,
68
90
  ok: () => import_sdk_services4.ok,
91
+ parseExpiry: () => parseExpiry,
92
+ parseRecapCapabilities: () => parseRecapCapabilities,
69
93
  parseSpaceUri: () => parseSpaceUri,
94
+ resolveManifest: () => resolveManifest,
70
95
  serviceError: () => import_sdk_services4.serviceError,
71
96
  submitHostDelegation: () => submitHostDelegation,
72
97
  validateClientSession: () => validateClientSession,
98
+ validateManifest: () => validateManifest,
73
99
  validatePersistedSessionData: () => validatePersistedSessionData
74
100
  });
75
101
  module.exports = __toCommonJS(index_exports);
@@ -3779,12 +3805,405 @@ async function checkNodeInfo(host, sdkProtocol, fetchFn = globalThis.fetch.bind(
3779
3805
  quotaUrl: data.quota_url
3780
3806
  };
3781
3807
  }
3808
+
3809
+ // src/manifest.ts
3810
+ var import_ms = __toESM(require("ms"), 1);
3811
+ var ManifestValidationError = class extends Error {
3812
+ constructor(message) {
3813
+ super(`Manifest validation failed: ${message}`);
3814
+ this.name = "ManifestValidationError";
3815
+ }
3816
+ };
3817
+ var DEFAULT_EXPIRY = "30d";
3818
+ var DEFAULT_DEFAULTS = true;
3819
+ var SERVICE_SHORT_TO_LONG = Object.freeze({
3820
+ kv: "tinycloud.kv",
3821
+ sql: "tinycloud.sql",
3822
+ duckdb: "tinycloud.duckdb",
3823
+ capabilities: "tinycloud.capabilities",
3824
+ hooks: "tinycloud.hooks"
3825
+ });
3826
+ var SERVICE_LONG_TO_SHORT = Object.freeze(
3827
+ Object.fromEntries(
3828
+ Object.entries(SERVICE_SHORT_TO_LONG).map(([s, l]) => [l, s])
3829
+ )
3830
+ );
3831
+ var DEFAULT_STANDARD_ENTRIES = [
3832
+ {
3833
+ service: "tinycloud.kv",
3834
+ space: "default",
3835
+ path: "/",
3836
+ actions: ["get", "put", "del", "list", "metadata"]
3837
+ },
3838
+ {
3839
+ service: "tinycloud.sql",
3840
+ space: "default",
3841
+ path: "/",
3842
+ actions: ["read", "write"]
3843
+ },
3844
+ {
3845
+ service: "tinycloud.capabilities",
3846
+ space: "default",
3847
+ path: "/",
3848
+ actions: ["read"]
3849
+ }
3850
+ ];
3851
+ var DEFAULT_ADMIN_ENTRIES = [
3852
+ {
3853
+ service: "tinycloud.kv",
3854
+ space: "default",
3855
+ path: "/",
3856
+ actions: ["get", "put", "del", "list", "metadata"]
3857
+ },
3858
+ {
3859
+ service: "tinycloud.sql",
3860
+ space: "default",
3861
+ path: "/",
3862
+ actions: ["read", "write", "ddl"]
3863
+ },
3864
+ {
3865
+ service: "tinycloud.capabilities",
3866
+ space: "default",
3867
+ path: "/",
3868
+ actions: ["read", "admin"]
3869
+ }
3870
+ ];
3871
+ var DEFAULT_ALL_ENTRIES = [
3872
+ {
3873
+ service: "tinycloud.kv",
3874
+ space: "default",
3875
+ path: "/",
3876
+ actions: ["get", "put", "del", "list", "metadata"]
3877
+ },
3878
+ {
3879
+ service: "tinycloud.sql",
3880
+ space: "default",
3881
+ path: "/",
3882
+ actions: ["read", "write", "ddl"]
3883
+ },
3884
+ {
3885
+ service: "tinycloud.duckdb",
3886
+ space: "default",
3887
+ path: "/",
3888
+ actions: ["read", "write"]
3889
+ },
3890
+ {
3891
+ service: "tinycloud.capabilities",
3892
+ space: "default",
3893
+ path: "/",
3894
+ actions: ["read", "admin"]
3895
+ }
3896
+ ];
3897
+ function parseExpiry(duration) {
3898
+ if (typeof duration !== "string" || duration.length === 0) {
3899
+ throw new ManifestValidationError(
3900
+ `expiry must be a non-empty duration string (got ${JSON.stringify(duration)})`
3901
+ );
3902
+ }
3903
+ const parsed = (0, import_ms.default)(
3904
+ duration
3905
+ );
3906
+ if (typeof parsed !== "number" || !Number.isFinite(parsed) || parsed <= 0) {
3907
+ throw new ManifestValidationError(
3908
+ `invalid expiry duration: ${JSON.stringify(duration)}`
3909
+ );
3910
+ }
3911
+ return parsed;
3912
+ }
3913
+ function expandActionShortNames(service, actions) {
3914
+ return actions.map((a) => {
3915
+ if (a.includes("/")) {
3916
+ return a;
3917
+ }
3918
+ return `${service}/${a}`;
3919
+ });
3920
+ }
3921
+ function applyPrefix(prefix, path, skipPrefix) {
3922
+ if (skipPrefix) {
3923
+ return path;
3924
+ }
3925
+ if (prefix === "") {
3926
+ return path;
3927
+ }
3928
+ if (path.startsWith("/")) {
3929
+ return `${prefix}${path}`;
3930
+ }
3931
+ return `${prefix}/${path}`;
3932
+ }
3933
+ async function loadManifest(url) {
3934
+ const fetchFn = globalThis.fetch;
3935
+ if (typeof fetchFn !== "function") {
3936
+ throw new ManifestValidationError(
3937
+ "loadManifest requires a global fetch; pass the manifest object directly on runtimes without fetch"
3938
+ );
3939
+ }
3940
+ const res = await fetchFn(url);
3941
+ if (!res.ok) {
3942
+ throw new ManifestValidationError(
3943
+ `failed to fetch manifest from ${url}: HTTP ${res.status}`
3944
+ );
3945
+ }
3946
+ const json = await res.json();
3947
+ return validateManifest(json);
3948
+ }
3949
+ function validateManifest(input) {
3950
+ if (input === null || typeof input !== "object") {
3951
+ throw new ManifestValidationError("manifest must be an object");
3952
+ }
3953
+ const m = input;
3954
+ if (typeof m.id !== "string" || m.id.length === 0) {
3955
+ throw new ManifestValidationError("manifest.id is required and must be a non-empty string");
3956
+ }
3957
+ if (typeof m.name !== "string" || m.name.length === 0) {
3958
+ throw new ManifestValidationError("manifest.name is required and must be a non-empty string");
3959
+ }
3960
+ if (m.expiry !== void 0) {
3961
+ parseExpiry(m.expiry);
3962
+ }
3963
+ if (m.permissions !== void 0) {
3964
+ if (!Array.isArray(m.permissions)) {
3965
+ throw new ManifestValidationError("manifest.permissions must be an array");
3966
+ }
3967
+ m.permissions.forEach(
3968
+ (p, i) => validatePermissionEntry(p, `permissions[${i}]`)
3969
+ );
3970
+ }
3971
+ if (m.delegations !== void 0) {
3972
+ if (!Array.isArray(m.delegations)) {
3973
+ throw new ManifestValidationError("manifest.delegations must be an array");
3974
+ }
3975
+ m.delegations.forEach((d, i) => {
3976
+ if (typeof d?.to !== "string" || d.to.length === 0) {
3977
+ throw new ManifestValidationError(
3978
+ `delegations[${i}].to is required and must be a non-empty DID string`
3979
+ );
3980
+ }
3981
+ if (d.expiry !== void 0) {
3982
+ parseExpiry(d.expiry);
3983
+ }
3984
+ if (!Array.isArray(d.permissions)) {
3985
+ throw new ManifestValidationError(
3986
+ `delegations[${i}].permissions must be an array`
3987
+ );
3988
+ }
3989
+ d.permissions.forEach(
3990
+ (p, j) => validatePermissionEntry(p, `delegations[${i}].permissions[${j}]`)
3991
+ );
3992
+ });
3993
+ }
3994
+ return m;
3995
+ }
3996
+ function validatePermissionEntry(p, path) {
3997
+ if (p === null || typeof p !== "object") {
3998
+ throw new ManifestValidationError(`${path} must be an object`);
3999
+ }
4000
+ const entry = p;
4001
+ if (typeof entry.service !== "string" || entry.service.length === 0) {
4002
+ throw new ManifestValidationError(`${path}.service is required`);
4003
+ }
4004
+ if (typeof entry.space !== "string" || entry.space.length === 0) {
4005
+ throw new ManifestValidationError(`${path}.space is required`);
4006
+ }
4007
+ if (typeof entry.path !== "string") {
4008
+ throw new ManifestValidationError(
4009
+ `${path}.path is required (use "" or "/" for root)`
4010
+ );
4011
+ }
4012
+ if (!Array.isArray(entry.actions) || entry.actions.length === 0) {
4013
+ throw new ManifestValidationError(
4014
+ `${path}.actions must be a non-empty array`
4015
+ );
4016
+ }
4017
+ if (entry.expiry !== void 0) {
4018
+ parseExpiry(entry.expiry);
4019
+ }
4020
+ }
4021
+ function normalizeDefaults(value) {
4022
+ if (value === void 0) {
4023
+ return DEFAULT_DEFAULTS;
4024
+ }
4025
+ if (typeof value === "boolean") {
4026
+ return value;
4027
+ }
4028
+ if (typeof value !== "string") {
4029
+ return true;
4030
+ }
4031
+ const normalized = value.trim().toLowerCase();
4032
+ if (normalized === "admin" || normalized === "all") {
4033
+ return normalized;
4034
+ }
4035
+ return true;
4036
+ }
4037
+ function defaultEntriesForTier(tier) {
4038
+ if (tier === false) {
4039
+ return [];
4040
+ }
4041
+ const source = tier === "admin" ? DEFAULT_ADMIN_ENTRIES : tier === "all" ? DEFAULT_ALL_ENTRIES : DEFAULT_STANDARD_ENTRIES;
4042
+ return source.map((e) => ({
4043
+ service: e.service,
4044
+ space: e.space,
4045
+ path: e.path,
4046
+ actions: [...e.actions]
4047
+ }));
4048
+ }
4049
+ function resolveManifest(input) {
4050
+ const manifest = validateManifest(input);
4051
+ const prefix = manifest.prefix !== void 0 ? manifest.prefix : manifest.id;
4052
+ const expiryMs = parseExpiry(manifest.expiry ?? DEFAULT_EXPIRY);
4053
+ const includePublicSpace = manifest.includePublicSpace ?? true;
4054
+ const tier = normalizeDefaults(manifest.defaults);
4055
+ const defaultEntries = defaultEntriesForTier(tier);
4056
+ const explicitEntries = manifest.permissions ?? [];
4057
+ const allEntries = [...defaultEntries, ...explicitEntries];
4058
+ const resources = allEntries.map(
4059
+ (entry) => resolveEntry(entry, prefix, expiryMs)
4060
+ );
4061
+ const additionalDelegates = (manifest.delegations ?? []).map((d) => ({
4062
+ did: d.to,
4063
+ name: d.name,
4064
+ expiryMs: parseExpiry(d.expiry ?? manifest.expiry ?? DEFAULT_EXPIRY),
4065
+ permissions: d.permissions.map(
4066
+ (entry) => resolveEntry(
4067
+ entry,
4068
+ prefix,
4069
+ parseExpiry(d.expiry ?? manifest.expiry ?? DEFAULT_EXPIRY)
4070
+ )
4071
+ )
4072
+ }));
4073
+ return {
4074
+ id: manifest.id,
4075
+ resources,
4076
+ expiryMs,
4077
+ includePublicSpace,
4078
+ additionalDelegates
4079
+ };
4080
+ }
4081
+ function resolveEntry(entry, prefix, _inheritedExpiryMs) {
4082
+ const resolvedPath = applyPrefix(
4083
+ prefix,
4084
+ entry.path,
4085
+ entry.skipPrefix === true
4086
+ );
4087
+ const resolvedActions = expandActionShortNames(entry.service, entry.actions);
4088
+ const entryExpiryMs = entry.expiry !== void 0 ? parseExpiry(entry.expiry) : void 0;
4089
+ return {
4090
+ service: entry.service,
4091
+ space: entry.space,
4092
+ path: resolvedPath,
4093
+ actions: resolvedActions,
4094
+ // Only populate `expiryMs` when the entry had its own expiry override.
4095
+ // When absent, callers use the parent (delegation or manifest) expiry
4096
+ // which is carried on ResolvedDelegate.expiryMs / ResolvedCapabilities.expiryMs.
4097
+ ...entryExpiryMs !== void 0 ? { expiryMs: entryExpiryMs } : {}
4098
+ };
4099
+ }
4100
+
4101
+ // src/capabilities.ts
4102
+ var PermissionNotInManifestError = class extends Error {
4103
+ constructor(missing, granted) {
4104
+ super(
4105
+ `Requested capabilities exceed current session. Missing ${missing.length} entries.`
4106
+ );
4107
+ this.name = "PermissionNotInManifestError";
4108
+ this.missing = missing;
4109
+ this.granted = granted;
4110
+ }
4111
+ };
4112
+ var SessionExpiredError = class extends Error {
4113
+ constructor(expiredAt) {
4114
+ super(`Session expired at ${expiredAt.toISOString()}`);
4115
+ this.name = "SessionExpiredError";
4116
+ this.expiredAt = expiredAt;
4117
+ }
4118
+ };
4119
+ function isCapabilitySubset(requested, granted) {
4120
+ const missing = [];
4121
+ for (const req of requested) {
4122
+ const match = granted.find((g) => canonicalizeEntryMatches(req, g));
4123
+ if (match === void 0) {
4124
+ missing.push(cloneEntry(req));
4125
+ continue;
4126
+ }
4127
+ }
4128
+ return { subset: missing.length === 0, missing };
4129
+ }
4130
+ function canonicalizeEntryMatches(requested, granted) {
4131
+ if (requested.service !== granted.service) {
4132
+ return false;
4133
+ }
4134
+ if (requested.space !== granted.space) {
4135
+ return false;
4136
+ }
4137
+ if (!pathContains(granted.path, requested.path)) {
4138
+ return false;
4139
+ }
4140
+ const reqActions = new Set(
4141
+ expandActionShortNames(requested.service, requested.actions)
4142
+ );
4143
+ const grantedActions = new Set(
4144
+ expandActionShortNames(granted.service, granted.actions)
4145
+ );
4146
+ for (const a of reqActions) {
4147
+ if (!grantedActions.has(a)) {
4148
+ return false;
4149
+ }
4150
+ }
4151
+ return true;
4152
+ }
4153
+ function pathContains(grantedPath, requestedPath) {
4154
+ if (grantedPath === "" || grantedPath === "/") {
4155
+ return true;
4156
+ }
4157
+ if (grantedPath.endsWith("/")) {
4158
+ return requestedPath.startsWith(grantedPath);
4159
+ }
4160
+ return requestedPath === grantedPath;
4161
+ }
4162
+ function cloneEntry(entry) {
4163
+ return {
4164
+ service: entry.service,
4165
+ space: entry.space,
4166
+ path: entry.path,
4167
+ actions: [...entry.actions],
4168
+ ...entry.skipPrefix !== void 0 ? { skipPrefix: entry.skipPrefix } : {},
4169
+ ...entry.expiry !== void 0 ? { expiry: entry.expiry } : {}
4170
+ };
4171
+ }
4172
+ function parseRecapCapabilities(parseWasm, siwe) {
4173
+ const raw = parseWasm(siwe);
4174
+ if (!Array.isArray(raw)) {
4175
+ throw new Error(
4176
+ "parseRecapFromSiwe returned a non-array value; wasm binding may be out of sync"
4177
+ );
4178
+ }
4179
+ const normalized = raw.map((entry) => {
4180
+ const longService = SERVICE_SHORT_TO_LONG[entry.service] ?? // Unknown short names pass through. If the recap already contained a
4181
+ // long-form service (e.g. a future tinycloud-node version emits long
4182
+ // form directly), don't double-prefix.
4183
+ (entry.service.startsWith("tinycloud.") ? entry.service : `tinycloud.${entry.service}`);
4184
+ return {
4185
+ service: longService,
4186
+ space: entry.space,
4187
+ path: entry.path,
4188
+ actions: [...entry.actions]
4189
+ };
4190
+ });
4191
+ normalized.sort((a, b) => {
4192
+ if (a.space !== b.space) return a.space < b.space ? -1 : 1;
4193
+ if (a.service !== b.service) return a.service < b.service ? -1 : 1;
4194
+ if (a.path !== b.path) return a.path < b.path ? -1 : 1;
4195
+ return 0;
4196
+ });
4197
+ return normalized;
4198
+ }
3782
4199
  // Annotate the CommonJS export names for ESM import in node:
3783
4200
  0 && (module.exports = {
3784
4201
  AutoApproveSpaceCreationHandler,
3785
4202
  CapabilityKeyRegistry,
3786
4203
  CapabilityKeyRegistryErrorCodes,
3787
4204
  ClientSessionSchema,
4205
+ DEFAULT_DEFAULTS,
4206
+ DEFAULT_EXPIRY,
3788
4207
  DataVaultService,
3789
4208
  DatabaseHandle,
3790
4209
  DelegationErrorCodes,
@@ -3796,11 +4215,16 @@ async function checkNodeInfo(host, sdkProtocol, fetchFn = globalThis.fetch.bind(
3796
4215
  ErrorCodes,
3797
4216
  HooksService,
3798
4217
  KVService,
4218
+ ManifestValidationError,
4219
+ PermissionNotInManifestError,
3799
4220
  PrefixedKVService,
3800
4221
  ProtocolMismatchError,
4222
+ SERVICE_LONG_TO_SHORT,
4223
+ SERVICE_SHORT_TO_LONG,
3801
4224
  SQLAction,
3802
4225
  SQLService,
3803
4226
  ServiceContext,
4227
+ SessionExpiredError,
3804
4228
  SharingService,
3805
4229
  SilentNotificationHandler,
3806
4230
  SiweConfigSchema,
@@ -3814,6 +4238,7 @@ async function checkNodeInfo(host, sdkProtocol, fetchFn = globalThis.fetch.bind(
3814
4238
  VaultPublicSpaceKVActions,
3815
4239
  VersionCheckError,
3816
4240
  activateSessionWithHost,
4241
+ applyPrefix,
3817
4242
  buildSpaceUri,
3818
4243
  checkNodeInfo,
3819
4244
  createCapabilityKeyRegistry,
@@ -3824,13 +4249,21 @@ async function checkNodeInfo(host, sdkProtocol, fetchFn = globalThis.fetch.bind(
3824
4249
  defaultSignStrategy,
3825
4250
  defaultSpaceCreationHandler,
3826
4251
  err,
4252
+ expandActionShortNames,
3827
4253
  fetchPeerId,
4254
+ isCapabilitySubset,
4255
+ loadManifest,
3828
4256
  makePublicSpaceId,
4257
+ normalizeDefaults,
3829
4258
  ok,
4259
+ parseExpiry,
4260
+ parseRecapCapabilities,
3830
4261
  parseSpaceUri,
4262
+ resolveManifest,
3831
4263
  serviceError,
3832
4264
  submitHostDelegation,
3833
4265
  validateClientSession,
4266
+ validateManifest,
3834
4267
  validatePersistedSessionData
3835
4268
  });
3836
4269
  //# sourceMappingURL=index.cjs.map