@oliphaunt/wasix-tools 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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,33 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 (2026-09-05)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * **wasix-ts:** run host runtimes through Rust Node-API ([#156](https://github.com/f0rr0/oliphaunt/issues/156))
9
+ * **sdk:** unify embedded PostgreSQL public APIs ([#153](https://github.com/f0rr0/oliphaunt/issues/153))
10
+ * Rust WASIX removes temporary/application-data storage variants, and browser IndexedDB uses the new per-database v3 layout without migrating prior generations.
11
+
12
+ ### Features
13
+
14
+ * **sdk:** unify embedded PostgreSQL public APIs ([#153](https://github.com/f0rr0/oliphaunt/issues/153)) ([4384d1b](https://github.com/f0rr0/oliphaunt/commit/4384d1bdfafee07e4e1963ac68027b4bcf002a1e))
15
+ * unify native and WASIX runtimes and SDKs ([#129](https://github.com/f0rr0/oliphaunt/issues/129)) ([fae2bd7](https://github.com/f0rr0/oliphaunt/commit/fae2bd7bde00ae436d9b62ba6a37d919679ac790))
16
+ * **wasix-ts:** run host runtimes through Rust Node-API ([#156](https://github.com/f0rr0/oliphaunt/issues/156)) ([28e07be](https://github.com/f0rr0/oliphaunt/commit/28e07be782388915b28ad3fd30e3e78143710d28))
17
+
18
+
19
+ ### Bug Fixes
20
+
21
+ * **ci:** preserve native lifecycle server sessions ([#165](https://github.com/f0rr0/oliphaunt/issues/165)) ([b8cab0b](https://github.com/f0rr0/oliphaunt/commit/b8cab0be2b86c6b9fab4c279add89113c5797d23))
22
+
23
+
24
+ ### Performance Improvements
25
+
26
+ * **js:** streamline exec response handling ([#158](https://github.com/f0rr0/oliphaunt/issues/158)) ([5eaf05b](https://github.com/f0rr0/oliphaunt/commit/5eaf05b8a8d21bd974b9fcb6d618103be5689151))
27
+ * **wasix:** preserve and accelerate seek end ([#154](https://github.com/f0rr0/oliphaunt/issues/154)) ([169852f](https://github.com/f0rr0/oliphaunt/commit/169852f22d1c5eab4cfd30c17ccca014b8d84592))
28
+
29
+
30
+ ### Code Refactoring
31
+
32
+ * **ci:** align product and release task boundaries ([#170](https://github.com/f0rr0/oliphaunt/issues/170)) ([009a5f5](https://github.com/f0rr0/oliphaunt/commit/009a5f5ec0659d70f6a22902c071a81e0806fabe))
33
+ * **ci:** model independent product dependencies ([#173](https://github.com/f0rr0/oliphaunt/issues/173)) ([2d5f90c](https://github.com/f0rr0/oliphaunt/commit/2d5f90c837ef7ecd8b43c2547e4b3c9b04767121))
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 oliphaunt-wasix Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # @oliphaunt/wasix-tools
2
+
3
+ Optional standard PostgreSQL `pg_dump` and non-interactive `psql` runners for
4
+ an open `@oliphaunt/wasix-ts` database. This package remains the public opt-in
5
+ facade on every host. Browsers load separately carried portable tool binaries;
6
+ Node.js, Bun, Deno, and Electron call the copies compiled into the matching Node-API
7
+ platform carrier.
8
+
9
+ `pgDump()` returns PostgreSQL's ordinary plain SQL dump, including normal
10
+ `COPY` data. `psql()` accepts a command or script and can restore that output.
11
+ Both operations exclusively own the database session until they finish.
12
+ They reset PostgreSQL session state before and after running, so raw-protocol
13
+ callers must not expect prepared statements or session settings to survive.
14
+
15
+ ```sh
16
+ pnpm add @oliphaunt/wasix-ts @oliphaunt/wasix-tools
17
+ ```
18
+
19
+ ```ts
20
+ import Oliphaunt from '@oliphaunt/wasix-ts';
21
+ import WorkerOliphaunt from '@oliphaunt/wasix-ts/worker';
22
+ import { pgDump, psql } from '@oliphaunt/wasix-tools';
23
+
24
+ await using source = await Oliphaunt.open();
25
+ const sql = await pgDump(source, { args: ['--schema-only'] });
26
+ await using target = await WorkerOliphaunt.open();
27
+ await psql(target, { script: sql });
28
+ ```
29
+
30
+ `pgDump()` supports databases from the root, `/direct`, and `/worker` entrypoints.
31
+ In browsers, `psql()` requires `/worker` because COPY restore is full duplex.
32
+ On Node.js, Bun, Deno, and Electron the Rust tool bridge supports `psql()` on
33
+ all three entrypoints.
34
+ Ordinary PostgreSQL
35
+ arguments are passed through, except connection, input/output, encoding, dump
36
+ format, compression, and parallel-job arguments owned by the runner.
37
+ `pgDump()` always uses plain UTF-8 output and rejects custom formats; it does
38
+ not force `--inserts` or rewrite valid dump SQL. `psql()` accepts `command` or
39
+ `script`, uses no user psqlrc, and stops on the first SQL error. Interactive
40
+ input and `pg_restore` are not part of this package.
41
+
42
+ Tool failures throw `PostgresToolError` with `tool`, `exitCode`, `stdout`, and
43
+ `stderr` fields.
@@ -0,0 +1,20 @@
1
+ # Third-Party Notices
2
+
3
+ Oliphaunt source code in this repository is licensed under the MIT license in
4
+ `LICENSE`.
5
+
6
+ This file is the repository-level notice index. Product-specific runtime and
7
+ packaging notices live next to the product that ships the relevant artifacts:
8
+
9
+ - `src/runtimes/liboliphaunt/native/THIRD_PARTY_NOTICES.md`
10
+ - `src/bindings/wasix-rust/THIRD_PARTY_NOTICES.md`
11
+
12
+ Shared PostgreSQL source pins, third-party source pins, and extension metadata
13
+ are maintained in `src/postgres/versions/18/`, `src/sources/third-party/`, and
14
+ `src/extensions/`. Generated release artifacts must include the notices and
15
+ exact pinned license bytes for every product and third-party component they
16
+ ship.
17
+
18
+ Canonical runtime license snapshots live in
19
+ `src/runtimes/liboliphaunt/licenses/`; their source pins and digests are
20
+ enforced by `tools/release/release-notices.mjs`.
package/lib/index.d.ts ADDED
@@ -0,0 +1,30 @@
1
+ import type { OliphauntDatabase } from '@oliphaunt/wasix-ts';
2
+ export type PgDumpOptions = Readonly<{
3
+ /** Ordinary PostgreSQL pg_dump arguments. Connection, file input/output, format, compression, encoding, and job flags are managed. */
4
+ args?: readonly string[];
5
+ }>;
6
+ export type PsqlOptions = Readonly<{
7
+ /** Ordinary PostgreSQL psql arguments. Connection, input, and output are managed. */
8
+ args?: readonly string[];
9
+ command?: string;
10
+ script?: string;
11
+ }>;
12
+ export declare class PostgresToolError extends Error {
13
+ readonly tool: 'pg_dump' | 'psql';
14
+ readonly exitCode: number | null;
15
+ readonly stdout: string;
16
+ readonly stderr: string;
17
+ constructor(tool: 'pg_dump' | 'psql', message: string, options?: {
18
+ exitCode?: number | null;
19
+ stdout?: string;
20
+ stderr?: string;
21
+ cause?: unknown;
22
+ });
23
+ }
24
+ /** Run standard PostgreSQL plain pg_dump against an open WASIX database. */
25
+ export declare function pgDump(database: OliphauntDatabase, options?: PgDumpOptions): Promise<string>;
26
+ /**
27
+ * Run standard non-interactive psql. Browsers require a `/worker` database;
28
+ * Node.js, Bun, Deno, and Electron support root, `/direct`, and `/worker` databases.
29
+ */
30
+ export declare function psql(database: OliphauntDatabase, options?: PsqlOptions): Promise<string>;
package/lib/index.js ADDED
@@ -0,0 +1,268 @@
1
+ import tools from '@oliphaunt/liboliphaunt-wasix-tools';
2
+ import { getWasixDatabaseIdentity, runWasixToolProcess } from '@oliphaunt/wasix-ts/internal/tools';
3
+ assertToolsCarrier();
4
+ const VIRTUAL_TOOL_HOST = '127.0.0.1';
5
+ const VIRTUAL_TOOL_PORT = '65432';
6
+ // PostgreSQL 18 getopt_long optstrings. A value-taking option owns the rest
7
+ // of its token, so a managed-looking character inside that value stays data.
8
+ const PG_DUMP_SHORT_OPTIONS = 'abBcCd:e:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxXZ:';
9
+ const PSQL_SHORT_OPTIONS = 'aAbc:d:eEf:F:h:HlL:no:p:P:qR:sStT:U:v:VwWxXz?01';
10
+ const PG_DUMP_VALUE_OPTIONS = [
11
+ '--extension',
12
+ '--schema',
13
+ '--exclude-schema',
14
+ '--superuser',
15
+ '--table',
16
+ '--exclude-table',
17
+ '--exclude-table-data',
18
+ '--extra-float-digits',
19
+ '--lock-wait-timeout',
20
+ '--role',
21
+ '--section',
22
+ '--snapshot',
23
+ '--rows-per-insert',
24
+ '--include-foreign-data',
25
+ '--table-and-children',
26
+ '--exclude-table-and-children',
27
+ '--exclude-table-data-and-children',
28
+ '--sync-method',
29
+ '--exclude-extension',
30
+ '--restrict-key',
31
+ ];
32
+ const PSQL_VALUE_OPTIONS = [
33
+ '--field-separator',
34
+ '--pset',
35
+ '--record-separator',
36
+ '--table-attr',
37
+ '--set',
38
+ '--variable',
39
+ ];
40
+ export class PostgresToolError extends Error {
41
+ tool;
42
+ exitCode;
43
+ stdout;
44
+ stderr;
45
+ constructor(tool, message, options = {}) {
46
+ super(message, { cause: options.cause });
47
+ this.name = 'PostgresToolError';
48
+ this.tool = tool;
49
+ this.exitCode = options.exitCode ?? null;
50
+ this.stdout = options.stdout ?? '';
51
+ this.stderr = options.stderr ?? '';
52
+ }
53
+ }
54
+ /** Run standard PostgreSQL plain pg_dump against an open WASIX database. */
55
+ export async function pgDump(database, options = {}) {
56
+ const args = validatedArguments('pg_dump', options.args, pgDumpManagedArgument, PG_DUMP_SHORT_OPTIONS, PG_DUMP_VALUE_OPTIONS);
57
+ const identity = getWasixDatabaseIdentity(database);
58
+ return runTool('pg_dump', database, [
59
+ ...args,
60
+ '--encoding=UTF8',
61
+ '--no-password',
62
+ `--username=${identity.username}`,
63
+ `--host=${VIRTUAL_TOOL_HOST}`,
64
+ `--port=${VIRTUAL_TOOL_PORT}`,
65
+ `--dbname=${identity.database}`,
66
+ ]);
67
+ }
68
+ /**
69
+ * Run standard non-interactive psql. Browsers require a `/worker` database;
70
+ * Node.js, Bun, Deno, and Electron support root, `/direct`, and `/worker` databases.
71
+ */
72
+ export async function psql(database, options = {}) {
73
+ const args = validatedArguments('psql', options.args, psqlManagedArgument, PSQL_SHORT_OPTIONS, PSQL_VALUE_OPTIONS);
74
+ if (options.command !== undefined && options.script !== undefined) {
75
+ throw new TypeError('psql accepts command or script, not both');
76
+ }
77
+ const command = validatedInput(options.command, 'psql command');
78
+ const script = validatedInput(options.script, 'psql script');
79
+ if (command === undefined && script === undefined && args.length === 0) {
80
+ throw new TypeError('psql requires non-interactive input through command, script, or args');
81
+ }
82
+ const inputArgs = command !== undefined ? ['--command', command] : script !== undefined ? ['--file=-'] : [];
83
+ const identity = getWasixDatabaseIdentity(database);
84
+ return runTool('psql', database, [
85
+ ...args,
86
+ '--no-psqlrc',
87
+ '--no-password',
88
+ '--set=ON_ERROR_STOP=1',
89
+ `--username=${identity.username}`,
90
+ `--host=${VIRTUAL_TOOL_HOST}`,
91
+ `--port=${VIRTUAL_TOOL_PORT}`,
92
+ `--dbname=${identity.database}`,
93
+ ...inputArgs,
94
+ ], script === undefined ? undefined : new TextEncoder().encode(script));
95
+ }
96
+ async function runTool(name, database, args, stdin) {
97
+ const descriptor = name === 'pg_dump' ? tools.pgDump : tools.psql;
98
+ let result;
99
+ try {
100
+ result = await runWasixToolProcess(database, {
101
+ runtimeVersion: tools.runtimeVersion,
102
+ tool: descriptor,
103
+ args,
104
+ stdin,
105
+ });
106
+ }
107
+ catch (cause) {
108
+ const detail = cause instanceof Error ? cause.message : String(cause);
109
+ throw new PostgresToolError(name, `could not run ${name}: ${detail}`, { cause });
110
+ }
111
+ if (result.exitCode !== 0) {
112
+ // Diagnostics are best-effort text just like native process output. Keep
113
+ // the structured failure even if either stream contains invalid UTF-8.
114
+ const stdout = decodeDiagnostics(result.stdout);
115
+ const stderr = decodeDiagnostics(result.stderr);
116
+ throw new PostgresToolError(name, `${name} exited with status ${result.exitCode}${stderr.trim() === '' ? '' : `: ${stderr.trim()}`}`, { exitCode: result.exitCode, stdout, stderr });
117
+ }
118
+ try {
119
+ return decode(result.stdout, `${name} output`);
120
+ }
121
+ catch (cause) {
122
+ throw new PostgresToolError(name, `${name} output is not valid UTF-8`, {
123
+ exitCode: result.exitCode,
124
+ stdout: decodeDiagnostics(result.stdout),
125
+ stderr: decodeDiagnostics(result.stderr),
126
+ cause,
127
+ });
128
+ }
129
+ }
130
+ function validatedInput(value, label) {
131
+ if (value === undefined)
132
+ return undefined;
133
+ if (typeof value !== 'string')
134
+ throw new TypeError(`${label} must be a string`);
135
+ if (value.includes('\0'))
136
+ throw new TypeError(`${label} must not contain NUL bytes`);
137
+ return value;
138
+ }
139
+ function validatedArguments(tool, value, managed, shortOptions, valueOptions) {
140
+ if (value === undefined)
141
+ return [];
142
+ if (!Array.isArray(value))
143
+ throw new TypeError(`${tool} args must be an array of strings`);
144
+ let expectsValue = false;
145
+ const validated = value.map((argument) => {
146
+ if (typeof argument !== 'string')
147
+ throw new TypeError(`${tool} argument must be a string`);
148
+ if (argument.includes('\0'))
149
+ throw new TypeError(`${tool} argument must not contain NUL bytes`);
150
+ if (expectsValue) {
151
+ expectsValue = false;
152
+ return argument;
153
+ }
154
+ const label = managed(argument);
155
+ if (label !== undefined) {
156
+ throw new TypeError(`${tool} argument ${JSON.stringify(argument)} conflicts with Oliphaunt's managed ${label}`);
157
+ }
158
+ if (argument === '-' || !argument.startsWith('-')) {
159
+ throw new TypeError(`${tool} argument ${JSON.stringify(argument)} conflicts with Oliphaunt's managed database or username`);
160
+ }
161
+ expectsValue = optionConsumesNext(argument, shortOptions, valueOptions);
162
+ return argument;
163
+ });
164
+ if (expectsValue) {
165
+ throw new TypeError(`${tool} argument ${JSON.stringify(validated.at(-1))} requires a value`);
166
+ }
167
+ return validated;
168
+ }
169
+ function pgDumpManagedArgument(argument) {
170
+ if (argument === '--')
171
+ return 'option terminator';
172
+ return managedArgument(argument, [
173
+ ['--password', '-W', 'password prompting'],
174
+ ['--filter', '', 'input file'],
175
+ ['--file', '-f', 'output file'],
176
+ ['--format', '-F', 'output format'],
177
+ ['--compress', '-Z', 'output compression'],
178
+ ['--encoding', '-E', 'output encoding'],
179
+ ['--host', '-h', 'host'],
180
+ ['--port', '-p', 'port'],
181
+ ['--username', '-U', 'username'],
182
+ ['--dbname', '-d', 'database'],
183
+ ['--jobs', '-j', 'job count'],
184
+ ], PG_DUMP_SHORT_OPTIONS);
185
+ }
186
+ function optionConsumesNext(argument, shortOptions, valueOptions) {
187
+ if (argument.startsWith('--')) {
188
+ if (argument.includes('='))
189
+ return false;
190
+ return valueOptions.some((option) => option.startsWith(argument));
191
+ }
192
+ for (let index = 1; index < argument.length; index += 1) {
193
+ const option = argument[index];
194
+ if (option === undefined)
195
+ return false;
196
+ const position = shortOptions.indexOf(option);
197
+ if (position < 0)
198
+ return false;
199
+ if (shortOptions[position + 1] === ':')
200
+ return index === argument.length - 1;
201
+ }
202
+ return false;
203
+ }
204
+ function psqlManagedArgument(argument) {
205
+ if (argument === '--')
206
+ return 'option terminator';
207
+ return managedArgument(argument, [
208
+ ['--password', '-W', 'password prompting'],
209
+ ['--single-step', '-s', 'interactive prompting'],
210
+ ['--host', '-h', 'host'],
211
+ ['--port', '-p', 'port'],
212
+ ['--username', '-U', 'username'],
213
+ ['--dbname', '-d', 'database'],
214
+ ['--output', '-o', 'stdout capture'],
215
+ ['--log-file', '-L', 'stderr capture'],
216
+ ['--command', '-c', 'input'],
217
+ ['--file', '-f', 'input'],
218
+ ], PSQL_SHORT_OPTIONS);
219
+ }
220
+ function managedArgument(argument, flags, shortOptions) {
221
+ const longName = argument.split('=', 1)[0];
222
+ if (longName !== undefined && longName.length > 2 && longName.startsWith('--')) {
223
+ for (const [long, , label] of flags) {
224
+ // Native getopt_long accepts unique prefixes while PostgreSQL's bundled
225
+ // fallback requires exact names. Reject either spelling consistently so
226
+ // a managed option cannot become host-dependent.
227
+ if (long.startsWith(longName))
228
+ return label;
229
+ }
230
+ }
231
+ if (argument.length < 2 || argument[0] !== '-' || argument[1] === '-') {
232
+ return undefined;
233
+ }
234
+ for (let index = 1; index < argument.length; index += 1) {
235
+ const option = argument[index];
236
+ if (option === undefined)
237
+ return undefined;
238
+ const position = shortOptions.indexOf(option);
239
+ if (position < 0)
240
+ return undefined;
241
+ const managed = flags.find(([, short]) => short === `-${option}`);
242
+ if (managed !== undefined)
243
+ return managed[2];
244
+ if (shortOptions[position + 1] === ':')
245
+ return undefined;
246
+ }
247
+ return undefined;
248
+ }
249
+ function decode(bytes, label) {
250
+ try {
251
+ return new TextDecoder('utf-8', { fatal: true }).decode(bytes);
252
+ }
253
+ catch (cause) {
254
+ throw new Error(`${label} is not valid UTF-8`, { cause });
255
+ }
256
+ }
257
+ function decodeDiagnostics(bytes) {
258
+ return new TextDecoder().decode(bytes);
259
+ }
260
+ function assertToolsCarrier() {
261
+ if (tools.schema !== 'oliphaunt-wasix-tools-v1' ||
262
+ tools.product !== 'oliphaunt-wasix-tools' ||
263
+ tools.runtimeProduct !== 'liboliphaunt-wasix' ||
264
+ typeof tools.runtimeVersion !== 'string' ||
265
+ tools.runtimeVersion.length === 0) {
266
+ throw new Error('@oliphaunt/liboliphaunt-wasix-tools has an invalid descriptor');
267
+ }
268
+ }
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@oliphaunt/wasix-tools",
3
+ "version": "0.1.0",
4
+ "description": "Optional in-process PostgreSQL pg_dump and psql runners for Oliphaunt WASIX.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/f0rr0/oliphaunt.git",
11
+ "directory": "src/bindings/wasix-ts/tools-package"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/f0rr0/oliphaunt/issues"
15
+ },
16
+ "homepage": "https://oliphaunt.dev",
17
+ "publishConfig": {
18
+ "access": "public",
19
+ "provenance": true
20
+ },
21
+ "oliphaunt": {
22
+ "runtimeProduct": "liboliphaunt-wasix",
23
+ "runtimeVersion": "0.2.0"
24
+ },
25
+ "exports": {
26
+ ".": {
27
+ "types": "./lib/index.d.ts",
28
+ "default": "./lib/index.js"
29
+ },
30
+ "./package.json": "./package.json"
31
+ },
32
+ "main": "lib/index.js",
33
+ "types": "lib/index.d.ts",
34
+ "files": [
35
+ "lib",
36
+ "README.md",
37
+ "CHANGELOG.md",
38
+ "LICENSE",
39
+ "THIRD_PARTY_NOTICES.md"
40
+ ],
41
+ "dependencies": {
42
+ "@oliphaunt/liboliphaunt-wasix-tools": "0.2.0"
43
+ },
44
+ "peerDependencies": {
45
+ "@oliphaunt/wasix-ts": "0.1.0"
46
+ },
47
+ "engines": {
48
+ "node": ">=22.13 <25",
49
+ "bun": ">=1.3.14",
50
+ "deno": ">=2.8.1"
51
+ }
52
+ }