crossbind 2.0.0-beta.62 → 2.0.0-beta.64

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 (132) hide show
  1. package/AGENTS.md +2 -1
  2. package/README.md +2 -2
  3. package/package.json +7 -2
  4. package/src/actions/buildBinTools.js +0 -0
  5. package/src/actions/buildDependencies.js +15 -2
  6. package/src/actions/buildExternal.js +9 -1
  7. package/src/actions/buildJs.js +2 -9
  8. package/src/actions/buildLib.js +25 -8
  9. package/src/actions/buildNativeCommand.js +84 -0
  10. package/src/actions/buildNode.js +49 -10
  11. package/src/actions/buildTypes.js +8 -2
  12. package/src/actions/buildWasiCommand.js +10 -9
  13. package/src/actions/buildWasm.js +72 -31
  14. package/src/actions/cloudCommands.js +133 -0
  15. package/src/actions/createImportedBridges.js +71 -0
  16. package/src/actions/createInterface.js +247 -68
  17. package/src/actions/createLib.js +70 -42
  18. package/src/actions/getData.js +6 -2
  19. package/src/actions/getEnv.js +7 -0
  20. package/src/actions/getLinkInputs.js +12 -5
  21. package/src/actions/isSourceNewer.js +2 -0
  22. package/src/actions/licenses.js +143 -19
  23. package/src/actions/prepareConanDependencies.js +51 -0
  24. package/src/actions/publishBinary.js +32 -0
  25. package/src/actions/run.js +63 -66
  26. package/src/actions/runnerCommands.js +433 -0
  27. package/src/actions/writeRuntimeEntry.js +80 -0
  28. package/src/assets/cmake/command/CMakeLists.txt +21 -0
  29. package/src/assets/cmake/command/command.cpp +1 -0
  30. package/src/assets/cmake/dist.cmake +2 -1
  31. package/src/assets/configure/install.sh +22 -0
  32. package/src/assets/cpp-runtime/node.cpp +18 -1
  33. package/src/assets/js-runtime/adapters/path-url.js +6 -3
  34. package/src/assets/js-runtime/adapters/unboundMessage.js +2 -0
  35. package/src/assets/js-runtime/adapters/worker-comlink.js +138 -60
  36. package/src/assets/js-runtime/core.js +12 -0
  37. package/src/assets/node-import-hooks.mjs +61 -0
  38. package/src/assets/toolchain-digests.json +19 -19
  39. package/src/assets/wasi-runtime/http/README.md +17 -0
  40. package/src/assets/wasi-runtime/http/bindings/crossbind_http.c +2537 -0
  41. package/src/assets/wasi-runtime/http/bindings/crossbind_http.h +826 -0
  42. package/src/assets/wasi-runtime/http/bindings/crossbind_http_component_type.o +0 -0
  43. package/src/assets/wasi-runtime/http/handler.cpp +298 -0
  44. package/src/assets/wasi-runtime/http/include/crossbind/http.h +31 -0
  45. package/src/assets/wasi-runtime/http/wit/deps/LICENSE.md +8 -0
  46. package/src/assets/wasi-runtime/http/wit/deps/cli.wit +28 -0
  47. package/src/assets/wasi-runtime/http/wit/deps/clocks.wit +43 -0
  48. package/src/assets/wasi-runtime/http/wit/deps/http.wit +509 -0
  49. package/src/assets/wasi-runtime/http/wit/deps/random.wit +18 -0
  50. package/src/assets/wasi-runtime/http/wit/world.wit +6 -0
  51. package/src/assets/wasi-runtime/stubs.c +29 -0
  52. package/src/bin.js +218 -161
  53. package/src/cli.js +14 -0
  54. package/src/index.js +3 -0
  55. package/src/integration/bindHeaderImports.js +18 -0
  56. package/src/integration/getCrossbindScript.js +50 -13
  57. package/src/integration/getDependFilePath.js +9 -0
  58. package/src/integration/resolveNativeImport.js +14 -0
  59. package/src/node/dev.mjs +19 -0
  60. package/src/program.js +169 -0
  61. package/src/runner/blobs.js +135 -0
  62. package/src/runner/files.js +193 -0
  63. package/src/runner/server.js +320 -0
  64. package/src/state/attachConanDependencies.js +56 -0
  65. package/src/state/index.js +20 -11
  66. package/src/state/loadConfig.js +37 -35
  67. package/src/state/refreshBuiltDependencies.js +20 -0
  68. package/src/state/refreshConanDependencies.js +17 -0
  69. package/src/utils/androidToolchain.js +4 -0
  70. package/src/utils/appRustCrates.js +19 -1
  71. package/src/utils/appSources.js +97 -6
  72. package/src/utils/archiveFlags.js +12 -0
  73. package/src/utils/boundHeaders.js +79 -0
  74. package/src/utils/bridgeExtras.js +554 -0
  75. package/src/utils/bridgeRuntimeFixes.js +48 -0
  76. package/src/utils/bundledLicenses.js +26 -0
  77. package/src/utils/cargoLock.js +19 -0
  78. package/src/utils/cargoTarget.js +21 -3
  79. package/src/utils/cloudAccount.js +55 -0
  80. package/src/utils/conanDependencies.js +66 -0
  81. package/src/utils/conanImport.js +58 -0
  82. package/src/utils/conanInstall.js +154 -0
  83. package/src/utils/conanProfile.js +203 -0
  84. package/src/utils/conanStage.js +292 -0
  85. package/src/utils/copyToDistSource.js +13 -0
  86. package/src/utils/cppDts.js +60 -15
  87. package/src/utils/darwinToolchain.js +11 -0
  88. package/src/utils/dirLock.js +137 -28
  89. package/src/utils/downloadAndExtractFile.js +34 -7
  90. package/src/utils/embindArgumentGuards.js +41 -2
  91. package/src/utils/embindCallArguments.js +19 -0
  92. package/src/utils/embindJspiQueue.js +25 -0
  93. package/src/utils/embindObjectLifetime.js +35 -0
  94. package/src/utils/execContainer.js +49 -0
  95. package/src/utils/headerImports.js +16 -5
  96. package/src/utils/hostPlatform.js +21 -0
  97. package/src/utils/iosToolchain.js +6 -0
  98. package/src/utils/isSourceCmakePackage.js +10 -0
  99. package/src/utils/licenseReport.js +11 -2
  100. package/src/utils/linuxToolchain.js +24 -0
  101. package/src/utils/makeTreeWritable.js +12 -0
  102. package/src/utils/nativeImports.js +23 -0
  103. package/src/utils/nativeSourceStamp.js +36 -0
  104. package/src/utils/nodeDevBuild.js +92 -0
  105. package/src/utils/packageLicense.js +103 -0
  106. package/src/utils/pthreadMainScript.js +14 -0
  107. package/src/utils/pullDockerImage.js +3 -1
  108. package/src/utils/relocatePrebuilt.js +119 -0
  109. package/src/utils/remoteClient.js +127 -0
  110. package/src/utils/remoteOutputs.js +39 -0
  111. package/src/utils/remoteRunner.js +97 -0
  112. package/src/utils/remoteUpload.js +134 -0
  113. package/src/utils/replaceFile.js +8 -0
  114. package/src/utils/resetCMakeCacheForSource.js +26 -0
  115. package/src/utils/runCargo.js +48 -51
  116. package/src/utils/runConan.js +257 -0
  117. package/src/utils/runtimeEntries.js +56 -0
  118. package/src/utils/rustBridgeGen.js +22 -8
  119. package/src/utils/rustMt.js +9 -0
  120. package/src/utils/rustSysroot.js +7 -5
  121. package/src/utils/scopedEmbind.js +22 -0
  122. package/src/utils/selectRunner.js +81 -0
  123. package/src/utils/sourceFingerprint.js +4 -3
  124. package/src/utils/swigInterface.js +70 -4
  125. package/src/utils/systemConfig.js +13 -0
  126. package/src/utils/systemKeys.js +35 -3
  127. package/src/utils/targets.js +60 -4
  128. package/src/utils/toolchainNotices.js +5 -0
  129. package/src/utils/wasiHttp.js +50 -0
  130. package/src/utils/wasiToolchain.js +14 -1
  131. package/src/utils/windowsToolchain.js +18 -0
  132. package/vitest.config.js +11 -0
@@ -5,6 +5,8 @@ import {
5
5
  callWithVectorCoercion, wrapWithVectorCoercion, setCoercionModule, unwrapCoercionProxy,
6
6
  } from './vector-coercion.js';
7
7
  import { patchModuleForExceptionDecode } from './exception-decode.js';
8
+ import urlPath from './path-url.js';
9
+ import { UNBOUND_MESSAGE } from './unboundMessage.js';
8
10
 
9
11
  const isWorkerScope = typeof WorkerGlobalScope !== 'undefined'
10
12
  && typeof self !== 'undefined'
@@ -12,17 +14,87 @@ const isWorkerScope = typeof WorkerGlobalScope !== 'undefined'
12
14
 
13
15
  // === Embind <-> Comlink Bridge ===
14
16
  // Worker-side registry: id -> original embind object
15
- // Main-thread registry: Comlink proxy -> id
17
+ // Main-thread registry: handle -> id
16
18
  const embindRegistry = new Map();
17
19
  const embindProxyIds = new WeakMap();
18
20
  let nextEmbindId = 1;
19
21
 
22
+ // The objects the worker hands out are reached through the module's channel, so writes to them keep their order
23
+ // with the calls after them; messages on different channels keep none.
24
+ const HANDLES = '__crossbindHandles';
25
+ let remoteModule = null;
26
+
27
+ // How many ids name each object, as a raw pointer handed out twice is one object.
28
+ const holders = new WeakMap();
29
+
20
30
  function registerEmbindObject(obj) {
21
31
  const id = nextEmbindId++;
22
32
  embindRegistry.set(id, obj);
33
+ holders.set(obj, (holders.get(obj) ?? 0) + 1);
23
34
  return id;
24
35
  }
25
36
 
37
+ // What JavaScript owns: an object behind a smart pointer, or a copy embind made of a returned value or an object
38
+ // JavaScript constructed, which utils/embindObjectLifetime.js marks. A raw pointer stays its library's.
39
+ const ownsObject = (obj) => {
40
+ const $$ = obj?.$$;
41
+ return Boolean($$ && ($$.smartPtr || $$.count?.owned));
42
+ };
43
+
44
+ const freeIfOwned = (obj) => {
45
+ if (ownsObject(obj) && !obj.isDeleted()) obj.delete();
46
+ };
47
+
48
+ // The worker side of a handle the main thread no longer reaches: its id goes, and the object with the last id.
49
+ const RELEASE = '__crossbindRelease';
50
+
51
+ function releaseEmbindObjects(ids) {
52
+ for (const id of ids) {
53
+ const obj = embindRegistry.get(id);
54
+ if (!embindRegistry.delete(id)) continue;
55
+ const left = holders.get(obj) - 1;
56
+ if (left > 0) {
57
+ holders.set(obj, left);
58
+ } else {
59
+ holders.delete(obj);
60
+ freeIfOwned(obj);
61
+ }
62
+ }
63
+ }
64
+
65
+ // Comlink answers every member with a remote one: JSON.stringify and String() called toJSON and Symbol.toPrimitive
66
+ // on the worker's object, which has neither, and a member below the module is thenable. A handle answers them as a
67
+ // direct-mode object does.
68
+ const LOCAL_MEMBERS = {
69
+ then: undefined,
70
+ toJSON: () => ({}),
71
+ [Symbol.toPrimitive]: (hint) => (hint === 'number' ? NaN : '[object Object]'),
72
+ };
73
+
74
+ // The main thread side: once a handle is collected, the worker lets go of its object, the ids collected in one task
75
+ // in one message.
76
+ const collected = [];
77
+
78
+ function releaseLater(id) {
79
+ collected.push(id);
80
+ if (collected.length > 1) return;
81
+ setTimeout(() => {
82
+ const ids = collected.splice(0);
83
+ Promise.resolve(remoteModule?.[RELEASE](ids)).catch(() => {});
84
+ }, 0);
85
+ }
86
+
87
+ const handleWatcher = typeof FinalizationRegistry === 'function' ? new FinalizationRegistry(releaseLater) : null;
88
+
89
+ function handleFor(id) {
90
+ const handle = new Proxy(remoteModule[HANDLES][id], {
91
+ get: (target, prop) => (Object.hasOwn(LOCAL_MEMBERS, prop) ? LOCAL_MEMBERS[prop] : target[prop]),
92
+ });
93
+ embindProxyIds.set(handle, id);
94
+ handleWatcher?.register(handle, id);
95
+ return handle;
96
+ }
97
+
26
98
  // Reorder transfer handlers for correct priority
27
99
  const _proxyHandler = Comlink.transferHandlers.get('proxy');
28
100
  const _throwHandler = Comlink.transferHandlers.get('throw');
@@ -47,22 +119,16 @@ Comlink.transferHandlers.set('proxy', {
47
119
  canHandle: _proxyHandler.canHandle,
48
120
  serialize(obj) {
49
121
  if (typeof obj.delete === 'function' && typeof obj.isDeleted === 'function') {
50
- // CONSTRUCT results (Comlink marks them with proxy()) are exposed through
51
- // the same coercion wrapper as method-returned objects (embindObject
52
- // handler), and the registry keeps the RAW object so arguments resolve
53
- // back to real embind identities.
54
- const raw = unwrapCoercionProxy(obj);
55
- const [port, transferables] = _proxyHandler.serialize(wrapWithVectorCoercion(raw));
56
- const id = registerEmbindObject(raw);
57
- return [{ __embindId: id, __port: port }, transferables];
122
+ // CONSTRUCT results (Comlink marks them with proxy()) are handed out as
123
+ // method-returned objects are (embindObject handler), and the registry keeps
124
+ // the RAW object so arguments resolve back to real embind identities.
125
+ return [{ __embindId: registerEmbindObject(unwrapCoercionProxy(obj)) }, []];
58
126
  }
59
127
  return _proxyHandler.serialize(obj);
60
128
  },
61
129
  deserialize(data) {
62
130
  if (data != null && typeof data === 'object' && '__embindId' in data) {
63
- const proxy = _proxyHandler.deserialize(data.__port);
64
- embindProxyIds.set(proxy, data.__embindId);
65
- return proxy;
131
+ return handleFor(data.__embindId);
66
132
  }
67
133
  return _proxyHandler.deserialize(data);
68
134
  },
@@ -102,40 +168,20 @@ Comlink.transferHandlers.set('embindVector', {
102
168
  serialize(obj) {
103
169
  const len = obj.size();
104
170
  const elements = new Array(len);
105
- let hasObjects = false;
106
171
  for (let i = 0; i < len; i++) {
107
172
  const elem = obj.get(i);
108
- elements[i] = elem;
109
- if (!hasObjects && elem !== null && typeof elem === 'object') {
110
- hasObjects = true;
111
- }
112
- }
113
- if (!hasObjects) {
114
- return [elements, []];
115
- }
116
- const transferables = [];
117
- for (let i = 0; i < len; i++) {
118
- const elem = elements[i];
119
- if (elem !== null && typeof elem === 'object') {
120
- const id = registerEmbindObject(elem);
121
- const { port1, port2 } = new MessageChannel();
122
- Comlink.expose(wrapWithVectorCoercion(elem), port1);
123
- transferables.push(port2);
124
- elements[i] = { __comlinkProxy: true, __embindId: id, port: port2 };
125
- }
173
+ elements[i] = elem !== null && typeof elem === 'object'
174
+ ? { __comlinkProxy: true, __embindId: registerEmbindObject(elem) }
175
+ : elem;
126
176
  }
127
- return [elements, transferables];
177
+ // The main thread gets the array alone, so nothing can reach a vector the call handed over any more.
178
+ freeIfOwned(unwrapCoercionProxy(obj));
179
+ return [elements, []];
128
180
  },
129
181
  deserialize(elements) {
130
- return elements.map((elem) => {
131
- if (elem && typeof elem === 'object' && elem.__comlinkProxy) {
132
- elem.port.start();
133
- const proxy = Comlink.wrap(elem.port);
134
- embindProxyIds.set(proxy, elem.__embindId);
135
- return proxy;
136
- }
137
- return elem;
138
- });
182
+ return elements.map((elem) => (elem && typeof elem === 'object' && elem.__comlinkProxy
183
+ ? handleFor(elem.__embindId)
184
+ : elem));
139
185
  },
140
186
  });
141
187
 
@@ -210,20 +256,13 @@ Comlink.transferHandlers.set('embindObject', {
210
256
  && typeof obj.isDeleted === 'function';
211
257
  },
212
258
  serialize(obj) {
213
- // An object a property returns comes wrapped by its owner's coercion proxy. Registering and exposing
214
- // the raw object, as the proxy handler does, keeps objects read from it one wrapper deep, which an
215
- // argument's unwrapping undoes.
216
- const raw = unwrapCoercionProxy(obj);
217
- const id = registerEmbindObject(raw);
218
- const { port1, port2 } = new MessageChannel();
219
- Comlink.expose(wrapWithVectorCoercion(raw), port1);
220
- return [{ __embindId: id, port: port2 }, [port2]];
259
+ // An object a property returns comes wrapped by its owner's coercion proxy. Registering the raw object,
260
+ // as the proxy handler does, keeps objects read from it one wrapper deep, which an argument's unwrapping
261
+ // undoes.
262
+ return [{ __embindId: registerEmbindObject(unwrapCoercionProxy(obj)) }, []];
221
263
  },
222
264
  deserialize(data) {
223
- data.port.start();
224
- const proxy = Comlink.wrap(data.port);
225
- embindProxyIds.set(proxy, data.__embindId);
226
- return proxy;
265
+ return handleFor(data.__embindId);
227
266
  },
228
267
  });
229
268
 
@@ -251,16 +290,29 @@ Comlink.transferHandlers.set('embindProxyArray', {
251
290
 
252
291
  let _worker = null;
253
292
 
293
+ // Configurable, so the coercion wrapper wraps the objects read through it as it does every other member.
294
+ export function moduleRoot(m) {
295
+ Object.defineProperty(m, HANDLES, {
296
+ value: new Proxy({}, { get: (_, id) => embindRegistry.get(Number(id)) }),
297
+ configurable: true,
298
+ });
299
+ Object.defineProperty(m, RELEASE, { value: releaseEmbindObjects, configurable: true });
300
+ return wrapWithVectorCoercion(m);
301
+ }
302
+
303
+ export function adoptModule(remote) {
304
+ remoteModule = remote;
305
+ return remote;
306
+ }
307
+
254
308
  function resolveScriptUrl(config) {
255
309
  const fileName = config.paths.js || config.paths.worker;
256
- let prefix = '';
310
+ let prefix = urlPath.getDefaultPathPrefix();
257
311
  if (config.path) {
258
312
  prefix = config.path;
259
313
  if (prefix.slice(-1) !== '/') prefix += '/';
260
314
  }
261
- let output = prefix + fileName;
262
- if (output.substring(0, 4) !== 'http' && output[0] !== '/') output = `/${output}`;
263
- return output;
315
+ return urlPath.finalizePath(prefix + fileName);
264
316
  }
265
317
 
266
318
  function exposeWorker(systemConfig, createModule) {
@@ -273,7 +325,7 @@ function exposeWorker(systemConfig, createModule) {
273
325
  // comlink throw handler, a raw WebAssembly.Exception does not.
274
326
  patchModuleForExceptionDecode(m);
275
327
  registerModuleEnums(m);
276
- return Comlink.proxy(wrapWithVectorCoercion(m));
328
+ return Comlink.proxy(moduleRoot(m));
277
329
  },
278
330
  };
279
331
  Comlink.expose(workerApi);
@@ -288,10 +340,35 @@ async function initWithWorker(config, userConfig) {
288
340
  logHandler, errorHandler, onRuntimeInitialized, getWasmFunction, useWorker, workerUrl,
289
341
  ...serializableConfig
290
342
  } = userConfig;
291
- const module = await workerApi.init(serializableConfig);
343
+ // The worker resolves a relative URL against its own script, so it gets the path resolved against the page.
344
+ const workerConfig = config.path ? { ...serializableConfig, path: urlPath.finalizePath(config.path) } : serializableConfig;
345
+ return moduleOnMainThread(adoptModule(await workerApi.init(workerConfig)));
346
+ }
347
+
348
+ // A call the worker cannot make, because the module lacks the function, fails there with a TypeError from inside comlink
349
+ // that names nothing: the failed call asks for the member, and names it when the module has none.
350
+ function namedCall(member, name, module) {
351
+ return new Proxy(member, {
352
+ // A member still needs Comlink's then to read its value (including an absent export).
353
+ get(target, prop) {
354
+ // Console formatters inspect function names and constructors synchronously.
355
+ if (prop === 'name') return name;
356
+ if (prop === 'constructor') return Function;
357
+ return prop !== 'then' && Object.hasOwn(LOCAL_MEMBERS, prop) ? LOCAL_MEMBERS[prop] : target[prop];
358
+ },
359
+ apply: (call, thisArg, args) => Reflect.apply(call, thisArg, args).catch(async (error) => {
360
+ const isBound = await Promise.resolve(module[name]).then((value) => value !== undefined, () => true);
361
+ if (isBound) throw error;
362
+ throw new Error(`crossbind: ${name} ${UNBOUND_MESSAGE}`);
363
+ }),
364
+ });
365
+ }
292
366
 
367
+ export function moduleOnMainThread(module) {
293
368
  return new Proxy(module, {
294
369
  get(target, prop) {
370
+ if (Object.hasOwn(LOCAL_MEMBERS, prop)) return LOCAL_MEMBERS[prop];
371
+ if (prop === 'constructor') return Object;
295
372
  if (prop === 'toArray') {
296
373
  return function toArray(vector) {
297
374
  if (Array.isArray(vector)) return vector;
@@ -306,7 +383,8 @@ async function initWithWorker(config, userConfig) {
306
383
  return target.toVector(classOrName, array);
307
384
  };
308
385
  }
309
- return target[prop];
386
+ const member = target[prop];
387
+ return typeof prop === 'string' && typeof member === 'function' ? namedCall(member, prop, target) : member;
310
388
  },
311
389
  });
312
390
  }
@@ -73,6 +73,17 @@ function buildLocateFile(config, adapter) {
73
73
  };
74
74
  }
75
75
 
76
+ // WASMFS's FS.writeFile writes at the end of a file that exists (emsdk 6.0.9 js_api.cpp), where the classic FS
77
+ // replaces it; opening the file for writing empties it first, unless the flags ask to append.
78
+ function replaceOnWriteFile(FS) {
79
+ const { writeFile } = FS;
80
+ FS.writeFile = (path, data, opts) => {
81
+ const appends = typeof opts?.flags === 'string' && opts.flags.startsWith('a');
82
+ if (!appends) FS.close(FS.open(path, 'w', opts?.mode));
83
+ return writeFile(path, data, opts);
84
+ };
85
+ }
86
+
76
87
  export function createBaseModule(Module, config, adapter) {
77
88
  return new Promise((resolve, reject) => {
78
89
  const locateFile = buildLocateFile(config, adapter);
@@ -137,6 +148,7 @@ export function createBaseModule(Module, config, adapter) {
137
148
  // fs-browser), which emscripten's onRuntimeInitialized callback cannot, so
138
149
  // both hooks run after the factory settles - adapter first, then the user's.
139
150
  Module(m).then(async (mod) => {
151
+ if (mod.FS) replaceOnWriteFile(mod.FS);
140
152
  if (adapter.onModuleReady) await adapter.onModuleReady(mod, config);
141
153
  if (config.onRuntimeInitialized) config.onRuntimeInitialized(mod);
142
154
  return mod;
@@ -0,0 +1,61 @@
1
+ // Node's module hooks for the native imports one crossbind build bound, so a Node app imports a header, a
2
+ // conan: header or a package's header the way a bundler plugin lets it. Each resolves to a module that
3
+ // re-exports its names from the runtime entry, which initNative() fills. Copied into dist by the build.
4
+ import { registerHooks } from 'node:module';
5
+ import path from 'node:path';
6
+ import { fileURLToPath, pathToFileURL } from 'node:url';
7
+
8
+ const SCHEME = /^(cargo|conan):/;
9
+ const isBare = (specifier) => !/^(\.{1,2}\/|\/|[A-Za-z]:[\\/]|file:)/.test(specifier);
10
+
11
+ function notBound(what, parentURL, cause) {
12
+ const from = parentURL ? `, imported from ${parentURL},` : '';
13
+ return new Error(`crossbind: ${what}${from} is not bound by the last crossbind build. Build again after adding a native import, or start Node with --import crossbind/node/dev to build whenever one is added; an import specifier computed at run time is not found by the build.`, { cause });
14
+ }
15
+
16
+ const exportOf = (name) => (typeof name === 'string' ? name : `${name.wire} as ${name.local}`);
17
+ const proxyOf = (entry, names) => `export { ${['initNative', 'AllSymbols', ...names.map(exportOf)].join(', ')} } from ${JSON.stringify(entry)};\n`;
18
+
19
+ // Another hook may still serve it; Node's own resolver hands an unknown scheme on unresolved.
20
+ function resolveUnbound(specifier, context, nextResolve) {
21
+ let resolved;
22
+ try {
23
+ resolved = nextResolve(specifier, context);
24
+ } catch (error) {
25
+ throw notBound(specifier, context.parentURL, error);
26
+ }
27
+ if (SCHEME.test(resolved.url)) throw notBound(specifier, context.parentURL);
28
+ return resolved;
29
+ }
30
+
31
+ // `natives` lists each bound file, relative to the entry, with the bare specifiers that name it and the names it
32
+ // exports, a name the entry exports under another as { local, wire }.
33
+ export default function registerNativeImports(entry, { extensions, natives }) {
34
+ const entryUrl = new URL(entry).href;
35
+ const dir = path.dirname(fileURLToPath(entryUrl));
36
+ const isNative = new RegExp(`\\.(${extensions.join('|')})$`);
37
+ const byFile = new Map();
38
+ const bySpecifier = new Map();
39
+ natives.forEach(({ file, specifiers, names }) => {
40
+ const absolute = path.resolve(dir, file);
41
+ byFile.set(absolute, names);
42
+ specifiers.forEach((specifier) => bySpecifier.set(specifier, pathToFileURL(absolute).href));
43
+ });
44
+
45
+ registerHooks({
46
+ resolve(specifier, context, nextResolve) {
47
+ const url = bySpecifier.get(specifier);
48
+ if (url) return { url, format: 'module', shortCircuit: true };
49
+ if (isBare(specifier) && (SCHEME.test(specifier) || isNative.test(specifier))) return resolveUnbound(specifier, context, nextResolve);
50
+ return nextResolve(specifier, context);
51
+ },
52
+ load(url, context, nextLoad) {
53
+ if (!url.startsWith('file:')) return nextLoad(url, context);
54
+ const file = fileURLToPath(url);
55
+ const names = byFile.get(file);
56
+ if (names) return { format: 'module', source: proxyOf(entryUrl, names), shortCircuit: true };
57
+ if (isNative.test(file)) throw notBound(file);
58
+ return nextLoad(url, context);
59
+ },
60
+ });
61
+ }
@@ -1,49 +1,49 @@
1
1
  {
2
- "version": "1.0.8",
2
+ "version": "1.0.13",
3
3
  "registry": "ghcr.io/crossbind",
4
4
  "toolchains": {
5
- "rust": "1.98.1"
5
+ "rust": "1.99.1"
6
6
  },
7
7
  "images": {
8
8
  "rust-sysroot": {
9
- "index": "sha256:d816d27f1f3b110aed92f7f81cb8eb99f58ba95cafc96a797c46f30536549a45",
9
+ "index": "sha256:c545ea396bd8919f7a671617dbbdf184bd0d6eb904d3170dd120d6671db3e0a9",
10
10
  "platforms": {
11
- "linux/arm64": "sha256:217f908b3e1c883bed8cd4db65aae48044481e88126c09c3ce9bf754288d8720",
12
- "linux/amd64": "sha256:15719494b51ea8ee6f3a71556b2c04f37828e632bc46d2c3fb7121a1afb37303"
11
+ "linux/arm64": "sha256:c53ee0a31c52fdc6eba175e479218ddbff0c071654b5e93a08ae768cdbbfe35a",
12
+ "linux/amd64": "sha256:1427255e619c3f5dcd3fa7fd284d15afbe00eef1a0798e08a9b50ebbb86c5234"
13
13
  }
14
14
  },
15
15
  "base": {
16
- "index": "sha256:051aa70184d29e95d5192cc60cc42523b42fab72291efbf13f71006482f063c0",
16
+ "index": "sha256:5f5d133504cb2c81ee67a93ca5795698423277522caac742478510d6158ed559",
17
17
  "platforms": {
18
- "linux/arm64": "sha256:cdf189ad4ae220698088fda54c4a5005a207542bba8ab2c01fe58fc008586df8",
19
- "linux/amd64": "sha256:f501545972e2a253b19f1900946cfe39ce5dea74600d885cae47db3f320b506b"
18
+ "linux/arm64": "sha256:df1c82f4b7e00a4ab33e2423f6d5bbaa66dd67bc45b96e5498e3391ec5f55fae",
19
+ "linux/amd64": "sha256:ffc0a178bc821e56975d9348b7e44d8081b9c6fb9351bc168b38d5063d9864ea"
20
20
  }
21
21
  },
22
22
  "web": {
23
- "index": "sha256:89e0ea288d42d9f24671e43b3e59dc6b9913e389b68e51d8f0e314e81d111bf3",
23
+ "index": "sha256:b5ee271886c316404cae103c5412bc02d811ad48fd36a919fdf3a82af14fe688",
24
24
  "platforms": {
25
- "linux/amd64": "sha256:40fe9f7573ee5a696c68043b12486e4562e0b2a87f74ef21a9b11c08454aff75",
26
- "linux/arm64": "sha256:7172f994c262600dcd1334806eee9b44e873ac42fe286bf8b0983491586d1225"
25
+ "linux/arm64": "sha256:91d35c9eac65f479f5139ba476b3daa3b517aab6f21f5206070af610cec5b62c",
26
+ "linux/amd64": "sha256:f308cb1702f308d69d9f21eb99aa109cdbf62e655549b483580476d8f01fb07b"
27
27
  }
28
28
  },
29
29
  "android": {
30
- "index": "sha256:ef6dc5ed724e7ea977a1f644793967a5ad7ff62b9a6bb6f2971d4a2a9e8f5a4b",
30
+ "index": "sha256:a01ddeaed550091f45e720d09bd559978d4c46e396c4553b3fc4544b90033fd1",
31
31
  "platforms": {
32
- "linux/amd64": "sha256:d6843be98e4bc56a21e39b2670d081f60d27b45c149c0ee132d2c45bde337ec8"
32
+ "linux/amd64": "sha256:3bafd0a3f80bf7370a19522a3b75cb58a977d1b1b28940ed02b87db1058a8fd6"
33
33
  }
34
34
  },
35
35
  "linux": {
36
- "index": "sha256:e8b6e6afd1af9f3a84eee739bd25ce6abb8416c5af87a175b780cd18d871513d",
36
+ "index": "sha256:c1d7f9672edb6eeea86172d8ba8cc8ba824f6c53134587ea247ea5dc126d6c43",
37
37
  "platforms": {
38
- "linux/amd64": "sha256:39fe90d1cb2fbb411823bc396d463e298020b5a93b9a5244f27644c5c0b87b2a",
39
- "linux/arm64": "sha256:9cd9550a39010bf7f00bf08d8505a22f85e31155bf63c8a46597852dc72ef4cc"
38
+ "linux/amd64": "sha256:0ff9ee1d5c139eb97934919f7b9535af0eb2c3bebd9c1974fa95cfb143e3ccf7",
39
+ "linux/arm64": "sha256:f476e4a95635c04aca741937c2174a1915731c52c9d182b247e17d4ca4163b33"
40
40
  }
41
41
  },
42
42
  "windows": {
43
- "index": "sha256:c90a7c495e8becc6154b20fc6226362813938413bb3af626d0d0982cceaf32c5",
43
+ "index": "sha256:53b08c8918a84023ea90c6420fff783564aced0ec711fa0e7b71a09578643a32",
44
44
  "platforms": {
45
- "linux/amd64": "sha256:1fbc5c59e09253cfe86bb54fa76f0c1c6b1c1e942e4059725658340f64708e59",
46
- "linux/arm64": "sha256:229389bbabe838124cbb572b91b42ae0e3fdcd6020462203fdbbcf5864c4a234"
45
+ "linux/arm64": "sha256:402f115c8eed9e6418cf9e47a6bcad2d456d15d032c9b2baaa54a5005f00c8ba",
46
+ "linux/amd64": "sha256:50f359882015ca93c8b356a01bf03ce4cb098307f72c30122c7b6ad123eae339"
47
47
  }
48
48
  }
49
49
  }
@@ -0,0 +1,17 @@
1
+ # HTTP component runtime
2
+
3
+ What an http build (`crossbind build -p wasi -e http`) links besides the app: `handler.cpp`, the
4
+ `wasi:http/handler` export that calls the app's `crossbind::http::handle` (`include/crossbind/http.h`),
5
+ and `bindings/`, which `wit-bindgen c` generated from `wit/`.
6
+
7
+ `wit/deps/` holds the `http` package of the WASI 0.3.0 release, unchanged: `wasi-wit-0.3.0.tar.gz` from
8
+ https://github.com/WebAssembly/WASI/releases/tag/v0.3.0 (sha256
9
+ `596182ecd79311c8d010389b78b0253d54bee596254002df504ea5cf0124b894`), licensed as `wit/deps/LICENSE.md` says.
10
+
11
+ After changing `wit/`, regenerate the bindings with wit-bindgen 0.62.0; with `--check` it confirms instead
12
+ that the committed ones, the `.o` included, are what it generates:
13
+
14
+ ```bash
15
+ wit-bindgen c wit --world crossbind-http --out-dir bindings
16
+ wit-bindgen c wit --world crossbind-http --out-dir bindings --check
17
+ ```