@secure-exec/core 0.2.1-rc.1 → 0.3.0-rc.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/README.md +5 -5
- package/dist/binary.d.ts +4 -0
- package/dist/binary.js +25 -0
- package/dist/bytes.d.ts +2 -0
- package/dist/bytes.js +6 -0
- package/dist/callbacks.d.ts +41 -0
- package/dist/callbacks.js +94 -0
- package/dist/cargo.d.ts +2 -0
- package/dist/cargo.js +142 -0
- package/dist/correlation.d.ts +10 -0
- package/dist/correlation.js +49 -0
- package/dist/descriptors.d.ts +34 -0
- package/dist/descriptors.js +37 -0
- package/dist/event-buffer.d.ts +90 -0
- package/dist/event-buffer.js +313 -0
- package/dist/ext.d.ts +7 -0
- package/dist/ext.js +13 -0
- package/dist/filesystem.d.ts +41 -0
- package/dist/filesystem.js +70 -0
- package/dist/frame-payload-codec.d.ts +8 -0
- package/dist/frame-payload-codec.js +14 -0
- package/dist/frame-rpc.d.ts +38 -0
- package/dist/frame-rpc.js +73 -0
- package/dist/frame-stream.d.ts +27 -0
- package/dist/frame-stream.js +99 -0
- package/dist/framing.d.ts +7 -0
- package/dist/framing.js +22 -0
- package/dist/generated/AcpLimitsConfig.d.ts +4 -0
- package/dist/generated/AcpLimitsConfig.js +2 -0
- package/dist/generated/CreateVmConfig.d.ts +19 -0
- package/dist/generated/FsPermissionRule.d.ts +6 -0
- package/dist/generated/FsPermissionRuleSet.d.ts +6 -0
- package/dist/generated/FsPermissionRuleSet.js +1 -0
- package/dist/generated/FsPermissionScope.d.ts +3 -0
- package/dist/generated/FsPermissionScope.js +1 -0
- package/dist/generated/HttpLimitsConfig.d.ts +3 -0
- package/dist/generated/HttpLimitsConfig.js +2 -0
- package/dist/generated/JsModuleResolution.d.ts +1 -0
- package/dist/generated/JsModuleResolution.js +2 -0
- package/dist/generated/JsRuntimeConfig.d.ts +26 -0
- package/dist/generated/JsRuntimeConfig.js +1 -0
- package/dist/generated/JsRuntimeLimitsConfig.d.ts +7 -0
- package/dist/generated/JsRuntimeLimitsConfig.js +2 -0
- package/dist/generated/JsRuntimePlatform.d.ts +1 -0
- package/dist/generated/JsRuntimePlatform.js +2 -0
- package/dist/generated/MountPluginDescriptor.d.ts +4 -0
- package/dist/generated/MountPluginDescriptor.js +2 -0
- package/dist/generated/NativeRootFilesystemConfig.d.ts +5 -0
- package/dist/generated/NativeRootFilesystemConfig.js +1 -0
- package/dist/generated/PatternPermissionRule.d.ts +6 -0
- package/dist/generated/PatternPermissionRule.js +1 -0
- package/dist/generated/PatternPermissionRuleSet.d.ts +6 -0
- package/dist/generated/PatternPermissionRuleSet.js +1 -0
- package/dist/generated/PatternPermissionScope.d.ts +3 -0
- package/dist/generated/PatternPermissionScope.js +1 -0
- package/dist/generated/PermissionMode.d.ts +1 -0
- package/dist/generated/PermissionMode.js +2 -0
- package/dist/generated/PermissionsPolicy.d.ts +10 -0
- package/dist/generated/PermissionsPolicy.js +1 -0
- package/dist/generated/PluginLimitsConfig.d.ts +4 -0
- package/dist/generated/PluginLimitsConfig.js +2 -0
- package/dist/generated/PythonLimitsConfig.d.ts +5 -0
- package/dist/generated/PythonLimitsConfig.js +2 -0
- package/dist/generated/ResourceLimitsConfig.d.ts +22 -0
- package/dist/generated/ResourceLimitsConfig.js +2 -0
- package/dist/generated/RootFilesystemConfig.d.ts +9 -0
- package/dist/generated/RootFilesystemConfig.js +1 -0
- package/dist/generated/RootFilesystemEntry.d.ts +13 -0
- package/dist/generated/RootFilesystemEntry.js +1 -0
- package/dist/generated/RootFilesystemEntryEncoding.d.ts +1 -0
- package/dist/generated/RootFilesystemEntryEncoding.js +2 -0
- package/dist/generated/RootFilesystemEntryKind.d.ts +1 -0
- package/dist/generated/RootFilesystemEntryKind.js +2 -0
- package/dist/generated/RootFilesystemLowerDescriptor.d.ts +7 -0
- package/dist/generated/RootFilesystemLowerDescriptor.js +1 -0
- package/dist/generated/RootFilesystemMode.d.ts +1 -0
- package/dist/generated/RootFilesystemMode.js +2 -0
- package/dist/generated/ToolLimitsConfig.d.ts +10 -0
- package/dist/generated/ToolLimitsConfig.js +2 -0
- package/dist/generated/VmDnsConfig.d.ts +6 -0
- package/dist/generated/VmDnsConfig.js +2 -0
- package/dist/generated/VmLimitsConfig.d.ts +18 -0
- package/dist/generated/VmLimitsConfig.js +1 -0
- package/dist/generated/VmListenPolicyConfig.d.ts +5 -0
- package/dist/generated/VmListenPolicyConfig.js +2 -0
- package/dist/generated/WasmLimitsConfig.d.ts +5 -0
- package/dist/generated/WasmLimitsConfig.js +2 -0
- package/dist/generated-protocol.d.ts +1037 -0
- package/dist/generated-protocol.js +2887 -0
- package/dist/index.d.ts +24 -62
- package/dist/index.js +24 -53
- package/dist/json.d.ts +2 -0
- package/dist/json.js +20 -0
- package/dist/kernel-proxy.d.ts +149 -0
- package/dist/kernel-proxy.js +1733 -0
- package/dist/native-client.d.ts +41 -0
- package/dist/native-client.js +124 -0
- package/dist/node-runtime.d.ts +443 -0
- package/dist/node-runtime.js +569 -0
- package/dist/numbers.d.ts +1 -0
- package/dist/numbers.js +8 -0
- package/dist/ownership.d.ts +18 -0
- package/dist/ownership.js +77 -0
- package/dist/permissions.d.ts +29 -0
- package/dist/permissions.js +68 -0
- package/dist/process.d.ts +35 -0
- package/dist/process.js +125 -0
- package/dist/protocol-client.d.ts +46 -0
- package/dist/protocol-client.js +180 -0
- package/dist/protocol-frames.d.ts +68 -0
- package/dist/protocol-frames.js +139 -0
- package/dist/protocol-maps.d.ts +28 -0
- package/dist/protocol-maps.js +217 -0
- package/dist/protocol-schema.d.ts +10 -0
- package/dist/protocol-schema.js +11 -0
- package/dist/request-payloads.d.ts +137 -0
- package/dist/request-payloads.js +210 -0
- package/dist/response-payloads.d.ts +107 -0
- package/dist/response-payloads.js +161 -0
- package/dist/sidecar-client.d.ts +242 -0
- package/dist/sidecar-client.js +797 -0
- package/dist/state.d.ts +40 -0
- package/dist/state.js +44 -0
- package/dist/test-runtime.d.ts +526 -0
- package/dist/test-runtime.js +2119 -0
- package/dist/vm-config.d.ts +31 -0
- package/dist/vm-config.js +1 -0
- package/fixtures/alpine-defaults.json +520 -0
- package/fixtures/base-filesystem.json +528 -0
- package/package.json +193 -115
- package/LICENSE +0 -191
- package/dist/bridge-setup.d.ts +0 -6
- package/dist/bridge-setup.js +0 -9
- package/dist/esm-compiler.d.ts +0 -18
- package/dist/esm-compiler.js +0 -72
- package/dist/fs-helpers.d.ts +0 -23
- package/dist/fs-helpers.js +0 -41
- package/dist/generated/isolate-runtime.d.ts +0 -19
- package/dist/generated/isolate-runtime.js +0 -21
- package/dist/generated/polyfills.d.ts +0 -82
- package/dist/generated/polyfills.js +0 -82
- package/dist/isolate-runtime/apply-custom-global-policy.js +0 -53
- package/dist/isolate-runtime/apply-timing-mitigation-freeze.js +0 -130
- package/dist/isolate-runtime/apply-timing-mitigation-off.js +0 -14
- package/dist/isolate-runtime/bridge-attach.js +0 -29
- package/dist/isolate-runtime/bridge-initial-globals.js +0 -385
- package/dist/isolate-runtime/eval-script-result.js +0 -8
- package/dist/isolate-runtime/global-exposure-helpers.js +0 -36
- package/dist/isolate-runtime/init-commonjs-module-globals.js +0 -28
- package/dist/isolate-runtime/override-process-cwd.js +0 -8
- package/dist/isolate-runtime/override-process-env.js +0 -8
- package/dist/isolate-runtime/require-setup.js +0 -4153
- package/dist/isolate-runtime/set-commonjs-file-globals.js +0 -36
- package/dist/isolate-runtime/set-stdin-data.js +0 -10
- package/dist/isolate-runtime/setup-dynamic-import.js +0 -123
- package/dist/isolate-runtime/setup-fs-facade.js +0 -87
- package/dist/kernel/command-registry.d.ts +0 -44
- package/dist/kernel/command-registry.js +0 -114
- package/dist/kernel/device-backend.d.ts +0 -14
- package/dist/kernel/device-backend.js +0 -251
- package/dist/kernel/device-layer.d.ts +0 -12
- package/dist/kernel/device-layer.js +0 -271
- package/dist/kernel/dns-cache.d.ts +0 -29
- package/dist/kernel/dns-cache.js +0 -52
- package/dist/kernel/fd-table.d.ts +0 -84
- package/dist/kernel/fd-table.js +0 -278
- package/dist/kernel/file-lock.d.ts +0 -34
- package/dist/kernel/file-lock.js +0 -122
- package/dist/kernel/host-adapter.d.ts +0 -50
- package/dist/kernel/host-adapter.js +0 -8
- package/dist/kernel/index.d.ts +0 -36
- package/dist/kernel/index.js +0 -34
- package/dist/kernel/kernel.d.ts +0 -9
- package/dist/kernel/kernel.js +0 -1415
- package/dist/kernel/mount-table.d.ts +0 -75
- package/dist/kernel/mount-table.js +0 -353
- package/dist/kernel/permissions.d.ts +0 -36
- package/dist/kernel/permissions.js +0 -150
- package/dist/kernel/pipe-manager.d.ts +0 -64
- package/dist/kernel/pipe-manager.js +0 -267
- package/dist/kernel/proc-backend.d.ts +0 -30
- package/dist/kernel/proc-backend.js +0 -428
- package/dist/kernel/proc-layer.d.ts +0 -11
- package/dist/kernel/proc-layer.js +0 -507
- package/dist/kernel/process-table.d.ts +0 -126
- package/dist/kernel/process-table.js +0 -651
- package/dist/kernel/pty.d.ts +0 -109
- package/dist/kernel/pty.js +0 -552
- package/dist/kernel/socket-table.d.ts +0 -312
- package/dist/kernel/socket-table.js +0 -1188
- package/dist/kernel/timer-table.d.ts +0 -54
- package/dist/kernel/timer-table.js +0 -108
- package/dist/kernel/types.d.ts +0 -541
- package/dist/kernel/types.js +0 -98
- package/dist/kernel/user.d.ts +0 -29
- package/dist/kernel/user.js +0 -35
- package/dist/kernel/vfs.d.ts +0 -82
- package/dist/kernel/vfs.js +0 -25
- package/dist/kernel/wait.d.ts +0 -45
- package/dist/kernel/wait.js +0 -112
- package/dist/kernel/wstatus.d.ts +0 -21
- package/dist/kernel/wstatus.js +0 -33
- package/dist/module-resolver.d.ts +0 -29
- package/dist/module-resolver.js +0 -314
- package/dist/package-bundler.d.ts +0 -41
- package/dist/package-bundler.js +0 -497
- package/dist/runtime-driver.d.ts +0 -66
- package/dist/shared/api-types.d.ts +0 -83
- package/dist/shared/bridge-contract.d.ts +0 -772
- package/dist/shared/bridge-contract.js +0 -169
- package/dist/shared/console-formatter.d.ts +0 -22
- package/dist/shared/console-formatter.js +0 -161
- package/dist/shared/constants.d.ts +0 -3
- package/dist/shared/constants.js +0 -3
- package/dist/shared/errors.d.ts +0 -16
- package/dist/shared/errors.js +0 -21
- package/dist/shared/esm-utils.d.ts +0 -28
- package/dist/shared/esm-utils.js +0 -97
- package/dist/shared/global-exposure.d.ts +0 -38
- package/dist/shared/global-exposure.js +0 -876
- package/dist/shared/in-memory-fs.d.ts +0 -16
- package/dist/shared/in-memory-fs.js +0 -115
- package/dist/shared/permissions.d.ts +0 -36
- package/dist/shared/permissions.js +0 -314
- package/dist/shared/require-setup.d.ts +0 -6
- package/dist/shared/require-setup.js +0 -9
- package/dist/test/block-store-conformance.d.ts +0 -34
- package/dist/test/block-store-conformance.js +0 -251
- package/dist/test/metadata-store-conformance.d.ts +0 -37
- package/dist/test/metadata-store-conformance.js +0 -646
- package/dist/test/vfs-conformance.d.ts +0 -65
- package/dist/test/vfs-conformance.js +0 -842
- package/dist/types.d.ts +0 -98
- package/dist/types.js +0 -6
- package/dist/vfs/chunked-vfs.d.ts +0 -66
- package/dist/vfs/chunked-vfs.js +0 -1290
- package/dist/vfs/host-block-store.d.ts +0 -19
- package/dist/vfs/host-block-store.js +0 -97
- package/dist/vfs/memory-block-store.d.ts +0 -16
- package/dist/vfs/memory-block-store.js +0 -45
- package/dist/vfs/memory-metadata.d.ts +0 -75
- package/dist/vfs/memory-metadata.js +0 -528
- package/dist/vfs/sqlite-metadata.d.ts +0 -91
- package/dist/vfs/sqlite-metadata.js +0 -582
- package/dist/vfs/types.d.ts +0 -210
- package/dist/vfs/types.js +0 -8
- /package/dist/{runtime-driver.js → generated/CreateVmConfig.js} +0 -0
- /package/dist/{shared/api-types.js → generated/FsPermissionRule.js} +0 -0
|
@@ -1,528 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure JS Map-based FsMetadataStore for ephemeral VMs and tests.
|
|
3
|
-
*
|
|
4
|
-
* All data lives in memory. Root inode (ino=1, type='directory') is created
|
|
5
|
-
* at construction time. transaction() just calls the callback directly since
|
|
6
|
-
* single-threaded JS has no interleaving risk within synchronous sections.
|
|
7
|
-
*
|
|
8
|
-
* Optionally supports versioning when `{ versioning: true }` is passed to
|
|
9
|
-
* the constructor. Version retention (automatic cleanup of old versions)
|
|
10
|
-
* defaults to false. There is intentionally no background cleanup task;
|
|
11
|
-
* callers are expected to prune versions explicitly via ChunkedVFS.
|
|
12
|
-
*/
|
|
13
|
-
import { KernelError } from "../kernel/types.js";
|
|
14
|
-
const SYMLOOP_MAX = 40;
|
|
15
|
-
const S_IFREG = 0o100000;
|
|
16
|
-
const S_IFDIR = 0o040000;
|
|
17
|
-
const S_IFLNK = 0o120000;
|
|
18
|
-
export class InMemoryMetadataStore {
|
|
19
|
-
nextIno = 2;
|
|
20
|
-
inodes = new Map();
|
|
21
|
-
dentries = new Map();
|
|
22
|
-
symlinkTargets = new Map();
|
|
23
|
-
chunks = new Map();
|
|
24
|
-
versioningEnabled;
|
|
25
|
-
versions = new Map();
|
|
26
|
-
constructor(options) {
|
|
27
|
-
this.versioningEnabled = options?.versioning ?? false;
|
|
28
|
-
const now = Date.now();
|
|
29
|
-
const rootInode = {
|
|
30
|
-
ino: 1,
|
|
31
|
-
type: "directory",
|
|
32
|
-
mode: S_IFDIR | 0o755,
|
|
33
|
-
uid: 0,
|
|
34
|
-
gid: 0,
|
|
35
|
-
size: 0,
|
|
36
|
-
nlink: 2,
|
|
37
|
-
atimeMs: now,
|
|
38
|
-
mtimeMs: now,
|
|
39
|
-
ctimeMs: now,
|
|
40
|
-
birthtimeMs: now,
|
|
41
|
-
storageMode: "inline",
|
|
42
|
-
inlineContent: null,
|
|
43
|
-
};
|
|
44
|
-
this.inodes.set(1, rootInode);
|
|
45
|
-
this.dentries.set(1, new Map());
|
|
46
|
-
}
|
|
47
|
-
// -- Transactions --
|
|
48
|
-
async transaction(fn) {
|
|
49
|
-
return fn();
|
|
50
|
-
}
|
|
51
|
-
// -- Inode lifecycle --
|
|
52
|
-
async createInode(attrs) {
|
|
53
|
-
const ino = this.nextIno++;
|
|
54
|
-
const now = Date.now();
|
|
55
|
-
let mode = attrs.mode;
|
|
56
|
-
if (attrs.type === "file")
|
|
57
|
-
mode |= S_IFREG;
|
|
58
|
-
else if (attrs.type === "directory")
|
|
59
|
-
mode |= S_IFDIR;
|
|
60
|
-
else if (attrs.type === "symlink")
|
|
61
|
-
mode |= S_IFLNK;
|
|
62
|
-
const meta = {
|
|
63
|
-
ino,
|
|
64
|
-
type: attrs.type,
|
|
65
|
-
mode,
|
|
66
|
-
uid: attrs.uid,
|
|
67
|
-
gid: attrs.gid,
|
|
68
|
-
size: 0,
|
|
69
|
-
nlink: 0,
|
|
70
|
-
atimeMs: now,
|
|
71
|
-
mtimeMs: now,
|
|
72
|
-
ctimeMs: now,
|
|
73
|
-
birthtimeMs: now,
|
|
74
|
-
storageMode: "inline",
|
|
75
|
-
inlineContent: null,
|
|
76
|
-
};
|
|
77
|
-
this.inodes.set(ino, meta);
|
|
78
|
-
if (attrs.type === "directory") {
|
|
79
|
-
this.dentries.set(ino, new Map());
|
|
80
|
-
}
|
|
81
|
-
if (attrs.type === "symlink" && attrs.symlinkTarget !== undefined) {
|
|
82
|
-
this.symlinkTargets.set(ino, attrs.symlinkTarget);
|
|
83
|
-
}
|
|
84
|
-
return ino;
|
|
85
|
-
}
|
|
86
|
-
async getInode(ino) {
|
|
87
|
-
const meta = this.inodes.get(ino);
|
|
88
|
-
if (!meta)
|
|
89
|
-
return null;
|
|
90
|
-
return { ...meta };
|
|
91
|
-
}
|
|
92
|
-
async updateInode(ino, updates) {
|
|
93
|
-
const meta = this.inodes.get(ino);
|
|
94
|
-
if (!meta)
|
|
95
|
-
return;
|
|
96
|
-
Object.assign(meta, updates);
|
|
97
|
-
}
|
|
98
|
-
async deleteInode(ino) {
|
|
99
|
-
this.inodes.delete(ino);
|
|
100
|
-
this.dentries.delete(ino);
|
|
101
|
-
this.symlinkTargets.delete(ino);
|
|
102
|
-
this.chunks.delete(ino);
|
|
103
|
-
}
|
|
104
|
-
// -- Directory entries --
|
|
105
|
-
async lookup(parentIno, name) {
|
|
106
|
-
const dir = this.dentries.get(parentIno);
|
|
107
|
-
if (!dir)
|
|
108
|
-
return null;
|
|
109
|
-
const entry = dir.get(name);
|
|
110
|
-
return entry ? entry.childIno : null;
|
|
111
|
-
}
|
|
112
|
-
async createDentry(parentIno, name, childIno, type) {
|
|
113
|
-
let dir = this.dentries.get(parentIno);
|
|
114
|
-
if (!dir) {
|
|
115
|
-
dir = new Map();
|
|
116
|
-
this.dentries.set(parentIno, dir);
|
|
117
|
-
}
|
|
118
|
-
if (dir.has(name)) {
|
|
119
|
-
throw new KernelError("EEXIST", `'${name}' already exists in directory`);
|
|
120
|
-
}
|
|
121
|
-
dir.set(name, { childIno, type });
|
|
122
|
-
}
|
|
123
|
-
async removeDentry(parentIno, name) {
|
|
124
|
-
const dir = this.dentries.get(parentIno);
|
|
125
|
-
if (dir) {
|
|
126
|
-
dir.delete(name);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
async listDir(parentIno) {
|
|
130
|
-
const dir = this.dentries.get(parentIno);
|
|
131
|
-
if (!dir)
|
|
132
|
-
return [];
|
|
133
|
-
const result = [];
|
|
134
|
-
for (const [name, entry] of dir) {
|
|
135
|
-
result.push({ name, ino: entry.childIno, type: entry.type });
|
|
136
|
-
}
|
|
137
|
-
return result;
|
|
138
|
-
}
|
|
139
|
-
async listDirWithStats(parentIno) {
|
|
140
|
-
const dir = this.dentries.get(parentIno);
|
|
141
|
-
if (!dir)
|
|
142
|
-
return [];
|
|
143
|
-
const result = [];
|
|
144
|
-
for (const [name, entry] of dir) {
|
|
145
|
-
const meta = this.inodes.get(entry.childIno);
|
|
146
|
-
if (meta) {
|
|
147
|
-
result.push({ name, ino: entry.childIno, type: entry.type, stat: { ...meta } });
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
return result;
|
|
151
|
-
}
|
|
152
|
-
async renameDentry(srcParentIno, srcName, dstParentIno, dstName) {
|
|
153
|
-
const srcDir = this.dentries.get(srcParentIno);
|
|
154
|
-
if (!srcDir)
|
|
155
|
-
return;
|
|
156
|
-
const entry = srcDir.get(srcName);
|
|
157
|
-
if (!entry)
|
|
158
|
-
return;
|
|
159
|
-
srcDir.delete(srcName);
|
|
160
|
-
let dstDir = this.dentries.get(dstParentIno);
|
|
161
|
-
if (!dstDir) {
|
|
162
|
-
dstDir = new Map();
|
|
163
|
-
this.dentries.set(dstParentIno, dstDir);
|
|
164
|
-
}
|
|
165
|
-
dstDir.set(dstName, entry);
|
|
166
|
-
}
|
|
167
|
-
// -- Path resolution --
|
|
168
|
-
async resolvePath(path) {
|
|
169
|
-
return this.resolvePathSync(path);
|
|
170
|
-
}
|
|
171
|
-
async resolveParentPath(path) {
|
|
172
|
-
const components = splitPathComponents(path);
|
|
173
|
-
if (components.length === 0) {
|
|
174
|
-
throw new KernelError("ENOENT", `cannot resolve parent of root`);
|
|
175
|
-
}
|
|
176
|
-
const name = components[components.length - 1];
|
|
177
|
-
const parentComponents = components.slice(0, -1);
|
|
178
|
-
const parentIno = this.resolveComponentsCore(parentComponents, 0);
|
|
179
|
-
return { parentIno, name };
|
|
180
|
-
}
|
|
181
|
-
// -- Symlinks --
|
|
182
|
-
async readSymlink(ino) {
|
|
183
|
-
const target = this.symlinkTargets.get(ino);
|
|
184
|
-
if (target === undefined) {
|
|
185
|
-
throw new KernelError("EINVAL", `inode ${ino} is not a symlink`);
|
|
186
|
-
}
|
|
187
|
-
return target;
|
|
188
|
-
}
|
|
189
|
-
// -- Chunk mapping --
|
|
190
|
-
async getChunkKey(ino, chunkIndex) {
|
|
191
|
-
const map = this.chunks.get(ino);
|
|
192
|
-
if (!map)
|
|
193
|
-
return null;
|
|
194
|
-
return map.get(chunkIndex) ?? null;
|
|
195
|
-
}
|
|
196
|
-
async setChunkKey(ino, chunkIndex, key) {
|
|
197
|
-
let map = this.chunks.get(ino);
|
|
198
|
-
if (!map) {
|
|
199
|
-
map = new Map();
|
|
200
|
-
this.chunks.set(ino, map);
|
|
201
|
-
}
|
|
202
|
-
map.set(chunkIndex, key);
|
|
203
|
-
}
|
|
204
|
-
async getAllChunkKeys(ino) {
|
|
205
|
-
const map = this.chunks.get(ino);
|
|
206
|
-
if (!map)
|
|
207
|
-
return [];
|
|
208
|
-
const entries = [];
|
|
209
|
-
for (const [chunkIndex, key] of map) {
|
|
210
|
-
entries.push({ chunkIndex, key });
|
|
211
|
-
}
|
|
212
|
-
entries.sort((a, b) => a.chunkIndex - b.chunkIndex);
|
|
213
|
-
return entries;
|
|
214
|
-
}
|
|
215
|
-
async deleteAllChunks(ino) {
|
|
216
|
-
const map = this.chunks.get(ino);
|
|
217
|
-
if (!map)
|
|
218
|
-
return [];
|
|
219
|
-
const keys = Array.from(map.values());
|
|
220
|
-
this.chunks.delete(ino);
|
|
221
|
-
return keys;
|
|
222
|
-
}
|
|
223
|
-
async deleteChunksFrom(ino, startIndex) {
|
|
224
|
-
const map = this.chunks.get(ino);
|
|
225
|
-
if (!map)
|
|
226
|
-
return [];
|
|
227
|
-
const deleted = [];
|
|
228
|
-
for (const [idx, key] of map) {
|
|
229
|
-
if (idx >= startIndex) {
|
|
230
|
-
deleted.push(key);
|
|
231
|
-
map.delete(idx);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
return deleted;
|
|
235
|
-
}
|
|
236
|
-
// -- Synchronous accessors (for prepareOpenSync in kernel) --
|
|
237
|
-
resolvePathSync(path) {
|
|
238
|
-
const components = splitPathComponents(path);
|
|
239
|
-
return this.resolveComponentsCore(components, 0);
|
|
240
|
-
}
|
|
241
|
-
lookupSync(parentIno, name) {
|
|
242
|
-
const dir = this.dentries.get(parentIno);
|
|
243
|
-
if (!dir)
|
|
244
|
-
return null;
|
|
245
|
-
const entry = dir.get(name);
|
|
246
|
-
return entry ? entry.childIno : null;
|
|
247
|
-
}
|
|
248
|
-
getInodeSync(ino) {
|
|
249
|
-
const meta = this.inodes.get(ino);
|
|
250
|
-
if (!meta)
|
|
251
|
-
return null;
|
|
252
|
-
return { ...meta };
|
|
253
|
-
}
|
|
254
|
-
createInodeSync(attrs) {
|
|
255
|
-
const ino = this.nextIno++;
|
|
256
|
-
const now = Date.now();
|
|
257
|
-
let mode = attrs.mode;
|
|
258
|
-
if (attrs.type === "file")
|
|
259
|
-
mode |= S_IFREG;
|
|
260
|
-
else if (attrs.type === "directory")
|
|
261
|
-
mode |= S_IFDIR;
|
|
262
|
-
else if (attrs.type === "symlink")
|
|
263
|
-
mode |= S_IFLNK;
|
|
264
|
-
const meta = {
|
|
265
|
-
ino,
|
|
266
|
-
type: attrs.type,
|
|
267
|
-
mode,
|
|
268
|
-
uid: attrs.uid,
|
|
269
|
-
gid: attrs.gid,
|
|
270
|
-
size: 0,
|
|
271
|
-
nlink: 0,
|
|
272
|
-
atimeMs: now,
|
|
273
|
-
mtimeMs: now,
|
|
274
|
-
ctimeMs: now,
|
|
275
|
-
birthtimeMs: now,
|
|
276
|
-
storageMode: "inline",
|
|
277
|
-
inlineContent: null,
|
|
278
|
-
};
|
|
279
|
-
this.inodes.set(ino, meta);
|
|
280
|
-
if (attrs.type === "directory") {
|
|
281
|
-
this.dentries.set(ino, new Map());
|
|
282
|
-
}
|
|
283
|
-
if (attrs.type === "symlink" && attrs.symlinkTarget !== undefined) {
|
|
284
|
-
this.symlinkTargets.set(ino, attrs.symlinkTarget);
|
|
285
|
-
}
|
|
286
|
-
return ino;
|
|
287
|
-
}
|
|
288
|
-
updateInodeSync(ino, updates) {
|
|
289
|
-
const meta = this.inodes.get(ino);
|
|
290
|
-
if (!meta)
|
|
291
|
-
return;
|
|
292
|
-
Object.assign(meta, updates);
|
|
293
|
-
}
|
|
294
|
-
createDentrySync(parentIno, name, childIno, type) {
|
|
295
|
-
let dir = this.dentries.get(parentIno);
|
|
296
|
-
if (!dir) {
|
|
297
|
-
dir = new Map();
|
|
298
|
-
this.dentries.set(parentIno, dir);
|
|
299
|
-
}
|
|
300
|
-
if (dir.has(name)) {
|
|
301
|
-
throw new KernelError("EEXIST", `'${name}' already exists in directory`);
|
|
302
|
-
}
|
|
303
|
-
dir.set(name, { childIno, type });
|
|
304
|
-
}
|
|
305
|
-
deleteAllChunksSync(ino) {
|
|
306
|
-
const map = this.chunks.get(ino);
|
|
307
|
-
if (!map)
|
|
308
|
-
return [];
|
|
309
|
-
const keys = Array.from(map.values());
|
|
310
|
-
this.chunks.delete(ino);
|
|
311
|
-
return keys;
|
|
312
|
-
}
|
|
313
|
-
// -- Versioning --
|
|
314
|
-
// No background cleanup task is implemented. This is intentional; callers
|
|
315
|
-
// prune versions explicitly via ChunkedVFS.pruneVersions(). Version
|
|
316
|
-
// retention defaults to false (no automatic cleanup).
|
|
317
|
-
async createVersion(ino) {
|
|
318
|
-
if (!this.versioningEnabled) {
|
|
319
|
-
throw new Error("versioning is not enabled");
|
|
320
|
-
}
|
|
321
|
-
const meta = this.inodes.get(ino);
|
|
322
|
-
if (!meta) {
|
|
323
|
-
throw new KernelError("ENOENT", `inode ${ino} not found`);
|
|
324
|
-
}
|
|
325
|
-
const records = this.versions.get(ino) ?? [];
|
|
326
|
-
const version = records.length > 0 ? records[records.length - 1].version + 1 : 1;
|
|
327
|
-
const chunkMap = [];
|
|
328
|
-
const inoChunks = this.chunks.get(ino);
|
|
329
|
-
if (inoChunks) {
|
|
330
|
-
for (const [chunkIndex, key] of inoChunks) {
|
|
331
|
-
chunkMap.push({ chunkIndex, key });
|
|
332
|
-
}
|
|
333
|
-
chunkMap.sort((a, b) => a.chunkIndex - b.chunkIndex);
|
|
334
|
-
}
|
|
335
|
-
const record = {
|
|
336
|
-
version,
|
|
337
|
-
size: meta.size,
|
|
338
|
-
createdAt: Date.now(),
|
|
339
|
-
storageMode: meta.storageMode,
|
|
340
|
-
inlineContent: meta.inlineContent ? new Uint8Array(meta.inlineContent) : null,
|
|
341
|
-
chunkMap,
|
|
342
|
-
};
|
|
343
|
-
records.push(record);
|
|
344
|
-
this.versions.set(ino, records);
|
|
345
|
-
return version;
|
|
346
|
-
}
|
|
347
|
-
async getVersion(ino, version) {
|
|
348
|
-
if (!this.versioningEnabled) {
|
|
349
|
-
throw new Error("versioning is not enabled");
|
|
350
|
-
}
|
|
351
|
-
const records = this.versions.get(ino);
|
|
352
|
-
if (!records)
|
|
353
|
-
return null;
|
|
354
|
-
const record = records.find((r) => r.version === version);
|
|
355
|
-
if (!record)
|
|
356
|
-
return null;
|
|
357
|
-
return {
|
|
358
|
-
version: record.version,
|
|
359
|
-
size: record.size,
|
|
360
|
-
createdAt: record.createdAt,
|
|
361
|
-
storageMode: record.storageMode,
|
|
362
|
-
inlineContent: record.inlineContent ? new Uint8Array(record.inlineContent) : null,
|
|
363
|
-
};
|
|
364
|
-
}
|
|
365
|
-
async listVersions(ino) {
|
|
366
|
-
if (!this.versioningEnabled) {
|
|
367
|
-
throw new Error("versioning is not enabled");
|
|
368
|
-
}
|
|
369
|
-
const records = this.versions.get(ino) ?? [];
|
|
370
|
-
return records
|
|
371
|
-
.map((r) => ({
|
|
372
|
-
version: r.version,
|
|
373
|
-
size: r.size,
|
|
374
|
-
createdAt: r.createdAt,
|
|
375
|
-
storageMode: r.storageMode,
|
|
376
|
-
inlineContent: r.inlineContent ? new Uint8Array(r.inlineContent) : null,
|
|
377
|
-
}))
|
|
378
|
-
.reverse();
|
|
379
|
-
}
|
|
380
|
-
async getVersionChunkMap(ino, version) {
|
|
381
|
-
if (!this.versioningEnabled) {
|
|
382
|
-
throw new Error("versioning is not enabled");
|
|
383
|
-
}
|
|
384
|
-
const records = this.versions.get(ino);
|
|
385
|
-
if (!records)
|
|
386
|
-
return [];
|
|
387
|
-
const record = records.find((r) => r.version === version);
|
|
388
|
-
if (!record)
|
|
389
|
-
return [];
|
|
390
|
-
return record.chunkMap.map((e) => ({ chunkIndex: e.chunkIndex, key: e.key }));
|
|
391
|
-
}
|
|
392
|
-
async deleteVersions(ino, versions) {
|
|
393
|
-
if (!this.versioningEnabled) {
|
|
394
|
-
throw new Error("versioning is not enabled");
|
|
395
|
-
}
|
|
396
|
-
if (versions.length === 0)
|
|
397
|
-
return [];
|
|
398
|
-
const records = this.versions.get(ino);
|
|
399
|
-
if (!records)
|
|
400
|
-
return [];
|
|
401
|
-
const versionSet = new Set(versions);
|
|
402
|
-
// Collect block keys from versions being deleted.
|
|
403
|
-
const deletedBlockKeys = new Set();
|
|
404
|
-
for (const r of records) {
|
|
405
|
-
if (versionSet.has(r.version)) {
|
|
406
|
-
for (const e of r.chunkMap) {
|
|
407
|
-
deletedBlockKeys.add(e.key);
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
// Remove the version records.
|
|
412
|
-
const remaining = records.filter((r) => !versionSet.has(r.version));
|
|
413
|
-
this.versions.set(ino, remaining);
|
|
414
|
-
if (deletedBlockKeys.size === 0)
|
|
415
|
-
return [];
|
|
416
|
-
// Find keys still referenced by remaining versions.
|
|
417
|
-
const referencedKeys = new Set();
|
|
418
|
-
for (const r of remaining) {
|
|
419
|
-
for (const e of r.chunkMap) {
|
|
420
|
-
referencedKeys.add(e.key);
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
// Also check the current chunk map.
|
|
424
|
-
const currentChunks = this.chunks.get(ino);
|
|
425
|
-
if (currentChunks) {
|
|
426
|
-
for (const key of currentChunks.values()) {
|
|
427
|
-
referencedKeys.add(key);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
// Return orphaned keys.
|
|
431
|
-
const orphanedKeys = [];
|
|
432
|
-
for (const key of deletedBlockKeys) {
|
|
433
|
-
if (!referencedKeys.has(key)) {
|
|
434
|
-
orphanedKeys.push(key);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
return orphanedKeys;
|
|
438
|
-
}
|
|
439
|
-
async restoreVersion(ino, version) {
|
|
440
|
-
if (!this.versioningEnabled) {
|
|
441
|
-
throw new Error("versioning is not enabled");
|
|
442
|
-
}
|
|
443
|
-
const records = this.versions.get(ino);
|
|
444
|
-
const record = records?.find((r) => r.version === version);
|
|
445
|
-
if (!record) {
|
|
446
|
-
throw new KernelError("ENOENT", `version ${version} not found for inode ${ino}`);
|
|
447
|
-
}
|
|
448
|
-
// Clear current chunk map.
|
|
449
|
-
this.chunks.delete(ino);
|
|
450
|
-
// Restore chunk map from version.
|
|
451
|
-
if (record.chunkMap.length > 0) {
|
|
452
|
-
const map = new Map();
|
|
453
|
-
for (const entry of record.chunkMap) {
|
|
454
|
-
map.set(entry.chunkIndex, entry.key);
|
|
455
|
-
}
|
|
456
|
-
this.chunks.set(ino, map);
|
|
457
|
-
}
|
|
458
|
-
// Restore inode metadata from version.
|
|
459
|
-
const meta = this.inodes.get(ino);
|
|
460
|
-
if (meta) {
|
|
461
|
-
meta.size = record.size;
|
|
462
|
-
meta.storageMode = record.storageMode;
|
|
463
|
-
meta.inlineContent = record.inlineContent ? new Uint8Array(record.inlineContent) : null;
|
|
464
|
-
meta.mtimeMs = Date.now();
|
|
465
|
-
meta.ctimeMs = Date.now();
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
// -- Internal helpers --
|
|
469
|
-
resolveComponentsCore(components, symlinkDepth) {
|
|
470
|
-
let currentIno = 1; // root
|
|
471
|
-
for (let i = 0; i < components.length; i++) {
|
|
472
|
-
const name = components[i];
|
|
473
|
-
const meta = this.inodes.get(currentIno);
|
|
474
|
-
if (!meta || meta.type !== "directory") {
|
|
475
|
-
throw new KernelError("ENOENT", `no such file or directory: component '${name}'`);
|
|
476
|
-
}
|
|
477
|
-
const dir = this.dentries.get(currentIno);
|
|
478
|
-
if (!dir) {
|
|
479
|
-
throw new KernelError("ENOENT", `no such file or directory: component '${name}'`);
|
|
480
|
-
}
|
|
481
|
-
const entry = dir.get(name);
|
|
482
|
-
if (!entry) {
|
|
483
|
-
throw new KernelError("ENOENT", `no such file or directory: '${name}'`);
|
|
484
|
-
}
|
|
485
|
-
currentIno = entry.childIno;
|
|
486
|
-
// Follow symlinks.
|
|
487
|
-
const childMeta = this.inodes.get(currentIno);
|
|
488
|
-
if (childMeta && childMeta.type === "symlink") {
|
|
489
|
-
if (symlinkDepth >= SYMLOOP_MAX) {
|
|
490
|
-
throw new KernelError("ELOOP", "too many levels of symbolic links");
|
|
491
|
-
}
|
|
492
|
-
const target = this.symlinkTargets.get(currentIno);
|
|
493
|
-
if (!target) {
|
|
494
|
-
throw new KernelError("ENOENT", "dangling symlink");
|
|
495
|
-
}
|
|
496
|
-
// Resolve symlink target relative to current position.
|
|
497
|
-
const targetComponents = splitPathComponents(target);
|
|
498
|
-
const remaining = components.slice(i + 1);
|
|
499
|
-
const fullComponents = target.startsWith("/")
|
|
500
|
-
? [...targetComponents, ...remaining]
|
|
501
|
-
: [
|
|
502
|
-
...this.getPathComponents(currentIno, components.slice(0, i)),
|
|
503
|
-
...targetComponents,
|
|
504
|
-
...remaining,
|
|
505
|
-
];
|
|
506
|
-
return this.resolveComponentsCore(fullComponents, symlinkDepth + 1);
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
return currentIno;
|
|
510
|
-
}
|
|
511
|
-
/**
|
|
512
|
-
* Get the parent path components for resolving a relative symlink.
|
|
513
|
-
* We need to reconstruct the parent directory path from the components
|
|
514
|
-
* we have already resolved (everything before the symlink).
|
|
515
|
-
*/
|
|
516
|
-
getPathComponents(_symlinkIno, parentComponents) {
|
|
517
|
-
return parentComponents;
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
// ---------------------------------------------------------------------------
|
|
521
|
-
// Helpers
|
|
522
|
-
// ---------------------------------------------------------------------------
|
|
523
|
-
function splitPathComponents(path) {
|
|
524
|
-
if (!path || path === "/")
|
|
525
|
-
return [];
|
|
526
|
-
const normalized = path.startsWith("/") ? path.slice(1) : path;
|
|
527
|
-
return normalized.split("/").filter((c) => c.length > 0);
|
|
528
|
-
}
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SQLite-backed FsMetadataStore for persistent local and cloud storage.
|
|
3
|
-
*
|
|
4
|
-
* All data is stored in four tables: inodes, dentries, symlinks, chunks.
|
|
5
|
-
* Root inode (ino=1, type='directory') is created at initialization.
|
|
6
|
-
* transaction() wraps in BEGIN/COMMIT, rolls back on error.
|
|
7
|
-
* resolvePath uses iterative SELECT queries with ELOOP limit of 40.
|
|
8
|
-
*
|
|
9
|
-
* Usage:
|
|
10
|
-
* const store = new SqliteMetadataStore({ dbPath: ':memory:' });
|
|
11
|
-
* // or: new SqliteMetadataStore({ dbPath: '/tmp/metadata.db' });
|
|
12
|
-
*
|
|
13
|
-
* The store implements the FsMetadataStore interface and can be composed
|
|
14
|
-
* with any FsBlockStore via createChunkedVfs() to form a full VirtualFileSystem.
|
|
15
|
-
*/
|
|
16
|
-
import type { CreateInodeAttrs, DentryInfo, DentryStatInfo, FsMetadataStore, FsMetadataStoreVersioning, InodeMeta, InodeType, VersionMeta } from "./types.js";
|
|
17
|
-
export interface SqliteMetadataStoreOptions {
|
|
18
|
-
/** Path to the SQLite database file. Use ':memory:' for in-memory. */
|
|
19
|
-
dbPath: string;
|
|
20
|
-
/** Enable file versioning support. Default: false. */
|
|
21
|
-
versioning?: boolean;
|
|
22
|
-
}
|
|
23
|
-
export declare class SqliteMetadataStore implements FsMetadataStore, FsMetadataStoreVersioning {
|
|
24
|
-
private db;
|
|
25
|
-
private versioningEnabled;
|
|
26
|
-
private stmtGetInode;
|
|
27
|
-
private stmtUpdateInode;
|
|
28
|
-
private stmtDeleteInode;
|
|
29
|
-
private stmtDeleteSymlink;
|
|
30
|
-
private stmtDeleteChunks;
|
|
31
|
-
private stmtDeleteDentriesForParent;
|
|
32
|
-
private stmtLookup;
|
|
33
|
-
private stmtCreateDentry;
|
|
34
|
-
private stmtRemoveDentry;
|
|
35
|
-
private stmtListDir;
|
|
36
|
-
private stmtListDirWithStats;
|
|
37
|
-
private stmtGetSymlink;
|
|
38
|
-
private stmtGetChunkKey;
|
|
39
|
-
private stmtSetChunkKey;
|
|
40
|
-
private stmtLookupFull;
|
|
41
|
-
private stmtGetAllChunkKeys;
|
|
42
|
-
private stmtDeleteAllChunks;
|
|
43
|
-
private stmtDeleteChunksFrom;
|
|
44
|
-
private stmtDeleteChunksFromDel;
|
|
45
|
-
private stmtRenameDentry;
|
|
46
|
-
private stmtCreateVersion;
|
|
47
|
-
private stmtGetVersion;
|
|
48
|
-
private stmtListVersions;
|
|
49
|
-
private stmtGetVersionChunkMap;
|
|
50
|
-
private stmtDeleteVersion;
|
|
51
|
-
private stmtMaxVersion;
|
|
52
|
-
constructor(options: SqliteMetadataStoreOptions);
|
|
53
|
-
private initSchema;
|
|
54
|
-
private rowToInodeMeta;
|
|
55
|
-
private savepointCounter;
|
|
56
|
-
transaction<T>(fn: () => Promise<T>): Promise<T>;
|
|
57
|
-
createInode(attrs: CreateInodeAttrs): Promise<number>;
|
|
58
|
-
getInode(ino: number): Promise<InodeMeta | null>;
|
|
59
|
-
updateInode(ino: number, updates: Partial<InodeMeta>): Promise<void>;
|
|
60
|
-
deleteInode(ino: number): Promise<void>;
|
|
61
|
-
lookup(parentIno: number, name: string): Promise<number | null>;
|
|
62
|
-
createDentry(parentIno: number, name: string, childIno: number, type: InodeType): Promise<void>;
|
|
63
|
-
removeDentry(parentIno: number, name: string): Promise<void>;
|
|
64
|
-
listDir(parentIno: number): Promise<DentryInfo[]>;
|
|
65
|
-
listDirWithStats(parentIno: number): Promise<DentryStatInfo[]>;
|
|
66
|
-
renameDentry(srcParentIno: number, srcName: string, dstParentIno: number, dstName: string): Promise<void>;
|
|
67
|
-
resolvePath(path: string): Promise<number>;
|
|
68
|
-
resolveParentPath(path: string): Promise<{
|
|
69
|
-
parentIno: number;
|
|
70
|
-
name: string;
|
|
71
|
-
}>;
|
|
72
|
-
private resolveComponents;
|
|
73
|
-
readSymlink(ino: number): Promise<string>;
|
|
74
|
-
getChunkKey(ino: number, chunkIndex: number): Promise<string | null>;
|
|
75
|
-
setChunkKey(ino: number, chunkIndex: number, key: string): Promise<void>;
|
|
76
|
-
getAllChunkKeys(ino: number): Promise<{
|
|
77
|
-
chunkIndex: number;
|
|
78
|
-
key: string;
|
|
79
|
-
}[]>;
|
|
80
|
-
deleteAllChunks(ino: number): Promise<string[]>;
|
|
81
|
-
deleteChunksFrom(ino: number, startIndex: number): Promise<string[]>;
|
|
82
|
-
createVersion(ino: number): Promise<number>;
|
|
83
|
-
getVersion(ino: number, version: number): Promise<VersionMeta | null>;
|
|
84
|
-
listVersions(ino: number): Promise<VersionMeta[]>;
|
|
85
|
-
getVersionChunkMap(ino: number, version: number): Promise<{
|
|
86
|
-
chunkIndex: number;
|
|
87
|
-
key: string;
|
|
88
|
-
}[]>;
|
|
89
|
-
deleteVersions(ino: number, versions: number[]): Promise<string[]>;
|
|
90
|
-
restoreVersion(ino: number, version: number): Promise<void>;
|
|
91
|
-
}
|