@alpha2-sh/node 0.0.0 → 0.1.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.
@@ -0,0 +1,64 @@
1
+ /** The version of the line-delimited JSON protocol spoken with `alpha2-host` over stdio. */
2
+ export declare const PROTOCOL_VERSION: 1;
3
+ /**
4
+ * A request object for the host: `payload`'s fields beside the protocol version, operation and id.
5
+ */
6
+ export declare function createRequest(operation: "capabilities" | "export" | "conform", requestId: string, payload?: Readonly<Record<string, unknown>>): Readonly<Record<string, unknown>>;
7
+ /** An error as the host reports it. */
8
+ export interface ProtocolError {
9
+ /** A non-empty machine-readable code, such as `INVALID_INPUT`. */
10
+ readonly code: string;
11
+ readonly message: string;
12
+ /** Structured detail, such as the conform report of a `CONFORM_FAILED`. */
13
+ readonly details?: unknown;
14
+ /**
15
+ * A failed export's report: its class (`code`), the step and frame it failed at, and the host's
16
+ * diagnostics. See `alpha2/export-failure`.
17
+ */
18
+ readonly failure?: Readonly<Record<string, unknown>>;
19
+ }
20
+ /**
21
+ * `frames` while frames are encoded; `finalizing` once every one is and the output is being flushed
22
+ * and closed.
23
+ */
24
+ export type ProgressPhase = "frames" | "finalizing";
25
+ /** Progress of the request `requestId`; `completed` never exceeds `total`. */
26
+ export interface ProgressMessage {
27
+ readonly protocolVersion: typeof PROTOCOL_VERSION;
28
+ readonly type: "progress";
29
+ readonly requestId: string;
30
+ readonly phase?: ProgressPhase;
31
+ readonly completed: number;
32
+ readonly total: number;
33
+ }
34
+ /** A failure of the host process itself, not tied to a request. */
35
+ export interface FatalMessage {
36
+ readonly protocolVersion: typeof PROTOCOL_VERSION;
37
+ readonly type: "fatal";
38
+ readonly error: ProtocolError;
39
+ }
40
+ /** The final answer to request `requestId`. */
41
+ export type ResponseMessage = {
42
+ readonly protocolVersion: typeof PROTOCOL_VERSION;
43
+ readonly requestId: string;
44
+ readonly ok: true;
45
+ readonly result: unknown;
46
+ } | {
47
+ readonly protocolVersion: typeof PROTOCOL_VERSION;
48
+ readonly requestId: string;
49
+ readonly ok: false;
50
+ readonly error: ProtocolError;
51
+ };
52
+ export type HostMessage = ProgressMessage | FatalMessage | ResponseMessage;
53
+ /**
54
+ * Parses and checks one line the host wrote. Throws when the line is not JSON, speaks another
55
+ * protocol version, or is not a well-formed message.
56
+ */
57
+ export declare function parseHostMessage(line: string): HostMessage;
58
+ /**
59
+ * Whether a host that ended without answering was refused by the operating system's loader: a
60
+ * library it links (FFmpeg's above all) is missing or of another version. That is the machine's
61
+ * setup, so the same host fails the same way however often it is started, unlike a host that ran
62
+ * and died, which says nothing of a cause.
63
+ */
64
+ export declare function loaderRefused(exitCode: number | null, stderr: string): boolean;
@@ -0,0 +1,97 @@
1
+ /** The version of the line-delimited JSON protocol spoken with `alpha2-host` over stdio. */
2
+ export const PROTOCOL_VERSION = 1;
3
+ /**
4
+ * A request object for the host: `payload`'s fields beside the protocol version, operation and id.
5
+ */
6
+ export function createRequest(operation, requestId, payload = {}) {
7
+ return {
8
+ protocolVersion: PROTOCOL_VERSION,
9
+ operation,
10
+ requestId,
11
+ ...payload,
12
+ };
13
+ }
14
+ /**
15
+ * Parses and checks one line the host wrote. Throws when the line is not JSON, speaks another
16
+ * protocol version, or is not a well-formed message.
17
+ */
18
+ export function parseHostMessage(line) {
19
+ const value = JSON.parse(line);
20
+ if (!record(value))
21
+ throw new Error("host message must be an object");
22
+ if (value.protocolVersion !== PROTOCOL_VERSION) {
23
+ throw new Error(`unsupported host protocol ${String(value.protocolVersion)}; expected ${PROTOCOL_VERSION}`);
24
+ }
25
+ if (value.type === "progress") {
26
+ if (typeof value.requestId !== "string" ||
27
+ !nonNegativeInteger(value.completed) ||
28
+ !nonNegativeInteger(value.total) ||
29
+ value.completed > value.total ||
30
+ (value.phase !== undefined &&
31
+ value.phase !== "frames" &&
32
+ value.phase !== "finalizing")) {
33
+ throw new Error("invalid progress message");
34
+ }
35
+ return value;
36
+ }
37
+ if (value.type === "fatal") {
38
+ if (!protocolError(value.error))
39
+ throw new Error("invalid fatal message");
40
+ return value;
41
+ }
42
+ if (typeof value.requestId !== "string" || typeof value.ok !== "boolean") {
43
+ throw new Error("invalid response message");
44
+ }
45
+ if (!value.ok && !protocolError(value.error)) {
46
+ throw new Error("invalid error response");
47
+ }
48
+ if (value.ok && !("result" in value)) {
49
+ throw new Error("successful response is missing result");
50
+ }
51
+ return value;
52
+ }
53
+ /**
54
+ * Windows ends a process whose DLLs it cannot load with one of these statuses, before `main` and
55
+ * without a word on stderr: `STATUS_DLL_NOT_FOUND`, `STATUS_ORDINAL_NOT_FOUND`,
56
+ * `STATUS_ENTRYPOINT_NOT_FOUND` and `STATUS_INVALID_IMAGE_FORMAT`. Node reports them unsigned.
57
+ */
58
+ const WINDOWS_LOADER_STATUSES = new Set([
59
+ 0xc0000135, 0xc0000138, 0xc0000139, 0xc000007b,
60
+ ]);
61
+ /**
62
+ * What the dynamic loaders write when they refuse a program, before it runs a line of its own:
63
+ * glibc's ld.so (a missing library, one without a version the program names, a symbol it lacks),
64
+ * musl's, and macOS's dyld.
65
+ */
66
+ const LOADER_WORDS = [
67
+ /: error while loading shared libraries: /,
68
+ /: version `[^']+' not found \(required by /,
69
+ /: symbol lookup error: /,
70
+ /^Error loading shared library /m,
71
+ /^dyld(?:\[\d+\])?: (?:Library not loaded|Symbol not found)/m,
72
+ ];
73
+ /**
74
+ * Whether a host that ended without answering was refused by the operating system's loader: a
75
+ * library it links (FFmpeg's above all) is missing or of another version. That is the machine's
76
+ * setup, so the same host fails the same way however often it is started, unlike a host that ran
77
+ * and died, which says nothing of a cause.
78
+ */
79
+ export function loaderRefused(exitCode, stderr) {
80
+ if (exitCode !== null &&
81
+ WINDOWS_LOADER_STATUSES.has(exitCode < 0 ? exitCode + 2 ** 32 : exitCode))
82
+ return true;
83
+ return LOADER_WORDS.some((words) => words.test(stderr));
84
+ }
85
+ function record(value) {
86
+ return typeof value === "object" && value !== null && !Array.isArray(value);
87
+ }
88
+ function protocolError(value) {
89
+ return (record(value) &&
90
+ typeof value.code === "string" &&
91
+ value.code.length > 0 &&
92
+ typeof value.message === "string" &&
93
+ (value.failure === undefined || record(value.failure)));
94
+ }
95
+ function nonNegativeInteger(value) {
96
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
97
+ }
package/package.json CHANGED
@@ -1,20 +1,43 @@
1
1
  {
2
2
  "name": "@alpha2-sh/node",
3
- "version": "0.0.0",
4
- "description": "Node.js client for the alpha2 native rendering host. Placeholder; the first release is 0.1.0.",
3
+ "version": "0.1.0",
5
4
  "license": "MIT",
5
+ "description": "Node.js client for the alpha2 native rendering host.",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "git+https://github.com/alpha2-sh/alpha2.git"
8
+ "url": "git+https://github.com/alpha2-sh/alpha2.git",
9
+ "directory": "packages/alpha2-node"
10
+ },
11
+ "homepage": "https://github.com/alpha2-sh/alpha2#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/alpha2-sh/alpha2/issues"
14
+ },
15
+ "publishConfig": {
16
+ "access": "public"
9
17
  },
10
18
  "type": "module",
11
- "exports": "./index.js",
19
+ "engines": {
20
+ "node": ">=20"
21
+ },
22
+ "scripts": {
23
+ "build": "rm -rf dist && tsc -p tsconfig.json",
24
+ "test": "npm run build && node --test test/*.test.mjs"
25
+ },
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "import": "./dist/index.js"
30
+ }
31
+ },
12
32
  "files": [
13
- "index.js",
33
+ "dist",
14
34
  "README.md",
15
35
  "LICENSE.md"
16
36
  ],
17
- "publishConfig": {
18
- "access": "public"
37
+ "dependencies": {
38
+ "alpha2": "0.1.0"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "22.15.30"
19
42
  }
20
43
  }
package/index.js DELETED
@@ -1 +0,0 @@
1
- export {};