@tinycloud/sdk-core 2.1.0-beta.1 → 2.1.0-beta.2
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 +505 -392
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +161 -17
- package/dist/index.d.ts +161 -17
- package/dist/index.js +503 -392
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -86,12 +86,14 @@ __export(index_exports, {
|
|
|
86
86
|
isCapabilitySubset: () => isCapabilitySubset,
|
|
87
87
|
loadManifest: () => loadManifest,
|
|
88
88
|
makePublicSpaceId: () => makePublicSpaceId,
|
|
89
|
+
manifestAbilitiesUnion: () => manifestAbilitiesUnion,
|
|
89
90
|
normalizeDefaults: () => normalizeDefaults,
|
|
90
91
|
ok: () => import_sdk_services4.ok,
|
|
91
92
|
parseExpiry: () => parseExpiry,
|
|
92
93
|
parseRecapCapabilities: () => parseRecapCapabilities,
|
|
93
94
|
parseSpaceUri: () => parseSpaceUri,
|
|
94
95
|
resolveManifest: () => resolveManifest,
|
|
96
|
+
resourceCapabilitiesToAbilitiesMap: () => resourceCapabilitiesToAbilitiesMap,
|
|
95
97
|
serviceError: () => import_sdk_services4.serviceError,
|
|
96
98
|
submitHostDelegation: () => submitHostDelegation,
|
|
97
99
|
validateClientSession: () => validateClientSession,
|
|
@@ -585,6 +587,16 @@ var DelegationApiResponseSchema = import_zod3.z.object({
|
|
|
585
587
|
/** CID of the created delegation */
|
|
586
588
|
cid: import_zod3.z.string().optional()
|
|
587
589
|
});
|
|
590
|
+
var DelegatedResourceSchema = import_zod3.z.object({
|
|
591
|
+
/** Short-form service name, e.g. "kv", "sql", "duckdb", "capabilities", "hooks". */
|
|
592
|
+
service: import_zod3.z.string(),
|
|
593
|
+
/** Full space id string, e.g. "tinycloud:pkh:eip155:1:0x....:default". */
|
|
594
|
+
space: import_zod3.z.string(),
|
|
595
|
+
/** Resource path; empty string when the resource URI had no path segment. */
|
|
596
|
+
path: import_zod3.z.string(),
|
|
597
|
+
/** Full-URN ability strings, e.g. ["tinycloud.kv/get", "tinycloud.kv/put"]. */
|
|
598
|
+
actions: import_zod3.z.array(import_zod3.z.string())
|
|
599
|
+
});
|
|
588
600
|
var CreateDelegationWasmParamsSchema = import_zod3.z.object({
|
|
589
601
|
/** The session containing delegation credentials */
|
|
590
602
|
session: import_zod3.z.unknown().refine(
|
|
@@ -595,10 +607,23 @@ var CreateDelegationWasmParamsSchema = import_zod3.z.object({
|
|
|
595
607
|
delegateDID: import_zod3.z.string(),
|
|
596
608
|
/** Space ID this delegation applies to */
|
|
597
609
|
spaceId: import_zod3.z.string(),
|
|
598
|
-
/**
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
610
|
+
/**
|
|
611
|
+
* Multi-resource abilities map: short-service → path → full-URN actions.
|
|
612
|
+
* Matches the shape accepted by `prepareSession`.
|
|
613
|
+
*
|
|
614
|
+
* Example:
|
|
615
|
+
* ```
|
|
616
|
+
* {
|
|
617
|
+
* kv: {
|
|
618
|
+
* "com.listen.app/": ["tinycloud.kv/get", "tinycloud.kv/put"]
|
|
619
|
+
* },
|
|
620
|
+
* sql: {
|
|
621
|
+
* "com.listen.app/data.sqlite": ["tinycloud.sql/read"]
|
|
622
|
+
* }
|
|
623
|
+
* }
|
|
624
|
+
* ```
|
|
625
|
+
*/
|
|
626
|
+
abilities: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.record(import_zod3.z.string(), import_zod3.z.array(import_zod3.z.string()))),
|
|
602
627
|
/** Expiration time in seconds since Unix epoch */
|
|
603
628
|
expirationSecs: import_zod3.z.number(),
|
|
604
629
|
/** Optional not-before time in seconds since Unix epoch */
|
|
@@ -611,12 +636,13 @@ var CreateDelegationWasmResultSchema = import_zod3.z.object({
|
|
|
611
636
|
cid: import_zod3.z.string(),
|
|
612
637
|
/** DID of the delegate */
|
|
613
638
|
delegateDID: import_zod3.z.string(),
|
|
614
|
-
/** Resource path the delegation grants access to */
|
|
615
|
-
path: import_zod3.z.string(),
|
|
616
|
-
/** Actions the delegation authorizes */
|
|
617
|
-
actions: import_zod3.z.array(import_zod3.z.string()),
|
|
618
639
|
/** Expiration time */
|
|
619
|
-
expiry: import_zod3.z.coerce.date()
|
|
640
|
+
expiry: import_zod3.z.coerce.date(),
|
|
641
|
+
/**
|
|
642
|
+
* All (service, space, path, actions) entries granted by this delegation.
|
|
643
|
+
* Always non-empty on success.
|
|
644
|
+
*/
|
|
645
|
+
resources: import_zod3.z.array(DelegatedResourceSchema)
|
|
620
646
|
});
|
|
621
647
|
|
|
622
648
|
// src/spaces/spaces.schema.ts
|
|
@@ -2703,104 +2729,449 @@ function validateEncodedShareData(data) {
|
|
|
2703
2729
|
return { ok: true, data: result.data };
|
|
2704
2730
|
}
|
|
2705
2731
|
|
|
2706
|
-
// src/
|
|
2707
|
-
var
|
|
2708
|
-
var
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2732
|
+
// src/manifest.ts
|
|
2733
|
+
var import_ms = __toESM(require("ms"), 1);
|
|
2734
|
+
var ManifestValidationError = class extends Error {
|
|
2735
|
+
constructor(message) {
|
|
2736
|
+
super(`Manifest validation failed: ${message}`);
|
|
2737
|
+
this.name = "ManifestValidationError";
|
|
2738
|
+
}
|
|
2739
|
+
};
|
|
2740
|
+
var DEFAULT_EXPIRY = "30d";
|
|
2741
|
+
var DEFAULT_DEFAULTS = true;
|
|
2742
|
+
var SERVICE_SHORT_TO_LONG = Object.freeze({
|
|
2743
|
+
kv: "tinycloud.kv",
|
|
2744
|
+
sql: "tinycloud.sql",
|
|
2745
|
+
duckdb: "tinycloud.duckdb",
|
|
2746
|
+
capabilities: "tinycloud.capabilities",
|
|
2747
|
+
hooks: "tinycloud.hooks"
|
|
2748
|
+
});
|
|
2749
|
+
var SERVICE_LONG_TO_SHORT = Object.freeze(
|
|
2750
|
+
Object.fromEntries(
|
|
2751
|
+
Object.entries(SERVICE_SHORT_TO_LONG).map(([s, l]) => [l, s])
|
|
2752
|
+
)
|
|
2753
|
+
);
|
|
2754
|
+
var DEFAULT_STANDARD_ENTRIES = [
|
|
2755
|
+
{
|
|
2756
|
+
service: "tinycloud.kv",
|
|
2757
|
+
space: "default",
|
|
2758
|
+
path: "/",
|
|
2759
|
+
actions: ["get", "put", "del", "list", "metadata"]
|
|
2760
|
+
},
|
|
2761
|
+
{
|
|
2762
|
+
service: "tinycloud.sql",
|
|
2763
|
+
space: "default",
|
|
2764
|
+
path: "/",
|
|
2765
|
+
actions: ["read", "write"]
|
|
2766
|
+
},
|
|
2767
|
+
{
|
|
2768
|
+
service: "tinycloud.capabilities",
|
|
2769
|
+
space: "default",
|
|
2770
|
+
path: "/",
|
|
2771
|
+
actions: ["read"]
|
|
2772
|
+
}
|
|
2773
|
+
];
|
|
2774
|
+
var DEFAULT_ADMIN_ENTRIES = [
|
|
2775
|
+
{
|
|
2776
|
+
service: "tinycloud.kv",
|
|
2777
|
+
space: "default",
|
|
2778
|
+
path: "/",
|
|
2779
|
+
actions: ["get", "put", "del", "list", "metadata"]
|
|
2780
|
+
},
|
|
2781
|
+
{
|
|
2782
|
+
service: "tinycloud.sql",
|
|
2783
|
+
space: "default",
|
|
2784
|
+
path: "/",
|
|
2785
|
+
actions: ["read", "write", "ddl"]
|
|
2786
|
+
},
|
|
2787
|
+
{
|
|
2788
|
+
service: "tinycloud.capabilities",
|
|
2789
|
+
space: "default",
|
|
2790
|
+
path: "/",
|
|
2791
|
+
actions: ["read", "admin"]
|
|
2792
|
+
}
|
|
2793
|
+
];
|
|
2794
|
+
var DEFAULT_ALL_ENTRIES = [
|
|
2795
|
+
{
|
|
2796
|
+
service: "tinycloud.kv",
|
|
2797
|
+
space: "default",
|
|
2798
|
+
path: "/",
|
|
2799
|
+
actions: ["get", "put", "del", "list", "metadata"]
|
|
2800
|
+
},
|
|
2801
|
+
{
|
|
2802
|
+
service: "tinycloud.sql",
|
|
2803
|
+
space: "default",
|
|
2804
|
+
path: "/",
|
|
2805
|
+
actions: ["read", "write", "ddl"]
|
|
2806
|
+
},
|
|
2807
|
+
{
|
|
2808
|
+
service: "tinycloud.duckdb",
|
|
2809
|
+
space: "default",
|
|
2810
|
+
path: "/",
|
|
2811
|
+
actions: ["read", "write"]
|
|
2812
|
+
},
|
|
2813
|
+
{
|
|
2814
|
+
service: "tinycloud.capabilities",
|
|
2815
|
+
space: "default",
|
|
2816
|
+
path: "/",
|
|
2817
|
+
actions: ["read", "admin"]
|
|
2818
|
+
}
|
|
2819
|
+
];
|
|
2820
|
+
function parseExpiry(duration) {
|
|
2821
|
+
if (typeof duration !== "string" || duration.length === 0) {
|
|
2822
|
+
throw new ManifestValidationError(
|
|
2823
|
+
`expiry must be a non-empty duration string (got ${JSON.stringify(duration)})`
|
|
2824
|
+
);
|
|
2825
|
+
}
|
|
2826
|
+
const parsed = (0, import_ms.default)(
|
|
2827
|
+
duration
|
|
2828
|
+
);
|
|
2829
|
+
if (typeof parsed !== "number" || !Number.isFinite(parsed) || parsed <= 0) {
|
|
2830
|
+
throw new ManifestValidationError(
|
|
2831
|
+
`invalid expiry duration: ${JSON.stringify(duration)}`
|
|
2832
|
+
);
|
|
2833
|
+
}
|
|
2834
|
+
return parsed;
|
|
2718
2835
|
}
|
|
2719
|
-
function
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
}
|
|
2726
|
-
|
|
2836
|
+
function expandActionShortNames(service, actions) {
|
|
2837
|
+
return actions.map((a) => {
|
|
2838
|
+
if (a.includes("/")) {
|
|
2839
|
+
return a;
|
|
2840
|
+
}
|
|
2841
|
+
return `${service}/${a}`;
|
|
2842
|
+
});
|
|
2843
|
+
}
|
|
2844
|
+
function applyPrefix(prefix, path, skipPrefix) {
|
|
2845
|
+
if (skipPrefix) {
|
|
2846
|
+
return path;
|
|
2727
2847
|
}
|
|
2728
|
-
|
|
2848
|
+
if (prefix === "") {
|
|
2849
|
+
return path;
|
|
2850
|
+
}
|
|
2851
|
+
if (path.startsWith("/")) {
|
|
2852
|
+
return `${prefix}${path}`;
|
|
2853
|
+
}
|
|
2854
|
+
return `${prefix}/${path}`;
|
|
2729
2855
|
}
|
|
2730
|
-
function
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2856
|
+
async function loadManifest(url) {
|
|
2857
|
+
const fetchFn = globalThis.fetch;
|
|
2858
|
+
if (typeof fetchFn !== "function") {
|
|
2859
|
+
throw new ManifestValidationError(
|
|
2860
|
+
"loadManifest requires a global fetch; pass the manifest object directly on runtimes without fetch"
|
|
2861
|
+
);
|
|
2734
2862
|
}
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
throw new Error("No base64 decoding available");
|
|
2863
|
+
const res = await fetchFn(url);
|
|
2864
|
+
if (!res.ok) {
|
|
2865
|
+
throw new ManifestValidationError(
|
|
2866
|
+
`failed to fetch manifest from ${url}: HTTP ${res.status}`
|
|
2867
|
+
);
|
|
2741
2868
|
}
|
|
2869
|
+
const json = await res.json();
|
|
2870
|
+
return validateManifest(json);
|
|
2742
2871
|
}
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
*/
|
|
2747
|
-
constructor(config) {
|
|
2748
|
-
this.hosts = config.hosts;
|
|
2749
|
-
this.session = config.session;
|
|
2750
|
-
this.invoke = config.invoke;
|
|
2751
|
-
this.fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);
|
|
2752
|
-
this.keyProvider = config.keyProvider;
|
|
2753
|
-
this.registry = config.registry;
|
|
2754
|
-
this.delegationManager = config.delegationManager;
|
|
2755
|
-
this.createKVService = config.createKVService;
|
|
2756
|
-
this.baseUrl = (config.baseUrl ?? "").replace(/\/$/, "");
|
|
2757
|
-
this.createDelegationFn = config.createDelegation;
|
|
2758
|
-
this.createDelegationWasmFn = config.createDelegationWasm;
|
|
2759
|
-
this.pathPrefix = config.pathPrefix ?? "";
|
|
2760
|
-
this.sessionExpiry = config.sessionExpiry;
|
|
2761
|
-
this.onRootDelegationNeeded = config.onRootDelegationNeeded;
|
|
2872
|
+
function validateManifest(input) {
|
|
2873
|
+
if (input === null || typeof input !== "object") {
|
|
2874
|
+
throw new ManifestValidationError("manifest must be an object");
|
|
2762
2875
|
}
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
get host() {
|
|
2767
|
-
return this.hosts[0];
|
|
2876
|
+
const m = input;
|
|
2877
|
+
if (typeof m.id !== "string" || m.id.length === 0) {
|
|
2878
|
+
throw new ManifestValidationError("manifest.id is required and must be a non-empty string");
|
|
2768
2879
|
}
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
*/
|
|
2772
|
-
updateSession(session) {
|
|
2773
|
-
this.session = session;
|
|
2880
|
+
if (typeof m.name !== "string" || m.name.length === 0) {
|
|
2881
|
+
throw new ManifestValidationError("manifest.name is required and must be a non-empty string");
|
|
2774
2882
|
}
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
this.session = config.session;
|
|
2782
|
-
}
|
|
2783
|
-
if (config.delegationManager !== void 0) {
|
|
2784
|
-
this.delegationManager = config.delegationManager;
|
|
2785
|
-
}
|
|
2786
|
-
if (config.createDelegation !== void 0) {
|
|
2787
|
-
this.createDelegationFn = config.createDelegation;
|
|
2788
|
-
}
|
|
2789
|
-
if (config.createDelegationWasm !== void 0) {
|
|
2790
|
-
this.createDelegationWasmFn = config.createDelegationWasm;
|
|
2791
|
-
}
|
|
2792
|
-
if (config.sessionExpiry !== void 0) {
|
|
2793
|
-
this.sessionExpiry = config.sessionExpiry;
|
|
2794
|
-
}
|
|
2795
|
-
if (config.onRootDelegationNeeded !== void 0) {
|
|
2796
|
-
this.onRootDelegationNeeded = config.onRootDelegationNeeded;
|
|
2883
|
+
if (m.expiry !== void 0) {
|
|
2884
|
+
parseExpiry(m.expiry);
|
|
2885
|
+
}
|
|
2886
|
+
if (m.permissions !== void 0) {
|
|
2887
|
+
if (!Array.isArray(m.permissions)) {
|
|
2888
|
+
throw new ManifestValidationError("manifest.permissions must be an array");
|
|
2797
2889
|
}
|
|
2890
|
+
m.permissions.forEach(
|
|
2891
|
+
(p, i) => validatePermissionEntry(p, `permissions[${i}]`)
|
|
2892
|
+
);
|
|
2798
2893
|
}
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2894
|
+
if (m.delegations !== void 0) {
|
|
2895
|
+
if (!Array.isArray(m.delegations)) {
|
|
2896
|
+
throw new ManifestValidationError("manifest.delegations must be an array");
|
|
2897
|
+
}
|
|
2898
|
+
m.delegations.forEach((d, i) => {
|
|
2899
|
+
if (typeof d?.to !== "string" || d.to.length === 0) {
|
|
2900
|
+
throw new ManifestValidationError(
|
|
2901
|
+
`delegations[${i}].to is required and must be a non-empty DID string`
|
|
2902
|
+
);
|
|
2903
|
+
}
|
|
2904
|
+
if (d.expiry !== void 0) {
|
|
2905
|
+
parseExpiry(d.expiry);
|
|
2906
|
+
}
|
|
2907
|
+
if (!Array.isArray(d.permissions)) {
|
|
2908
|
+
throw new ManifestValidationError(
|
|
2909
|
+
`delegations[${i}].permissions must be an array`
|
|
2910
|
+
);
|
|
2911
|
+
}
|
|
2912
|
+
d.permissions.forEach(
|
|
2913
|
+
(p, j) => validatePermissionEntry(p, `delegations[${i}].permissions[${j}]`)
|
|
2914
|
+
);
|
|
2915
|
+
});
|
|
2916
|
+
}
|
|
2917
|
+
return m;
|
|
2918
|
+
}
|
|
2919
|
+
function validatePermissionEntry(p, path) {
|
|
2920
|
+
if (p === null || typeof p !== "object") {
|
|
2921
|
+
throw new ManifestValidationError(`${path} must be an object`);
|
|
2922
|
+
}
|
|
2923
|
+
const entry = p;
|
|
2924
|
+
if (typeof entry.service !== "string" || entry.service.length === 0) {
|
|
2925
|
+
throw new ManifestValidationError(`${path}.service is required`);
|
|
2926
|
+
}
|
|
2927
|
+
if (typeof entry.space !== "string" || entry.space.length === 0) {
|
|
2928
|
+
throw new ManifestValidationError(`${path}.space is required`);
|
|
2929
|
+
}
|
|
2930
|
+
if (typeof entry.path !== "string") {
|
|
2931
|
+
throw new ManifestValidationError(
|
|
2932
|
+
`${path}.path is required (use "" or "/" for root)`
|
|
2933
|
+
);
|
|
2934
|
+
}
|
|
2935
|
+
if (!Array.isArray(entry.actions) || entry.actions.length === 0) {
|
|
2936
|
+
throw new ManifestValidationError(
|
|
2937
|
+
`${path}.actions must be a non-empty array`
|
|
2938
|
+
);
|
|
2939
|
+
}
|
|
2940
|
+
if (entry.expiry !== void 0) {
|
|
2941
|
+
parseExpiry(entry.expiry);
|
|
2942
|
+
}
|
|
2943
|
+
}
|
|
2944
|
+
function normalizeDefaults(value) {
|
|
2945
|
+
if (value === void 0) {
|
|
2946
|
+
return DEFAULT_DEFAULTS;
|
|
2947
|
+
}
|
|
2948
|
+
if (typeof value === "boolean") {
|
|
2949
|
+
return value;
|
|
2950
|
+
}
|
|
2951
|
+
if (typeof value !== "string") {
|
|
2952
|
+
return true;
|
|
2953
|
+
}
|
|
2954
|
+
const normalized = value.trim().toLowerCase();
|
|
2955
|
+
if (normalized === "admin" || normalized === "all") {
|
|
2956
|
+
return normalized;
|
|
2957
|
+
}
|
|
2958
|
+
return true;
|
|
2959
|
+
}
|
|
2960
|
+
function defaultEntriesForTier(tier) {
|
|
2961
|
+
if (tier === false) {
|
|
2962
|
+
return [];
|
|
2963
|
+
}
|
|
2964
|
+
const source = tier === "admin" ? DEFAULT_ADMIN_ENTRIES : tier === "all" ? DEFAULT_ALL_ENTRIES : DEFAULT_STANDARD_ENTRIES;
|
|
2965
|
+
return source.map((e) => ({
|
|
2966
|
+
service: e.service,
|
|
2967
|
+
space: e.space,
|
|
2968
|
+
path: e.path,
|
|
2969
|
+
actions: [...e.actions]
|
|
2970
|
+
}));
|
|
2971
|
+
}
|
|
2972
|
+
function resolveManifest(input) {
|
|
2973
|
+
const manifest = validateManifest(input);
|
|
2974
|
+
const prefix = manifest.prefix !== void 0 ? manifest.prefix : manifest.id;
|
|
2975
|
+
const expiryMs = parseExpiry(manifest.expiry ?? DEFAULT_EXPIRY);
|
|
2976
|
+
const includePublicSpace = manifest.includePublicSpace ?? true;
|
|
2977
|
+
const tier = normalizeDefaults(manifest.defaults);
|
|
2978
|
+
const defaultEntries = defaultEntriesForTier(tier);
|
|
2979
|
+
const explicitEntries = manifest.permissions ?? [];
|
|
2980
|
+
const allEntries = [...defaultEntries, ...explicitEntries];
|
|
2981
|
+
const resources = allEntries.map(
|
|
2982
|
+
(entry) => resolveEntry(entry, prefix, expiryMs)
|
|
2983
|
+
);
|
|
2984
|
+
const additionalDelegates = (manifest.delegations ?? []).map((d) => ({
|
|
2985
|
+
did: d.to,
|
|
2986
|
+
name: d.name,
|
|
2987
|
+
expiryMs: parseExpiry(d.expiry ?? manifest.expiry ?? DEFAULT_EXPIRY),
|
|
2988
|
+
permissions: d.permissions.map(
|
|
2989
|
+
(entry) => resolveEntry(
|
|
2990
|
+
entry,
|
|
2991
|
+
prefix,
|
|
2992
|
+
parseExpiry(d.expiry ?? manifest.expiry ?? DEFAULT_EXPIRY)
|
|
2993
|
+
)
|
|
2994
|
+
)
|
|
2995
|
+
}));
|
|
2996
|
+
return {
|
|
2997
|
+
id: manifest.id,
|
|
2998
|
+
resources,
|
|
2999
|
+
expiryMs,
|
|
3000
|
+
includePublicSpace,
|
|
3001
|
+
additionalDelegates
|
|
3002
|
+
};
|
|
3003
|
+
}
|
|
3004
|
+
function resolveEntry(entry, prefix, _inheritedExpiryMs) {
|
|
3005
|
+
const resolvedPath = applyPrefix(
|
|
3006
|
+
prefix,
|
|
3007
|
+
entry.path,
|
|
3008
|
+
entry.skipPrefix === true
|
|
3009
|
+
);
|
|
3010
|
+
const resolvedActions = expandActionShortNames(entry.service, entry.actions);
|
|
3011
|
+
const entryExpiryMs = entry.expiry !== void 0 ? parseExpiry(entry.expiry) : void 0;
|
|
3012
|
+
return {
|
|
3013
|
+
service: entry.service,
|
|
3014
|
+
space: entry.space,
|
|
3015
|
+
path: resolvedPath,
|
|
3016
|
+
actions: resolvedActions,
|
|
3017
|
+
// Only populate `expiryMs` when the entry had its own expiry override.
|
|
3018
|
+
// When absent, callers use the parent (delegation or manifest) expiry
|
|
3019
|
+
// which is carried on ResolvedDelegate.expiryMs / ResolvedCapabilities.expiryMs.
|
|
3020
|
+
...entryExpiryMs !== void 0 ? { expiryMs: entryExpiryMs } : {}
|
|
3021
|
+
};
|
|
3022
|
+
}
|
|
3023
|
+
function resourceCapabilitiesToAbilitiesMap(resources) {
|
|
3024
|
+
const out = {};
|
|
3025
|
+
for (const r of resources) {
|
|
3026
|
+
const shortService = SERVICE_LONG_TO_SHORT[r.service];
|
|
3027
|
+
if (shortService === void 0) {
|
|
3028
|
+
throw new ManifestValidationError(
|
|
3029
|
+
`unknown service '${r.service}' \u2014 no short-form mapping. Known services: ${Object.keys(SERVICE_LONG_TO_SHORT).join(", ")}`
|
|
3030
|
+
);
|
|
3031
|
+
}
|
|
3032
|
+
if (out[shortService] === void 0) {
|
|
3033
|
+
out[shortService] = {};
|
|
3034
|
+
}
|
|
3035
|
+
const pathsMap = out[shortService];
|
|
3036
|
+
const existing = pathsMap[r.path];
|
|
3037
|
+
if (existing === void 0) {
|
|
3038
|
+
pathsMap[r.path] = [...r.actions];
|
|
3039
|
+
} else {
|
|
3040
|
+
const seen = new Set(existing);
|
|
3041
|
+
for (const action of r.actions) {
|
|
3042
|
+
if (!seen.has(action)) {
|
|
3043
|
+
existing.push(action);
|
|
3044
|
+
seen.add(action);
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
return out;
|
|
3050
|
+
}
|
|
3051
|
+
function manifestAbilitiesUnion(resolved) {
|
|
3052
|
+
const all = [...resolved.resources];
|
|
3053
|
+
for (const delegate of resolved.additionalDelegates) {
|
|
3054
|
+
for (const perm of delegate.permissions) {
|
|
3055
|
+
all.push(perm);
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
return resourceCapabilitiesToAbilitiesMap(all);
|
|
3059
|
+
}
|
|
3060
|
+
|
|
3061
|
+
// src/delegations/SharingService.ts
|
|
3062
|
+
function inferShortServiceFromActionUrns(actions) {
|
|
3063
|
+
let short;
|
|
3064
|
+
for (const action of actions) {
|
|
3065
|
+
const slash = action.indexOf("/");
|
|
3066
|
+
if (slash === -1) return void 0;
|
|
3067
|
+
const longService = action.slice(0, slash);
|
|
3068
|
+
const candidate = SERVICE_LONG_TO_SHORT[longService];
|
|
3069
|
+
if (candidate === void 0) return void 0;
|
|
3070
|
+
if (short === void 0) {
|
|
3071
|
+
short = candidate;
|
|
3072
|
+
} else if (short !== candidate) {
|
|
3073
|
+
return void 0;
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
return short;
|
|
3077
|
+
}
|
|
3078
|
+
var DEFAULT_READ_ACTIONS = ["tinycloud.kv/get", "tinycloud.kv/metadata"];
|
|
3079
|
+
var DEFAULT_EXPIRY_MS = 24 * 60 * 60 * 1e3;
|
|
3080
|
+
var BASE64_PREFIX = "tc1:";
|
|
3081
|
+
function createError2(code, message, cause, meta) {
|
|
3082
|
+
return {
|
|
3083
|
+
code,
|
|
3084
|
+
message,
|
|
3085
|
+
service: "delegation",
|
|
3086
|
+
cause,
|
|
3087
|
+
meta
|
|
3088
|
+
};
|
|
3089
|
+
}
|
|
3090
|
+
function base64UrlEncode(data) {
|
|
3091
|
+
let base64;
|
|
3092
|
+
if (typeof btoa !== "undefined") {
|
|
3093
|
+
base64 = btoa(unescape(encodeURIComponent(data)));
|
|
3094
|
+
} else if (typeof Buffer !== "undefined") {
|
|
3095
|
+
base64 = Buffer.from(data, "utf-8").toString("base64");
|
|
3096
|
+
} else {
|
|
3097
|
+
throw new Error("No base64 encoding available");
|
|
3098
|
+
}
|
|
3099
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
3100
|
+
}
|
|
3101
|
+
function base64UrlDecode(encoded) {
|
|
3102
|
+
let base64 = encoded.replace(/-/g, "+").replace(/_/g, "/");
|
|
3103
|
+
while (base64.length % 4) {
|
|
3104
|
+
base64 += "=";
|
|
3105
|
+
}
|
|
3106
|
+
if (typeof atob !== "undefined") {
|
|
3107
|
+
return decodeURIComponent(escape(atob(base64)));
|
|
3108
|
+
} else if (typeof Buffer !== "undefined") {
|
|
3109
|
+
return Buffer.from(base64, "base64").toString("utf-8");
|
|
3110
|
+
} else {
|
|
3111
|
+
throw new Error("No base64 decoding available");
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3114
|
+
var SharingService = class {
|
|
3115
|
+
/**
|
|
3116
|
+
* Creates a new SharingService instance.
|
|
3117
|
+
*/
|
|
3118
|
+
constructor(config) {
|
|
3119
|
+
this.hosts = config.hosts;
|
|
3120
|
+
this.session = config.session;
|
|
3121
|
+
this.invoke = config.invoke;
|
|
3122
|
+
this.fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);
|
|
3123
|
+
this.keyProvider = config.keyProvider;
|
|
3124
|
+
this.registry = config.registry;
|
|
3125
|
+
this.delegationManager = config.delegationManager;
|
|
3126
|
+
this.createKVService = config.createKVService;
|
|
3127
|
+
this.baseUrl = (config.baseUrl ?? "").replace(/\/$/, "");
|
|
3128
|
+
this.createDelegationFn = config.createDelegation;
|
|
3129
|
+
this.createDelegationWasmFn = config.createDelegationWasm;
|
|
3130
|
+
this.pathPrefix = config.pathPrefix ?? "";
|
|
3131
|
+
this.sessionExpiry = config.sessionExpiry;
|
|
3132
|
+
this.onRootDelegationNeeded = config.onRootDelegationNeeded;
|
|
3133
|
+
}
|
|
3134
|
+
/**
|
|
3135
|
+
* Gets the primary host URL.
|
|
3136
|
+
*/
|
|
3137
|
+
get host() {
|
|
3138
|
+
return this.hosts[0];
|
|
3139
|
+
}
|
|
3140
|
+
/**
|
|
3141
|
+
* Updates the session (e.g., after re-authentication).
|
|
3142
|
+
*/
|
|
3143
|
+
updateSession(session) {
|
|
3144
|
+
this.session = session;
|
|
3145
|
+
}
|
|
3146
|
+
/**
|
|
3147
|
+
* Updates the service configuration.
|
|
3148
|
+
* Used to add full capabilities (session, delegationManager, createDelegation, createDelegationWasm) after signIn.
|
|
3149
|
+
*/
|
|
3150
|
+
updateConfig(config) {
|
|
3151
|
+
if (config.session !== void 0) {
|
|
3152
|
+
this.session = config.session;
|
|
3153
|
+
}
|
|
3154
|
+
if (config.delegationManager !== void 0) {
|
|
3155
|
+
this.delegationManager = config.delegationManager;
|
|
3156
|
+
}
|
|
3157
|
+
if (config.createDelegation !== void 0) {
|
|
3158
|
+
this.createDelegationFn = config.createDelegation;
|
|
3159
|
+
}
|
|
3160
|
+
if (config.createDelegationWasm !== void 0) {
|
|
3161
|
+
this.createDelegationWasmFn = config.createDelegationWasm;
|
|
3162
|
+
}
|
|
3163
|
+
if (config.sessionExpiry !== void 0) {
|
|
3164
|
+
this.sessionExpiry = config.sessionExpiry;
|
|
3165
|
+
}
|
|
3166
|
+
if (config.onRootDelegationNeeded !== void 0) {
|
|
3167
|
+
this.onRootDelegationNeeded = config.onRootDelegationNeeded;
|
|
3168
|
+
}
|
|
3169
|
+
}
|
|
3170
|
+
/**
|
|
3171
|
+
* Generate a sharing link with an embedded private key.
|
|
3172
|
+
*
|
|
3173
|
+
* Flow:
|
|
3174
|
+
* 1. Spawn new session key (unique per share)
|
|
2804
3175
|
* 2. Create delegation from current session to spawned key
|
|
2805
3176
|
* 3. Package: { key (with private!), delegation, path, host }
|
|
2806
3177
|
* 4. Encode based on schema (base64 for now)
|
|
@@ -3047,12 +3418,34 @@ var SharingService = class {
|
|
|
3047
3418
|
}
|
|
3048
3419
|
if (this.createDelegationWasmFn) {
|
|
3049
3420
|
try {
|
|
3421
|
+
if (actions.length === 0) {
|
|
3422
|
+
return {
|
|
3423
|
+
ok: false,
|
|
3424
|
+
error: createError2(
|
|
3425
|
+
DelegationErrorCodes.VALIDATION_ERROR,
|
|
3426
|
+
"createDelegation requires at least one action"
|
|
3427
|
+
)
|
|
3428
|
+
};
|
|
3429
|
+
}
|
|
3430
|
+
const shortService = inferShortServiceFromActionUrns(actions);
|
|
3431
|
+
if (shortService === void 0) {
|
|
3432
|
+
return {
|
|
3433
|
+
ok: false,
|
|
3434
|
+
error: createError2(
|
|
3435
|
+
DelegationErrorCodes.VALIDATION_ERROR,
|
|
3436
|
+
`createDelegation: cannot infer service from actions ${JSON.stringify(actions)} \u2014 expected full URNs like "tinycloud.kv/get"`
|
|
3437
|
+
)
|
|
3438
|
+
};
|
|
3439
|
+
}
|
|
3050
3440
|
const wasmResult = this.createDelegationWasmFn({
|
|
3051
3441
|
session: this.session,
|
|
3052
3442
|
delegateDID,
|
|
3053
3443
|
spaceId: this.session.spaceId,
|
|
3054
|
-
|
|
3055
|
-
|
|
3444
|
+
abilities: {
|
|
3445
|
+
[shortService]: {
|
|
3446
|
+
[path]: [...actions]
|
|
3447
|
+
}
|
|
3448
|
+
},
|
|
3056
3449
|
expirationSecs: Math.floor(expiry.getTime() / 1e3)
|
|
3057
3450
|
});
|
|
3058
3451
|
const registerRes = await this.fetchFn(`${this.host}/delegate`, {
|
|
@@ -3071,12 +3464,22 @@ var SharingService = class {
|
|
|
3071
3464
|
)
|
|
3072
3465
|
};
|
|
3073
3466
|
}
|
|
3467
|
+
if (wasmResult.resources.length === 0) {
|
|
3468
|
+
return {
|
|
3469
|
+
ok: false,
|
|
3470
|
+
error: createError2(
|
|
3471
|
+
DelegationErrorCodes.CREATION_FAILED,
|
|
3472
|
+
"createDelegation WASM returned empty resources array for a single-entry request"
|
|
3473
|
+
)
|
|
3474
|
+
};
|
|
3475
|
+
}
|
|
3476
|
+
const primary = wasmResult.resources[0];
|
|
3074
3477
|
return {
|
|
3075
3478
|
cid: wasmResult.cid,
|
|
3076
3479
|
delegateDID: wasmResult.delegateDID,
|
|
3077
3480
|
spaceId: this.session.spaceId,
|
|
3078
|
-
path:
|
|
3079
|
-
actions:
|
|
3481
|
+
path: primary.path,
|
|
3482
|
+
actions: primary.actions,
|
|
3080
3483
|
expiry: wasmResult.expiry,
|
|
3081
3484
|
isRevoked: false,
|
|
3082
3485
|
authHeader: wasmResult.delegation,
|
|
@@ -3806,298 +4209,6 @@ async function checkNodeInfo(host, sdkProtocol, fetchFn = globalThis.fetch.bind(
|
|
|
3806
4209
|
};
|
|
3807
4210
|
}
|
|
3808
4211
|
|
|
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
4212
|
// src/capabilities.ts
|
|
4102
4213
|
var PermissionNotInManifestError = class extends Error {
|
|
4103
4214
|
constructor(missing, granted) {
|
|
@@ -4254,12 +4365,14 @@ function parseRecapCapabilities(parseWasm, siwe) {
|
|
|
4254
4365
|
isCapabilitySubset,
|
|
4255
4366
|
loadManifest,
|
|
4256
4367
|
makePublicSpaceId,
|
|
4368
|
+
manifestAbilitiesUnion,
|
|
4257
4369
|
normalizeDefaults,
|
|
4258
4370
|
ok,
|
|
4259
4371
|
parseExpiry,
|
|
4260
4372
|
parseRecapCapabilities,
|
|
4261
4373
|
parseSpaceUri,
|
|
4262
4374
|
resolveManifest,
|
|
4375
|
+
resourceCapabilitiesToAbilitiesMap,
|
|
4263
4376
|
serviceError,
|
|
4264
4377
|
submitHostDelegation,
|
|
4265
4378
|
validateClientSession,
|