@opys/runtime 0.1.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/README.md ADDED
@@ -0,0 +1,138 @@
1
+ # @opys/installer
2
+
3
+ Programmatic install and launch for Opys manifests. Downloads artifacts in parallel, verifies integrity, extracts natives, and spawns the JVM.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ npm install @opys/installer @opys/core
9
+ ```
10
+
11
+ ## Manifest sources
12
+
13
+ Both `install` and `launch` accept a **manifest source** as their first argument:
14
+
15
+ | Value | Example |
16
+ | ----------------- | ------------------------------------------ |
17
+ | File path string | `'opys.json'` |
18
+ | HTTPS URL object | `new URL('https://example.com/pack.json')` |
19
+ | Parsed `Manifest` | object returned by `resolveManifest` |
20
+
21
+ ---
22
+
23
+ ## `install(source, options?)`
24
+
25
+ Streams missing artifacts to `<finalPath>.partial` then renames them into place; extracts zips for artifacts with `extract` rules. Already-cached artifacts (path exists on disk) are skipped. A failed integrity check throws `IntegrityError` immediately.
26
+
27
+ ```ts
28
+ import { install } from '@opys/installer';
29
+
30
+ await install('opys.json', {
31
+ vars: {
32
+ root: '/opt/minecraft/1.20.1',
33
+ username: 'Player',
34
+ uuid: '...',
35
+ token: '...',
36
+ },
37
+ concurrency: 16,
38
+ onProgress(p) {
39
+ if (p.phase === 'download') {
40
+ process.stderr.write(` ${p.fetched}/${p.total}\r`);
41
+ }
42
+ },
43
+ });
44
+ ```
45
+
46
+ **Options**
47
+
48
+ | Option | Type | Default | Description |
49
+ | ----------------- | ------------------------------ | ------- | ---------------------------------- |
50
+ | `platform` | `OsOptions` | auto | Override OS/arch detection |
51
+ | `vars` | `Record<string, string>` | `{}` | Extra vars; override manifest vars |
52
+ | `concurrency` | `number` | `8` | Max parallel downloads |
53
+ | `onProgress` | `(p: InstallProgress) => void` | — | Progress callback |
54
+ | `verifyIntegrity` | `boolean` | `true` | Skip hash checks if `false` |
55
+
56
+ **`InstallProgress`**
57
+
58
+ ```ts
59
+ type InstallProgress =
60
+ | { phase: 'resolve' }
61
+ | { phase: 'download'; fetched: number; total: number; skipped: number }
62
+ | { phase: 'verify' }
63
+ | { phase: 'extract'; count: number };
64
+ ```
65
+
66
+ ---
67
+
68
+ ## `launch(source, options?)`
69
+
70
+ Runs `install` then spawns the process described by the manifest's launch config. Returns a `ChildProcess` — the caller decides how to wait on it.
71
+
72
+ Pass `install: false` to skip installation.
73
+
74
+ ```ts
75
+ import { launch } from '@opys/installer';
76
+
77
+ const child = await launch('opys.json', {
78
+ vars: {
79
+ root: '/opt/minecraft/1.20.1',
80
+ username: 'Player',
81
+ uuid: '...',
82
+ token: '...',
83
+ },
84
+ install: { onProgress: (p) => console.log(p) },
85
+ });
86
+
87
+ await new Promise<void>((resolve, reject) => {
88
+ child.on('exit', (code) =>
89
+ code === 0 || code === null ? resolve() : reject(new Error(`exit ${code}`)),
90
+ );
91
+ child.on('error', reject);
92
+ });
93
+ ```
94
+
95
+ **Options**
96
+
97
+ | Option | Type | Default | Description |
98
+ | ---------- | ------------------------- | ------- | -------------------------------------- |
99
+ | `platform` | `OsOptions` | auto | Override OS/arch detection |
100
+ | `vars` | `Record<string, string>` | `{}` | Extra vars; typically auth credentials |
101
+ | `install` | `InstallOptions \| false` | `{}` | Install options, or `false` to skip |
102
+ | `log` | `(level, msg) => void` | — | Debug/warn logger for spawn details |
103
+
104
+ ---
105
+
106
+ ## `resolveManifest(source)`
107
+
108
+ Resolves any manifest source to a `Manifest` object.
109
+
110
+ ```ts
111
+ import { resolveManifest } from '@opys/installer';
112
+
113
+ const manifest = await resolveManifest('opys.json');
114
+ console.log(manifest.artifacts.length);
115
+ ```
116
+
117
+ ---
118
+
119
+ ## `currentPlatform()`
120
+
121
+ Returns the `OsOptions` for the current host.
122
+
123
+ ```ts
124
+ import { currentPlatform } from '@opys/installer';
125
+
126
+ const platform = currentPlatform();
127
+ // { name: 'linux', arch: 'x64', version: '...' }
128
+ ```
129
+
130
+ ---
131
+
132
+ ## Error types
133
+
134
+ | Class | When |
135
+ | ----------------- | ---------------------------------- |
136
+ | `NetworkError` | HTTP download failure |
137
+ | `IntegrityError` | Hash mismatch on a downloaded file |
138
+ | `ExtractionError` | ZIP extraction failure |
package/dist/index.cjs ADDED
@@ -0,0 +1,129 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
12
+ key = keys[i];
13
+ if (!__hasOwnProp.call(to, key) && key !== except) {
14
+ __defProp(to, key, {
15
+ get: ((k) => from[k]).bind(null, key),
16
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
17
+ });
18
+ }
19
+ }
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
24
+ value: mod,
25
+ enumerable: true
26
+ }) : target, mod));
27
+
28
+ //#endregion
29
+ let node_child_process = require("node:child_process");
30
+ let _opys_runtime_binding = require("@opys/runtime-binding");
31
+ _opys_runtime_binding = __toESM(_opys_runtime_binding);
32
+
33
+ //#region lib/index.ts
34
+ /**
35
+ * `@opys/runtime` — install + launch executor. Behaviors are backed by the
36
+ * Rust `opys-runtime` crate (via napi-rs); the TS surface wraps the
37
+ * binding with a Node `child_process.spawn` for `launch`, and translates
38
+ * the napi-thrown messages back into the typed `NetworkError` /
39
+ * `IntegrityError` / `ExtractionError` classes consumers still
40
+ * `instanceof`-check.
41
+ */
42
+ /**
43
+ * Compat error classes. The Rust bridge currently throws
44
+ * `napi::Error::from_reason(msg)` with the discriminant baked into the
45
+ * message ("HTTP …", "Integrity check failed: …", "Failed to extract …");
46
+ * `translateError` re-wraps those into these classes so consumers using
47
+ * `instanceof` keep working. Q10's `code`-discriminant model is the
48
+ * follow-up — when the Rust side emits structured errors, these classes
49
+ * either gain a `code` field or get replaced wholesale.
50
+ */
51
+ var NetworkError = class extends Error {
52
+ kind = "network";
53
+ constructor(url, status, message) {
54
+ super(message);
55
+ this.url = url;
56
+ this.status = status;
57
+ this.name = "NetworkError";
58
+ }
59
+ };
60
+ var IntegrityError = class extends Error {
61
+ kind = "integrity";
62
+ constructor(paths) {
63
+ super(`Integrity check failed: ${paths.join(", ")}`);
64
+ this.paths = paths;
65
+ this.name = "IntegrityError";
66
+ }
67
+ };
68
+ var ExtractionError = class extends Error {
69
+ kind = "extraction";
70
+ constructor(artifactPath, options) {
71
+ super(`Failed to extract ${artifactPath}`, options);
72
+ this.artifactPath = artifactPath;
73
+ this.name = "ExtractionError";
74
+ }
75
+ };
76
+ function translateError(err) {
77
+ if (!(err instanceof Error)) return err;
78
+ const msg = err.message;
79
+ let m = /^HTTP (\d+) downloading (\S+)/.exec(msg);
80
+ if (m) return new NetworkError(m[2], Number(m[1]), msg);
81
+ m = /^Integrity check failed:\s*(.+)$/.exec(msg);
82
+ if (m) return new IntegrityError(m[1].split(",").map((s) => s.trim()).filter(Boolean));
83
+ m = /^Failed to extract (\S+):/.exec(msg);
84
+ if (m) return new ExtractionError(m[1], { cause: err });
85
+ return err;
86
+ }
87
+ async function install(manifest, options = {}) {
88
+ const { onProgress, ...rest } = options;
89
+ const bridge = onProgress ? (event) => onProgress(event) : void 0;
90
+ try {
91
+ await _opys_runtime_binding.install(manifest, rest, bridge);
92
+ } catch (err) {
93
+ throw translateError(err);
94
+ }
95
+ }
96
+ async function buildLaunch(manifest, options = {}) {
97
+ try {
98
+ return await _opys_runtime_binding.buildLaunch(manifest, options);
99
+ } catch (err) {
100
+ throw translateError(err);
101
+ }
102
+ }
103
+ async function launch(manifest, options = {}) {
104
+ const { install: installOpts = {}, cwd, ...launchRest } = options;
105
+ if (installOpts !== false) await install(manifest, installOpts);
106
+ const spec = await buildLaunch(manifest, {
107
+ ...launchRest,
108
+ cwd
109
+ });
110
+ return (0, node_child_process.spawn)(spec.command, spec.args, {
111
+ cwd: spec.workdir,
112
+ env: {
113
+ ...process.env,
114
+ ...spec.envs
115
+ },
116
+ stdio: "inherit"
117
+ });
118
+ }
119
+ const currentPlatform = _opys_runtime_binding.currentPlatform;
120
+
121
+ //#endregion
122
+ exports.ExtractionError = ExtractionError;
123
+ exports.IntegrityError = IntegrityError;
124
+ exports.NetworkError = NetworkError;
125
+ exports.buildLaunch = buildLaunch;
126
+ exports.currentPlatform = currentPlatform;
127
+ exports.install = install;
128
+ exports.launch = launch;
129
+ exports.translateError = translateError;
@@ -0,0 +1,89 @@
1
+ import { ChildProcess } from "node:child_process";
2
+ import * as napi from "@opys/runtime-binding";
3
+
4
+ //#region lib/index.d.ts
5
+ /**
6
+ * Discriminated by `phase`. The Rust bridge populates only the fields
7
+ * relevant to each phase; the union encodes that contract so consumers can
8
+ * narrow on `phase` and access the populated fields without optional-chain
9
+ * dances.
10
+ */
11
+ type InstallProgress = {
12
+ phase: 'resolve';
13
+ resolved: number;
14
+ } | {
15
+ phase: 'download';
16
+ fetched: number;
17
+ total: number;
18
+ skipped: number;
19
+ } | {
20
+ phase: 'download:start';
21
+ path: string;
22
+ total: number;
23
+ } | {
24
+ phase: 'download:bytes';
25
+ path: string;
26
+ bytes: number;
27
+ } | {
28
+ phase: 'download:done';
29
+ path: string;
30
+ } | {
31
+ phase: 'verify';
32
+ } | {
33
+ phase: 'extract';
34
+ count: number;
35
+ } | {
36
+ phase: 'sweep';
37
+ removed: number;
38
+ };
39
+ interface InstallOptions {
40
+ platform?: napi.OsOptions;
41
+ vars?: Record<string, string>;
42
+ concurrency?: number;
43
+ verifyIntegrity?: boolean;
44
+ features?: string[];
45
+ onProgress?: (p: InstallProgress) => void;
46
+ }
47
+ interface LaunchOptions {
48
+ platform?: napi.OsOptions;
49
+ features?: string[];
50
+ vars?: Record<string, string>;
51
+ cwd?: string;
52
+ install?: InstallOptions | false;
53
+ }
54
+ /**
55
+ * Compat error classes. The Rust bridge currently throws
56
+ * `napi::Error::from_reason(msg)` with the discriminant baked into the
57
+ * message ("HTTP …", "Integrity check failed: …", "Failed to extract …");
58
+ * `translateError` re-wraps those into these classes so consumers using
59
+ * `instanceof` keep working. Q10's `code`-discriminant model is the
60
+ * follow-up — when the Rust side emits structured errors, these classes
61
+ * either gain a `code` field or get replaced wholesale.
62
+ */
63
+ declare class NetworkError extends Error {
64
+ readonly url: string;
65
+ readonly status: number;
66
+ readonly kind: "network";
67
+ constructor(url: string, status: number, message: string);
68
+ }
69
+ declare class IntegrityError extends Error {
70
+ readonly paths: string[];
71
+ readonly kind: "integrity";
72
+ constructor(paths: string[]);
73
+ }
74
+ declare class ExtractionError extends Error {
75
+ readonly artifactPath: string;
76
+ readonly kind: "extraction";
77
+ constructor(artifactPath: string, options?: ErrorOptions);
78
+ }
79
+ type InstallError = NetworkError | IntegrityError | ExtractionError;
80
+ declare function translateError(err: unknown): unknown;
81
+ declare function install(manifest: unknown, options?: InstallOptions): Promise<void>;
82
+ declare function buildLaunch(manifest: unknown, options?: Omit<LaunchOptions, 'install'>): Promise<napi.LaunchSpec>;
83
+ declare function launch(manifest: unknown, options?: LaunchOptions): Promise<ChildProcess>;
84
+ declare const currentPlatform: any;
85
+ type OsOptions = napi.OsOptions;
86
+ type LaunchSpec = napi.LaunchSpec;
87
+ //#endregion
88
+ export { ExtractionError, InstallError, InstallOptions, InstallProgress, IntegrityError, LaunchOptions, LaunchSpec, NetworkError, OsOptions, buildLaunch, currentPlatform, install, launch, translateError };
89
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../lib/index.ts"],"mappings":";;;;;;;;;;KAkBY,eAAA;EACN,KAAA;EAAkB,QAAA;AAAA;EAClB,KAAA;EAAmB,OAAA;EAAiB,KAAA;EAAe,OAAA;AAAA;EACnD,KAAA;EAAyB,IAAA;EAAc,KAAA;AAAA;EACvC,KAAA;EAAyB,IAAA;EAAc,KAAA;AAAA;EACvC,KAAA;EAAwB,IAAA;AAAA;EACxB,KAAA;AAAA;EACA,KAAA;EAAkB,KAAA;AAAA;EAClB,KAAA;EAAgB,OAAA;AAAA;AAAA,UAEL,cAAA;EACf,QAAA,GAAW,IAAA,CAAK,SAAA;EAChB,IAAA,GAAO,MAAA;EACP,WAAA;EACA,eAAA;EACA,QAAA;EACA,UAAA,IAAc,CAAA,EAAG,eAAA;AAAA;AAAA,UAGF,aAAA;EACf,QAAA,GAAW,IAAA,CAAK,SAAA;EAChB,QAAA;EACA,IAAA,GAAO,MAAA;EACP,GAAA;EACA,OAAA,GAAU,cAAA;AAAA;;;;;;;;;;cAYC,YAAA,SAAqB,KAAA;EAAA,SAGrB,GAAA;EAAA,SACA,MAAA;EAAA,SAHF,IAAA;cAEE,GAAA,UACA,MAAA,UACT,OAAA;AAAA;AAAA,cAMS,cAAA,SAAuB,KAAA;EAAA,SAEb,KAAA;EAAA,SADZ,IAAA;cACY,KAAA;AAAA;AAAA,cAKV,eAAA,SAAwB,KAAA;EAAA,SAGxB,YAAA;EAAA,SAFF,IAAA;cAEE,YAAA,UACT,OAAA,GAAU,YAAA;AAAA;AAAA,KAMF,YAAA,GAAe,YAAA,GAAe,cAAA,GAAiB,eAAA;AAAA,iBAE3C,cAAA,CAAe,GAAA;AAAA,iBAkBT,OAAA,CACpB,QAAA,WACA,OAAA,GAAS,cAAA,GACR,OAAA;AAAA,iBAYmB,WAAA,CACpB,QAAA,WACA,OAAA,GAAS,IAAA,CAAK,aAAA,eACb,OAAA,CAAQ,IAAA,CAAK,UAAA;AAAA,iBAQM,MAAA,CACpB,QAAA,WACA,OAAA,GAAS,aAAA,GACR,OAAA,CAAQ,YAAA;AAAA,cAaE,eAAA;AAAA,KACD,SAAA,GAAY,IAAA,CAAK,SAAA;AAAA,KACjB,UAAA,GAAa,IAAA,CAAK,UAAA"}
@@ -0,0 +1,89 @@
1
+ import { ChildProcess } from "node:child_process";
2
+ import * as napi from "@opys/runtime-binding";
3
+
4
+ //#region lib/index.d.ts
5
+ /**
6
+ * Discriminated by `phase`. The Rust bridge populates only the fields
7
+ * relevant to each phase; the union encodes that contract so consumers can
8
+ * narrow on `phase` and access the populated fields without optional-chain
9
+ * dances.
10
+ */
11
+ type InstallProgress = {
12
+ phase: 'resolve';
13
+ resolved: number;
14
+ } | {
15
+ phase: 'download';
16
+ fetched: number;
17
+ total: number;
18
+ skipped: number;
19
+ } | {
20
+ phase: 'download:start';
21
+ path: string;
22
+ total: number;
23
+ } | {
24
+ phase: 'download:bytes';
25
+ path: string;
26
+ bytes: number;
27
+ } | {
28
+ phase: 'download:done';
29
+ path: string;
30
+ } | {
31
+ phase: 'verify';
32
+ } | {
33
+ phase: 'extract';
34
+ count: number;
35
+ } | {
36
+ phase: 'sweep';
37
+ removed: number;
38
+ };
39
+ interface InstallOptions {
40
+ platform?: napi.OsOptions;
41
+ vars?: Record<string, string>;
42
+ concurrency?: number;
43
+ verifyIntegrity?: boolean;
44
+ features?: string[];
45
+ onProgress?: (p: InstallProgress) => void;
46
+ }
47
+ interface LaunchOptions {
48
+ platform?: napi.OsOptions;
49
+ features?: string[];
50
+ vars?: Record<string, string>;
51
+ cwd?: string;
52
+ install?: InstallOptions | false;
53
+ }
54
+ /**
55
+ * Compat error classes. The Rust bridge currently throws
56
+ * `napi::Error::from_reason(msg)` with the discriminant baked into the
57
+ * message ("HTTP …", "Integrity check failed: …", "Failed to extract …");
58
+ * `translateError` re-wraps those into these classes so consumers using
59
+ * `instanceof` keep working. Q10's `code`-discriminant model is the
60
+ * follow-up — when the Rust side emits structured errors, these classes
61
+ * either gain a `code` field or get replaced wholesale.
62
+ */
63
+ declare class NetworkError extends Error {
64
+ readonly url: string;
65
+ readonly status: number;
66
+ readonly kind: "network";
67
+ constructor(url: string, status: number, message: string);
68
+ }
69
+ declare class IntegrityError extends Error {
70
+ readonly paths: string[];
71
+ readonly kind: "integrity";
72
+ constructor(paths: string[]);
73
+ }
74
+ declare class ExtractionError extends Error {
75
+ readonly artifactPath: string;
76
+ readonly kind: "extraction";
77
+ constructor(artifactPath: string, options?: ErrorOptions);
78
+ }
79
+ type InstallError = NetworkError | IntegrityError | ExtractionError;
80
+ declare function translateError(err: unknown): unknown;
81
+ declare function install(manifest: unknown, options?: InstallOptions): Promise<void>;
82
+ declare function buildLaunch(manifest: unknown, options?: Omit<LaunchOptions, 'install'>): Promise<napi.LaunchSpec>;
83
+ declare function launch(manifest: unknown, options?: LaunchOptions): Promise<ChildProcess>;
84
+ declare const currentPlatform: any;
85
+ type OsOptions = napi.OsOptions;
86
+ type LaunchSpec = napi.LaunchSpec;
87
+ //#endregion
88
+ export { ExtractionError, InstallError, InstallOptions, InstallProgress, IntegrityError, LaunchOptions, LaunchSpec, NetworkError, OsOptions, buildLaunch, currentPlatform, install, launch, translateError };
89
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../lib/index.ts"],"mappings":";;;;;;;;;;KAkBY,eAAA;EACN,KAAA;EAAkB,QAAA;AAAA;EAClB,KAAA;EAAmB,OAAA;EAAiB,KAAA;EAAe,OAAA;AAAA;EACnD,KAAA;EAAyB,IAAA;EAAc,KAAA;AAAA;EACvC,KAAA;EAAyB,IAAA;EAAc,KAAA;AAAA;EACvC,KAAA;EAAwB,IAAA;AAAA;EACxB,KAAA;AAAA;EACA,KAAA;EAAkB,KAAA;AAAA;EAClB,KAAA;EAAgB,OAAA;AAAA;AAAA,UAEL,cAAA;EACf,QAAA,GAAW,IAAA,CAAK,SAAA;EAChB,IAAA,GAAO,MAAA;EACP,WAAA;EACA,eAAA;EACA,QAAA;EACA,UAAA,IAAc,CAAA,EAAG,eAAA;AAAA;AAAA,UAGF,aAAA;EACf,QAAA,GAAW,IAAA,CAAK,SAAA;EAChB,QAAA;EACA,IAAA,GAAO,MAAA;EACP,GAAA;EACA,OAAA,GAAU,cAAA;AAAA;;;;;;;;;;cAYC,YAAA,SAAqB,KAAA;EAAA,SAGrB,GAAA;EAAA,SACA,MAAA;EAAA,SAHF,IAAA;cAEE,GAAA,UACA,MAAA,UACT,OAAA;AAAA;AAAA,cAMS,cAAA,SAAuB,KAAA;EAAA,SAEb,KAAA;EAAA,SADZ,IAAA;cACY,KAAA;AAAA;AAAA,cAKV,eAAA,SAAwB,KAAA;EAAA,SAGxB,YAAA;EAAA,SAFF,IAAA;cAEE,YAAA,UACT,OAAA,GAAU,YAAA;AAAA;AAAA,KAMF,YAAA,GAAe,YAAA,GAAe,cAAA,GAAiB,eAAA;AAAA,iBAE3C,cAAA,CAAe,GAAA;AAAA,iBAkBT,OAAA,CACpB,QAAA,WACA,OAAA,GAAS,cAAA,GACR,OAAA;AAAA,iBAYmB,WAAA,CACpB,QAAA,WACA,OAAA,GAAS,IAAA,CAAK,aAAA,eACb,OAAA,CAAQ,IAAA,CAAK,UAAA;AAAA,iBAQM,MAAA,CACpB,QAAA,WACA,OAAA,GAAS,aAAA,GACR,OAAA,CAAQ,YAAA;AAAA,cAaE,eAAA;AAAA,KACD,SAAA,GAAY,IAAA,CAAK,SAAA;AAAA,KACjB,UAAA,GAAa,IAAA,CAAK,UAAA"}
package/dist/index.mjs ADDED
@@ -0,0 +1,94 @@
1
+ import { spawn } from "node:child_process";
2
+ import * as napi from "@opys/runtime-binding";
3
+
4
+ //#region lib/index.ts
5
+ /**
6
+ * `@opys/runtime` — install + launch executor. Behaviors are backed by the
7
+ * Rust `opys-runtime` crate (via napi-rs); the TS surface wraps the
8
+ * binding with a Node `child_process.spawn` for `launch`, and translates
9
+ * the napi-thrown messages back into the typed `NetworkError` /
10
+ * `IntegrityError` / `ExtractionError` classes consumers still
11
+ * `instanceof`-check.
12
+ */
13
+ /**
14
+ * Compat error classes. The Rust bridge currently throws
15
+ * `napi::Error::from_reason(msg)` with the discriminant baked into the
16
+ * message ("HTTP …", "Integrity check failed: …", "Failed to extract …");
17
+ * `translateError` re-wraps those into these classes so consumers using
18
+ * `instanceof` keep working. Q10's `code`-discriminant model is the
19
+ * follow-up — when the Rust side emits structured errors, these classes
20
+ * either gain a `code` field or get replaced wholesale.
21
+ */
22
+ var NetworkError = class extends Error {
23
+ kind = "network";
24
+ constructor(url, status, message) {
25
+ super(message);
26
+ this.url = url;
27
+ this.status = status;
28
+ this.name = "NetworkError";
29
+ }
30
+ };
31
+ var IntegrityError = class extends Error {
32
+ kind = "integrity";
33
+ constructor(paths) {
34
+ super(`Integrity check failed: ${paths.join(", ")}`);
35
+ this.paths = paths;
36
+ this.name = "IntegrityError";
37
+ }
38
+ };
39
+ var ExtractionError = class extends Error {
40
+ kind = "extraction";
41
+ constructor(artifactPath, options) {
42
+ super(`Failed to extract ${artifactPath}`, options);
43
+ this.artifactPath = artifactPath;
44
+ this.name = "ExtractionError";
45
+ }
46
+ };
47
+ function translateError(err) {
48
+ if (!(err instanceof Error)) return err;
49
+ const msg = err.message;
50
+ let m = /^HTTP (\d+) downloading (\S+)/.exec(msg);
51
+ if (m) return new NetworkError(m[2], Number(m[1]), msg);
52
+ m = /^Integrity check failed:\s*(.+)$/.exec(msg);
53
+ if (m) return new IntegrityError(m[1].split(",").map((s) => s.trim()).filter(Boolean));
54
+ m = /^Failed to extract (\S+):/.exec(msg);
55
+ if (m) return new ExtractionError(m[1], { cause: err });
56
+ return err;
57
+ }
58
+ async function install(manifest, options = {}) {
59
+ const { onProgress, ...rest } = options;
60
+ const bridge = onProgress ? (event) => onProgress(event) : void 0;
61
+ try {
62
+ await napi.install(manifest, rest, bridge);
63
+ } catch (err) {
64
+ throw translateError(err);
65
+ }
66
+ }
67
+ async function buildLaunch(manifest, options = {}) {
68
+ try {
69
+ return await napi.buildLaunch(manifest, options);
70
+ } catch (err) {
71
+ throw translateError(err);
72
+ }
73
+ }
74
+ async function launch(manifest, options = {}) {
75
+ const { install: installOpts = {}, cwd, ...launchRest } = options;
76
+ if (installOpts !== false) await install(manifest, installOpts);
77
+ const spec = await buildLaunch(manifest, {
78
+ ...launchRest,
79
+ cwd
80
+ });
81
+ return spawn(spec.command, spec.args, {
82
+ cwd: spec.workdir,
83
+ env: {
84
+ ...process.env,
85
+ ...spec.envs
86
+ },
87
+ stdio: "inherit"
88
+ });
89
+ }
90
+ const currentPlatform = napi.currentPlatform;
91
+
92
+ //#endregion
93
+ export { ExtractionError, IntegrityError, NetworkError, buildLaunch, currentPlatform, install, launch, translateError };
94
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../lib/index.ts"],"sourcesContent":["/**\n * `@opys/runtime` — install + launch executor. Behaviors are backed by the\n * Rust `opys-runtime` crate (via napi-rs); the TS surface wraps the\n * binding with a Node `child_process.spawn` for `launch`, and translates\n * the napi-thrown messages back into the typed `NetworkError` /\n * `IntegrityError` / `ExtractionError` classes consumers still\n * `instanceof`-check.\n */\n\nimport { spawn, type ChildProcess } from 'node:child_process';\nimport * as napi from '@opys/runtime-binding';\n\n/**\n * Discriminated by `phase`. The Rust bridge populates only the fields\n * relevant to each phase; the union encodes that contract so consumers can\n * narrow on `phase` and access the populated fields without optional-chain\n * dances.\n */\nexport type InstallProgress =\n | { phase: 'resolve'; resolved: number }\n | { phase: 'download'; fetched: number; total: number; skipped: number }\n | { phase: 'download:start'; path: string; total: number }\n | { phase: 'download:bytes'; path: string; bytes: number }\n | { phase: 'download:done'; path: string }\n | { phase: 'verify' }\n | { phase: 'extract'; count: number }\n | { phase: 'sweep'; removed: number };\n\nexport interface InstallOptions {\n platform?: napi.OsOptions;\n vars?: Record<string, string>;\n concurrency?: number;\n verifyIntegrity?: boolean;\n features?: string[];\n onProgress?: (p: InstallProgress) => void;\n}\n\nexport interface LaunchOptions {\n platform?: napi.OsOptions;\n features?: string[];\n vars?: Record<string, string>;\n cwd?: string;\n install?: InstallOptions | false;\n}\n\n/**\n * Compat error classes. The Rust bridge currently throws\n * `napi::Error::from_reason(msg)` with the discriminant baked into the\n * message (\"HTTP …\", \"Integrity check failed: …\", \"Failed to extract …\");\n * `translateError` re-wraps those into these classes so consumers using\n * `instanceof` keep working. Q10's `code`-discriminant model is the\n * follow-up — when the Rust side emits structured errors, these classes\n * either gain a `code` field or get replaced wholesale.\n */\nexport class NetworkError extends Error {\n readonly kind = 'network' as const;\n constructor(\n readonly url: string,\n readonly status: number,\n message: string,\n ) {\n super(message);\n this.name = 'NetworkError';\n }\n}\nexport class IntegrityError extends Error {\n readonly kind = 'integrity' as const;\n constructor(readonly paths: string[]) {\n super(`Integrity check failed: ${paths.join(', ')}`);\n this.name = 'IntegrityError';\n }\n}\nexport class ExtractionError extends Error {\n readonly kind = 'extraction' as const;\n constructor(\n readonly artifactPath: string,\n options?: ErrorOptions,\n ) {\n super(`Failed to extract ${artifactPath}`, options);\n this.name = 'ExtractionError';\n }\n}\nexport type InstallError = NetworkError | IntegrityError | ExtractionError;\n\nexport function translateError(err: unknown): unknown {\n if (!(err instanceof Error)) return err;\n const msg = err.message;\n let m = /^HTTP (\\d+) downloading (\\S+)/.exec(msg);\n if (m) return new NetworkError(m[2]!, Number(m[1]!), msg);\n m = /^Integrity check failed:\\s*(.+)$/.exec(msg);\n if (m) {\n const paths = m[1]!\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n return new IntegrityError(paths);\n }\n m = /^Failed to extract (\\S+):/.exec(msg);\n if (m) return new ExtractionError(m[1]!, { cause: err });\n return err;\n}\n\nexport async function install(\n manifest: unknown,\n options: InstallOptions = {},\n): Promise<void> {\n const { onProgress, ...rest } = options;\n const bridge = onProgress\n ? (event: unknown) => onProgress(event as InstallProgress)\n : undefined;\n try {\n await napi.install(manifest, rest, bridge);\n } catch (err) {\n throw translateError(err);\n }\n}\n\nexport async function buildLaunch(\n manifest: unknown,\n options: Omit<LaunchOptions, 'install'> = {},\n): Promise<napi.LaunchSpec> {\n try {\n return await napi.buildLaunch(manifest, options);\n } catch (err) {\n throw translateError(err);\n }\n}\n\nexport async function launch(\n manifest: unknown,\n options: LaunchOptions = {},\n): Promise<ChildProcess> {\n const { install: installOpts = {}, cwd, ...launchRest } = options;\n if (installOpts !== false) {\n await install(manifest, installOpts);\n }\n const spec = await buildLaunch(manifest, { ...launchRest, cwd });\n return spawn(spec.command, spec.args, {\n cwd: spec.workdir,\n env: { ...process.env, ...spec.envs },\n stdio: 'inherit',\n });\n}\n\nexport const currentPlatform = napi.currentPlatform;\nexport type OsOptions = napi.OsOptions;\nexport type LaunchSpec = napi.LaunchSpec;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAsDA,IAAa,eAAb,cAAkC,MAAM;CACtC,AAAS,OAAO;CAChB,YACE,AAAS,KACT,AAAS,QACT,SACA;AACA,QAAM,QAAQ;EAJL;EACA;AAIT,OAAK,OAAO;;;AAGhB,IAAa,iBAAb,cAAoC,MAAM;CACxC,AAAS,OAAO;CAChB,YAAY,AAAS,OAAiB;AACpC,QAAM,2BAA2B,MAAM,KAAK,KAAK,GAAG;EADjC;AAEnB,OAAK,OAAO;;;AAGhB,IAAa,kBAAb,cAAqC,MAAM;CACzC,AAAS,OAAO;CAChB,YACE,AAAS,cACT,SACA;AACA,QAAM,qBAAqB,gBAAgB,QAAQ;EAH1C;AAIT,OAAK,OAAO;;;AAKhB,SAAgB,eAAe,KAAuB;AACpD,KAAI,EAAE,eAAe,OAAQ,QAAO;CACpC,MAAM,MAAM,IAAI;CAChB,IAAI,IAAI,gCAAgC,KAAK,IAAI;AACjD,KAAI,EAAG,QAAO,IAAI,aAAa,EAAE,IAAK,OAAO,EAAE,GAAI,EAAE,IAAI;AACzD,KAAI,mCAAmC,KAAK,IAAI;AAChD,KAAI,EAKF,QAAO,IAAI,eAJG,EAAE,GACb,MAAM,IAAI,CACV,KAAK,MAAM,EAAE,MAAM,CAAC,CACpB,OAAO,QAAQ,CACc;AAElC,KAAI,4BAA4B,KAAK,IAAI;AACzC,KAAI,EAAG,QAAO,IAAI,gBAAgB,EAAE,IAAK,EAAE,OAAO,KAAK,CAAC;AACxD,QAAO;;AAGT,eAAsB,QACpB,UACA,UAA0B,EAAE,EACb;CACf,MAAM,EAAE,YAAY,GAAG,SAAS;CAChC,MAAM,SAAS,cACV,UAAmB,WAAW,MAAyB,GACxD;AACJ,KAAI;AACF,QAAM,KAAK,QAAQ,UAAU,MAAM,OAAO;UACnC,KAAK;AACZ,QAAM,eAAe,IAAI;;;AAI7B,eAAsB,YACpB,UACA,UAA0C,EAAE,EAClB;AAC1B,KAAI;AACF,SAAO,MAAM,KAAK,YAAY,UAAU,QAAQ;UACzC,KAAK;AACZ,QAAM,eAAe,IAAI;;;AAI7B,eAAsB,OACpB,UACA,UAAyB,EAAE,EACJ;CACvB,MAAM,EAAE,SAAS,cAAc,EAAE,EAAE,KAAK,GAAG,eAAe;AAC1D,KAAI,gBAAgB,MAClB,OAAM,QAAQ,UAAU,YAAY;CAEtC,MAAM,OAAO,MAAM,YAAY,UAAU;EAAE,GAAG;EAAY;EAAK,CAAC;AAChE,QAAO,MAAM,KAAK,SAAS,KAAK,MAAM;EACpC,KAAK,KAAK;EACV,KAAK;GAAE,GAAG,QAAQ;GAAK,GAAG,KAAK;GAAM;EACrC,OAAO;EACR,CAAC;;AAGJ,MAAa,kBAAkB,KAAK"}
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@opys/runtime",
3
+ "version": "0.1.2",
4
+ "main": "./dist/index.js",
5
+ "module": "./dist/index.js",
6
+ "exports": {
7
+ ".": {
8
+ "import": "./dist/index.mjs",
9
+ "require": "./dist/index.cjs"
10
+ }
11
+ },
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsdown lib/index.ts --format esm,cjs --dts --clean",
17
+ "typecheck": "tsc --noEmit -p tsconfig.json",
18
+ "test": "vitest run tests/unit"
19
+ },
20
+ "type": "module",
21
+ "dependencies": {
22
+ "@opys/core": "^0.1.2",
23
+ "@opys/runtime-binding": "^0.1.2"
24
+ },
25
+ "engines": {
26
+ "node": ">=20"
27
+ },
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "git+https://github.com/harmoniya-net/opys.git",
31
+ "directory": "packages/runtime"
32
+ }
33
+ }