@indexable/sdk 0.0.4 → 0.2.0

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/src/index.ts CHANGED
@@ -1,146 +1,7 @@
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 = ix.spawn("ubuntu:24.04") // sync — no await
11
- * console.log(vm.id) // "vm_06e9..." (available immediately)
12
- * await vm.writeFile("/tmp/hi", "hello") // first await creates the VM on server
13
- * ```
14
- */
1
+ // @indexable/sdk — TypeScript bindings for the ix platform.
2
+ //
3
+ // This package re-exports UniFFI-generated bindings from ix-sdk-ffi.
4
+ // All business logic, RPC, and protocol handling lives in the Rust SDK.
5
+ // This package is a thin TypeScript wrapper — nothing more.
15
6
 
16
- // napi-rs native addon — loaded from platform-specific npm package.
17
- import { createRequire } from "node:module";
18
-
19
- const require = createRequire(import.meta.url);
20
-
21
- // Platform packages installed via optionalDependencies.
22
- // npm/bun only installs the one matching the current os+cpu.
23
- const PLATFORM_PACKAGES: Record<string, string> = {
24
- "linux-x64": "@indexable/sdk-linux-x64-gnu",
25
- };
26
-
27
- function loadNativeAddon() {
28
- const key = `${process.platform}-${process.arch}`;
29
- const pkg = PLATFORM_PACKAGES[key];
30
- if (!pkg) throw new Error(`@indexable/sdk: unsupported platform ${key}`);
31
- try {
32
- return require(pkg);
33
- } catch {
34
- throw new Error(
35
- `@indexable/sdk: failed to load native addon for ${key}. ` +
36
- `Ensure ${pkg} is installed (npm install / bun install).`,
37
- );
38
- }
39
- }
40
-
41
- const native = loadNativeAddon();
42
-
43
- export interface SpawnOptions {
44
- name?: string;
45
- memory?: number;
46
- cpus?: number;
47
- ipv4?: boolean;
48
- }
49
-
50
- export interface DirEntry {
51
- name: string;
52
- size: number;
53
- isDir: boolean;
54
- }
55
-
56
- export interface MergeResult {
57
- appliedOps: number;
58
- conflictPaths: string[];
59
- }
60
-
61
- export interface SecretEntry {
62
- name: string;
63
- createdAt: string;
64
- }
65
-
66
- /** Output from executing a command inside a VM. */
67
- export interface ExecResult {
68
- /** Process exit code (0 = success). */
69
- exitCode: number;
70
- /** Captured stdout. */
71
- stdout: string;
72
- /** Captured stderr. */
73
- stderr: string;
74
- }
75
-
76
- export interface ConnectOptions {
77
- server?: string;
78
- token?: string;
79
- }
80
-
81
- /** A virtual machine handle. Getters return `null` until the first awaited operation. */
82
- export interface Vm {
83
- /** VM identifier — always available, even before the VM is created on the server. */
84
- readonly id: string;
85
- readonly name: string | null;
86
- readonly image: string | null;
87
- readonly status: string | null;
88
- readonly ipv6: string | null;
89
- readonly ipv4: string | null;
90
- readonly subdomain: string | null;
91
- readonly startedAt: string | null;
92
- readonly stoppedAt: string | null;
93
- readonly failureReason: string | null;
94
-
95
- /** Explicitly trigger VM creation when you need metadata before any operation. */
96
- ready(): Promise<void>;
97
-
98
- stop(): Promise<void>;
99
- start(): Promise<void>;
100
- restart(): Promise<void>;
101
- delete(): Promise<void>;
102
-
103
- /** Execute a command inside the VM and return its output. */
104
- exec(command: string[], workingDir?: string): Promise<ExecResult>;
105
-
106
- writeFile(path: string, content: string): Promise<void>;
107
- readFile(path: string): Promise<string>;
108
- listDir(path: string): Promise<DirEntry[]>;
109
-
110
- fork(name?: string): Promise<Vm>;
111
- merge(source: Vm): Promise<MergeResult>;
112
-
113
- undo(): Promise<boolean>;
114
- redo(): Promise<boolean>;
115
- revert(timestampMs: number): Promise<void>;
116
-
117
- setSecret(key: string, value: string): Promise<void>;
118
- deleteSecret(key: string): Promise<void>;
119
- secrets(): Promise<SecretEntry[]>;
120
- }
121
-
122
- /** The ix SDK client. */
123
- export class Ix {
124
- private inner: InstanceType<typeof native.Ix>;
125
-
126
- constructor(options?: ConnectOptions) {
127
- this.inner = new native.Ix(options ?? undefined);
128
- }
129
-
130
- /** Create a VM handle from an OCI image. Returns immediately (sync). */
131
- spawn(image: string, options?: SpawnOptions): Vm {
132
- return this.inner.spawn(image, options ?? undefined);
133
- }
134
-
135
- /** Retrieve a single VM by ID. */
136
- async getVm(vmId: string): Promise<Vm> {
137
- return this.inner.getVm(vmId);
138
- }
139
-
140
- /** List all VMs. */
141
- async vms(): Promise<Vm[]> {
142
- return this.inner.vms();
143
- }
144
- }
145
-
146
- export default Ix;
7
+ export * from './bindings'
package/src/index.test.ts DELETED
@@ -1,165 +0,0 @@
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 → ready → getters → stop",
42
- async () => {
43
- const ix = new Ix();
44
- const vm = track(ix.spawn(IMAGE, { name: "sdk-lifecycle" }));
45
-
46
- // ID is available immediately (client-generated).
47
- expect(vm.id).toMatch(/^vm_/);
48
-
49
- await vm.ready();
50
- expect(vm.id).toBeTruthy();
51
- expect(vm.image).toBe(IMAGE);
52
- expect(vm.ipv6).toBeTruthy();
53
- expect(vm.status).toBe("running");
54
-
55
- await vm.stop();
56
- cleanup = [];
57
- },
58
- TIMEOUT_MS,
59
- );
60
- });
61
-
62
- describe("Vm filesystem", () => {
63
- test(
64
- "writeFile → readFile → listDir",
65
- async () => {
66
- const ix = new Ix();
67
- const vm = track(ix.spawn(IMAGE, { name: "sdk-fs" }));
68
-
69
- const content = `hello from sdk test ${Date.now()}`;
70
- await vm.writeFile("/tmp/sdk-test.txt", content);
71
-
72
- const read = await vm.readFile("/tmp/sdk-test.txt");
73
- expect(read).toBe(content);
74
-
75
- const entries = await vm.listDir("/tmp");
76
- const found = entries.find((e) => e.name === "sdk-test.txt");
77
- expect(found).toBeTruthy();
78
- },
79
- TIMEOUT_MS,
80
- );
81
- });
82
-
83
- describe("Vm exec", () => {
84
- test(
85
- "exec → capture stdout and exit code",
86
- async () => {
87
- const ix = new Ix();
88
- const vm = track(ix.spawn(IMAGE, { name: "sdk-exec" }));
89
-
90
- const result = await vm.exec(["echo", "hello from exec"]);
91
- expect(result.exitCode).toBe(0);
92
- expect(result.stdout).toContain("hello from exec");
93
- expect(result.stderr).toBe("");
94
- },
95
- TIMEOUT_MS,
96
- );
97
-
98
- test(
99
- "exec → nonzero exit code",
100
- async () => {
101
- const ix = new Ix();
102
- const vm = track(ix.spawn(IMAGE, { name: "sdk-exec-fail" }));
103
-
104
- const result = await vm.exec(["sh", "-c", "exit 42"]);
105
- expect(result.exitCode).toBe(42);
106
- },
107
- TIMEOUT_MS,
108
- );
109
-
110
- test(
111
- "exec → capture stderr",
112
- async () => {
113
- const ix = new Ix();
114
- const vm = track(ix.spawn(IMAGE, { name: "sdk-exec-stderr" }));
115
-
116
- const result = await vm.exec([
117
- "sh",
118
- "-c",
119
- "echo err >&2; echo out",
120
- ]);
121
- expect(result.exitCode).toBe(0);
122
- expect(result.stdout).toContain("out");
123
- expect(result.stderr).toContain("err");
124
- },
125
- TIMEOUT_MS,
126
- );
127
-
128
- test(
129
- "exec → working directory",
130
- async () => {
131
- const ix = new Ix();
132
- const vm = track(ix.spawn(IMAGE, { name: "sdk-exec-cwd" }));
133
-
134
- const result = await vm.exec(["pwd"], "/tmp");
135
- expect(result.exitCode).toBe(0);
136
- expect(result.stdout.trim()).toBe("/tmp");
137
- },
138
- TIMEOUT_MS,
139
- );
140
- });
141
-
142
- describe("Vm version control", () => {
143
- test(
144
- "fork → write on fork → merge back",
145
- async () => {
146
- const ix = new Ix();
147
- const main = track(ix.spawn(IMAGE, { name: "sdk-vcs-main" }));
148
-
149
- await main.writeFile("/tmp/file-a.txt", "from main");
150
-
151
- const fork = track(await main.fork("sdk-vcs-fork"));
152
-
153
- await fork.writeFile("/tmp/file-b.txt", "from fork");
154
-
155
- const result = await main.merge(fork);
156
- expect(result.conflictPaths.length).toBe(0);
157
-
158
- const a = await main.readFile("/tmp/file-a.txt");
159
- expect(a).toBe("from main");
160
- const b = await main.readFile("/tmp/file-b.txt");
161
- expect(b).toBe("from fork");
162
- },
163
- TIMEOUT_MS,
164
- );
165
- });