@indexable/sdk 0.0.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.
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "@indexable/sdk-linux-x64-gnu",
3
+ "version": "0.0.1",
4
+ "os": ["linux"],
5
+ "cpu": ["x64"],
6
+ "main": "ix-sdk.linux-x64-gnu.node",
7
+ "files": ["ix-sdk.linux-x64-gnu.node"],
8
+ "license": "MIT",
9
+ "engines": {
10
+ "node": ">= 18"
11
+ }
12
+ }
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@indexable/sdk",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "main": "./src/index.ts",
6
+ "exports": {
7
+ ".": "./src/index.ts"
8
+ },
9
+ "files": ["src/", "npm/"],
10
+ "scripts": {
11
+ "build": "cargo build -p ix-sdk-napi --profile release-external --target x86_64-unknown-linux-gnu && cp ../../target/x86_64-unknown-linux-gnu/release-external/libix_sdk_napi.so npm/linux-x64-gnu/ix-sdk.linux-x64-gnu.node",
12
+ "test": "bun test src/",
13
+ "typecheck": "bunx tsc --noEmit"
14
+ },
15
+ "optionalDependencies": {
16
+ "@indexable/sdk-linux-x64-gnu": "0.0.1"
17
+ },
18
+ "devDependencies": {
19
+ "bun-types": "latest"
20
+ },
21
+ "engines": {
22
+ "node": ">= 18"
23
+ },
24
+ "license": "MIT"
25
+ }
@@ -0,0 +1,102 @@
1
+ import { describe, test, expect, afterEach } from "bun:test";
2
+ import Ix from "./index";
3
+ import type { Vm } from "./index";
4
+
5
+ const IMAGE = "ubuntu:24.04";
6
+ const TIMEOUT_MS = 120_000;
7
+
8
+ let cleanup: Vm[] = [];
9
+
10
+ afterEach(async () => {
11
+ for (const vm of cleanup) {
12
+ try {
13
+ await vm.stop();
14
+ } catch {
15
+ // might already be stopped
16
+ }
17
+ try {
18
+ await vm.delete();
19
+ } catch {
20
+ // best-effort
21
+ }
22
+ }
23
+ cleanup = [];
24
+ });
25
+
26
+ function track(vm: Vm): Vm {
27
+ cleanup.push(vm);
28
+ return vm;
29
+ }
30
+
31
+ describe("Ix", () => {
32
+ test("lists VMs", async () => {
33
+ const ix = new Ix();
34
+ const vms = await ix.vms();
35
+ expect(Array.isArray(vms)).toBe(true);
36
+ });
37
+ });
38
+
39
+ describe("Vm lifecycle", () => {
40
+ test(
41
+ "spawn → getters → stop",
42
+ async () => {
43
+ const ix = new Ix();
44
+ const vm = track(await ix.spawn(IMAGE, { name: "sdk-lifecycle" }));
45
+
46
+ expect(vm.id).toBeTruthy();
47
+ expect(vm.image).toBe(IMAGE);
48
+ expect(vm.ipv6).toBeTruthy();
49
+ expect(vm.status).toBe("running");
50
+
51
+ await vm.stop();
52
+ cleanup = [];
53
+ },
54
+ TIMEOUT_MS,
55
+ );
56
+ });
57
+
58
+ describe("Vm filesystem", () => {
59
+ test(
60
+ "writeFile → readFile → listDir",
61
+ async () => {
62
+ const ix = new Ix();
63
+ const vm = track(await ix.spawn(IMAGE, { name: "sdk-fs" }));
64
+
65
+ const content = `hello from sdk test ${Date.now()}`;
66
+ await vm.writeFile("/tmp/sdk-test.txt", content);
67
+
68
+ const read = await vm.readFile("/tmp/sdk-test.txt");
69
+ expect(read).toBe(content);
70
+
71
+ const entries = await vm.listDir("/tmp");
72
+ const found = entries.find((e) => e.name === "sdk-test.txt");
73
+ expect(found).toBeTruthy();
74
+ },
75
+ TIMEOUT_MS,
76
+ );
77
+ });
78
+
79
+ describe("Vm version control", () => {
80
+ test(
81
+ "fork → write on fork → merge back",
82
+ async () => {
83
+ const ix = new Ix();
84
+ const main = track(await ix.spawn(IMAGE, { name: "sdk-vcs-main" }));
85
+
86
+ await main.writeFile("/tmp/file-a.txt", "from main");
87
+
88
+ const fork = track(await main.fork("sdk-vcs-fork"));
89
+
90
+ await fork.writeFile("/tmp/file-b.txt", "from fork");
91
+
92
+ const result = await main.merge(fork);
93
+ expect(result.conflictPaths.length).toBe(0);
94
+
95
+ const a = await main.readFile("/tmp/file-a.txt");
96
+ expect(a).toBe("from main");
97
+ const b = await main.readFile("/tmp/file-b.txt");
98
+ expect(b).toBe("from fork");
99
+ },
100
+ TIMEOUT_MS,
101
+ );
102
+ });
package/src/index.ts ADDED
@@ -0,0 +1,135 @@
1
+ /**
2
+ * @indexable/sdk — TypeScript SDK for the ix platform.
3
+ *
4
+ * Native addon built from Rust via napi-rs. No WASM, no runtime init.
5
+ *
6
+ * ```ts
7
+ * import Ix from "@indexable/sdk"
8
+ *
9
+ * const ix = new Ix()
10
+ * const vm = await ix.spawn("ubuntu:24.04")
11
+ * console.log(vm.ipv6)
12
+ *
13
+ * await vm.writeFile("/tmp/hi", "hello")
14
+ * const content = await vm.readFile("/tmp/hi")
15
+ *
16
+ * const fork = await vm.fork("experiment")
17
+ * await fork.writeFile("/tmp/new", "from fork")
18
+ * await vm.merge(fork)
19
+ *
20
+ * await vm.stop()
21
+ * ```
22
+ */
23
+
24
+ // napi-rs native addon — loaded from platform-specific npm package.
25
+ import { createRequire } from "node:module";
26
+
27
+ const require = createRequire(import.meta.url);
28
+
29
+ // Platform packages installed via optionalDependencies.
30
+ // npm/bun only installs the one matching the current os+cpu.
31
+ const PLATFORM_PACKAGES: Record<string, string> = {
32
+ "linux-x64": "@indexable/sdk-linux-x64-gnu",
33
+ };
34
+
35
+ function loadNativeAddon() {
36
+ const key = `${process.platform}-${process.arch}`;
37
+ const pkg = PLATFORM_PACKAGES[key];
38
+ if (!pkg) throw new Error(`@indexable/sdk: unsupported platform ${key}`);
39
+ try {
40
+ return require(pkg);
41
+ } catch {
42
+ throw new Error(
43
+ `@indexable/sdk: failed to load native addon for ${key}. ` +
44
+ `Ensure ${pkg} is installed (npm install / bun install).`,
45
+ );
46
+ }
47
+ }
48
+
49
+ const native = loadNativeAddon();
50
+
51
+ export interface SpawnOptions {
52
+ name?: string;
53
+ memory?: number;
54
+ cpus?: number;
55
+ ipv4?: boolean;
56
+ }
57
+
58
+ export interface DirEntry {
59
+ name: string;
60
+ size: number;
61
+ isDir: boolean;
62
+ }
63
+
64
+ export interface MergeResult {
65
+ appliedOps: number;
66
+ conflictPaths: string[];
67
+ }
68
+
69
+ export interface SecretEntry {
70
+ name: string;
71
+ createdAt: string;
72
+ }
73
+
74
+ export interface ConnectOptions {
75
+ server?: string;
76
+ token?: string;
77
+ }
78
+
79
+ /** A virtual machine. */
80
+ export interface Vm {
81
+ /** VM identifier (e.g. "vm_06e9...") */
82
+ readonly id: string;
83
+ readonly name: string;
84
+ readonly image: string;
85
+ readonly status: string;
86
+ readonly ipv6: string;
87
+ readonly ipv4: string | null;
88
+ readonly subdomain: string | null;
89
+
90
+ stop(): Promise<void>;
91
+ start(): Promise<void>;
92
+ restart(): Promise<void>;
93
+ delete(): Promise<void>;
94
+
95
+ writeFile(path: string, content: string): Promise<void>;
96
+ readFile(path: string): Promise<string>;
97
+ listDir(path: string): Promise<DirEntry[]>;
98
+
99
+ fork(name?: string): Promise<Vm>;
100
+ merge(source: Vm): Promise<MergeResult>;
101
+
102
+ undo(): Promise<boolean>;
103
+ redo(): Promise<boolean>;
104
+ revert(timestampMs: number): Promise<void>;
105
+
106
+ setSecret(key: string, value: string): Promise<void>;
107
+ deleteSecret(key: string): Promise<void>;
108
+ secrets(): Promise<SecretEntry[]>;
109
+ }
110
+
111
+ /** The ix SDK client. */
112
+ export class Ix {
113
+ private inner: InstanceType<typeof native.Ix>;
114
+
115
+ constructor(options?: ConnectOptions) {
116
+ this.inner = new native.Ix(options ?? undefined);
117
+ }
118
+
119
+ /** Create a VM from an OCI image. */
120
+ async spawn(image: string, options?: SpawnOptions): Promise<Vm> {
121
+ return this.inner.spawn(image, options ?? undefined);
122
+ }
123
+
124
+ /** Retrieve a single VM by ID. */
125
+ async getVm(vmId: string): Promise<Vm> {
126
+ return this.inner.getVm(vmId);
127
+ }
128
+
129
+ /** List all VMs. */
130
+ async vms(): Promise<Vm[]> {
131
+ return this.inner.vms();
132
+ }
133
+ }
134
+
135
+ export default Ix;