@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,4 @@
1
+ import type { OliphauntClient, OliphauntDatabase, OpenConfig } from './types.js';
2
+ /** Open PostgreSQL in the importing realm, where native work blocks its event loop. */
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, restoreNodeWasixDirect } from './node-client-common.js';
3
+ import { openNodeDirect } from './node-direct.js';
4
+ /** Open PostgreSQL in the importing realm, where native work blocks its event loop. */
5
+ export async function openWasix(config = {}) {
6
+ const openOptions = serializeOpenConfig(config);
7
+ requireNodeStorage(openOptions);
8
+ return openNodeDirect(openOptions);
9
+ }
10
+ export const Oliphaunt = {
11
+ open: openWasix,
12
+ restore: restoreNodeWasixDirect,
13
+ };
@@ -0,0 +1,2 @@
1
+ export { Oliphaunt, Oliphaunt as default } from './direct-client.js';
2
+ export * from './public.js';
@@ -0,0 +1,2 @@
1
+ export { Oliphaunt, Oliphaunt as default } from './direct-client.js';
2
+ export * from './public.js';
@@ -0,0 +1,25 @@
1
+ export type WasixStorageErrorCode = 'busy' | 'corrupt' | 'incomplete' | 'incompatible' | 'publication-failed' | 'unavailable';
2
+ /** What is known about the latest storage generation after an operation fails. */
3
+ export type WasixStorageCommitState = 'not-persisted' | 'persisted' | 'unchanged' | 'unknown';
4
+ /** Native persistence boundary at which a storage failure was observed. */
5
+ export type WasixStoragePhase = 'ownership' | 'open' | 'open-publication' | 'operation' | 'backup' | 'close' | 'restore-validation' | 'restore-staging' | 'restore-publication' | 'restore-durability';
6
+ /**
7
+ * A persistence or ownership failure, kept distinct from PostgreSQL's
8
+ * `PostgresError`. `code` is suitable for branching, `phase` identifies the
9
+ * native persistence boundary, and `commitState` describes only what is known
10
+ * about the stored generation. None of these fields by itself makes retrying
11
+ * an application transaction safe.
12
+ */
13
+ export declare class WasixStorageError extends Error {
14
+ readonly code: WasixStorageErrorCode;
15
+ readonly commitState: WasixStorageCommitState;
16
+ readonly phase: WasixStoragePhase | undefined;
17
+ constructor(message: string, options: Readonly<{
18
+ code: WasixStorageErrorCode;
19
+ commitState: WasixStorageCommitState;
20
+ phase?: WasixStoragePhase;
21
+ cause?: unknown;
22
+ }>);
23
+ }
24
+ /** @internal Preserve storage classification while retaining cleanup diagnostics. */
25
+ export declare function composeWasixStorageFailure(primary: Error, label: string, secondary: unknown): Error;
package/lib/errors.js ADDED
@@ -0,0 +1,34 @@
1
+ /**
2
+ * A persistence or ownership failure, kept distinct from PostgreSQL's
3
+ * `PostgresError`. `code` is suitable for branching, `phase` identifies the
4
+ * native persistence boundary, and `commitState` describes only what is known
5
+ * about the stored generation. None of these fields by itself makes retrying
6
+ * an application transaction safe.
7
+ */
8
+ export class WasixStorageError extends Error {
9
+ code;
10
+ commitState;
11
+ phase;
12
+ constructor(message, options) {
13
+ super(message, options.cause === undefined ? undefined : { cause: options.cause });
14
+ this.name = 'WasixStorageError';
15
+ this.code = options.code;
16
+ this.commitState = options.commitState;
17
+ this.phase = options.phase;
18
+ }
19
+ }
20
+ /** @internal Preserve storage classification while retaining cleanup diagnostics. */
21
+ export function composeWasixStorageFailure(primary, label, secondary) {
22
+ const detail = secondary instanceof Error ? secondary.message : String(secondary);
23
+ const message = `${primary.message}; ${label}: ${detail}`;
24
+ const cause = new AggregateError([primary, secondary], label);
25
+ if (primary instanceof WasixStorageError) {
26
+ return new WasixStorageError(message, {
27
+ code: primary.code,
28
+ commitState: primary.commitState,
29
+ ...(primary.phase === undefined ? {} : { phase: primary.phase }),
30
+ cause,
31
+ });
32
+ }
33
+ return new Error(message, { cause });
34
+ }
@@ -0,0 +1,14 @@
1
+ import type { SerializedExtensionCarrier } from './rpc.js';
2
+ import type { WasixExtensionDescriptor, WasixExtensionDescriptorInput } from './types.js';
3
+ export type SerializedWasixExtensions = {
4
+ selectedSqlNames: string[];
5
+ carriers: Record<string, SerializedExtensionCarrier>;
6
+ };
7
+ /** Optional package-author helper that validates and deeply freezes a descriptor. */
8
+ export declare function defineWasixExtension(input: WasixExtensionDescriptorInput): WasixExtensionDescriptor;
9
+ /**
10
+ * Validates imported package values and converts roots plus carrier closures to
11
+ * the worker wire shape. Imported carriers own their exact install metadata;
12
+ * the core runtime manifest deliberately owns no extension rows.
13
+ */
14
+ export declare function serializeWasixExtensionDescriptors(descriptors: readonly WasixExtensionDescriptor[]): SerializedWasixExtensions;
@@ -0,0 +1,419 @@
1
+ import { requireAssetSource, requireExactObject, requireSafeRelativeAssetPath, requireSha256, requireSize, requireString, requireVersion, serializeAssetSource, } from './descriptor-validation.js';
2
+ const DESCRIPTOR_FIELDS = [
3
+ 'carriers',
4
+ 'compatibility',
5
+ 'product',
6
+ 'runtime',
7
+ 'schema',
8
+ 'sqlName',
9
+ 'version',
10
+ ];
11
+ const COMPATIBILITY_FIELDS = [
12
+ 'extensionRuntimeContract',
13
+ 'postgresMajor',
14
+ 'wasixRuntimeProduct',
15
+ 'wasixRuntimeVersion',
16
+ ];
17
+ const CARRIER_FIELDS = [
18
+ 'archive',
19
+ 'install',
20
+ 'product',
21
+ 'sha256',
22
+ 'size',
23
+ 'source',
24
+ 'sqlName',
25
+ 'version',
26
+ ];
27
+ const INSTALL_FIELDS = [
28
+ 'coreExportsRequired',
29
+ 'dependencies',
30
+ 'installedFiles',
31
+ 'lifecycle',
32
+ 'loadOrder',
33
+ 'name',
34
+ 'nativeModule',
35
+ 'nativeModules',
36
+ 'schema',
37
+ 'unresolvedImports',
38
+ ];
39
+ const NATIVE_MODULE_FIELDS = ['moduleSha256', 'name', 'path', 'sha256', 'size'];
40
+ const IMPORT_FIELDS = ['kind', 'module', 'name'];
41
+ const LIFECYCLE_FIELDS = [
42
+ 'createExtension',
43
+ 'createSchema',
44
+ 'loadSql',
45
+ 'postCreateSql',
46
+ 'preloadRequired',
47
+ 'restartRequired',
48
+ 'sharedMemoryRequired',
49
+ 'startupConfig',
50
+ ];
51
+ const PRODUCT = /^oliphaunt-extension-[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
52
+ const SQL_NAME = /^[a-z0-9][a-z0-9_-]*$/;
53
+ const POSTGRES_MAJOR = /^(?:[1-9]\d*)$/;
54
+ const EXTENSION_RUNTIME_CONTRACT = 'oliphaunt-extension-runtime-contract-v1';
55
+ /** Optional package-author helper that validates and deeply freezes a descriptor. */
56
+ export function defineWasixExtension(input) {
57
+ validateDescriptor(input, 'WASIX extension descriptor');
58
+ return Object.freeze({
59
+ ...input,
60
+ compatibility: Object.freeze({ ...input.compatibility }),
61
+ carriers: Object.freeze(input.carriers.map(freezeCarrier)),
62
+ });
63
+ }
64
+ /**
65
+ * Validates imported package values and converts roots plus carrier closures to
66
+ * the worker wire shape. Imported carriers own their exact install metadata;
67
+ * the core runtime manifest deliberately owns no extension rows.
68
+ */
69
+ export function serializeWasixExtensionDescriptors(descriptors) {
70
+ if (!Array.isArray(descriptors)) {
71
+ throw new Error('WASIX extensions must be an array of imported extension descriptors');
72
+ }
73
+ const selectedSqlNames = [];
74
+ const rootSqlNames = new Set();
75
+ const carrierBySqlName = new Map();
76
+ const sqlNameByArchive = new Map();
77
+ for (const [index, descriptor] of descriptors.entries()) {
78
+ const label = `WASIX extension descriptor ${index}`;
79
+ validateDescriptor(descriptor, label);
80
+ if (rootSqlNames.has(descriptor.sqlName)) {
81
+ throw new Error(`WASIX extension descriptors repeat root SQL name '${descriptor.sqlName}'`);
82
+ }
83
+ rootSqlNames.add(descriptor.sqlName);
84
+ selectedSqlNames.push(descriptor.sqlName);
85
+ for (const carrier of descriptor.carriers) {
86
+ const archiveOwner = sqlNameByArchive.get(carrier.archive);
87
+ if (archiveOwner !== undefined && archiveOwner !== carrier.sqlName) {
88
+ throw new Error(`WASIX extension carriers '${archiveOwner}' and '${carrier.sqlName}' conflict on archive '${carrier.archive}'`);
89
+ }
90
+ sqlNameByArchive.set(carrier.archive, carrier.sqlName);
91
+ const existing = carrierBySqlName.get(carrier.sqlName);
92
+ if (existing === undefined) {
93
+ carrierBySqlName.set(carrier.sqlName, {
94
+ carrier,
95
+ compatibility: descriptor.compatibility,
96
+ });
97
+ }
98
+ else if (!sameCarrierIdentity(existing.carrier, carrier) ||
99
+ !structurallyEqual(existing.compatibility, descriptor.compatibility)) {
100
+ throw new Error(`WASIX extension carrier '${carrier.sqlName}' has conflicting identity, install, or compatibility metadata`);
101
+ }
102
+ }
103
+ }
104
+ return {
105
+ selectedSqlNames: selectedSqlNames.sort(),
106
+ carriers: Object.fromEntries([...carrierBySqlName]
107
+ .sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))
108
+ .map(([sqlName, { carrier, compatibility }]) => [
109
+ sqlName,
110
+ serializeCarrier(carrier, compatibility),
111
+ ])),
112
+ };
113
+ }
114
+ function validateDescriptor(value, label) {
115
+ const descriptor = requireExactObject(value, DESCRIPTOR_FIELDS, label);
116
+ if (descriptor.schema !== 'oliphaunt-wasix-extension-v1') {
117
+ throw new Error(`${label} has unsupported schema`);
118
+ }
119
+ if (descriptor.runtime !== 'wasix') {
120
+ throw new Error(`${label} must target runtime 'wasix'`);
121
+ }
122
+ const product = requireProduct(descriptor.product, `${label} product`);
123
+ const version = requireVersion(descriptor.version, `${label} version`);
124
+ const sqlName = requireSqlName(descriptor.sqlName, `${label} SQL name`);
125
+ validateCompatibility(descriptor.compatibility, `${label} compatibility`);
126
+ if (!Array.isArray(descriptor.carriers) || descriptor.carriers.length === 0) {
127
+ throw new Error(`${label} carriers must be a non-empty array`);
128
+ }
129
+ let root;
130
+ const carrierSqlNames = new Set();
131
+ const carrierSqlNameByArchive = new Map();
132
+ const carrierBySqlName = new Map();
133
+ for (const [index, carrier] of descriptor.carriers.entries()) {
134
+ validateCarrier(carrier, `${label} carrier ${index}`);
135
+ if (carrierSqlNames.has(carrier.sqlName)) {
136
+ throw new Error(`${label} repeats carrier SQL name '${carrier.sqlName}'`);
137
+ }
138
+ carrierSqlNames.add(carrier.sqlName);
139
+ carrierBySqlName.set(carrier.sqlName, carrier);
140
+ const archiveOwner = carrierSqlNameByArchive.get(carrier.archive);
141
+ if (archiveOwner !== undefined) {
142
+ throw new Error(`${label} carriers '${archiveOwner}' and '${carrier.sqlName}' conflict on archive '${carrier.archive}'`);
143
+ }
144
+ carrierSqlNameByArchive.set(carrier.archive, carrier.sqlName);
145
+ if (carrier.sqlName === sqlName) {
146
+ root = carrier;
147
+ }
148
+ }
149
+ if (root === undefined) {
150
+ throw new Error(`${label} carriers do not contain root SQL name '${sqlName}'`);
151
+ }
152
+ if (root.product !== product || root.version !== version) {
153
+ throw new Error(`${label} product and version must match its root carrier '${sqlName}'`);
154
+ }
155
+ validateExactDescriptorClosure(carrierBySqlName, sqlName, label);
156
+ }
157
+ function validateCompatibility(value, label) {
158
+ const compatibility = requireExactObject(value, COMPATIBILITY_FIELDS, label);
159
+ if (compatibility.extensionRuntimeContract !== EXTENSION_RUNTIME_CONTRACT) {
160
+ throw new Error(`${label} has an unsupported extension runtime contract`);
161
+ }
162
+ const postgresMajor = requireString(compatibility.postgresMajor, `${label} PostgreSQL major`);
163
+ if (!POSTGRES_MAJOR.test(postgresMajor)) {
164
+ throw new Error(`${label} PostgreSQL major must be a positive integer string`);
165
+ }
166
+ if (compatibility.wasixRuntimeProduct !== 'liboliphaunt-wasix') {
167
+ throw new Error(`${label} WASIX runtime product must be 'liboliphaunt-wasix'`);
168
+ }
169
+ requireVersion(compatibility.wasixRuntimeVersion, `${label} WASIX runtime version`);
170
+ }
171
+ function validateCarrier(value, label) {
172
+ const carrier = requireExactObject(value, CARRIER_FIELDS, label);
173
+ requireProduct(carrier.product, `${label} product`);
174
+ requireVersion(carrier.version, `${label} version`);
175
+ const sqlName = requireSqlName(carrier.sqlName, `${label} SQL name`);
176
+ const archive = requireExtensionArchive(carrier.archive, `${label} archive`);
177
+ if (archive !== `extensions/${sqlName}.tar.zst`) {
178
+ throw new Error(`${label} archive must be extensions/${sqlName}.tar.zst`);
179
+ }
180
+ requireSha256(carrier.sha256, `${label} SHA-256`);
181
+ const size = requireSize(carrier.size, `${label} size`);
182
+ requireAssetSource(carrier.source, `${label} source`, size, 'carrier');
183
+ validateInstall(carrier.install, `${label} install`, sqlName);
184
+ }
185
+ function validateInstall(value, label, sqlName) {
186
+ const install = requireExactObject(value, INSTALL_FIELDS, label);
187
+ if (install.schema !== 'oliphaunt-wasix-extension-install-v1') {
188
+ throw new Error(`${label} has unsupported schema`);
189
+ }
190
+ requireString(install.name, `${label} name`);
191
+ if (install.nativeModule !== null) {
192
+ requireSafeRelativeAssetPath(install.nativeModule, `${label} native module`);
193
+ }
194
+ if (!Array.isArray(install.nativeModules)) {
195
+ throw new Error(`${label} native modules must be an array`);
196
+ }
197
+ install.nativeModules.forEach((module, index) => {
198
+ validateNativeModule(module, `${label} native module ${index}`);
199
+ });
200
+ requireUnique(install.nativeModules.map((module) => module.name), `${label} native module names`);
201
+ requireUnique(install.nativeModules.map((module) => module.path), `${label} native module paths`);
202
+ if ((install.nativeModule === null) !== (install.nativeModules.length === 0)) {
203
+ throw new Error(`${label} nativeModule must be null exactly when nativeModules is empty`);
204
+ }
205
+ const dependencies = requireUniqueSqlNameArray(install.dependencies, `${label} dependencies`);
206
+ if (dependencies.includes(sqlName)) {
207
+ throw new Error(`${label} dependencies must not include its own SQL name '${sqlName}'`);
208
+ }
209
+ requireUniqueStringArray(install.coreExportsRequired, `${label} core exports required`);
210
+ const loadOrder = requireUniquePathArray(install.loadOrder, `${label} load order`);
211
+ validateLifecycle(install.lifecycle, `${label} lifecycle`);
212
+ const installedFiles = requireUniquePathArray(install.installedFiles, `${label} installed files`);
213
+ for (const module of install.nativeModules) {
214
+ if (!installedFiles.includes(module.path)) {
215
+ throw new Error(`${label} native module path is absent from installedFiles: ${module.path}`);
216
+ }
217
+ }
218
+ for (const path of loadOrder) {
219
+ if (!installedFiles.includes(path)) {
220
+ throw new Error(`${label} load-order path is absent from installedFiles: ${path}`);
221
+ }
222
+ }
223
+ if (!Array.isArray(install.unresolvedImports)) {
224
+ throw new Error(`${label} unresolved imports must be an array`);
225
+ }
226
+ install.unresolvedImports.forEach((entry, index) => {
227
+ validateImport(entry, `${label} unresolved import ${index}`);
228
+ });
229
+ }
230
+ function validateImport(value, label) {
231
+ const entry = requireExactObject(value, IMPORT_FIELDS, label);
232
+ requireString(entry.module, `${label} module`);
233
+ requireString(entry.name, `${label} name`);
234
+ requireString(entry.kind, `${label} kind`);
235
+ }
236
+ function validateNativeModule(value, label) {
237
+ const module = requireExactObject(value, NATIVE_MODULE_FIELDS, label);
238
+ requireString(module.name, `${label} name`);
239
+ requireSafeRelativeAssetPath(module.path, `${label} path`);
240
+ requireSha256(module.sha256, `${label} SHA-256`);
241
+ requireSha256(module.moduleSha256, `${label} module SHA-256`);
242
+ requireSize(module.size, `${label} size`);
243
+ }
244
+ function validateLifecycle(value, label) {
245
+ const lifecycle = requireExactObject(value, LIFECYCLE_FIELDS, label);
246
+ for (const field of [
247
+ 'createExtension',
248
+ 'preloadRequired',
249
+ 'restartRequired',
250
+ 'sharedMemoryRequired',
251
+ ]) {
252
+ if (typeof lifecycle[field] !== 'boolean') {
253
+ throw new Error(`${label} ${field} must be a boolean`);
254
+ }
255
+ }
256
+ if (lifecycle.createSchema !== null) {
257
+ requireString(lifecycle.createSchema, `${label} createSchema`);
258
+ }
259
+ for (const field of ['loadSql', 'postCreateSql', 'startupConfig']) {
260
+ requireUniqueStringArray(lifecycle[field], `${label} ${field}`);
261
+ }
262
+ }
263
+ function serializeCarrier(carrier, compatibility) {
264
+ return {
265
+ product: carrier.product,
266
+ version: carrier.version,
267
+ sqlName: carrier.sqlName,
268
+ archive: carrier.archive,
269
+ sha256: carrier.sha256,
270
+ size: carrier.size,
271
+ source: serializeAssetSource(carrier.source),
272
+ compatibility: { ...compatibility },
273
+ install: {
274
+ schema: carrier.install.schema,
275
+ name: carrier.install.name,
276
+ nativeModule: carrier.install.nativeModule,
277
+ nativeModules: [...carrier.install.nativeModules],
278
+ dependencies: [...carrier.install.dependencies],
279
+ coreExportsRequired: [...carrier.install.coreExportsRequired],
280
+ loadOrder: [...carrier.install.loadOrder],
281
+ lifecycle: {
282
+ ...carrier.install.lifecycle,
283
+ loadSql: [...carrier.install.lifecycle.loadSql],
284
+ postCreateSql: [...carrier.install.lifecycle.postCreateSql],
285
+ startupConfig: [...carrier.install.lifecycle.startupConfig],
286
+ },
287
+ installedFiles: [...carrier.install.installedFiles],
288
+ unresolvedImports: carrier.install.unresolvedImports.map((entry) => ({
289
+ ...entry,
290
+ })),
291
+ },
292
+ };
293
+ }
294
+ function freezeCarrier(carrier) {
295
+ const lifecycle = carrier.install.lifecycle;
296
+ return Object.freeze({
297
+ ...carrier,
298
+ install: Object.freeze({
299
+ ...carrier.install,
300
+ nativeModules: Object.freeze(carrier.install.nativeModules.map((module) => Object.freeze({ ...module }))),
301
+ dependencies: Object.freeze([...carrier.install.dependencies]),
302
+ coreExportsRequired: Object.freeze([...carrier.install.coreExportsRequired]),
303
+ loadOrder: Object.freeze([...carrier.install.loadOrder]),
304
+ lifecycle: Object.freeze({
305
+ ...lifecycle,
306
+ loadSql: Object.freeze([...lifecycle.loadSql]),
307
+ postCreateSql: Object.freeze([...lifecycle.postCreateSql]),
308
+ startupConfig: Object.freeze([...lifecycle.startupConfig]),
309
+ }),
310
+ installedFiles: Object.freeze([...carrier.install.installedFiles]),
311
+ unresolvedImports: Object.freeze(carrier.install.unresolvedImports.map((entry) => Object.freeze({ ...entry }))),
312
+ }),
313
+ });
314
+ }
315
+ function requireProduct(value, label) {
316
+ const product = requireString(value, label);
317
+ if (!PRODUCT.test(product)) {
318
+ throw new Error(`${label} must be an Oliphaunt extension product id`);
319
+ }
320
+ return product;
321
+ }
322
+ function requireSqlName(value, label) {
323
+ const sqlName = requireString(value, label);
324
+ if (!SQL_NAME.test(sqlName)) {
325
+ throw new Error(`${label} must be a portable PostgreSQL extension name`);
326
+ }
327
+ return sqlName;
328
+ }
329
+ function requireExtensionArchive(value, label) {
330
+ const archive = requireSafeRelativeAssetPath(value, label);
331
+ if (!archive.startsWith('extensions/')) {
332
+ throw new Error(`${label} must be a safe relative path under extensions/`);
333
+ }
334
+ return archive;
335
+ }
336
+ function requireStringArray(value, label) {
337
+ if (!Array.isArray(value)) {
338
+ throw new Error(`${label} must be an array`);
339
+ }
340
+ return value.map((entry, index) => requireString(entry, `${label} entry ${index}`));
341
+ }
342
+ function requireUniqueSqlNameArray(value, label) {
343
+ const values = requireStringArray(value, label).map((entry, index) => requireSqlName(entry, `${label} entry ${index}`));
344
+ requireUnique(values, label);
345
+ return values;
346
+ }
347
+ function requireUniqueStringArray(value, label) {
348
+ const values = requireStringArray(value, label);
349
+ requireUnique(values, label);
350
+ return values;
351
+ }
352
+ function requireUniquePathArray(value, label) {
353
+ const values = requireStringArray(value, label).map((path, index) => requireSafeRelativeAssetPath(path, `${label} entry ${index}`));
354
+ requireUnique(values, label);
355
+ return values;
356
+ }
357
+ function requireUnique(values, label) {
358
+ if (new Set(values).size !== values.length) {
359
+ throw new Error(`${label} must not repeat values`);
360
+ }
361
+ }
362
+ function validateExactDescriptorClosure(carriers, rootSqlName, label) {
363
+ const visited = new Set();
364
+ const visiting = new Set();
365
+ const visit = (sqlName) => {
366
+ if (visited.has(sqlName)) {
367
+ return;
368
+ }
369
+ if (visiting.has(sqlName)) {
370
+ throw new Error(`${label} has cyclic carrier dependencies involving '${sqlName}'`);
371
+ }
372
+ const carrier = carriers.get(sqlName);
373
+ if (carrier === undefined) {
374
+ return;
375
+ }
376
+ visiting.add(sqlName);
377
+ for (const dependency of carrier.install.dependencies) {
378
+ if (carriers.has(dependency)) {
379
+ visit(dependency);
380
+ }
381
+ }
382
+ visiting.delete(sqlName);
383
+ visited.add(sqlName);
384
+ };
385
+ visit(rootSqlName);
386
+ const unexpected = [...carriers.keys()].filter((sqlName) => !visited.has(sqlName)).sort();
387
+ if (unexpected.length > 0) {
388
+ throw new Error(`${label} carriers must be the exact dependency closure for '${rootSqlName}'; unexpected ${unexpected.join(', ')}`);
389
+ }
390
+ }
391
+ function sameCarrierIdentity(left, right) {
392
+ return (left.product === right.product &&
393
+ left.version === right.version &&
394
+ left.sqlName === right.sqlName &&
395
+ left.archive === right.archive &&
396
+ left.sha256 === right.sha256 &&
397
+ left.size === right.size &&
398
+ structurallyEqual(left.install, right.install));
399
+ }
400
+ function structurallyEqual(left, right) {
401
+ if (Object.is(left, right)) {
402
+ return true;
403
+ }
404
+ if (Array.isArray(left) || Array.isArray(right)) {
405
+ return (Array.isArray(left) &&
406
+ Array.isArray(right) &&
407
+ left.length === right.length &&
408
+ left.every((value, index) => structurallyEqual(value, right[index])));
409
+ }
410
+ if (left === null || right === null || typeof left !== 'object' || typeof right !== 'object') {
411
+ return false;
412
+ }
413
+ const leftRecord = left;
414
+ const rightRecord = right;
415
+ const leftKeys = Object.keys(leftRecord).sort();
416
+ const rightKeys = Object.keys(rightRecord).sort();
417
+ return (leftKeys.length === rightKeys.length &&
418
+ leftKeys.every((key, index) => key === rightKeys[index] && structurallyEqual(leftRecord[key], rightRecord[key])));
419
+ }
@@ -0,0 +1,68 @@
1
+ import { type ExtractedArchive, type WasixDirectoryMount, type WasixRuntimeLayout } from './archive.js';
2
+ import type { SerializedOpenOptions, SerializedRuntimeDescriptor, SerializedToolRuntimeDescriptor } from './rpc.js';
3
+ import { type WasixPhysicalIdentity } from './storage-provider.js';
4
+ import type { WasixAssetManifest } from './types.js';
5
+ type CatalogProfile = 'standard' | 'icu';
6
+ /** Internal kebab-case projection used while installing an imported carrier. */
7
+ type ProjectedExtensionLifecycle = {
8
+ 'create-extension': boolean;
9
+ 'create-schema'?: string | null;
10
+ 'load-sql': readonly string[];
11
+ 'post-create-sql': readonly string[];
12
+ 'startup-config': readonly string[];
13
+ 'shared-memory-required': boolean;
14
+ };
15
+ /** Internal install shape projected solely from extension-owned carrier metadata. */
16
+ type ProjectedExtensionInstall = {
17
+ name: string;
18
+ 'sql-name': string;
19
+ archive: string;
20
+ sha256: string;
21
+ size: number;
22
+ 'native-module'?: string | null;
23
+ 'native-modules': readonly unknown[];
24
+ dependencies: readonly string[];
25
+ 'load-order': readonly string[];
26
+ lifecycle: ProjectedExtensionLifecycle;
27
+ 'installed-files': readonly string[];
28
+ 'unresolved-imports': readonly unknown[];
29
+ };
30
+ export type ResolvedWasixExtensions = {
31
+ extensions: ProjectedExtensionInstall[];
32
+ runtimeDependencies: string[];
33
+ };
34
+ export type PreparedWasixRuntime = {
35
+ layout: WasixRuntimeLayout;
36
+ loadClusterSeed(): Promise<WasixDirectoryMount>;
37
+ moduleSha256: string;
38
+ catalogProfile: 'standard' | 'icu';
39
+ icuEnabled: boolean;
40
+ startupGUCs: Record<string, string>;
41
+ physicalIdentity: WasixPhysicalIdentity;
42
+ };
43
+ /**
44
+ * Loads and verifies one exact runtime/ICU/extension closure. The matching
45
+ * cluster seed stays lazy until an exclusively leased storage provider reports
46
+ * a new root. Selection materializes exact artifacts and required startup
47
+ * configuration; database-local installation remains explicit application SQL.
48
+ */
49
+ export declare function prepareWasixRuntime(options: SerializedOpenOptions): Promise<PreparedWasixRuntime>;
50
+ /** Require the package-authored archive identities to match the canonical manifest. */
51
+ export declare function assertRuntimeDescriptorMatchesManifest(descriptor: SerializedRuntimeDescriptor, manifest: WasixAssetManifest): void;
52
+ /** Verify only the runtime assets consumed by PostgreSQL frontend tools. */
53
+ export declare function assertToolRuntimeDescriptorMatchesManifest(descriptor: SerializedToolRuntimeDescriptor, manifest: WasixAssetManifest): void;
54
+ export declare function assertExactCarrierClosure(resolved: readonly ProjectedExtensionInstall[], carriers: SerializedOpenOptions['extensionCarriers']): void;
55
+ export declare function parseWasixAssetManifest(bytes: Uint8Array): WasixAssetManifest;
56
+ export declare function resolveWasixExtensions(manifest: WasixAssetManifest, carriers: SerializedOpenOptions['extensionCarriers'], requested: readonly string[]): ResolvedWasixExtensions;
57
+ export declare function assertExtensionCarriersCompatible(runtime: SerializedRuntimeDescriptor, manifest: WasixAssetManifest, carriers: SerializedOpenOptions['extensionCarriers']): void;
58
+ export declare function overlayExtensionArchive(layout: WasixRuntimeLayout, archive: ExtractedArchive, extension: ProjectedExtensionInstall): void;
59
+ export declare function mergeExtensionStartupGUCs(configured: Readonly<Record<string, string>>, extensions: readonly ProjectedExtensionInstall[]): Record<string, string>;
60
+ /** @internal Validate the host-independent standard/ICU seed profile contract. */
61
+ export declare function assertClusterSeedProfileContract(value: unknown, expectedProfile: CatalogProfile): void;
62
+ /** @internal Verify that a separately distributed ICU carrier belongs to this runtime. */
63
+ export declare function assertIcuDescriptorMatchesRuntime(runtime: SerializedRuntimeDescriptor, icu: NonNullable<SerializedOpenOptions['icu']>, manifest: WasixAssetManifest): void;
64
+ /** @internal Validate and add exact-archive-verified ICU files to the runtime tree. */
65
+ export declare function overlayIcuArchive(runtime: ExtractedArchive, icu: ExtractedArchive): void;
66
+ /** @internal Shared verification for separately carried WASIX tool modules. */
67
+ export declare function assertSha256(bytes: Uint8Array, expected: string, label: string): Promise<void>;
68
+ export {};