@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
package/lib/rpc.d.ts ADDED
@@ -0,0 +1,203 @@
1
+ import type { WasixPersistenceMode, WasixProtocolConnectionMode, WasixProtocolStreamOutcome } from './database.js';
2
+ import { WasixStorageError } from './errors.js';
3
+ import { type PostgresErrorField } from './query.js';
4
+ import type { SerializedWasixStorage } from './storage.js';
5
+ import type { WasixStorageSyncBoundary } from './storage-provider.js';
6
+ import type { WasixProtocolConnection } from './pgwire-connection.js';
7
+ import type { WasixToolDescriptor, WasixToolProcessOptions, WasixToolProcessResult } from './tool-runtime.js';
8
+ export type SerializedAssetSource = string | Uint8Array;
9
+ export type SerializedExtensionCarrier = {
10
+ product: string;
11
+ version: string;
12
+ sqlName: string;
13
+ archive: string;
14
+ sha256: string;
15
+ size: number;
16
+ source: SerializedAssetSource;
17
+ compatibility: {
18
+ extensionRuntimeContract: string;
19
+ postgresMajor: string;
20
+ wasixRuntimeProduct: 'liboliphaunt-wasix';
21
+ wasixRuntimeVersion: string;
22
+ };
23
+ install: {
24
+ schema: 'oliphaunt-wasix-extension-install-v1';
25
+ name: string;
26
+ nativeModule: string | null;
27
+ nativeModules: {
28
+ name: string;
29
+ path: string;
30
+ sha256: string;
31
+ moduleSha256: string;
32
+ size: number;
33
+ }[];
34
+ dependencies: string[];
35
+ coreExportsRequired: string[];
36
+ loadOrder: string[];
37
+ lifecycle: {
38
+ createExtension: boolean;
39
+ createSchema?: string | null;
40
+ loadSql: string[];
41
+ postCreateSql: string[];
42
+ startupConfig: string[];
43
+ preloadRequired: boolean;
44
+ restartRequired: boolean;
45
+ sharedMemoryRequired: boolean;
46
+ };
47
+ installedFiles: string[];
48
+ unresolvedImports: {
49
+ module: string;
50
+ name: string;
51
+ kind: string;
52
+ }[];
53
+ };
54
+ };
55
+ export type SerializedRuntimeArchive = {
56
+ archive: string;
57
+ sha256: string;
58
+ size: number;
59
+ source: SerializedAssetSource;
60
+ };
61
+ export type SerializedRuntimeDescriptor = {
62
+ schema: 'oliphaunt-wasix-runtime-v2';
63
+ runtime: 'wasix';
64
+ product: 'liboliphaunt-wasix';
65
+ version: string;
66
+ runtimeArchive: SerializedRuntimeArchive;
67
+ standardSeedArchive: SerializedRuntimeArchive;
68
+ standardSeedManifest: {
69
+ sha256: string;
70
+ size: number;
71
+ source: SerializedAssetSource;
72
+ };
73
+ manifest: {
74
+ sha256: string;
75
+ size: number;
76
+ source: SerializedAssetSource;
77
+ };
78
+ };
79
+ /** Runtime subset required by PostgreSQL frontend tools. */
80
+ export type SerializedToolRuntimeDescriptor = Pick<SerializedRuntimeDescriptor, 'product' | 'version' | 'runtimeArchive' | 'manifest'>;
81
+ export type SerializedIcuDescriptor = {
82
+ schema: 'oliphaunt-wasix-icu-v1';
83
+ runtime: 'wasix';
84
+ product: 'oliphaunt-icu';
85
+ version: string;
86
+ compatibility: {
87
+ runtimeProduct: 'liboliphaunt-wasix';
88
+ runtimeVersion: string;
89
+ postgresMajor: '18';
90
+ physicalFormat: 'wasix-pg18-v1';
91
+ compatibilityKey: 'wasix-pg18-datum32-v1';
92
+ dataVersion: '76.1';
93
+ dataForm: 'files-le';
94
+ dataTreeSha256: string;
95
+ };
96
+ dataArchive: SerializedRuntimeArchive;
97
+ clusterSeedArchive: SerializedRuntimeArchive;
98
+ clusterSeedManifest: {
99
+ sha256: string;
100
+ size: number;
101
+ source: SerializedAssetSource;
102
+ };
103
+ };
104
+ /** Host-ready open options shared by both public execution surfaces. */
105
+ export type SerializedOpenOptions = {
106
+ runtime: SerializedRuntimeDescriptor;
107
+ icu?: SerializedIcuDescriptor;
108
+ /** Exact imported carrier closure, keyed by PostgreSQL SQL name. */
109
+ extensionCarriers: Record<string, SerializedExtensionCarrier>;
110
+ extensions: string[];
111
+ username: string;
112
+ database: string;
113
+ startupGUCs: Record<string, string>;
114
+ storage: SerializedWasixStorage;
115
+ };
116
+ export type WorkerRequest = {
117
+ id: number;
118
+ method: 'open';
119
+ options: SerializedOpenOptions;
120
+ } | {
121
+ id: number;
122
+ method: 'restore';
123
+ storage: SerializedWasixStorage;
124
+ bytes: Uint8Array;
125
+ } | {
126
+ id: number;
127
+ method: 'exec';
128
+ input: Uint8Array;
129
+ persistence: WasixPersistenceMode;
130
+ } | {
131
+ id: number;
132
+ method: 'execStream';
133
+ input: Uint8Array;
134
+ persistence: WasixPersistenceMode;
135
+ control: SharedArrayBuffer;
136
+ } | {
137
+ id: number;
138
+ method: 'sync';
139
+ boundary: WasixStorageSyncBoundary;
140
+ } | {
141
+ id: number;
142
+ method: 'backup';
143
+ } | {
144
+ id: number;
145
+ method: 'runPgDump';
146
+ tool: WasixToolDescriptor;
147
+ args: string[];
148
+ } | {
149
+ id: number;
150
+ method: 'runTool';
151
+ options: WasixToolProcessOptions;
152
+ } | {
153
+ id: number;
154
+ method: 'serve';
155
+ connection: WasixProtocolConnection;
156
+ mode: WasixProtocolConnectionMode;
157
+ } | {
158
+ id: number;
159
+ method: 'close';
160
+ };
161
+ export type SerializedWorkerError = {
162
+ name: 'WasixStorageError';
163
+ message: string;
164
+ code: WasixStorageError['code'];
165
+ commitState: WasixStorageError['commitState'];
166
+ phase?: NonNullable<WasixStorageError['phase']>;
167
+ } | {
168
+ name: 'OliphauntWasixToolError';
169
+ message: string;
170
+ code: 'tool-error';
171
+ tool: string;
172
+ exitCode: number | null;
173
+ stdout: Uint8Array;
174
+ stderr: Uint8Array;
175
+ } | {
176
+ name: 'PostgresError';
177
+ fields: PostgresErrorField[];
178
+ } | {
179
+ name: 'Error';
180
+ message: string;
181
+ errorName?: string;
182
+ stack?: string;
183
+ };
184
+ export type WorkerResponse = {
185
+ id: number;
186
+ kind: 'chunk';
187
+ sequence: number;
188
+ value: Uint8Array;
189
+ } | {
190
+ id: number;
191
+ ok: true;
192
+ streamOutcome: WasixProtocolStreamOutcome;
193
+ } | {
194
+ id: number;
195
+ ok: true;
196
+ value?: Uint8Array | WasixToolProcessResult;
197
+ } | {
198
+ id: number;
199
+ ok: false;
200
+ error: SerializedWorkerError;
201
+ };
202
+ export declare function serializeWorkerError(error: unknown): SerializedWorkerError;
203
+ export declare function deserializeWorkerError(error: SerializedWorkerError): Error;
package/lib/rpc.js ADDED
@@ -0,0 +1,84 @@
1
+ import { WasixStorageError } from './errors.js';
2
+ import { PostgresError } from './query.js';
3
+ export function serializeWorkerError(error) {
4
+ if (error instanceof WasixStorageError) {
5
+ return {
6
+ name: 'WasixStorageError',
7
+ message: error.message,
8
+ code: error.code,
9
+ commitState: error.commitState,
10
+ ...(error.phase === undefined ? {} : { phase: error.phase }),
11
+ };
12
+ }
13
+ if (error instanceof PostgresError) {
14
+ return {
15
+ name: 'PostgresError',
16
+ fields: error.fields.map((field) => ({ ...field })),
17
+ };
18
+ }
19
+ const nativeTool = nativeToolError(error);
20
+ if (nativeTool !== undefined) {
21
+ return {
22
+ name: 'OliphauntWasixToolError',
23
+ message: nativeTool.message,
24
+ code: 'tool-error',
25
+ tool: nativeTool.tool,
26
+ exitCode: nativeTool.exitCode,
27
+ stdout: nativeTool.stdout.slice(),
28
+ stderr: nativeTool.stderr.slice(),
29
+ };
30
+ }
31
+ const generic = {
32
+ name: 'Error',
33
+ message: error instanceof Error ? error.message : String(error),
34
+ ...(error instanceof Error && error.name !== 'Error' ? { errorName: error.name } : {}),
35
+ ...(error instanceof Error && error.stack !== undefined ? { stack: error.stack } : {}),
36
+ };
37
+ return generic;
38
+ }
39
+ export function deserializeWorkerError(error) {
40
+ if (error.name === 'WasixStorageError') {
41
+ return new WasixStorageError(error.message, {
42
+ code: error.code,
43
+ commitState: error.commitState,
44
+ ...(error.phase === undefined ? {} : { phase: error.phase }),
45
+ });
46
+ }
47
+ if (error.name === 'PostgresError') {
48
+ return new PostgresError(error.fields);
49
+ }
50
+ if (error.name === 'OliphauntWasixToolError') {
51
+ const restored = new Error(error.message);
52
+ restored.name = error.name;
53
+ return Object.assign(restored, {
54
+ oliphauntWasixError: 'tool',
55
+ oliphauntWasixAddonAbi: 1,
56
+ code: error.code,
57
+ tool: error.tool,
58
+ exitCode: error.exitCode,
59
+ stdout: error.stdout.slice(),
60
+ stderr: error.stderr.slice(),
61
+ });
62
+ }
63
+ const restored = new Error(error.message);
64
+ restored.name = error.errorName ?? 'Error';
65
+ if (error.stack !== undefined)
66
+ restored.stack = error.stack;
67
+ return restored;
68
+ }
69
+ function nativeToolError(error) {
70
+ if (typeof error !== 'object' || error === null)
71
+ return undefined;
72
+ const candidate = error;
73
+ if (candidate.oliphauntWasixError !== 'tool' ||
74
+ candidate.oliphauntWasixAddonAbi !== 1 ||
75
+ candidate.code !== 'tool-error' ||
76
+ typeof candidate.message !== 'string' ||
77
+ typeof candidate.tool !== 'string' ||
78
+ (candidate.exitCode !== null && !Number.isSafeInteger(candidate.exitCode)) ||
79
+ !(candidate.stdout instanceof Uint8Array) ||
80
+ !(candidate.stderr instanceof Uint8Array)) {
81
+ return undefined;
82
+ }
83
+ return candidate;
84
+ }
@@ -0,0 +1,3 @@
1
+ import type { SerializedRuntimeDescriptor } from './rpc.js';
2
+ /** Validate an imported runtime carrier before any bytes cross the worker boundary. */
3
+ export declare function serializeWasixRuntimeDescriptor(value: unknown): SerializedRuntimeDescriptor;
@@ -0,0 +1,79 @@
1
+ import { requireAssetSource, requireExactObject, requireSafeRelativeAssetPath, requireSha256, requireSize, requireVersion, serializeAssetSource, } from './descriptor-validation.js';
2
+ const DESCRIPTOR_FIELDS = [
3
+ 'manifest',
4
+ 'product',
5
+ 'runtime',
6
+ 'runtimeArchive',
7
+ 'schema',
8
+ 'standardSeedArchive',
9
+ 'standardSeedManifest',
10
+ 'version',
11
+ ];
12
+ const ARCHIVE_FIELDS = ['archive', 'sha256', 'size', 'source'];
13
+ const MANIFEST_FIELDS = ['sha256', 'size', 'source'];
14
+ /** Validate an imported runtime carrier before any bytes cross the worker boundary. */
15
+ export function serializeWasixRuntimeDescriptor(value) {
16
+ validateRuntimeDescriptor(value, 'WASIX runtime descriptor');
17
+ return {
18
+ schema: value.schema,
19
+ runtime: value.runtime,
20
+ product: value.product,
21
+ version: value.version,
22
+ runtimeArchive: serializeArchive(value.runtimeArchive),
23
+ standardSeedArchive: serializeArchive(value.standardSeedArchive),
24
+ standardSeedManifest: serializeManifest(value.standardSeedManifest),
25
+ manifest: {
26
+ sha256: value.manifest.sha256,
27
+ size: value.manifest.size,
28
+ source: serializeAssetSource(value.manifest.source),
29
+ },
30
+ };
31
+ }
32
+ function validateRuntimeDescriptor(value, label) {
33
+ const descriptor = requireExactObject(value, DESCRIPTOR_FIELDS, label);
34
+ if (descriptor.schema !== 'oliphaunt-wasix-runtime-v2') {
35
+ throw new Error(`${label} has unsupported schema`);
36
+ }
37
+ if (descriptor.runtime !== 'wasix') {
38
+ throw new Error(`${label} must target runtime 'wasix'`);
39
+ }
40
+ if (descriptor.product !== 'liboliphaunt-wasix') {
41
+ throw new Error(`${label} product must be 'liboliphaunt-wasix'`);
42
+ }
43
+ requireVersion(descriptor.version, `${label} version`);
44
+ validateArchive(descriptor.runtimeArchive, `${label} runtime archive`);
45
+ validateArchive(descriptor.standardSeedArchive, `${label} standard cluster seed archive`);
46
+ validateManifest(descriptor.standardSeedManifest, `${label} standard cluster seed manifest`);
47
+ validateManifest(descriptor.manifest, `${label} manifest`);
48
+ if (descriptor.runtimeArchive.archive === descriptor.standardSeedArchive.archive) {
49
+ throw new Error(`${label} runtime and standard cluster seed archives must have distinct paths`);
50
+ }
51
+ }
52
+ function validateArchive(value, label) {
53
+ const archive = requireExactObject(value, ARCHIVE_FIELDS, label);
54
+ requireSafeRelativeAssetPath(archive.archive, `${label} path`);
55
+ requireSha256(archive.sha256, `${label} SHA-256`);
56
+ const size = requireSize(archive.size, `${label} size`);
57
+ requireAssetSource(archive.source, `${label} source`, size);
58
+ }
59
+ function validateManifest(value, label) {
60
+ const manifest = requireExactObject(value, MANIFEST_FIELDS, label);
61
+ requireSha256(manifest.sha256, `${label} SHA-256`);
62
+ const size = requireSize(manifest.size, `${label} size`);
63
+ requireAssetSource(manifest.source, `${label} source`, size);
64
+ }
65
+ function serializeArchive(archive) {
66
+ return {
67
+ archive: archive.archive,
68
+ sha256: archive.sha256,
69
+ size: archive.size,
70
+ source: serializeAssetSource(archive.source),
71
+ };
72
+ }
73
+ function serializeManifest(manifest) {
74
+ return {
75
+ sha256: manifest.sha256,
76
+ size: manifest.size,
77
+ source: serializeAssetSource(manifest.source),
78
+ };
79
+ }
@@ -0,0 +1 @@
1
+ export { type OliphauntServer, openServer, type ServerListen, type ServerOpenConfig, } from './native-server.js';
@@ -0,0 +1 @@
1
+ export { openServer, } from './native-server.js';
@@ -0,0 +1,2 @@
1
+ /** @internal Canonicalize PostgreSQL's case-insensitive startup-GUC namespace. */
2
+ export declare function normalizeWasixStartupGUCs(gucs: Readonly<Record<string, string>>): Record<string, string>;
@@ -0,0 +1,19 @@
1
+ const STORAGE_OWNED_GUCS = new Set(['config_file', 'data_directory']);
2
+ /** @internal Canonicalize PostgreSQL's case-insensitive startup-GUC namespace. */
3
+ export function normalizeWasixStartupGUCs(gucs) {
4
+ const normalized = {};
5
+ for (const [providedName, value] of Object.entries(gucs)) {
6
+ const name = providedName.trim().toLowerCase();
7
+ if (!/^[a-z_][a-z0-9_$]*(?:\.[a-z_][a-z0-9_$]*)*$/u.test(name)) {
8
+ throw new Error(`PostgreSQL startup GUC name ${JSON.stringify(providedName)} must use dot-separated components that start with an ASCII letter or '_' and continue with ASCII letters, digits, '_', or '$'`);
9
+ }
10
+ if (value.includes('\0')) {
11
+ throw new Error(`PostgreSQL startup GUC ${JSON.stringify(name)} contains a NUL byte`);
12
+ }
13
+ if (STORAGE_OWNED_GUCS.has(name)) {
14
+ throw new Error(`@oliphaunt/wasix-ts owns PostgreSQL startup GUC ${JSON.stringify(name)}; configure the database through Oliphaunt's storage API`);
15
+ }
16
+ normalized[name] = value;
17
+ }
18
+ return normalized;
19
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Select native Rust-owned directory storage for Bun.
3
+ *
4
+ * Bun uses the same managed-root format and OS advisory lock as Node.js.
5
+ */
6
+ export { directory } from './node.js';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Select native Rust-owned directory storage for Bun.
3
+ *
4
+ * Bun uses the same managed-root format and OS advisory lock as Node.js.
5
+ */
6
+ export { directory } from './node.js';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Select native Rust-owned directory storage for Deno.
3
+ *
4
+ * Deno uses the same managed-root format and OS advisory lock as Node.js. The
5
+ * application must grant read and write permissions for the selected directory.
6
+ */
7
+ export { directory } from './node.js';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Select native Rust-owned directory storage for Deno.
3
+ *
4
+ * Deno uses the same managed-root format and OS advisory lock as Node.js. The
5
+ * application must grant read and write permissions for the selected directory.
6
+ */
7
+ export { directory } from './node.js';
@@ -0,0 +1,25 @@
1
+ import type { WasixStorageCommitState } from '../errors.js';
2
+ import type { WasixClusterSeedLoader, WasixStorageLease } from '../storage-provider.js';
3
+ import { type StorageDelta, type StoredSnapshot } from '../storage-snapshot.js';
4
+ export type ExclusiveStorageLock = {
5
+ release(): Promise<void>;
6
+ };
7
+ export type IncrementalStorageStore = {
8
+ read(): Promise<StoredSnapshot | undefined>;
9
+ /** Override snapshot presence when a direct provider has incomplete setup state. */
10
+ initializationState?(snapshot: StoredSnapshot | undefined): 'new' | 'existing';
11
+ apply(delta: StorageDelta): Promise<void>;
12
+ close(): void | Promise<void>;
13
+ };
14
+ export type IncrementalStorageBackend = {
15
+ acquireLock(): Promise<ExclusiveStorageLock>;
16
+ openStore(): Promise<IncrementalStorageStore>;
17
+ /** Atomic stores retain `not-persisted`; filesystem stores report `unknown`. */
18
+ writeFailureCommitState?: WasixStorageCommitState;
19
+ };
20
+ /**
21
+ * Shared lifecycle for browser stores backed by the Wasmer mutation journal.
22
+ * Providers own their atomicity and validation; this layer owns delta capture,
23
+ * cleanup ordering, and consistent failure semantics.
24
+ */
25
+ export declare function acquireIncrementalStorage(label: string, loadClusterSeed: WasixClusterSeedLoader, backend: IncrementalStorageBackend): Promise<WasixStorageLease>;
@@ -0,0 +1,154 @@
1
+ import { composeWasixStorageFailure, WasixStorageError } from '../errors.js';
2
+ import { applyStorageDeltaToEntries, snapshotStorageDelta, snapshotToMount, } from '../storage-snapshot.js';
3
+ /**
4
+ * Shared lifecycle for browser stores backed by the Wasmer mutation journal.
5
+ * Providers own their atomicity and validation; this layer owns delta capture,
6
+ * cleanup ordering, and consistent failure semantics.
7
+ */
8
+ export async function acquireIncrementalStorage(label, loadClusterSeed, backend) {
9
+ const lock = await backend.acquireLock();
10
+ let store;
11
+ try {
12
+ store = await backend.openStore();
13
+ const snapshot = await store.read();
14
+ const state = store.initializationState?.(snapshot) ?? (snapshot === undefined ? 'new' : 'existing');
15
+ const mount = snapshot === undefined ? await loadClusterSeed() : snapshotToMount(snapshot);
16
+ return new IncrementalStorageLease(label, store, lock, mount, snapshot !== undefined, state, backend.writeFailureCommitState ?? 'not-persisted');
17
+ }
18
+ catch (error) {
19
+ let primary = error instanceof WasixStorageError
20
+ ? error
21
+ : new WasixStorageError(`could not open ${label}: ${describeError(error)}`, {
22
+ code: 'unavailable',
23
+ commitState: 'unchanged',
24
+ cause: error,
25
+ });
26
+ try {
27
+ await store?.close();
28
+ }
29
+ catch (closeError) {
30
+ primary = composeWasixStorageFailure(primary, 'storage connection cleanup also failed', closeError);
31
+ }
32
+ try {
33
+ await lock.release();
34
+ }
35
+ catch (releaseError) {
36
+ throw composeWasixStorageFailure(primary, 'ownership release also failed', releaseError);
37
+ }
38
+ throw primary;
39
+ }
40
+ }
41
+ class IncrementalStorageLease {
42
+ state;
43
+ mount;
44
+ #label;
45
+ #store;
46
+ #lock;
47
+ #persistedEntries = new Map();
48
+ #writeFailureCommitState;
49
+ #closed = false;
50
+ #hasStoredGeneration;
51
+ #initializationPending;
52
+ constructor(label, store, lock, mount, hasStoredGeneration, state, writeFailureCommitState) {
53
+ this.#label = label;
54
+ this.#store = store;
55
+ this.#lock = lock;
56
+ this.state = state;
57
+ this.mount = mount;
58
+ this.#hasStoredGeneration = hasStoredGeneration;
59
+ this.#initializationPending = state === 'new';
60
+ this.#writeFailureCommitState = writeFailureCommitState;
61
+ if (hasStoredGeneration) {
62
+ for (const path of mount.directories)
63
+ this.#persistedEntries.set(path, 'dir');
64
+ for (const path of Object.keys(mount.files))
65
+ this.#persistedEntries.set(path, 'file');
66
+ }
67
+ }
68
+ async sync(directory, _boundary) {
69
+ if (this.#closed) {
70
+ throw new WasixStorageError(`${this.#label} is closed`, {
71
+ code: 'unavailable',
72
+ commitState: 'unchanged',
73
+ });
74
+ }
75
+ let failureCommitState = this.#writeFailureCommitState;
76
+ try {
77
+ const delta = await snapshotStorageDelta(directory, this.#persistedEntries, !this.#hasStoredGeneration);
78
+ if (!this.#initializationPending &&
79
+ this.#hasStoredGeneration &&
80
+ delta.directories.length === 0 &&
81
+ delta.files.length === 0 &&
82
+ delta.deleted.length === 0) {
83
+ failureCommitState = 'unchanged';
84
+ await directory.clearChanges?.();
85
+ return;
86
+ }
87
+ await this.#store.apply(delta);
88
+ applyStorageDeltaToEntries(this.#persistedEntries, delta);
89
+ this.#hasStoredGeneration = true;
90
+ this.#initializationPending = false;
91
+ failureCommitState = 'persisted';
92
+ // The backend commit is the acknowledgement point. Keeping the journal
93
+ // intact until now makes a failed publication retryable on close and
94
+ // prevents successful paths from being republished on every operation.
95
+ await directory.clearChanges?.();
96
+ }
97
+ catch (error) {
98
+ if (error instanceof WasixStorageError)
99
+ throw error;
100
+ throw new WasixStorageError(`could not persist ${this.#label}: ${describeError(error)}`, {
101
+ code: 'publication-failed',
102
+ commitState: failureCommitState,
103
+ cause: error,
104
+ });
105
+ }
106
+ }
107
+ async close(directory, outcome) {
108
+ if (this.#closed)
109
+ return;
110
+ let failure;
111
+ try {
112
+ if (outcome === 'clean') {
113
+ if (directory === undefined) {
114
+ throw new WasixStorageError(`cannot persist ${this.#label} without PGDATA`, {
115
+ code: 'publication-failed',
116
+ commitState: 'not-persisted',
117
+ });
118
+ }
119
+ await this.sync(directory, 'close');
120
+ }
121
+ }
122
+ catch (error) {
123
+ failure = error;
124
+ }
125
+ finally {
126
+ this.#closed = true;
127
+ try {
128
+ await this.#store.close();
129
+ }
130
+ catch (error) {
131
+ failure = combineCleanupFailure(failure, `${this.#label} could not close its storage connection`, 'storage connection cleanup also failed', error, this.#hasStoredGeneration);
132
+ }
133
+ try {
134
+ await this.#lock.release();
135
+ }
136
+ catch (error) {
137
+ failure = combineCleanupFailure(failure, `${this.#label} closed but its ownership lock could not be released`, 'ownership release also failed', error, this.#hasStoredGeneration);
138
+ }
139
+ }
140
+ if (failure !== undefined)
141
+ throw failure;
142
+ }
143
+ }
144
+ function combineCleanupFailure(prior, message, detail, cause, persisted) {
145
+ const cleanup = new WasixStorageError(message, {
146
+ code: 'unavailable',
147
+ commitState: persisted ? 'persisted' : 'unchanged',
148
+ cause,
149
+ });
150
+ return prior instanceof Error ? composeWasixStorageFailure(prior, detail, cleanup) : cleanup;
151
+ }
152
+ function describeError(error) {
153
+ return error instanceof Error ? error.message : String(error);
154
+ }
@@ -0,0 +1,40 @@
1
+ import { type WasixClusterSeedLoader, type WasixPhysicalIdentity, type WasixStorageLease } from '../storage-provider.js';
2
+ import { type StorageDelta, type StoredSnapshot } from '../storage-snapshot.js';
3
+ import { type ExclusiveStorageLock } from './incremental-storage.js';
4
+ type StoredMetadata = {
5
+ schema: 'oliphaunt-wasix-indexed-db-v1';
6
+ name: string;
7
+ physicalIdentity: WasixPhysicalIdentity;
8
+ };
9
+ type StoredEntry = {
10
+ path: string;
11
+ type: 'dir';
12
+ } | {
13
+ path: string;
14
+ type: 'file';
15
+ bytes: Uint8Array;
16
+ };
17
+ export type StoredDatabase = StoredMetadata & {
18
+ entries: StoredEntry[];
19
+ };
20
+ export type HeldLock = ExclusiveStorageLock;
21
+ export type StoredDatabaseStore = {
22
+ read(): Promise<unknown | undefined>;
23
+ apply(identity: WasixPhysicalIdentity, delta: StorageDelta): Promise<void>;
24
+ close(): void;
25
+ };
26
+ export type IndexedDbStorageBackend = {
27
+ acquireLock(name: string): Promise<HeldLock>;
28
+ openDatabase(name: string): Promise<StoredDatabaseStore>;
29
+ };
30
+ export declare function acquireIndexedDbStorage(name: string, loadClusterSeed: WasixClusterSeedLoader, identity: WasixPhysicalIdentity): Promise<WasixStorageLease>;
31
+ export declare function restoreIndexedDbStorage(name: string, snapshot: StoredSnapshot, identity: WasixPhysicalIdentity): Promise<void>;
32
+ /** @internal Narrow dependency seam for deterministic provider failure tests. */
33
+ export declare function acquireIndexedDbStorageWithBackend(name: string, loadClusterSeed: WasixClusterSeedLoader, identity: WasixPhysicalIdentity, backend: IndexedDbStorageBackend): Promise<WasixStorageLease>;
34
+ /** @internal Validate IndexedDB rows before any bytes reach Wasmer. */
35
+ export declare function validateStoredDatabase(value: unknown, name: string, identity: WasixPhysicalIdentity): StoredSnapshot;
36
+ /** @internal Stable physical IndexedDB name for one logical Oliphaunt database. */
37
+ export declare function indexedDbDatabaseName(name: string): string;
38
+ export declare function readStoredDatabase(database: IDBDatabase, name: string): Promise<StoredDatabase | undefined>;
39
+ export declare function applyStoredDatabaseDelta(database: IDBDatabase, name: string, identity: WasixPhysicalIdentity, delta: StorageDelta): Promise<void>;
40
+ export {};