@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.
Files changed (169) hide show
  1. package/ARCHITECTURE.md +655 -0
  2. package/CHANGELOG.md +33 -0
  3. package/LICENSE +21 -0
  4. package/README.md +404 -0
  5. package/THIRD_PARTY_NOTICES.md +20 -0
  6. package/lib/archive.d.ts +21 -0
  7. package/lib/archive.js +336 -0
  8. package/lib/asset-source.d.ts +4 -0
  9. package/lib/asset-source.js +15 -0
  10. package/lib/byte-channel.d.ts +27 -0
  11. package/lib/byte-channel.js +170 -0
  12. package/lib/client-common.d.ts +6 -0
  13. package/lib/client-common.js +36 -0
  14. package/lib/client.d.ts +7 -0
  15. package/lib/client.js +24 -0
  16. package/lib/database-root.d.ts +19 -0
  17. package/lib/database-root.js +139 -0
  18. package/lib/database.d.ts +135 -0
  19. package/lib/database.js +1039 -0
  20. package/lib/descriptor-validation.d.ts +10 -0
  21. package/lib/descriptor-validation.js +75 -0
  22. package/lib/direct-client-common.d.ts +50 -0
  23. package/lib/direct-client-common.js +717 -0
  24. package/lib/direct-client.d.ts +4 -0
  25. package/lib/direct-client.js +13 -0
  26. package/lib/direct.node.d.ts +2 -0
  27. package/lib/direct.node.js +2 -0
  28. package/lib/errors.d.ts +25 -0
  29. package/lib/errors.js +34 -0
  30. package/lib/extension-descriptor.d.ts +14 -0
  31. package/lib/extension-descriptor.js +419 -0
  32. package/lib/extensions.d.ts +68 -0
  33. package/lib/extensions.js +769 -0
  34. package/lib/host/LICENSE +21 -0
  35. package/lib/host/index.d.mts +123 -0
  36. package/lib/host/index.mjs +11 -0
  37. package/lib/host/provenance.json +16 -0
  38. package/lib/host/wasmer_js_bg.wasm +0 -0
  39. package/lib/host/worker.mjs +11 -0
  40. package/lib/host-runtime.d.ts +5 -0
  41. package/lib/host-runtime.js +13 -0
  42. package/lib/icu-descriptor.d.ts +3 -0
  43. package/lib/icu-descriptor.js +92 -0
  44. package/lib/index.bun.d.ts +2 -0
  45. package/lib/index.bun.js +2 -0
  46. package/lib/index.d.ts +2 -0
  47. package/lib/index.deno.d.ts +2 -0
  48. package/lib/index.deno.js +2 -0
  49. package/lib/index.js +2 -0
  50. package/lib/index.node.d.ts +2 -0
  51. package/lib/index.node.js +2 -0
  52. package/lib/internal-common.d.ts +12 -0
  53. package/lib/internal-common.js +274 -0
  54. package/lib/internal.d.ts +5 -0
  55. package/lib/internal.js +42 -0
  56. package/lib/internal.node.d.ts +5 -0
  57. package/lib/internal.node.js +8 -0
  58. package/lib/native-addon.d.ts +111 -0
  59. package/lib/native-addon.js +223 -0
  60. package/lib/native-server.d.ts +21 -0
  61. package/lib/native-server.js +109 -0
  62. package/lib/native-session.d.ts +60 -0
  63. package/lib/native-session.js +565 -0
  64. package/lib/node-actor.d.ts +7 -0
  65. package/lib/node-actor.js +10 -0
  66. package/lib/node-client-common.d.ts +9 -0
  67. package/lib/node-client-common.js +35 -0
  68. package/lib/node-client.d.ts +4 -0
  69. package/lib/node-client.js +13 -0
  70. package/lib/node-direct.d.ts +7 -0
  71. package/lib/node-direct.js +10 -0
  72. package/lib/node-worker-options.d.ts +5 -0
  73. package/lib/node-worker-options.js +65 -0
  74. package/lib/node-worker-port.d.ts +4 -0
  75. package/lib/node-worker-port.js +116 -0
  76. package/lib/node-worker.d.ts +1 -0
  77. package/lib/node-worker.js +38 -0
  78. package/lib/pgwire-connection.d.ts +60 -0
  79. package/lib/pgwire-connection.js +528 -0
  80. package/lib/pgwire.d.ts +3 -0
  81. package/lib/pgwire.js +105 -0
  82. package/lib/physical-archive.d.ts +29 -0
  83. package/lib/physical-archive.js +527 -0
  84. package/lib/protocol.d.ts +1 -0
  85. package/lib/protocol.js +1 -0
  86. package/lib/public.d.ts +4 -0
  87. package/lib/public.js +3 -0
  88. package/lib/query.d.ts +1 -0
  89. package/lib/query.js +1 -0
  90. package/lib/rpc.d.ts +203 -0
  91. package/lib/rpc.js +84 -0
  92. package/lib/runtime-descriptor.d.ts +3 -0
  93. package/lib/runtime-descriptor.js +79 -0
  94. package/lib/server.node.d.ts +1 -0
  95. package/lib/server.node.js +1 -0
  96. package/lib/startup-config.d.ts +2 -0
  97. package/lib/startup-config.js +19 -0
  98. package/lib/storage/bun.d.ts +6 -0
  99. package/lib/storage/bun.js +6 -0
  100. package/lib/storage/deno.d.ts +7 -0
  101. package/lib/storage/deno.js +7 -0
  102. package/lib/storage/incremental-storage.d.ts +25 -0
  103. package/lib/storage/incremental-storage.js +154 -0
  104. package/lib/storage/indexed-db-provider.d.ts +40 -0
  105. package/lib/storage/indexed-db-provider.js +259 -0
  106. package/lib/storage/indexed-db.d.ts +9 -0
  107. package/lib/storage/indexed-db.js +11 -0
  108. package/lib/storage/node.d.ts +10 -0
  109. package/lib/storage/node.js +13 -0
  110. package/lib/storage/opfs-pool.d.ts +31 -0
  111. package/lib/storage/opfs-pool.js +1271 -0
  112. package/lib/storage/opfs-provider.d.ts +4 -0
  113. package/lib/storage/opfs-provider.js +257 -0
  114. package/lib/storage/opfs.d.ts +8 -0
  115. package/lib/storage/opfs.js +10 -0
  116. package/lib/storage/restore-cleanup.d.ts +4 -0
  117. package/lib/storage/restore-cleanup.js +22 -0
  118. package/lib/storage/web-lock.d.ts +2 -0
  119. package/lib/storage/web-lock.js +64 -0
  120. package/lib/storage-provider.d.ts +47 -0
  121. package/lib/storage-provider.js +141 -0
  122. package/lib/storage-snapshot.d.ts +44 -0
  123. package/lib/storage-snapshot.js +274 -0
  124. package/lib/storage.d.ts +46 -0
  125. package/lib/storage.js +83 -0
  126. package/lib/tool-runtime.d.ts +43 -0
  127. package/lib/tool-runtime.js +93 -0
  128. package/lib/tool-worker-common.d.ts +48 -0
  129. package/lib/tool-worker-common.js +97 -0
  130. package/lib/tool-worker.d.ts +1 -0
  131. package/lib/tool-worker.js +10 -0
  132. package/lib/types.d.ts +230 -0
  133. package/lib/types.js +1 -0
  134. package/lib/wasix-runtime.d.ts +24 -0
  135. package/lib/wasix-runtime.js +186 -0
  136. package/lib/worker-client.d.ts +4 -0
  137. package/lib/worker-client.js +45 -0
  138. package/lib/worker-dispatch.d.ts +11 -0
  139. package/lib/worker-dispatch.js +174 -0
  140. package/lib/worker-entry.bun.d.ts +2 -0
  141. package/lib/worker-entry.bun.js +2 -0
  142. package/lib/worker-entry.d.ts +2 -0
  143. package/lib/worker-entry.deno.d.ts +2 -0
  144. package/lib/worker-entry.deno.js +2 -0
  145. package/lib/worker-entry.js +2 -0
  146. package/lib/worker-entry.node.d.ts +2 -0
  147. package/lib/worker-entry.node.js +2 -0
  148. package/lib/worker-node-client.d.ts +4 -0
  149. package/lib/worker-node-client.js +55 -0
  150. package/lib/worker-rpc.d.ts +36 -0
  151. package/lib/worker-rpc.js +422 -0
  152. package/lib/worker-transfer.d.ts +6 -0
  153. package/lib/worker-transfer.js +11 -0
  154. package/lib/worker.d.ts +1 -0
  155. package/lib/worker.js +21 -0
  156. package/lib/zstd.d.ts +4 -0
  157. package/lib/zstd.js +12 -0
  158. package/node_modules/@oliphaunt/js-core/README.md +7 -0
  159. package/node_modules/@oliphaunt/js-core/dist/commonjs/protocol.d.ts +1 -0
  160. package/node_modules/@oliphaunt/js-core/dist/commonjs/protocol.js +22 -0
  161. package/node_modules/@oliphaunt/js-core/dist/commonjs/query.d.ts +255 -0
  162. package/node_modules/@oliphaunt/js-core/dist/commonjs/query.js +2068 -0
  163. package/node_modules/@oliphaunt/js-core/dist/module/package.json +3 -0
  164. package/node_modules/@oliphaunt/js-core/dist/module/protocol.d.ts +1 -0
  165. package/node_modules/@oliphaunt/js-core/dist/module/protocol.js +19 -0
  166. package/node_modules/@oliphaunt/js-core/dist/module/query.d.ts +255 -0
  167. package/node_modules/@oliphaunt/js-core/dist/module/query.js +2039 -0
  168. package/node_modules/@oliphaunt/js-core/package.json +21 -0
  169. package/package.json +122 -0
@@ -0,0 +1,565 @@
1
+ import { WasixStorageError, } from './errors.js';
2
+ import { loadNativeWasixAddon, } from './native-addon.js';
3
+ import { normalizeWasixDatabaseIdentity, } from './database.js';
4
+ import { validateWasixToolDescriptor } from './tool-runtime.js';
5
+ /** @internal A synchronous Rust Oliphaunt owned by the importing JavaScript realm. */
6
+ export class NativeWasixSession {
7
+ identity;
8
+ terminalState;
9
+ #addon;
10
+ #handle;
11
+ #runtimeVersion;
12
+ #closed = false;
13
+ #closeAttempt;
14
+ constructor(addon, handle, identity, runtimeVersion) {
15
+ this.#addon = addon;
16
+ this.#handle = handle;
17
+ this.identity = identity;
18
+ this.#runtimeVersion = runtimeVersion;
19
+ this.terminalState = new NativeHandleTerminalState(() => this.#handle.closed, 'Oliphaunt WASIX direct owner stopped unexpectedly');
20
+ }
21
+ static async open(options) {
22
+ const addon = requireCompatibleNativeWasixAddon(options);
23
+ let nativeOptions;
24
+ if (options.storage.kind === 'memory') {
25
+ nativeOptions = nativeWasixOpenOptions(options, { kind: 'memory' });
26
+ }
27
+ else if (options.storage.kind === 'directory') {
28
+ nativeOptions = nativeWasixOpenOptions(options, {
29
+ kind: 'directory',
30
+ path: options.storage.path,
31
+ });
32
+ }
33
+ else {
34
+ const provider = options.storage.kind === 'indexed-db' ? 'IndexedDB' : 'OPFS';
35
+ throw new TypeError(`@oliphaunt/wasix-ts ${provider} storage is browser-only`);
36
+ }
37
+ try {
38
+ const handle = addon.NativeWasixDatabase.open(nativeOptions);
39
+ validateDatabaseHandle(handle);
40
+ return new NativeWasixSession(addon, handle, normalizeWasixDatabaseIdentity(options.username, options.database), options.runtime.version);
41
+ }
42
+ catch (error) {
43
+ throw mapNativeError(error);
44
+ }
45
+ }
46
+ async exec(input, _persistence = 'sync') {
47
+ this.#assertOpen();
48
+ try {
49
+ return binaryView(this.#handle.execProtocolRaw(input));
50
+ }
51
+ catch (error) {
52
+ throw this.#mapFailure(error);
53
+ }
54
+ }
55
+ async execStream(input, onChunk, _persistence = 'sync') {
56
+ this.#assertOpen();
57
+ let outcome;
58
+ try {
59
+ outcome = this.#handle.execProtocolRawStream(input, (chunk) => onChunk(binaryView(chunk)));
60
+ }
61
+ catch (error) {
62
+ throw this.#mapFailure(error);
63
+ }
64
+ if (outcome === 'complete')
65
+ return outcome;
66
+ if (outcome === 'callbackAborted')
67
+ return outcome;
68
+ throw new Error(`Oliphaunt WASIX native addon returned invalid stream outcome ${outcome}`);
69
+ }
70
+ async sync(_boundary) {
71
+ this.#assertOpen();
72
+ // Rust's host-directory filesystem publishes and flushes inside each
73
+ // synchronous protocol operation. Memory storage has no host boundary.
74
+ }
75
+ async backup() {
76
+ this.#assertOpen();
77
+ try {
78
+ return binaryView(this.#handle.backup());
79
+ }
80
+ catch (error) {
81
+ throw this.#mapFailure(error);
82
+ }
83
+ }
84
+ runPgDump(options) {
85
+ return this.runTool({
86
+ runtimeVersion: '',
87
+ tool: options.tool,
88
+ args: options.args,
89
+ });
90
+ }
91
+ async runTool(options) {
92
+ this.#assertOpen();
93
+ if (options.runtimeVersion !== '' && options.runtimeVersion !== this.#runtimeVersion) {
94
+ throw new Error(`WASIX tools runtime ${options.runtimeVersion} is incompatible with database runtime ${this.#runtimeVersion}`);
95
+ }
96
+ validateWasixToolDescriptor(options.tool);
97
+ const expectedIdentity = `${options.tool.sha256}:${options.tool.size}`;
98
+ if (this.#addon.toolIdentity(options.tool.name) !== expectedIdentity) {
99
+ throw new Error(`WASIX ${options.tool.name} descriptor does not match the tool embedded in the native addon`);
100
+ }
101
+ if (options.tool.name === 'pg_dump') {
102
+ try {
103
+ return toolProcessResult(this.#handle.pgDump(userPgDumpArguments(options.args, this.identity)));
104
+ }
105
+ catch (error) {
106
+ throw this.#mapFailure(error);
107
+ }
108
+ }
109
+ const parsed = userPsqlArguments(options.args, options.stdin, this.identity);
110
+ try {
111
+ return toolProcessResult(this.#handle.psql(parsed.args, parsed.command, parsed.script));
112
+ }
113
+ catch (error) {
114
+ throw this.#mapFailure(error);
115
+ }
116
+ }
117
+ close() {
118
+ if (this.#closeAttempt !== undefined)
119
+ return this.#closeAttempt;
120
+ this.#closed = true;
121
+ this.terminalState.beginOrderlyClose();
122
+ this.#closeAttempt = this.#close();
123
+ return this.#closeAttempt;
124
+ }
125
+ async #close() {
126
+ try {
127
+ this.#handle.close();
128
+ }
129
+ catch (error) {
130
+ throw mapNativeError(error);
131
+ }
132
+ }
133
+ #assertOpen() {
134
+ if (this.#closed) {
135
+ throw new Error('Oliphaunt WASIX native session is closed');
136
+ }
137
+ this.terminalState.assertActive();
138
+ }
139
+ #mapFailure(error) {
140
+ const mapped = mapNativeError(error);
141
+ this.terminalState.recordFailure(mapped);
142
+ return mapped;
143
+ }
144
+ }
145
+ /** @internal A Rust-owner actor whose Promise completions return to the importing realm. */
146
+ export class NativeWasixActorSession {
147
+ identity;
148
+ terminalState;
149
+ #addon;
150
+ #handle;
151
+ #runtimeVersion;
152
+ #closed = false;
153
+ #closeAttempt;
154
+ constructor(addon, handle, identity, runtimeVersion) {
155
+ this.#addon = addon;
156
+ this.#handle = handle;
157
+ this.identity = identity;
158
+ this.#runtimeVersion = runtimeVersion;
159
+ this.terminalState = new NativeHandleTerminalState(() => this.#handle.closed, 'Oliphaunt WASIX actor owner stopped unexpectedly');
160
+ }
161
+ static async open(options) {
162
+ const addon = requireCompatibleNativeWasixAddon(options);
163
+ try {
164
+ const handle = await addon.NativeWasixActorDatabase.open(nativeWasixOpenOptions(options, nativeStorage(options)));
165
+ validateActorDatabaseHandle(handle);
166
+ return new NativeWasixActorSession(addon, handle, normalizeWasixDatabaseIdentity(options.username, options.database), options.runtime.version);
167
+ }
168
+ catch (error) {
169
+ throw mapNativeError(error);
170
+ }
171
+ }
172
+ async exec(input, _persistence = 'sync') {
173
+ this.#assertOpen();
174
+ try {
175
+ return binaryView(await this.#handle.execProtocolRaw(input));
176
+ }
177
+ catch (error) {
178
+ throw this.#mapFailure(error);
179
+ }
180
+ }
181
+ async execStream(input, onChunk, _persistence = 'sync') {
182
+ this.#assertOpen();
183
+ let outcome;
184
+ try {
185
+ outcome = await this.#handle.execProtocolRawStream(input, (chunk) => onChunk(binaryView(chunk)));
186
+ }
187
+ catch (error) {
188
+ throw this.#mapFailure(error);
189
+ }
190
+ if (outcome === 'complete' || outcome === 'callbackAborted')
191
+ return outcome;
192
+ throw new Error(`Oliphaunt WASIX native actor returned invalid stream outcome ${outcome}`);
193
+ }
194
+ async sync(_boundary) {
195
+ this.#assertOpen();
196
+ // Rust publishes host-directory storage at the operation boundary.
197
+ }
198
+ async backup() {
199
+ this.#assertOpen();
200
+ try {
201
+ return binaryView(await this.#handle.backup());
202
+ }
203
+ catch (error) {
204
+ throw this.#mapFailure(error);
205
+ }
206
+ }
207
+ runPgDump(options) {
208
+ return this.runTool({ runtimeVersion: '', tool: options.tool, args: options.args });
209
+ }
210
+ async runTool(options) {
211
+ this.#assertOpen();
212
+ validateNativeToolCall(this.#addon, this.#runtimeVersion, options);
213
+ try {
214
+ if (options.tool.name === 'pg_dump') {
215
+ return toolProcessResult(await this.#handle.pgDump(userPgDumpArguments(options.args, this.identity)));
216
+ }
217
+ const parsed = userPsqlArguments(options.args, options.stdin, this.identity);
218
+ return toolProcessResult(await this.#handle.psql(parsed.args, parsed.command, parsed.script));
219
+ }
220
+ catch (error) {
221
+ throw this.#mapFailure(error);
222
+ }
223
+ }
224
+ close() {
225
+ if (this.#closeAttempt !== undefined)
226
+ return this.#closeAttempt;
227
+ this.#closed = true;
228
+ this.terminalState.beginOrderlyClose();
229
+ this.#closeAttempt = this.#close();
230
+ return this.#closeAttempt;
231
+ }
232
+ async #close() {
233
+ try {
234
+ await this.#handle.close();
235
+ }
236
+ catch (error) {
237
+ throw mapNativeError(error);
238
+ }
239
+ }
240
+ #assertOpen() {
241
+ if (this.#closed) {
242
+ throw new Error('Oliphaunt WASIX native actor session is closed');
243
+ }
244
+ this.terminalState.assertActive();
245
+ }
246
+ #mapFailure(error) {
247
+ const mapped = mapNativeError(error);
248
+ this.terminalState.recordFailure(mapped);
249
+ return mapped;
250
+ }
251
+ }
252
+ /** @internal Stable observation of a native owner which can retire below TypeScript. */
253
+ class NativeHandleTerminalState {
254
+ #isClosed;
255
+ #ownerLossMessage;
256
+ #orderlyClose = false;
257
+ #failure;
258
+ constructor(isClosed, ownerLossMessage) {
259
+ this.#isClosed = isClosed;
260
+ this.#ownerLossMessage = ownerLossMessage;
261
+ }
262
+ get terminal() {
263
+ this.#observeOwnerLoss();
264
+ return this.#failure !== undefined;
265
+ }
266
+ get failure() {
267
+ return this.#failure;
268
+ }
269
+ assertActive() {
270
+ this.#observeOwnerLoss();
271
+ if (this.#failure !== undefined)
272
+ throw this.#failure;
273
+ }
274
+ beginOrderlyClose() {
275
+ this.#orderlyClose = true;
276
+ }
277
+ recordFailure(error) {
278
+ if (this.#orderlyClose || !this.#closedAfterFailure())
279
+ return;
280
+ this.#failure ??= terminalError(error, this.#ownerLossMessage);
281
+ }
282
+ #observeOwnerLoss() {
283
+ if (this.#orderlyClose || this.#failure !== undefined)
284
+ return;
285
+ try {
286
+ if (this.#isClosed())
287
+ this.#failure = new Error(this.#ownerLossMessage);
288
+ }
289
+ catch (error) {
290
+ this.#failure = terminalError(error, this.#ownerLossMessage);
291
+ }
292
+ }
293
+ #closedAfterFailure() {
294
+ try {
295
+ return this.#isClosed();
296
+ }
297
+ catch {
298
+ // A synchronous failure while observing the native owner's terminal bit
299
+ // is itself loss of that owner. Preserve the operation's original error.
300
+ return true;
301
+ }
302
+ }
303
+ }
304
+ /** @internal Restore through the synchronous Rust archive validator. */
305
+ export async function restoreNativeWasix(options, bytes) {
306
+ const addon = requireCompatibleNativeWasixAddon(options);
307
+ return restoreNativeWasixStorage(options.storage, bytes, addon);
308
+ }
309
+ /** @internal Worker-side restore after the parent has validated package config. */
310
+ export async function restoreNativeWasixStorage(storage, bytes, addon = loadNativeWasixAddon()) {
311
+ if (storage.kind !== 'directory') {
312
+ throw new TypeError('WASIX restore requires Node-compatible directory storage');
313
+ }
314
+ try {
315
+ await addon.restore(storage.path, bytes);
316
+ }
317
+ catch (error) {
318
+ throw mapNativeError(error);
319
+ }
320
+ }
321
+ /** @internal Restore synchronously inside `/direct` or its dedicated Worker realm. */
322
+ export async function restoreNativeWasixDirect(options, bytes) {
323
+ const addon = requireCompatibleNativeWasixAddon(options);
324
+ return restoreNativeWasixStorageDirect(options.storage, bytes, addon);
325
+ }
326
+ /** @internal Worker-side synchronous restore after the parent validates config. */
327
+ export async function restoreNativeWasixStorageDirect(storage, bytes, addon = loadNativeWasixAddon()) {
328
+ if (storage.kind !== 'directory') {
329
+ throw new TypeError('WASIX restore requires Node-compatible directory storage');
330
+ }
331
+ try {
332
+ addon.restoreDirect(storage.path, bytes);
333
+ }
334
+ catch (error) {
335
+ throw mapNativeError(error);
336
+ }
337
+ }
338
+ /** @internal Validate serialized package descriptors against the selected addon. */
339
+ export function requireCompatibleNativeWasixAddon(options) {
340
+ const addon = loadNativeWasixAddon();
341
+ if (options.runtime.product !== 'liboliphaunt-wasix' ||
342
+ options.runtime.version !== addon.runtimeVersion()) {
343
+ throw new Error(`WASIX runtime ${options.runtime.version} is incompatible with native runtime ${addon.runtimeVersion()}`);
344
+ }
345
+ if (options.icu !== undefined &&
346
+ options.icu.compatibility.runtimeVersion !== options.runtime.version) {
347
+ throw new Error('WASIX ICU descriptor is incompatible with the selected native runtime');
348
+ }
349
+ requireEmbeddedPayloadIdentity(addon, 'runtimeArchive', options.runtime.runtimeArchive, 'runtime archive');
350
+ requireEmbeddedPayloadIdentity(addon, 'standardSeedArchive', options.runtime.standardSeedArchive, 'standard cluster seed archive');
351
+ requireEmbeddedPayloadIdentity(addon, 'standardSeedManifest', options.runtime.standardSeedManifest, 'standard cluster seed manifest');
352
+ if (options.icu !== undefined) {
353
+ requireEmbeddedPayloadIdentity(addon, 'icuDataArchive', options.icu.dataArchive, 'ICU data archive');
354
+ requireEmbeddedPayloadIdentity(addon, 'icuSeedArchive', options.icu.clusterSeedArchive, 'ICU cluster seed archive');
355
+ requireEmbeddedPayloadIdentity(addon, 'icuSeedManifest', options.icu.clusterSeedManifest, 'ICU cluster seed manifest');
356
+ }
357
+ for (const [sqlName, carrier] of Object.entries(options.extensionCarriers)) {
358
+ if (carrier.sqlName !== sqlName) {
359
+ throw new Error(`WASIX extension carrier key ${sqlName} does not match ${carrier.sqlName}`);
360
+ }
361
+ const expectedIdentity = `${carrier.sha256}:${carrier.size}`;
362
+ if (addon.extensionIdentity(sqlName) !== expectedIdentity) {
363
+ throw new Error(`WASIX extension ${sqlName} descriptor does not match the archive embedded in the native addon`);
364
+ }
365
+ }
366
+ return addon;
367
+ }
368
+ function requireEmbeddedPayloadIdentity(addon, component, descriptor, label) {
369
+ if (addon.payloadIdentity(component) !== `${descriptor.sha256}:${descriptor.size}`) {
370
+ throw new Error(`WASIX ${label} descriptor does not match the native addon payload`);
371
+ }
372
+ }
373
+ /** @internal Project already-validated TS config onto the narrow native ABI. */
374
+ export function nativeWasixOpenOptions(options, storage) {
375
+ const identity = normalizeWasixDatabaseIdentity(options.username, options.database);
376
+ return {
377
+ profile: options.icu === undefined ? 'standard' : 'icu',
378
+ storage,
379
+ username: identity.username,
380
+ database: identity.database,
381
+ startupGucs: { ...options.startupGUCs },
382
+ extensions: [...options.extensions],
383
+ };
384
+ }
385
+ function nativeStorage(options) {
386
+ if (options.storage.kind === 'memory')
387
+ return { kind: 'memory' };
388
+ if (options.storage.kind === 'directory') {
389
+ return { kind: 'directory', path: options.storage.path };
390
+ }
391
+ const provider = options.storage.kind === 'indexed-db' ? 'IndexedDB' : 'OPFS';
392
+ throw new TypeError(`@oliphaunt/wasix-ts ${provider} storage is browser-only`);
393
+ }
394
+ function validateNativeToolCall(addon, runtimeVersion, options) {
395
+ if (options.runtimeVersion !== '' && options.runtimeVersion !== runtimeVersion) {
396
+ throw new Error(`WASIX tools runtime ${options.runtimeVersion} is incompatible with database runtime ${runtimeVersion}`);
397
+ }
398
+ validateWasixToolDescriptor(options.tool);
399
+ const expectedIdentity = `${options.tool.sha256}:${options.tool.size}`;
400
+ if (addon.toolIdentity(options.tool.name) !== expectedIdentity) {
401
+ throw new Error(`WASIX ${options.tool.name} descriptor does not match the tool embedded in the native addon`);
402
+ }
403
+ }
404
+ function validateDatabaseHandle(handle) {
405
+ if (typeof handle !== 'object' ||
406
+ handle === null ||
407
+ typeof handle.closed !== 'boolean' ||
408
+ typeof handle.execProtocolRaw !== 'function' ||
409
+ typeof handle.execProtocolRawStream !== 'function' ||
410
+ typeof handle.backup !== 'function' ||
411
+ typeof handle.pgDump !== 'function' ||
412
+ typeof handle.psql !== 'function' ||
413
+ typeof handle.close !== 'function') {
414
+ throw new Error('Oliphaunt WASIX native addon returned an invalid database handle');
415
+ }
416
+ }
417
+ function validateActorDatabaseHandle(handle) {
418
+ if (typeof handle !== 'object' ||
419
+ handle === null ||
420
+ typeof handle.closed !== 'boolean' ||
421
+ typeof handle.execProtocolRaw !== 'function' ||
422
+ typeof handle.execProtocolRawStream !== 'function' ||
423
+ typeof handle.backup !== 'function' ||
424
+ typeof handle.pgDump !== 'function' ||
425
+ typeof handle.psql !== 'function' ||
426
+ typeof handle.close !== 'function') {
427
+ throw new Error('Oliphaunt WASIX native addon returned an invalid actor database handle');
428
+ }
429
+ }
430
+ function binaryView(value) {
431
+ if (value instanceof Uint8Array)
432
+ return value;
433
+ throw new TypeError('Oliphaunt WASIX native addon returned a non-binary value');
434
+ }
435
+ function toolProcessResult(result) {
436
+ if (typeof result !== 'object' ||
437
+ result === null ||
438
+ !Number.isSafeInteger(result.status) ||
439
+ !isBinary(result.stdout) ||
440
+ !isBinary(result.stderr)) {
441
+ throw new TypeError('Oliphaunt WASIX native tool returned an invalid process result');
442
+ }
443
+ return {
444
+ exitCode: result.status,
445
+ stdout: binaryView(result.stdout),
446
+ stderr: binaryView(result.stderr),
447
+ };
448
+ }
449
+ function userPgDumpArguments(args, identity) {
450
+ const suffix = [
451
+ '--encoding=UTF8',
452
+ '--no-password',
453
+ `--username=${identity.username}`,
454
+ '--host=127.0.0.1',
455
+ '--port=65432',
456
+ `--dbname=${identity.database}`,
457
+ ];
458
+ return stripManagedSuffix('pg_dump', args, suffix);
459
+ }
460
+ function userPsqlArguments(args, stdin, identity) {
461
+ const managed = [
462
+ '--no-psqlrc',
463
+ '--no-password',
464
+ '--set=ON_ERROR_STOP=1',
465
+ `--username=${identity.username}`,
466
+ '--host=127.0.0.1',
467
+ '--port=65432',
468
+ `--dbname=${identity.database}`,
469
+ ];
470
+ const start = findExactSequence(args, managed);
471
+ if (start < 0)
472
+ throw new Error('Oliphaunt WASIX psql call has an invalid managed argument set');
473
+ const user = args.slice(0, start);
474
+ const input = args.slice(start + managed.length);
475
+ if (input.length === 0)
476
+ return { args: user };
477
+ if (input.length === 2 && input[0] === '--command' && input[1] !== undefined) {
478
+ return { args: user, command: input[1] };
479
+ }
480
+ if (input.length === 1 && input[0] === '--file=-' && stdin !== undefined) {
481
+ return {
482
+ args: user,
483
+ script: new TextDecoder('utf-8', { fatal: true }).decode(stdin),
484
+ };
485
+ }
486
+ throw new Error('Oliphaunt WASIX psql call has invalid managed input arguments');
487
+ }
488
+ function stripManagedSuffix(tool, args, suffix) {
489
+ if (args.length < suffix.length ||
490
+ !suffix.every((argument, index) => args[args.length - suffix.length + index] === argument)) {
491
+ throw new Error(`Oliphaunt WASIX ${tool} call has an invalid managed argument set`);
492
+ }
493
+ return args.slice(0, -suffix.length);
494
+ }
495
+ function findExactSequence(values, expected) {
496
+ for (let start = values.length - expected.length; start >= 0; start -= 1) {
497
+ if (expected.every((value, offset) => values[start + offset] === value))
498
+ return start;
499
+ }
500
+ return -1;
501
+ }
502
+ /** @internal Translate only the exact tagged native storage contract. */
503
+ export function mapNativeError(error) {
504
+ if (error instanceof WasixStorageError)
505
+ return error;
506
+ const details = nativeStorageError(error);
507
+ if (details === undefined)
508
+ return error;
509
+ return new WasixStorageError(describeError(error), { ...details, cause: error });
510
+ }
511
+ const STORAGE_CODES = [
512
+ 'busy',
513
+ 'corrupt',
514
+ 'incomplete',
515
+ 'incompatible',
516
+ 'publication-failed',
517
+ 'unavailable',
518
+ ];
519
+ const STORAGE_COMMIT_STATES = [
520
+ 'not-persisted',
521
+ 'persisted',
522
+ 'unchanged',
523
+ 'unknown',
524
+ ];
525
+ const STORAGE_PHASES = [
526
+ 'ownership',
527
+ 'open',
528
+ 'open-publication',
529
+ 'operation',
530
+ 'backup',
531
+ 'close',
532
+ 'restore-validation',
533
+ 'restore-staging',
534
+ 'restore-publication',
535
+ 'restore-durability',
536
+ ];
537
+ function nativeStorageError(error) {
538
+ if (typeof error !== 'object' || error === null)
539
+ return undefined;
540
+ const candidate = error;
541
+ if (candidate.oliphauntWasixError !== 'storage' ||
542
+ candidate.oliphauntWasixAddonAbi !== 1 ||
543
+ !memberOf(candidate.code, STORAGE_CODES) ||
544
+ !memberOf(candidate.commitState, STORAGE_COMMIT_STATES) ||
545
+ !memberOf(candidate.phase, STORAGE_PHASES)) {
546
+ return undefined;
547
+ }
548
+ return {
549
+ code: candidate.code,
550
+ commitState: candidate.commitState,
551
+ phase: candidate.phase,
552
+ };
553
+ }
554
+ function memberOf(value, values) {
555
+ return typeof value === 'string' && values.includes(value);
556
+ }
557
+ function isBinary(value) {
558
+ return value instanceof Uint8Array;
559
+ }
560
+ function describeError(error) {
561
+ return error instanceof Error ? error.message : String(error);
562
+ }
563
+ function terminalError(error, fallback) {
564
+ return error instanceof Error ? error : new Error(fallback, { cause: error });
565
+ }
@@ -0,0 +1,7 @@
1
+ import { NativeWasixActorSession } from './native-session.js';
2
+ import type { SerializedOpenOptions } from './rpc.js';
3
+ import type { OliphauntDatabase } from './types.js';
4
+ /** @internal Open one Rust-owner actor without blocking the importing event loop. */
5
+ export declare function openNodeActorSession(options: SerializedOpenOptions): Promise<NativeWasixActorSession>;
6
+ /** @internal Open the public database contract over the native Rust owner. */
7
+ export declare function openNodeActor(options: SerializedOpenOptions): Promise<OliphauntDatabase>;
@@ -0,0 +1,10 @@
1
+ import { WasixDatabaseImpl } from './database.js';
2
+ import { NativeWasixActorSession } from './native-session.js';
3
+ /** @internal Open one Rust-owner actor without blocking the importing event loop. */
4
+ export async function openNodeActorSession(options) {
5
+ return NativeWasixActorSession.open(options);
6
+ }
7
+ /** @internal Open the public database contract over the native Rust owner. */
8
+ export async function openNodeActor(options) {
9
+ return new WasixDatabaseImpl(await openNodeActorSession(options));
10
+ }
@@ -0,0 +1,9 @@
1
+ import type { SerializedOpenOptions } from './rpc.js';
2
+ import type { PersistentWasixStorage } from './storage.js';
3
+ import type { BinaryInput } from './types.js';
4
+ /** @internal Restore a physical archive through the Rust WASIX implementation. */
5
+ export declare function restoreNodeWasix(storage: PersistentWasixStorage, bytes: BinaryInput): Promise<void>;
6
+ /** @internal Restore synchronously in the importing realm for `/direct`. */
7
+ export declare function restoreNodeWasixDirect(storage: PersistentWasixStorage, bytes: BinaryInput): Promise<void>;
8
+ /** @internal Validate and normalize storage shared by direct and Worker entrypoints. */
9
+ export declare function requireNodeStorage(options: SerializedOpenOptions): void;
@@ -0,0 +1,35 @@
1
+ import { isAbsolute, resolve } from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { serializeOpenConfig } from './client-common.js';
4
+ import { hostRuntime } from './host-runtime.js';
5
+ import { restoreNativeWasix, restoreNativeWasixDirect } from './native-session.js';
6
+ import { toUint8Array } from './query.js';
7
+ /** @internal Restore a physical archive through the Rust WASIX implementation. */
8
+ export async function restoreNodeWasix(storage, bytes) {
9
+ const options = serializeOpenConfig({ storage });
10
+ requireNodeStorage(options);
11
+ return restoreNativeWasix(options, toUint8Array(bytes).slice());
12
+ }
13
+ /** @internal Restore synchronously in the importing realm for `/direct`. */
14
+ export async function restoreNodeWasixDirect(storage, bytes) {
15
+ const options = serializeOpenConfig({ storage });
16
+ requireNodeStorage(options);
17
+ return restoreNativeWasixDirect(options, toUint8Array(bytes).slice());
18
+ }
19
+ /** @internal Validate and normalize storage shared by direct and Worker entrypoints. */
20
+ export function requireNodeStorage(options) {
21
+ if (options.storage.kind === 'indexed-db' || options.storage.kind === 'opfs') {
22
+ const provider = options.storage.kind === 'indexed-db' ? 'IndexedDB' : 'OPFS';
23
+ throw new TypeError(`@oliphaunt/wasix-ts ${provider} storage is browser-only; use memory or the storage/${hostRuntime()} adapter`);
24
+ }
25
+ if (options.storage.kind === 'directory') {
26
+ options.storage = {
27
+ ...options.storage,
28
+ path: resolveNodeDirectoryPath(options.storage.path),
29
+ };
30
+ }
31
+ }
32
+ function resolveNodeDirectoryPath(input) {
33
+ const requested = input.startsWith('file:') ? fileURLToPath(input) : input;
34
+ return isAbsolute(requested) ? requested : resolve(requested);
35
+ }
@@ -0,0 +1,4 @@
1
+ import type { OliphauntClient, OliphauntDatabase, OpenConfig } from './types.js';
2
+ /** Open PostgreSQL on a dedicated Rust owner while keeping the caller event loop responsive. */
3
+ export declare function openWasix(config?: OpenConfig): Promise<OliphauntDatabase>;
4
+ export declare const Oliphaunt: OliphauntClient;
@@ -0,0 +1,13 @@
1
+ import { serializeOpenConfig } from './client-common.js';
2
+ import { requireNodeStorage, restoreNodeWasix } from './node-client-common.js';
3
+ import { openNodeActor } from './node-actor.js';
4
+ /** Open PostgreSQL on a dedicated Rust owner while keeping the caller event loop responsive. */
5
+ export async function openWasix(config = {}) {
6
+ const openOptions = serializeOpenConfig(config);
7
+ requireNodeStorage(openOptions);
8
+ return openNodeActor(openOptions);
9
+ }
10
+ export const Oliphaunt = {
11
+ open: openWasix,
12
+ restore: restoreNodeWasix,
13
+ };
@@ -0,0 +1,7 @@
1
+ import { NativeWasixSession } from './native-session.js';
2
+ import type { SerializedOpenOptions } from './rpc.js';
3
+ import type { OliphauntDatabase } from './types.js';
4
+ /** @internal Own one synchronous Rust WASIX session in the current JS realm. */
5
+ export declare function openNodeDirectSession(options: SerializedOpenOptions): Promise<NativeWasixSession>;
6
+ /** @internal Open the public database contract in the current Node realm. */
7
+ export declare function openNodeDirect(options: SerializedOpenOptions): Promise<OliphauntDatabase>;
@@ -0,0 +1,10 @@
1
+ import { WasixDatabaseImpl } from './database.js';
2
+ import { NativeWasixSession } from './native-session.js';
3
+ /** @internal Own one synchronous Rust WASIX session in the current JS realm. */
4
+ export async function openNodeDirectSession(options) {
5
+ return NativeWasixSession.open(options);
6
+ }
7
+ /** @internal Open the public database contract in the current Node realm. */
8
+ export async function openNodeDirect(options) {
9
+ return new WasixDatabaseImpl(await openNodeDirectSession(options));
10
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Keep runtime configuration flags while removing invocation modes which
3
+ * replace a file-backed Worker program.
4
+ */
5
+ export declare function nodeWorkerExecArgv(execArgv?: readonly string[]): string[];