@secure-exec/core 0.2.0-rc.2 → 0.2.1-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/dist/generated/isolate-runtime.d.ts +1 -1
- package/dist/generated/isolate-runtime.js +1 -1
- package/dist/index.d.ts +17 -4
- package/dist/index.js +10 -2
- package/dist/isolate-runtime/require-setup.js +145 -7
- package/dist/kernel/device-backend.d.ts +14 -0
- package/dist/kernel/device-backend.js +251 -0
- package/dist/kernel/device-layer.js +9 -0
- package/dist/kernel/index.d.ts +4 -4
- package/dist/kernel/index.js +3 -3
- package/dist/kernel/kernel.js +141 -119
- package/dist/kernel/mount-table.d.ts +75 -0
- package/dist/kernel/mount-table.js +353 -0
- package/dist/kernel/permissions.d.ts +9 -0
- package/dist/kernel/permissions.js +33 -1
- package/dist/kernel/proc-backend.d.ts +30 -0
- package/dist/kernel/proc-backend.js +428 -0
- package/dist/kernel/proc-layer.js +6 -0
- package/dist/kernel/process-table.d.ts +3 -1
- package/dist/kernel/process-table.js +23 -3
- package/dist/kernel/pty.d.ts +3 -2
- package/dist/kernel/pty.js +13 -2
- package/dist/kernel/types.d.ts +45 -4
- package/dist/kernel/types.js +9 -0
- package/dist/kernel/vfs.d.ts +30 -2
- package/dist/kernel/vfs.js +19 -2
- package/dist/shared/api-types.d.ts +6 -0
- package/dist/shared/console-formatter.js +8 -8
- package/dist/shared/in-memory-fs.d.ts +14 -62
- package/dist/shared/in-memory-fs.js +101 -636
- package/dist/shared/permissions.js +5 -0
- package/dist/test/block-store-conformance.d.ts +34 -0
- package/dist/test/block-store-conformance.js +251 -0
- package/dist/test/metadata-store-conformance.d.ts +37 -0
- package/dist/test/metadata-store-conformance.js +646 -0
- package/dist/test/vfs-conformance.d.ts +65 -0
- package/dist/test/vfs-conformance.js +842 -0
- package/dist/types.d.ts +1 -0
- package/dist/vfs/chunked-vfs.d.ts +66 -0
- package/dist/vfs/chunked-vfs.js +1290 -0
- package/dist/vfs/host-block-store.d.ts +19 -0
- package/dist/vfs/host-block-store.js +97 -0
- package/dist/vfs/memory-block-store.d.ts +16 -0
- package/dist/vfs/memory-block-store.js +45 -0
- package/dist/vfs/memory-metadata.d.ts +75 -0
- package/dist/vfs/memory-metadata.js +528 -0
- package/dist/vfs/sqlite-metadata.d.ts +91 -0
- package/dist/vfs/sqlite-metadata.js +582 -0
- package/dist/vfs/types.d.ts +210 -0
- package/dist/vfs/types.js +8 -0
- package/package.json +20 -1
- package/dist/kernel/inode-table.d.ts +0 -43
- package/dist/kernel/inode-table.js +0 -85
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host filesystem-backed FsBlockStore for local dev environments.
|
|
3
|
+
*
|
|
4
|
+
* Stores blocks as files on the host filesystem. Block key "ino/chunkIndex"
|
|
5
|
+
* maps to file at "{baseDir}/ino/chunkIndex". Directories are created on
|
|
6
|
+
* demand for inode subdirectories.
|
|
7
|
+
*/
|
|
8
|
+
import type { FsBlockStore } from "./types.js";
|
|
9
|
+
export declare class HostBlockStore implements FsBlockStore {
|
|
10
|
+
private baseDir;
|
|
11
|
+
constructor(baseDir: string);
|
|
12
|
+
private keyToPath;
|
|
13
|
+
read(key: string): Promise<Uint8Array>;
|
|
14
|
+
readRange(key: string, offset: number, length: number): Promise<Uint8Array>;
|
|
15
|
+
write(key: string, data: Uint8Array): Promise<void>;
|
|
16
|
+
delete(key: string): Promise<void>;
|
|
17
|
+
deleteMany(keys: string[]): Promise<void>;
|
|
18
|
+
copy(srcKey: string, dstKey: string): Promise<void>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host filesystem-backed FsBlockStore for local dev environments.
|
|
3
|
+
*
|
|
4
|
+
* Stores blocks as files on the host filesystem. Block key "ino/chunkIndex"
|
|
5
|
+
* maps to file at "{baseDir}/ino/chunkIndex". Directories are created on
|
|
6
|
+
* demand for inode subdirectories.
|
|
7
|
+
*/
|
|
8
|
+
import * as fs from "node:fs/promises";
|
|
9
|
+
import * as path from "node:path";
|
|
10
|
+
import { KernelError } from "../kernel/types.js";
|
|
11
|
+
export class HostBlockStore {
|
|
12
|
+
baseDir;
|
|
13
|
+
constructor(baseDir) {
|
|
14
|
+
this.baseDir = baseDir;
|
|
15
|
+
}
|
|
16
|
+
keyToPath(key) {
|
|
17
|
+
return path.join(this.baseDir, key);
|
|
18
|
+
}
|
|
19
|
+
async read(key) {
|
|
20
|
+
const filePath = this.keyToPath(key);
|
|
21
|
+
try {
|
|
22
|
+
const buf = await fs.readFile(filePath);
|
|
23
|
+
return new Uint8Array(buf);
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
if (isNodeError(err) && err.code === "ENOENT") {
|
|
27
|
+
throw new KernelError("ENOENT", `block not found: ${key}`);
|
|
28
|
+
}
|
|
29
|
+
throw err;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async readRange(key, offset, length) {
|
|
33
|
+
const filePath = this.keyToPath(key);
|
|
34
|
+
let handle;
|
|
35
|
+
try {
|
|
36
|
+
handle = await fs.open(filePath, "r");
|
|
37
|
+
const stat = await handle.stat();
|
|
38
|
+
const available = Math.max(0, stat.size - offset);
|
|
39
|
+
const toRead = Math.min(length, available);
|
|
40
|
+
if (toRead === 0) {
|
|
41
|
+
return new Uint8Array(0);
|
|
42
|
+
}
|
|
43
|
+
const buf = Buffer.alloc(toRead);
|
|
44
|
+
const { bytesRead } = await handle.read(buf, 0, toRead, offset);
|
|
45
|
+
return new Uint8Array(buf.buffer, buf.byteOffset, bytesRead);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
if (isNodeError(err) && err.code === "ENOENT") {
|
|
49
|
+
throw new KernelError("ENOENT", `block not found: ${key}`);
|
|
50
|
+
}
|
|
51
|
+
throw err;
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
if (handle)
|
|
55
|
+
await handle.close();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async write(key, data) {
|
|
59
|
+
const filePath = this.keyToPath(key);
|
|
60
|
+
const dir = path.dirname(filePath);
|
|
61
|
+
await fs.mkdir(dir, { recursive: true });
|
|
62
|
+
await fs.writeFile(filePath, data);
|
|
63
|
+
}
|
|
64
|
+
async delete(key) {
|
|
65
|
+
const filePath = this.keyToPath(key);
|
|
66
|
+
try {
|
|
67
|
+
await fs.unlink(filePath);
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
if (isNodeError(err) && err.code === "ENOENT") {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
throw err;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async deleteMany(keys) {
|
|
77
|
+
await Promise.all(keys.map((key) => this.delete(key)));
|
|
78
|
+
}
|
|
79
|
+
async copy(srcKey, dstKey) {
|
|
80
|
+
const srcPath = this.keyToPath(srcKey);
|
|
81
|
+
const dstPath = this.keyToPath(dstKey);
|
|
82
|
+
const dstDir = path.dirname(dstPath);
|
|
83
|
+
try {
|
|
84
|
+
await fs.mkdir(dstDir, { recursive: true });
|
|
85
|
+
await fs.copyFile(srcPath, dstPath);
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
if (isNodeError(err) && err.code === "ENOENT") {
|
|
89
|
+
throw new KernelError("ENOENT", `block not found: ${srcKey}`);
|
|
90
|
+
}
|
|
91
|
+
throw err;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function isNodeError(err) {
|
|
96
|
+
return err instanceof Error && "code" in err;
|
|
97
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure JS Map-based FsBlockStore for ephemeral VMs and tests.
|
|
3
|
+
*
|
|
4
|
+
* All blocks live in memory as Uint8Array values keyed by string.
|
|
5
|
+
* Suitable for short-lived processes where persistence is not needed.
|
|
6
|
+
*/
|
|
7
|
+
import type { FsBlockStore } from "./types.js";
|
|
8
|
+
export declare class InMemoryBlockStore implements FsBlockStore {
|
|
9
|
+
private blocks;
|
|
10
|
+
read(key: string): Promise<Uint8Array>;
|
|
11
|
+
readRange(key: string, offset: number, length: number): Promise<Uint8Array>;
|
|
12
|
+
write(key: string, data: Uint8Array): Promise<void>;
|
|
13
|
+
delete(key: string): Promise<void>;
|
|
14
|
+
deleteMany(keys: string[]): Promise<void>;
|
|
15
|
+
copy(srcKey: string, dstKey: string): Promise<void>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure JS Map-based FsBlockStore for ephemeral VMs and tests.
|
|
3
|
+
*
|
|
4
|
+
* All blocks live in memory as Uint8Array values keyed by string.
|
|
5
|
+
* Suitable for short-lived processes where persistence is not needed.
|
|
6
|
+
*/
|
|
7
|
+
import { KernelError } from "../kernel/types.js";
|
|
8
|
+
export class InMemoryBlockStore {
|
|
9
|
+
blocks = new Map();
|
|
10
|
+
async read(key) {
|
|
11
|
+
const data = this.blocks.get(key);
|
|
12
|
+
if (!data) {
|
|
13
|
+
throw new KernelError("ENOENT", `block not found: ${key}`);
|
|
14
|
+
}
|
|
15
|
+
return data;
|
|
16
|
+
}
|
|
17
|
+
async readRange(key, offset, length) {
|
|
18
|
+
const data = this.blocks.get(key);
|
|
19
|
+
if (!data) {
|
|
20
|
+
throw new KernelError("ENOENT", `block not found: ${key}`);
|
|
21
|
+
}
|
|
22
|
+
// Short read: return available bytes if range extends beyond block size.
|
|
23
|
+
const end = Math.min(offset + length, data.length);
|
|
24
|
+
return data.slice(offset, end);
|
|
25
|
+
}
|
|
26
|
+
async write(key, data) {
|
|
27
|
+
this.blocks.set(key, new Uint8Array(data));
|
|
28
|
+
}
|
|
29
|
+
async delete(key) {
|
|
30
|
+
this.blocks.delete(key);
|
|
31
|
+
}
|
|
32
|
+
async deleteMany(keys) {
|
|
33
|
+
for (const key of keys) {
|
|
34
|
+
this.blocks.delete(key);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async copy(srcKey, dstKey) {
|
|
38
|
+
const data = this.blocks.get(srcKey);
|
|
39
|
+
if (!data) {
|
|
40
|
+
throw new KernelError("ENOENT", `block not found: ${srcKey}`);
|
|
41
|
+
}
|
|
42
|
+
// Create a new copy, not a reference.
|
|
43
|
+
this.blocks.set(dstKey, new Uint8Array(data));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
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 type { CreateInodeAttrs, DentryInfo, DentryStatInfo, FsMetadataStore, FsMetadataStoreVersioning, InodeMeta, InodeType, VersionMeta } from "./types.js";
|
|
14
|
+
export interface InMemoryMetadataStoreOptions {
|
|
15
|
+
/** Enable file versioning support. Default: false. */
|
|
16
|
+
versioning?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare class InMemoryMetadataStore implements FsMetadataStore, FsMetadataStoreVersioning {
|
|
19
|
+
private nextIno;
|
|
20
|
+
private inodes;
|
|
21
|
+
private dentries;
|
|
22
|
+
private symlinkTargets;
|
|
23
|
+
private chunks;
|
|
24
|
+
private versioningEnabled;
|
|
25
|
+
private versions;
|
|
26
|
+
constructor(options?: InMemoryMetadataStoreOptions);
|
|
27
|
+
transaction<T>(fn: () => Promise<T>): Promise<T>;
|
|
28
|
+
createInode(attrs: CreateInodeAttrs): Promise<number>;
|
|
29
|
+
getInode(ino: number): Promise<InodeMeta | null>;
|
|
30
|
+
updateInode(ino: number, updates: Partial<InodeMeta>): Promise<void>;
|
|
31
|
+
deleteInode(ino: number): Promise<void>;
|
|
32
|
+
lookup(parentIno: number, name: string): Promise<number | null>;
|
|
33
|
+
createDentry(parentIno: number, name: string, childIno: number, type: InodeType): Promise<void>;
|
|
34
|
+
removeDentry(parentIno: number, name: string): Promise<void>;
|
|
35
|
+
listDir(parentIno: number): Promise<DentryInfo[]>;
|
|
36
|
+
listDirWithStats(parentIno: number): Promise<DentryStatInfo[]>;
|
|
37
|
+
renameDentry(srcParentIno: number, srcName: string, dstParentIno: number, dstName: string): Promise<void>;
|
|
38
|
+
resolvePath(path: string): Promise<number>;
|
|
39
|
+
resolveParentPath(path: string): Promise<{
|
|
40
|
+
parentIno: number;
|
|
41
|
+
name: string;
|
|
42
|
+
}>;
|
|
43
|
+
readSymlink(ino: number): Promise<string>;
|
|
44
|
+
getChunkKey(ino: number, chunkIndex: number): Promise<string | null>;
|
|
45
|
+
setChunkKey(ino: number, chunkIndex: number, key: string): Promise<void>;
|
|
46
|
+
getAllChunkKeys(ino: number): Promise<{
|
|
47
|
+
chunkIndex: number;
|
|
48
|
+
key: string;
|
|
49
|
+
}[]>;
|
|
50
|
+
deleteAllChunks(ino: number): Promise<string[]>;
|
|
51
|
+
deleteChunksFrom(ino: number, startIndex: number): Promise<string[]>;
|
|
52
|
+
resolvePathSync(path: string): number;
|
|
53
|
+
lookupSync(parentIno: number, name: string): number | null;
|
|
54
|
+
getInodeSync(ino: number): InodeMeta | null;
|
|
55
|
+
createInodeSync(attrs: CreateInodeAttrs): number;
|
|
56
|
+
updateInodeSync(ino: number, updates: Partial<InodeMeta>): void;
|
|
57
|
+
createDentrySync(parentIno: number, name: string, childIno: number, type: InodeType): void;
|
|
58
|
+
deleteAllChunksSync(ino: number): string[];
|
|
59
|
+
createVersion(ino: number): Promise<number>;
|
|
60
|
+
getVersion(ino: number, version: number): Promise<VersionMeta | null>;
|
|
61
|
+
listVersions(ino: number): Promise<VersionMeta[]>;
|
|
62
|
+
getVersionChunkMap(ino: number, version: number): Promise<{
|
|
63
|
+
chunkIndex: number;
|
|
64
|
+
key: string;
|
|
65
|
+
}[]>;
|
|
66
|
+
deleteVersions(ino: number, versions: number[]): Promise<string[]>;
|
|
67
|
+
restoreVersion(ino: number, version: number): Promise<void>;
|
|
68
|
+
private resolveComponentsCore;
|
|
69
|
+
/**
|
|
70
|
+
* Get the parent path components for resolving a relative symlink.
|
|
71
|
+
* We need to reconstruct the parent directory path from the components
|
|
72
|
+
* we have already resolved (everything before the symlink).
|
|
73
|
+
*/
|
|
74
|
+
private getPathComponents;
|
|
75
|
+
}
|