@oliphaunt/wasix-ts 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/ARCHITECTURE.md +655 -0
- package/CHANGELOG.md +33 -0
- package/LICENSE +21 -0
- package/README.md +404 -0
- package/THIRD_PARTY_NOTICES.md +20 -0
- package/lib/archive.d.ts +21 -0
- package/lib/archive.js +336 -0
- package/lib/asset-source.d.ts +4 -0
- package/lib/asset-source.js +15 -0
- package/lib/byte-channel.d.ts +27 -0
- package/lib/byte-channel.js +170 -0
- package/lib/client-common.d.ts +6 -0
- package/lib/client-common.js +36 -0
- package/lib/client.d.ts +7 -0
- package/lib/client.js +24 -0
- package/lib/database-root.d.ts +19 -0
- package/lib/database-root.js +139 -0
- package/lib/database.d.ts +135 -0
- package/lib/database.js +1039 -0
- package/lib/descriptor-validation.d.ts +10 -0
- package/lib/descriptor-validation.js +75 -0
- package/lib/direct-client-common.d.ts +50 -0
- package/lib/direct-client-common.js +717 -0
- package/lib/direct-client.d.ts +4 -0
- package/lib/direct-client.js +13 -0
- package/lib/direct.node.d.ts +2 -0
- package/lib/direct.node.js +2 -0
- package/lib/errors.d.ts +25 -0
- package/lib/errors.js +34 -0
- package/lib/extension-descriptor.d.ts +14 -0
- package/lib/extension-descriptor.js +419 -0
- package/lib/extensions.d.ts +68 -0
- package/lib/extensions.js +769 -0
- package/lib/host/LICENSE +21 -0
- package/lib/host/index.d.mts +123 -0
- package/lib/host/index.mjs +11 -0
- package/lib/host/provenance.json +16 -0
- package/lib/host/wasmer_js_bg.wasm +0 -0
- package/lib/host/worker.mjs +11 -0
- package/lib/host-runtime.d.ts +5 -0
- package/lib/host-runtime.js +13 -0
- package/lib/icu-descriptor.d.ts +3 -0
- package/lib/icu-descriptor.js +92 -0
- package/lib/index.bun.d.ts +2 -0
- package/lib/index.bun.js +2 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.deno.d.ts +2 -0
- package/lib/index.deno.js +2 -0
- package/lib/index.js +2 -0
- package/lib/index.node.d.ts +2 -0
- package/lib/index.node.js +2 -0
- package/lib/internal-common.d.ts +12 -0
- package/lib/internal-common.js +274 -0
- package/lib/internal.d.ts +5 -0
- package/lib/internal.js +42 -0
- package/lib/internal.node.d.ts +5 -0
- package/lib/internal.node.js +8 -0
- package/lib/native-addon.d.ts +111 -0
- package/lib/native-addon.js +223 -0
- package/lib/native-server.d.ts +21 -0
- package/lib/native-server.js +109 -0
- package/lib/native-session.d.ts +60 -0
- package/lib/native-session.js +565 -0
- package/lib/node-actor.d.ts +7 -0
- package/lib/node-actor.js +10 -0
- package/lib/node-client-common.d.ts +9 -0
- package/lib/node-client-common.js +35 -0
- package/lib/node-client.d.ts +4 -0
- package/lib/node-client.js +13 -0
- package/lib/node-direct.d.ts +7 -0
- package/lib/node-direct.js +10 -0
- package/lib/node-worker-options.d.ts +5 -0
- package/lib/node-worker-options.js +65 -0
- package/lib/node-worker-port.d.ts +4 -0
- package/lib/node-worker-port.js +116 -0
- package/lib/node-worker.d.ts +1 -0
- package/lib/node-worker.js +38 -0
- package/lib/pgwire-connection.d.ts +60 -0
- package/lib/pgwire-connection.js +528 -0
- package/lib/pgwire.d.ts +3 -0
- package/lib/pgwire.js +105 -0
- package/lib/physical-archive.d.ts +29 -0
- package/lib/physical-archive.js +527 -0
- package/lib/protocol.d.ts +1 -0
- package/lib/protocol.js +1 -0
- package/lib/public.d.ts +4 -0
- package/lib/public.js +3 -0
- package/lib/query.d.ts +1 -0
- package/lib/query.js +1 -0
- package/lib/rpc.d.ts +203 -0
- package/lib/rpc.js +84 -0
- package/lib/runtime-descriptor.d.ts +3 -0
- package/lib/runtime-descriptor.js +79 -0
- package/lib/server.node.d.ts +1 -0
- package/lib/server.node.js +1 -0
- package/lib/startup-config.d.ts +2 -0
- package/lib/startup-config.js +19 -0
- package/lib/storage/bun.d.ts +6 -0
- package/lib/storage/bun.js +6 -0
- package/lib/storage/deno.d.ts +7 -0
- package/lib/storage/deno.js +7 -0
- package/lib/storage/incremental-storage.d.ts +25 -0
- package/lib/storage/incremental-storage.js +154 -0
- package/lib/storage/indexed-db-provider.d.ts +40 -0
- package/lib/storage/indexed-db-provider.js +259 -0
- package/lib/storage/indexed-db.d.ts +9 -0
- package/lib/storage/indexed-db.js +11 -0
- package/lib/storage/node.d.ts +10 -0
- package/lib/storage/node.js +13 -0
- package/lib/storage/opfs-pool.d.ts +31 -0
- package/lib/storage/opfs-pool.js +1271 -0
- package/lib/storage/opfs-provider.d.ts +4 -0
- package/lib/storage/opfs-provider.js +257 -0
- package/lib/storage/opfs.d.ts +8 -0
- package/lib/storage/opfs.js +10 -0
- package/lib/storage/restore-cleanup.d.ts +4 -0
- package/lib/storage/restore-cleanup.js +22 -0
- package/lib/storage/web-lock.d.ts +2 -0
- package/lib/storage/web-lock.js +64 -0
- package/lib/storage-provider.d.ts +47 -0
- package/lib/storage-provider.js +141 -0
- package/lib/storage-snapshot.d.ts +44 -0
- package/lib/storage-snapshot.js +274 -0
- package/lib/storage.d.ts +46 -0
- package/lib/storage.js +83 -0
- package/lib/tool-runtime.d.ts +43 -0
- package/lib/tool-runtime.js +93 -0
- package/lib/tool-worker-common.d.ts +48 -0
- package/lib/tool-worker-common.js +97 -0
- package/lib/tool-worker.d.ts +1 -0
- package/lib/tool-worker.js +10 -0
- package/lib/types.d.ts +230 -0
- package/lib/types.js +1 -0
- package/lib/wasix-runtime.d.ts +24 -0
- package/lib/wasix-runtime.js +186 -0
- package/lib/worker-client.d.ts +4 -0
- package/lib/worker-client.js +45 -0
- package/lib/worker-dispatch.d.ts +11 -0
- package/lib/worker-dispatch.js +174 -0
- package/lib/worker-entry.bun.d.ts +2 -0
- package/lib/worker-entry.bun.js +2 -0
- package/lib/worker-entry.d.ts +2 -0
- package/lib/worker-entry.deno.d.ts +2 -0
- package/lib/worker-entry.deno.js +2 -0
- package/lib/worker-entry.js +2 -0
- package/lib/worker-entry.node.d.ts +2 -0
- package/lib/worker-entry.node.js +2 -0
- package/lib/worker-node-client.d.ts +4 -0
- package/lib/worker-node-client.js +55 -0
- package/lib/worker-rpc.d.ts +36 -0
- package/lib/worker-rpc.js +422 -0
- package/lib/worker-transfer.d.ts +6 -0
- package/lib/worker-transfer.js +11 -0
- package/lib/worker.d.ts +1 -0
- package/lib/worker.js +21 -0
- package/lib/zstd.d.ts +4 -0
- package/lib/zstd.js +12 -0
- package/node_modules/@oliphaunt/js-core/README.md +7 -0
- package/node_modules/@oliphaunt/js-core/dist/commonjs/protocol.d.ts +1 -0
- package/node_modules/@oliphaunt/js-core/dist/commonjs/protocol.js +22 -0
- package/node_modules/@oliphaunt/js-core/dist/commonjs/query.d.ts +255 -0
- package/node_modules/@oliphaunt/js-core/dist/commonjs/query.js +2068 -0
- package/node_modules/@oliphaunt/js-core/dist/module/package.json +3 -0
- package/node_modules/@oliphaunt/js-core/dist/module/protocol.d.ts +1 -0
- package/node_modules/@oliphaunt/js-core/dist/module/protocol.js +19 -0
- package/node_modules/@oliphaunt/js-core/dist/module/query.d.ts +255 -0
- package/node_modules/@oliphaunt/js-core/dist/module/query.js +2039 -0
- package/node_modules/@oliphaunt/js-core/package.json +21 -0
- package/package.json +122 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import defaultWasixRuntime from '@oliphaunt/liboliphaunt-wasix';
|
|
2
|
+
import { closeWasixByteChannel, createWasixByteChannel, failWasixByteChannel, wasixByteChannelProtocolOutcomeUnknown, } from './byte-channel.js';
|
|
3
|
+
import { assertWasixProtocolConnectionTarget, registerWasixDatabaseResource, reserveWasixProtocolConnection, runWasixPgDumpProcess, } from './database.js';
|
|
4
|
+
import { validateWasixToolDescriptor, } from './tool-runtime.js';
|
|
5
|
+
const controllers = new WeakMap();
|
|
6
|
+
/** @internal Shared browser/server tool orchestration; public API lives in the optional package. */
|
|
7
|
+
export function runWasixToolProcess(database, options, createWorker) {
|
|
8
|
+
if (options.runtimeVersion !== defaultWasixRuntime.version) {
|
|
9
|
+
throw new Error(`WASIX tools runtime ${options.runtimeVersion} is incompatible with database runtime ${defaultWasixRuntime.version}`);
|
|
10
|
+
}
|
|
11
|
+
validateWasixToolDescriptor(options.tool);
|
|
12
|
+
if (options.tool.name === 'pg_dump') {
|
|
13
|
+
return runWasixPgDumpProcess(database, options);
|
|
14
|
+
}
|
|
15
|
+
assertWasixProtocolConnectionTarget(database);
|
|
16
|
+
let controller = controllers.get(database);
|
|
17
|
+
if (controller === undefined) {
|
|
18
|
+
controller = new WasixToolController(database, createWorker);
|
|
19
|
+
registerWasixDatabaseResource(database, controller);
|
|
20
|
+
controllers.set(database, controller);
|
|
21
|
+
}
|
|
22
|
+
return controller.run(options);
|
|
23
|
+
}
|
|
24
|
+
class WasixToolController {
|
|
25
|
+
#database;
|
|
26
|
+
#createWorker;
|
|
27
|
+
#pending = new Map();
|
|
28
|
+
#preparedPsqlIdentity;
|
|
29
|
+
#retiredWorkerTerminations = new Set();
|
|
30
|
+
#connections = new Set();
|
|
31
|
+
#worker;
|
|
32
|
+
#nextId = 1;
|
|
33
|
+
#tail = Promise.resolve();
|
|
34
|
+
#closing = false;
|
|
35
|
+
#fatal;
|
|
36
|
+
#termination;
|
|
37
|
+
constructor(database, createWorker) {
|
|
38
|
+
this.#database = database;
|
|
39
|
+
this.#createWorker = createWorker;
|
|
40
|
+
}
|
|
41
|
+
run(options) {
|
|
42
|
+
if (this.#closing) {
|
|
43
|
+
return Promise.reject(new Error('Oliphaunt WASIX tool worker is closing'));
|
|
44
|
+
}
|
|
45
|
+
if (this.#fatal !== undefined)
|
|
46
|
+
return Promise.reject(this.#fatal);
|
|
47
|
+
if (options.tool.name !== 'psql') {
|
|
48
|
+
return Promise.reject(new TypeError('the bounded tool worker supports only psql'));
|
|
49
|
+
}
|
|
50
|
+
let connection;
|
|
51
|
+
try {
|
|
52
|
+
// Reserve the database FIFO during the public call. Preparing a cold
|
|
53
|
+
// tool must not let a later query or tool overtake this invocation.
|
|
54
|
+
connection = this.#reserveConnection();
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
return Promise.reject(error);
|
|
58
|
+
}
|
|
59
|
+
const operation = this.#tail.then(() => this.#runOne(options, connection));
|
|
60
|
+
this.#tail = operation.then(() => undefined, () => undefined);
|
|
61
|
+
return operation;
|
|
62
|
+
}
|
|
63
|
+
async close() {
|
|
64
|
+
if (this.#termination !== undefined)
|
|
65
|
+
return this.#termination;
|
|
66
|
+
this.#closing = true;
|
|
67
|
+
const error = this.#fatal ?? new Error('Oliphaunt WASIX tool worker is closing');
|
|
68
|
+
this.#fatal = error;
|
|
69
|
+
for (const pending of this.#pending.values())
|
|
70
|
+
pending.reject(error);
|
|
71
|
+
this.#pending.clear();
|
|
72
|
+
this.#failConnections(error);
|
|
73
|
+
const worker = this.#worker;
|
|
74
|
+
this.#worker = undefined;
|
|
75
|
+
this.#preparedPsqlIdentity = undefined;
|
|
76
|
+
const terminations = [...this.#retiredWorkerTerminations];
|
|
77
|
+
if (worker !== undefined)
|
|
78
|
+
terminations.push(terminateWorker(worker));
|
|
79
|
+
this.#termination = Promise.all(terminations).then(() => undefined);
|
|
80
|
+
return this.#termination;
|
|
81
|
+
}
|
|
82
|
+
async #runOne(options, connection) {
|
|
83
|
+
const { frontend, backend } = connection;
|
|
84
|
+
try {
|
|
85
|
+
if (this.#closing)
|
|
86
|
+
throw this.#fatal;
|
|
87
|
+
if (this.#fatal !== undefined)
|
|
88
|
+
throw this.#fatal;
|
|
89
|
+
if (connection.failure !== undefined)
|
|
90
|
+
throw connection.failure;
|
|
91
|
+
await this.#prepare(options.tool);
|
|
92
|
+
if (this.#closing)
|
|
93
|
+
throw this.#fatal;
|
|
94
|
+
if (this.#fatal !== undefined)
|
|
95
|
+
throw this.#fatal;
|
|
96
|
+
if (connection.failure !== undefined)
|
|
97
|
+
throw connection.failure;
|
|
98
|
+
const databaseServing = connection.database.start();
|
|
99
|
+
connection.started = true;
|
|
100
|
+
const serving = databaseServing.then(() => ({ ok: true }), (error) => {
|
|
101
|
+
connection.failure ??= asError(error);
|
|
102
|
+
failWasixByteChannel(frontend);
|
|
103
|
+
failWasixByteChannel(backend);
|
|
104
|
+
return { ok: false, error };
|
|
105
|
+
});
|
|
106
|
+
connection.serving = serving;
|
|
107
|
+
const stdin = options.stdin;
|
|
108
|
+
let response;
|
|
109
|
+
let responseFailure;
|
|
110
|
+
try {
|
|
111
|
+
response = await this.#request({
|
|
112
|
+
kind: 'run',
|
|
113
|
+
tool: 'psql',
|
|
114
|
+
args: [...options.args],
|
|
115
|
+
stdin,
|
|
116
|
+
frontend,
|
|
117
|
+
backend,
|
|
118
|
+
}, stdin === undefined ||
|
|
119
|
+
!(stdin.buffer instanceof ArrayBuffer) ||
|
|
120
|
+
stdin.byteOffset !== 0 ||
|
|
121
|
+
stdin.byteLength !== stdin.buffer.byteLength
|
|
122
|
+
? []
|
|
123
|
+
: [stdin.buffer]);
|
|
124
|
+
if (!response.ok)
|
|
125
|
+
throw new Error(response.message);
|
|
126
|
+
if (response.kind !== 'completed') {
|
|
127
|
+
throw new Error('Oliphaunt WASIX tool worker returned an invalid completion response');
|
|
128
|
+
}
|
|
129
|
+
closeWasixByteChannel(frontend);
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
responseFailure = error;
|
|
133
|
+
finishFailedToolChannels(connection);
|
|
134
|
+
}
|
|
135
|
+
if (responseFailure !== undefined)
|
|
136
|
+
throw responseFailure;
|
|
137
|
+
const served = await serving;
|
|
138
|
+
if (!served.ok)
|
|
139
|
+
throw served.error;
|
|
140
|
+
if (response === undefined || !response.ok || response.kind !== 'completed') {
|
|
141
|
+
throw new Error('Oliphaunt WASIX tool worker did not return a result');
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
exitCode: response.exitCode,
|
|
145
|
+
stdout: response.stdout,
|
|
146
|
+
stderr: response.stderr,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
finally {
|
|
150
|
+
closeWasixByteChannel(frontend);
|
|
151
|
+
if (!connection.started)
|
|
152
|
+
connection.database.cancel();
|
|
153
|
+
if (connection.serving !== undefined && !this.#closing)
|
|
154
|
+
await connection.serving;
|
|
155
|
+
closeWasixByteChannel(backend);
|
|
156
|
+
this.#connections.delete(connection);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
#reserveConnection() {
|
|
160
|
+
const frontend = createWasixByteChannel();
|
|
161
|
+
const backend = createWasixByteChannel();
|
|
162
|
+
try {
|
|
163
|
+
const database = reserveWasixProtocolConnection(this.#database, { frontend, backend }, 'tool');
|
|
164
|
+
const connection = {
|
|
165
|
+
frontend,
|
|
166
|
+
backend,
|
|
167
|
+
database,
|
|
168
|
+
started: false,
|
|
169
|
+
};
|
|
170
|
+
this.#connections.add(connection);
|
|
171
|
+
return connection;
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
failWasixByteChannel(frontend);
|
|
175
|
+
failWasixByteChannel(backend);
|
|
176
|
+
throw error;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
#failConnections(error) {
|
|
180
|
+
for (const connection of this.#connections) {
|
|
181
|
+
connection.failure ??= error;
|
|
182
|
+
if (!connection.started)
|
|
183
|
+
connection.database.cancel();
|
|
184
|
+
finishFailedToolChannels(connection);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
async #prepare(tool) {
|
|
188
|
+
const identity = `${tool.sha256}:${tool.size}`;
|
|
189
|
+
if (this.#preparedPsqlIdentity === identity)
|
|
190
|
+
return;
|
|
191
|
+
const response = await this.#request({
|
|
192
|
+
kind: 'prepare',
|
|
193
|
+
tool,
|
|
194
|
+
});
|
|
195
|
+
if (!response.ok)
|
|
196
|
+
throw new Error(response.message);
|
|
197
|
+
if (response.kind !== 'prepared') {
|
|
198
|
+
throw new Error('Oliphaunt WASIX tool worker returned an invalid preparation response');
|
|
199
|
+
}
|
|
200
|
+
this.#preparedPsqlIdentity = identity;
|
|
201
|
+
}
|
|
202
|
+
#request(request, transfer = []) {
|
|
203
|
+
if (this.#fatal !== undefined)
|
|
204
|
+
return Promise.reject(this.#fatal);
|
|
205
|
+
const worker = this.#ensureWorker();
|
|
206
|
+
const id = this.#nextId++;
|
|
207
|
+
return new Promise((resolve, reject) => {
|
|
208
|
+
this.#pending.set(id, { resolve, reject });
|
|
209
|
+
try {
|
|
210
|
+
worker.postMessage({ ...request, id }, transfer);
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
this.#pending.delete(id);
|
|
214
|
+
reject(asError(error));
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
#ensureWorker() {
|
|
219
|
+
if (this.#worker !== undefined)
|
|
220
|
+
return this.#worker;
|
|
221
|
+
const worker = this.#createWorker();
|
|
222
|
+
this.#worker = worker;
|
|
223
|
+
worker.onMessage((response) => {
|
|
224
|
+
if (this.#worker !== worker)
|
|
225
|
+
return;
|
|
226
|
+
const pending = this.#pending.get(response.id);
|
|
227
|
+
if (pending === undefined)
|
|
228
|
+
return;
|
|
229
|
+
this.#pending.delete(response.id);
|
|
230
|
+
pending.resolve(response);
|
|
231
|
+
});
|
|
232
|
+
worker.onFatal((error) => {
|
|
233
|
+
if (this.#worker !== worker)
|
|
234
|
+
return;
|
|
235
|
+
this.#handleFatal(error);
|
|
236
|
+
});
|
|
237
|
+
return worker;
|
|
238
|
+
}
|
|
239
|
+
#handleFatal(error) {
|
|
240
|
+
const protocolOutcomeUnknown = [...this.#connections].some((connection) => wasixByteChannelProtocolOutcomeUnknown(connection.frontend));
|
|
241
|
+
for (const pending of this.#pending.values())
|
|
242
|
+
pending.reject(error);
|
|
243
|
+
this.#pending.clear();
|
|
244
|
+
this.#failConnections(error);
|
|
245
|
+
const worker = this.#worker;
|
|
246
|
+
this.#worker = undefined;
|
|
247
|
+
if (worker !== undefined) {
|
|
248
|
+
const termination = terminateWorker(worker);
|
|
249
|
+
// The worker fatal path has no awaiting caller, but database.close()
|
|
250
|
+
// must still observe a failed termination instead of reporting a clean
|
|
251
|
+
// shutdown while the retired worker may remain alive.
|
|
252
|
+
void termination.catch(() => undefined);
|
|
253
|
+
this.#retiredWorkerTerminations.add(termination);
|
|
254
|
+
}
|
|
255
|
+
this.#preparedPsqlIdentity = undefined;
|
|
256
|
+
if (protocolOutcomeUnknown || this.#closing)
|
|
257
|
+
this.#fatal = error;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
function finishFailedToolChannels(connection) {
|
|
261
|
+
if (wasixByteChannelProtocolOutcomeUnknown(connection.frontend)) {
|
|
262
|
+
failWasixByteChannel(connection.frontend);
|
|
263
|
+
failWasixByteChannel(connection.backend);
|
|
264
|
+
}
|
|
265
|
+
closeWasixByteChannel(connection.frontend);
|
|
266
|
+
}
|
|
267
|
+
function terminateWorker(worker) {
|
|
268
|
+
return Promise.resolve()
|
|
269
|
+
.then(() => worker.terminate())
|
|
270
|
+
.then(() => undefined);
|
|
271
|
+
}
|
|
272
|
+
function asError(error) {
|
|
273
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
274
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { OliphauntDatabase } from './types.js';
|
|
2
|
+
import { type WasixToolProcessOptions, type WasixToolProcessResult } from './internal-common.js';
|
|
3
|
+
export { getWasixDatabaseIdentity } from './database.js';
|
|
4
|
+
export type { WasixToolDescriptor, WasixToolProcessOptions, WasixToolProcessResult, } from './internal-common.js';
|
|
5
|
+
export declare function runWasixToolProcess(database: OliphauntDatabase, options: WasixToolProcessOptions): Promise<WasixToolProcessResult>;
|
package/lib/internal.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { runWasixToolProcess as runTool, } from './internal-common.js';
|
|
2
|
+
export { getWasixDatabaseIdentity } from './database.js';
|
|
3
|
+
export function runWasixToolProcess(database, options) {
|
|
4
|
+
return runTool(database, options, createBrowserToolWorker);
|
|
5
|
+
}
|
|
6
|
+
function createBrowserToolWorker() {
|
|
7
|
+
if (typeof Worker === 'undefined') {
|
|
8
|
+
throw new Error('WASIX tools require Web Workers');
|
|
9
|
+
}
|
|
10
|
+
const worker = new Worker(new URL('./tool-worker.js', import.meta.url), {
|
|
11
|
+
type: 'module',
|
|
12
|
+
name: 'oliphaunt-wasix-tool',
|
|
13
|
+
});
|
|
14
|
+
let messageListener;
|
|
15
|
+
let fatalListener;
|
|
16
|
+
let fatalDelivered = false;
|
|
17
|
+
worker.addEventListener('message', (event) => {
|
|
18
|
+
messageListener?.(event.data);
|
|
19
|
+
});
|
|
20
|
+
worker.addEventListener('error', (event) => {
|
|
21
|
+
if (fatalDelivered)
|
|
22
|
+
return;
|
|
23
|
+
fatalDelivered = true;
|
|
24
|
+
fatalListener?.(new Error(event.message || 'Oliphaunt WASIX tool worker crashed'));
|
|
25
|
+
});
|
|
26
|
+
worker.addEventListener('messageerror', () => {
|
|
27
|
+
if (fatalDelivered)
|
|
28
|
+
return;
|
|
29
|
+
fatalDelivered = true;
|
|
30
|
+
fatalListener?.(new Error('Oliphaunt WASIX tool worker returned an unreadable response'));
|
|
31
|
+
});
|
|
32
|
+
return {
|
|
33
|
+
postMessage: (request, transfer = []) => worker.postMessage(request, transfer),
|
|
34
|
+
onMessage: (listener) => {
|
|
35
|
+
messageListener = listener;
|
|
36
|
+
},
|
|
37
|
+
onFatal: (listener) => {
|
|
38
|
+
fatalListener = listener;
|
|
39
|
+
},
|
|
40
|
+
terminate: () => worker.terminate(),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { OliphauntDatabase } from './types.js';
|
|
2
|
+
import type { WasixToolProcessOptions, WasixToolProcessResult } from './internal-common.js';
|
|
3
|
+
export { getWasixDatabaseIdentity } from './database.js';
|
|
4
|
+
export type { WasixToolDescriptor, WasixToolProcessOptions, WasixToolProcessResult, } from './internal-common.js';
|
|
5
|
+
export declare function runWasixToolProcess(database: OliphauntDatabase, options: WasixToolProcessOptions): Promise<WasixToolProcessResult>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { tryRunWasixNativeToolProcess } from './database.js';
|
|
2
|
+
export { getWasixDatabaseIdentity } from './database.js';
|
|
3
|
+
export function runWasixToolProcess(database, options) {
|
|
4
|
+
const native = tryRunWasixNativeToolProcess(database, options);
|
|
5
|
+
if (native !== undefined)
|
|
6
|
+
return native;
|
|
7
|
+
throw new Error('this database is not backed by the Oliphaunt WASIX native runtime required by Node.js, Bun, Deno, and Electron tools');
|
|
8
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
export type NativeWasixStorage = Readonly<{
|
|
2
|
+
kind: 'memory';
|
|
3
|
+
}> | Readonly<{
|
|
4
|
+
kind: 'directory';
|
|
5
|
+
path: string;
|
|
6
|
+
}>;
|
|
7
|
+
export type NativeProfile = 'standard' | 'icu';
|
|
8
|
+
export type NativeWasixOpenOptions = Readonly<{
|
|
9
|
+
profile: NativeProfile;
|
|
10
|
+
storage: NativeWasixStorage;
|
|
11
|
+
username: string;
|
|
12
|
+
database: string;
|
|
13
|
+
startupGucs: Record<string, string>;
|
|
14
|
+
extensions: string[];
|
|
15
|
+
}>;
|
|
16
|
+
export type NativeWasixServerListen = Readonly<{
|
|
17
|
+
transport: 'tcp';
|
|
18
|
+
port?: number;
|
|
19
|
+
}> | Readonly<{
|
|
20
|
+
transport: 'unix';
|
|
21
|
+
directory: string;
|
|
22
|
+
port?: number;
|
|
23
|
+
}>;
|
|
24
|
+
export type NativeWasixServerOpenOptions = NativeWasixOpenOptions & Readonly<{
|
|
25
|
+
listen: NativeWasixServerListen;
|
|
26
|
+
}>;
|
|
27
|
+
export type NativeWasixDatabaseHandle = {
|
|
28
|
+
readonly closed: boolean;
|
|
29
|
+
execProtocolRaw(request: Uint8Array): Uint8Array;
|
|
30
|
+
execProtocolRawStream(request: Uint8Array, onChunk: (chunk: Uint8Array) => void): 'complete' | 'callbackAborted';
|
|
31
|
+
backup(): Uint8Array;
|
|
32
|
+
pgDump(args: string[]): NativeWasixToolResult;
|
|
33
|
+
psql(args: string[], command?: string, script?: string): NativeWasixToolResult;
|
|
34
|
+
close(): void;
|
|
35
|
+
};
|
|
36
|
+
export type NativeWasixActorDatabaseHandle = {
|
|
37
|
+
readonly closed: boolean;
|
|
38
|
+
execProtocolRaw(request: Uint8Array): Promise<Uint8Array>;
|
|
39
|
+
execProtocolRawStream(request: Uint8Array, onChunk: (chunk: Uint8Array) => void): Promise<'complete' | 'callbackAborted'>;
|
|
40
|
+
backup(): Promise<Uint8Array>;
|
|
41
|
+
pgDump(args: string[]): Promise<NativeWasixToolResult>;
|
|
42
|
+
psql(args: string[], command?: string, script?: string): Promise<NativeWasixToolResult>;
|
|
43
|
+
close(): Promise<void>;
|
|
44
|
+
};
|
|
45
|
+
export type NativeWasixToolResult = Readonly<{
|
|
46
|
+
status: number;
|
|
47
|
+
stdout: Uint8Array;
|
|
48
|
+
stderr: Uint8Array;
|
|
49
|
+
}>;
|
|
50
|
+
export type NativeWasixServerHandle = {
|
|
51
|
+
readonly connectionString: string;
|
|
52
|
+
readonly closed: boolean;
|
|
53
|
+
close(): Promise<void>;
|
|
54
|
+
};
|
|
55
|
+
export type NativeWasixAddon = {
|
|
56
|
+
NativeWasixDatabase: {
|
|
57
|
+
open(options: NativeWasixOpenOptions): NativeWasixDatabaseHandle;
|
|
58
|
+
readonly prototype: NativeWasixDatabaseHandle;
|
|
59
|
+
};
|
|
60
|
+
NativeWasixActorDatabase: {
|
|
61
|
+
open(options: NativeWasixOpenOptions): Promise<NativeWasixActorDatabaseHandle>;
|
|
62
|
+
readonly prototype: NativeWasixActorDatabaseHandle;
|
|
63
|
+
};
|
|
64
|
+
NativeWasixServer: {
|
|
65
|
+
open(options: NativeWasixServerOpenOptions): Promise<NativeWasixServerHandle>;
|
|
66
|
+
readonly prototype: NativeWasixServerHandle;
|
|
67
|
+
};
|
|
68
|
+
restore(destination: string, bytes: Uint8Array): Promise<void>;
|
|
69
|
+
restoreDirect(destination: string, bytes: Uint8Array): void;
|
|
70
|
+
addonAbiVersion(): number;
|
|
71
|
+
nodeApiVersion(): number;
|
|
72
|
+
runtimeVersion(): string;
|
|
73
|
+
supportedProfiles(): readonly NativeProfile[];
|
|
74
|
+
payloadIdentity(component: 'runtimeArchive' | 'standardSeedArchive' | 'standardSeedManifest' | 'icuDataArchive' | 'icuSeedArchive' | 'icuSeedManifest'): string;
|
|
75
|
+
extensionIdentity(sqlName: string): string;
|
|
76
|
+
toolIdentity(name: 'pg_dump' | 'psql'): string;
|
|
77
|
+
};
|
|
78
|
+
type WasixPackageMetadata = Readonly<{
|
|
79
|
+
name?: string;
|
|
80
|
+
oliphaunt?: {
|
|
81
|
+
runtimeProduct?: string;
|
|
82
|
+
runtimeVersion?: string;
|
|
83
|
+
wasixNapiProduct?: string;
|
|
84
|
+
wasixNapiVersion?: string;
|
|
85
|
+
wasixAddonAbiVersion?: number;
|
|
86
|
+
nodeApiVersion?: number;
|
|
87
|
+
};
|
|
88
|
+
}>;
|
|
89
|
+
type NativeTarget = Readonly<{
|
|
90
|
+
id: string;
|
|
91
|
+
packageName: string;
|
|
92
|
+
libc?: 'glibc';
|
|
93
|
+
}>;
|
|
94
|
+
type LinuxLibc = 'glibc' | 'musl' | 'unknown';
|
|
95
|
+
type LinuxDiagnosticReport = Readonly<{
|
|
96
|
+
header?: Readonly<{
|
|
97
|
+
glibcVersionRuntime?: unknown;
|
|
98
|
+
}>;
|
|
99
|
+
sharedObjects?: unknown;
|
|
100
|
+
}>;
|
|
101
|
+
/** @internal Load and verify the platform addon selected by the server runtime. */
|
|
102
|
+
export declare function loadNativeWasixAddon(): NativeWasixAddon;
|
|
103
|
+
/** @internal Read an optional override without making Deno env permission mandatory. */
|
|
104
|
+
export declare function optionalEnvironmentValue(name: string, read?: (name: string) => string | undefined, denoRuntime?: boolean): string | undefined;
|
|
105
|
+
/** @internal Resolve only the GNU Linux carriers; musl addons are not published. */
|
|
106
|
+
export declare function nativeTarget(currentPlatform: string, currentArch: string, currentLibc?: LinuxLibc): NativeTarget;
|
|
107
|
+
/** @internal Detect Linux libc without probing mutable external commands. */
|
|
108
|
+
export declare function detectLinuxLibc(report?: LinuxDiagnosticReport | undefined, versions?: Readonly<Record<string, string | undefined>>, denoTarget?: string | undefined): LinuxLibc;
|
|
109
|
+
/** @internal Enforce the complete v1 release surface before any database opens. */
|
|
110
|
+
export declare function validateNativeWasixAddon(addon: NativeWasixAddon, path: string, metadata: WasixPackageMetadata): void;
|
|
111
|
+
export {};
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { arch, platform } from 'node:os';
|
|
4
|
+
import { dirname, join, resolve } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
const ADDON_ENV = 'OLIPHAUNT_WASIX_NAPI';
|
|
8
|
+
const ADDON_STEM = 'oliphaunt_wasix_napi';
|
|
9
|
+
const EXPECTED_NAPI_VERSION = 8;
|
|
10
|
+
const PRODUCT = 'oliphaunt-wasix-napi';
|
|
11
|
+
let loadedAddon;
|
|
12
|
+
/** @internal Load and verify the platform addon selected by the server runtime. */
|
|
13
|
+
export function loadNativeWasixAddon() {
|
|
14
|
+
if (loadedAddon !== undefined)
|
|
15
|
+
return loadedAddon;
|
|
16
|
+
const runtimeNapi = Number(process.versions.napi);
|
|
17
|
+
if (!Number.isSafeInteger(runtimeNapi) || runtimeNapi < EXPECTED_NAPI_VERSION) {
|
|
18
|
+
throw new Error(`this runtime does not provide the Node-API ${EXPECTED_NAPI_VERSION} ABI required by @oliphaunt/wasix-ts`);
|
|
19
|
+
}
|
|
20
|
+
const runtimePlatform = platform();
|
|
21
|
+
const runtimeLibc = runtimePlatform === 'linux' ? detectLinuxLibc() : 'unknown';
|
|
22
|
+
if (runtimePlatform === 'linux')
|
|
23
|
+
requireSupportedLinuxLibc(runtimeLibc);
|
|
24
|
+
const metadata = packageMetadata();
|
|
25
|
+
const path = resolveNativeAddonPath(metadata, runtimePlatform, runtimeLibc);
|
|
26
|
+
const loaded = require(path);
|
|
27
|
+
const addon = normalizeAddon(loaded);
|
|
28
|
+
validateNativeWasixAddon(addon, path, metadata);
|
|
29
|
+
loadedAddon = addon;
|
|
30
|
+
return addon;
|
|
31
|
+
}
|
|
32
|
+
function resolveNativeAddonPath(metadata, runtimePlatform, runtimeLibc) {
|
|
33
|
+
const explicit = optionalEnvironmentValue(ADDON_ENV);
|
|
34
|
+
if (explicit !== undefined && explicit.trim().length > 0) {
|
|
35
|
+
if (explicit.includes('\0'))
|
|
36
|
+
throw new Error(`${ADDON_ENV} must not contain NUL bytes`);
|
|
37
|
+
const path = resolve(explicit);
|
|
38
|
+
requireRegularFile(path, ADDON_ENV);
|
|
39
|
+
return path;
|
|
40
|
+
}
|
|
41
|
+
for (const candidate of packageAdjacentAddons()) {
|
|
42
|
+
if (isRegularFile(candidate))
|
|
43
|
+
return candidate;
|
|
44
|
+
}
|
|
45
|
+
const version = metadata.oliphaunt?.wasixNapiVersion;
|
|
46
|
+
if (version === undefined || !/^\d+\.\d+\.\d+$/u.test(version)) {
|
|
47
|
+
throw new Error('@oliphaunt/wasix-ts package metadata does not pin wasixNapiVersion');
|
|
48
|
+
}
|
|
49
|
+
const target = nativeTarget(runtimePlatform, arch(), runtimeLibc);
|
|
50
|
+
let packageJsonPath;
|
|
51
|
+
try {
|
|
52
|
+
packageJsonPath = require.resolve(`${target.packageName}/package.json`);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
throw new Error(`${target.packageName} ${version} is not installed; reinstall @oliphaunt/wasix-ts with optional dependencies enabled`);
|
|
56
|
+
}
|
|
57
|
+
const carrier = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
58
|
+
if (carrier.name !== target.packageName ||
|
|
59
|
+
carrier.version !== version ||
|
|
60
|
+
JSON.stringify(carrier.libc) !==
|
|
61
|
+
JSON.stringify(target.libc === undefined ? undefined : [target.libc]) ||
|
|
62
|
+
carrier.oliphaunt?.target !== target.id ||
|
|
63
|
+
carrier.oliphaunt?.runtimeProduct !== metadata.oliphaunt?.runtimeProduct ||
|
|
64
|
+
carrier.oliphaunt?.runtimeVersion !== metadata.oliphaunt?.runtimeVersion ||
|
|
65
|
+
carrier.oliphaunt?.addonAbiVersion !== metadata.oliphaunt?.wasixAddonAbiVersion ||
|
|
66
|
+
carrier.oliphaunt?.nodeApiVersion !== metadata.oliphaunt?.nodeApiVersion ||
|
|
67
|
+
JSON.stringify(carrier.oliphaunt?.profiles) !== JSON.stringify(['standard', 'icu'])) {
|
|
68
|
+
throw new Error(`${target.packageName} metadata is incompatible with @oliphaunt/wasix-ts`);
|
|
69
|
+
}
|
|
70
|
+
const addonPath = require.resolve(`${target.packageName}/${ADDON_STEM}.node`);
|
|
71
|
+
requireRegularFile(addonPath, `${target.packageName} addon`);
|
|
72
|
+
return addonPath;
|
|
73
|
+
}
|
|
74
|
+
/** @internal Read an optional override without making Deno env permission mandatory. */
|
|
75
|
+
export function optionalEnvironmentValue(name, read = (key) => process.env[key], denoRuntime = 'Deno' in globalThis) {
|
|
76
|
+
try {
|
|
77
|
+
return read(name);
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
if (denoRuntime &&
|
|
81
|
+
error instanceof Error &&
|
|
82
|
+
(error.name === 'NotCapable' || error.name === 'PermissionDenied')) {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function packageMetadata() {
|
|
89
|
+
const path = fileURLToPath(new URL('../package.json', import.meta.url));
|
|
90
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
91
|
+
}
|
|
92
|
+
function packageAdjacentAddons() {
|
|
93
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
94
|
+
const file = `${ADDON_STEM}.node`;
|
|
95
|
+
return [join(here, file), join(here, '..', file)];
|
|
96
|
+
}
|
|
97
|
+
/** @internal Resolve only the GNU Linux carriers; musl addons are not published. */
|
|
98
|
+
export function nativeTarget(currentPlatform, currentArch, currentLibc = currentPlatform === 'linux' ? detectLinuxLibc() : 'unknown') {
|
|
99
|
+
if (currentPlatform === 'darwin' && currentArch === 'arm64') {
|
|
100
|
+
return { id: 'macos-arm64', packageName: '@oliphaunt/wasix-napi-darwin-arm64' };
|
|
101
|
+
}
|
|
102
|
+
if (currentPlatform === 'linux')
|
|
103
|
+
requireSupportedLinuxLibc(currentLibc);
|
|
104
|
+
if (currentPlatform === 'linux' && currentArch === 'arm64') {
|
|
105
|
+
return {
|
|
106
|
+
id: 'linux-arm64-gnu',
|
|
107
|
+
packageName: '@oliphaunt/wasix-napi-linux-arm64-gnu',
|
|
108
|
+
libc: 'glibc',
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (currentPlatform === 'linux' && currentArch === 'x64') {
|
|
112
|
+
return {
|
|
113
|
+
id: 'linux-x64-gnu',
|
|
114
|
+
packageName: '@oliphaunt/wasix-napi-linux-x64-gnu',
|
|
115
|
+
libc: 'glibc',
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
if (currentPlatform === 'win32' && currentArch === 'x64') {
|
|
119
|
+
return { id: 'windows-x64-msvc', packageName: '@oliphaunt/wasix-napi-win32-x64-msvc' };
|
|
120
|
+
}
|
|
121
|
+
throw new Error(`no Oliphaunt WASIX Node-API package is defined for ${currentPlatform}/${currentArch}`);
|
|
122
|
+
}
|
|
123
|
+
/** @internal Detect Linux libc without probing mutable external commands. */
|
|
124
|
+
export function detectLinuxLibc(report = undefined, versions = process.versions, denoTarget = denoBuildTarget()) {
|
|
125
|
+
if (typeof versions.musl === 'string' && versions.musl.length > 0)
|
|
126
|
+
return 'musl';
|
|
127
|
+
if (denoTarget?.endsWith('-linux-musl'))
|
|
128
|
+
return 'musl';
|
|
129
|
+
if (denoTarget?.endsWith('-linux-gnu'))
|
|
130
|
+
return 'glibc';
|
|
131
|
+
const diagnostic = report ?? process.report?.getReport?.();
|
|
132
|
+
if (Array.isArray(diagnostic?.sharedObjects) &&
|
|
133
|
+
diagnostic.sharedObjects.some((member) => typeof member === 'string' && /(?:^|[/\\])ld-musl-/iu.test(member))) {
|
|
134
|
+
return 'musl';
|
|
135
|
+
}
|
|
136
|
+
if (typeof diagnostic?.header?.glibcVersionRuntime === 'string' &&
|
|
137
|
+
diagnostic.header.glibcVersionRuntime.length > 0) {
|
|
138
|
+
return 'glibc';
|
|
139
|
+
}
|
|
140
|
+
return 'unknown';
|
|
141
|
+
}
|
|
142
|
+
function denoBuildTarget() {
|
|
143
|
+
const deno = globalThis.Deno;
|
|
144
|
+
return typeof deno?.build?.target === 'string' ? deno.build.target : undefined;
|
|
145
|
+
}
|
|
146
|
+
function requireSupportedLinuxLibc(libc) {
|
|
147
|
+
if (libc === 'musl') {
|
|
148
|
+
throw new Error('Oliphaunt WASIX Node-API does not support Linux musl; install on a glibc-based system');
|
|
149
|
+
}
|
|
150
|
+
if (libc !== 'glibc') {
|
|
151
|
+
throw new Error('Oliphaunt WASIX Node-API could not verify a supported Linux glibc runtime');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function normalizeAddon(loaded) {
|
|
155
|
+
const maybeDefault = loaded;
|
|
156
|
+
return (maybeDefault.default ?? loaded);
|
|
157
|
+
}
|
|
158
|
+
/** @internal Enforce the complete v1 release surface before any database opens. */
|
|
159
|
+
export function validateNativeWasixAddon(addon, path, metadata) {
|
|
160
|
+
const databasePrototype = addon?.NativeWasixDatabase?.prototype;
|
|
161
|
+
const actorDatabasePrototype = addon?.NativeWasixActorDatabase?.prototype;
|
|
162
|
+
const serverPrototype = addon?.NativeWasixServer?.prototype;
|
|
163
|
+
if (metadata.name !== '@oliphaunt/wasix-ts' ||
|
|
164
|
+
typeof addon !== 'object' ||
|
|
165
|
+
addon === null ||
|
|
166
|
+
typeof addon.NativeWasixDatabase?.open !== 'function' ||
|
|
167
|
+
typeof databasePrototype?.execProtocolRaw !== 'function' ||
|
|
168
|
+
typeof databasePrototype.execProtocolRawStream !== 'function' ||
|
|
169
|
+
typeof databasePrototype.backup !== 'function' ||
|
|
170
|
+
typeof databasePrototype.pgDump !== 'function' ||
|
|
171
|
+
typeof databasePrototype.psql !== 'function' ||
|
|
172
|
+
typeof databasePrototype.close !== 'function' ||
|
|
173
|
+
typeof addon.NativeWasixActorDatabase?.open !== 'function' ||
|
|
174
|
+
typeof actorDatabasePrototype?.execProtocolRaw !== 'function' ||
|
|
175
|
+
typeof actorDatabasePrototype.execProtocolRawStream !== 'function' ||
|
|
176
|
+
typeof actorDatabasePrototype.backup !== 'function' ||
|
|
177
|
+
typeof actorDatabasePrototype.pgDump !== 'function' ||
|
|
178
|
+
typeof actorDatabasePrototype.psql !== 'function' ||
|
|
179
|
+
typeof actorDatabasePrototype.close !== 'function' ||
|
|
180
|
+
typeof addon.NativeWasixServer?.open !== 'function' ||
|
|
181
|
+
typeof serverPrototype?.close !== 'function' ||
|
|
182
|
+
typeof addon.restore !== 'function' ||
|
|
183
|
+
typeof addon.restoreDirect !== 'function' ||
|
|
184
|
+
typeof addon.addonAbiVersion !== 'function' ||
|
|
185
|
+
typeof addon.nodeApiVersion !== 'function' ||
|
|
186
|
+
typeof addon.runtimeVersion !== 'function' ||
|
|
187
|
+
typeof addon.supportedProfiles !== 'function' ||
|
|
188
|
+
typeof addon.payloadIdentity !== 'function' ||
|
|
189
|
+
typeof addon.extensionIdentity !== 'function' ||
|
|
190
|
+
typeof addon.toolIdentity !== 'function') {
|
|
191
|
+
throw new Error(`Oliphaunt WASIX native addon ${path} has an invalid export surface`);
|
|
192
|
+
}
|
|
193
|
+
const expectedAbi = metadata.oliphaunt?.wasixAddonAbiVersion;
|
|
194
|
+
if (expectedAbi !== 1 || addon.addonAbiVersion() !== expectedAbi) {
|
|
195
|
+
throw new Error(`Oliphaunt WASIX native addon ${path} has an incompatible addon ABI`);
|
|
196
|
+
}
|
|
197
|
+
const expectedNodeApi = metadata.oliphaunt?.nodeApiVersion;
|
|
198
|
+
if (expectedNodeApi !== EXPECTED_NAPI_VERSION || addon.nodeApiVersion() !== expectedNodeApi) {
|
|
199
|
+
throw new Error(`Oliphaunt WASIX native addon ${path} does not use Node-API ${EXPECTED_NAPI_VERSION}`);
|
|
200
|
+
}
|
|
201
|
+
const expectedRuntime = metadata.oliphaunt?.runtimeVersion;
|
|
202
|
+
if (expectedRuntime === undefined || addon.runtimeVersion() !== expectedRuntime) {
|
|
203
|
+
throw new Error(`Oliphaunt WASIX native addon ${path} has an incompatible runtime version`);
|
|
204
|
+
}
|
|
205
|
+
if (metadata.oliphaunt?.wasixNapiProduct !== PRODUCT) {
|
|
206
|
+
throw new Error('@oliphaunt/wasix-ts package metadata has an invalid WASIX N-API product');
|
|
207
|
+
}
|
|
208
|
+
if (JSON.stringify(addon.supportedProfiles()) !== JSON.stringify(['standard', 'icu'])) {
|
|
209
|
+
throw new Error(`Oliphaunt WASIX native addon ${path} has incompatible catalog profiles`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function requireRegularFile(path, source) {
|
|
213
|
+
if (!isRegularFile(path))
|
|
214
|
+
throw new Error(`${source} does not point to a regular file: ${path}`);
|
|
215
|
+
}
|
|
216
|
+
function isRegularFile(path) {
|
|
217
|
+
try {
|
|
218
|
+
return statSync(path).isFile();
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
}
|