crossbind 2.0.0-beta.60 → 2.0.0-beta.63

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 (118) hide show
  1. package/README.md +8 -6
  2. package/package.json +6 -1
  3. package/src/actions/buildDependencies.js +37 -9
  4. package/src/actions/buildExternal.js +9 -1
  5. package/src/actions/buildJs.js +1 -1
  6. package/src/actions/buildLib.js +27 -8
  7. package/src/actions/buildNativeCommand.js +84 -0
  8. package/src/actions/buildNode.js +190 -0
  9. package/src/actions/buildTypes.js +8 -2
  10. package/src/actions/buildWasiCommand.js +0 -4
  11. package/src/actions/buildWasm.js +92 -95
  12. package/src/actions/cloudCommands.js +133 -0
  13. package/src/actions/createImportedBridges.js +64 -0
  14. package/src/actions/createInterface.js +318 -65
  15. package/src/actions/createLib.js +60 -43
  16. package/src/actions/getAllBridges.js +1 -1
  17. package/src/actions/getData.js +4 -1
  18. package/src/actions/getLinkInputs.js +70 -0
  19. package/src/actions/isSourceNewer.js +2 -0
  20. package/src/actions/licenses.js +175 -17
  21. package/src/actions/prepareConanDependencies.js +51 -0
  22. package/src/actions/publishBinary.js +32 -0
  23. package/src/actions/run.js +124 -32
  24. package/src/actions/runnerCommands.js +421 -0
  25. package/src/actions/writeRuntimeEntry.js +69 -0
  26. package/src/assets/cmake/CMakeLists.txt +1 -1
  27. package/src/assets/cmake/command/CMakeLists.txt +21 -0
  28. package/src/assets/cmake/command/command.cpp +1 -0
  29. package/src/assets/cmake/dist.cmake +2 -1
  30. package/src/assets/configure/install.sh +22 -0
  31. package/src/assets/js-runtime/adapters/path-url.js +6 -3
  32. package/src/assets/js-runtime/adapters/unboundMessage.js +2 -0
  33. package/src/assets/js-runtime/adapters/worker-comlink.js +87 -56
  34. package/src/assets/js-runtime/core.js +11 -0
  35. package/src/assets/node-import-hooks.mjs +61 -0
  36. package/src/assets/toolchain-digests.json +27 -13
  37. package/src/assets/wasi-runtime/stubs.c +29 -0
  38. package/src/bin.js +290 -37
  39. package/src/index.js +6 -1
  40. package/src/integration/bindHeaderImports.js +18 -0
  41. package/src/integration/getCrossbindScript.js +53 -19
  42. package/src/integration/getDependFilePath.js +18 -12
  43. package/src/integration/resolveNativeImport.js +14 -0
  44. package/src/node/dev.mjs +19 -0
  45. package/src/runner/blobs.js +135 -0
  46. package/src/runner/files.js +193 -0
  47. package/src/runner/server.js +320 -0
  48. package/src/state/attachConanDependencies.js +56 -0
  49. package/src/state/index.js +31 -20
  50. package/src/state/loadConfig.js +36 -24
  51. package/src/state/refreshBuiltDependencies.js +20 -0
  52. package/src/state/refreshConanDependencies.js +17 -0
  53. package/src/utils/androidToolchain.js +4 -0
  54. package/src/utils/appRustCrates.js +17 -1
  55. package/src/utils/appSources.js +112 -0
  56. package/src/utils/archiveFlags.js +12 -0
  57. package/src/utils/boundHeaders.js +79 -0
  58. package/src/utils/bridgeExtras.js +486 -0
  59. package/src/utils/bridgeRuntimeFixes.js +48 -0
  60. package/src/utils/bundledLicenses.js +26 -0
  61. package/src/utils/cargoImport.js +44 -0
  62. package/src/utils/cargoTarget.js +23 -3
  63. package/src/utils/cloudAccount.js +55 -0
  64. package/src/utils/conanDependencies.js +66 -0
  65. package/src/utils/conanImport.js +58 -0
  66. package/src/utils/conanInstall.js +154 -0
  67. package/src/utils/conanProfile.js +203 -0
  68. package/src/utils/conanStage.js +292 -0
  69. package/src/utils/copyToDistSource.js +13 -0
  70. package/src/utils/cppDts.js +134 -55
  71. package/src/utils/darwinToolchain.js +11 -0
  72. package/src/utils/dirLock.js +137 -28
  73. package/src/utils/downloadAndExtractFile.js +34 -7
  74. package/src/utils/embindArgumentGuards.js +84 -0
  75. package/src/utils/embindCallArguments.js +19 -0
  76. package/src/utils/embindJspiQueue.js +25 -0
  77. package/src/utils/embindRsFingerprint.js +26 -0
  78. package/src/utils/execContainer.js +49 -0
  79. package/src/utils/headerImports.js +56 -0
  80. package/src/utils/hostPlatform.js +21 -0
  81. package/src/utils/inputStamp.js +23 -0
  82. package/src/utils/iosToolchain.js +6 -0
  83. package/src/utils/isSourceCmakePackage.js +10 -0
  84. package/src/utils/licenseReport.js +11 -2
  85. package/src/utils/linkLayout.js +8 -1
  86. package/src/utils/linuxToolchain.js +24 -0
  87. package/src/utils/makeTreeWritable.js +12 -0
  88. package/src/utils/nativeImports.js +23 -0
  89. package/src/utils/nativeSourceStamp.js +36 -0
  90. package/src/utils/nodeDevBuild.js +92 -0
  91. package/src/utils/packageLicense.js +101 -0
  92. package/src/utils/pthreadMainScript.js +14 -0
  93. package/src/utils/pullDockerImage.js +10 -5
  94. package/src/utils/relocatePrebuilt.js +119 -0
  95. package/src/utils/remoteClient.js +127 -0
  96. package/src/utils/remoteOutputs.js +39 -0
  97. package/src/utils/remoteRunner.js +97 -0
  98. package/src/utils/remoteUpload.js +134 -0
  99. package/src/utils/replaceFile.js +8 -0
  100. package/src/utils/resolveCompanionPackage.js +51 -0
  101. package/src/utils/resolveEmbindNapi.js +22 -0
  102. package/src/utils/resolveEmbindRust.js +17 -52
  103. package/src/utils/runCargo.js +51 -53
  104. package/src/utils/runConan.js +257 -0
  105. package/src/utils/runtimeEntries.js +56 -0
  106. package/src/utils/rustBridgeGen.js +903 -253
  107. package/src/utils/rustMt.js +4 -0
  108. package/src/utils/scopedEmbind.js +22 -0
  109. package/src/utils/selectRunner.js +81 -0
  110. package/src/utils/sourceFingerprint.js +4 -3
  111. package/src/utils/swigInterface.js +86 -6
  112. package/src/utils/systemKeys.js +43 -3
  113. package/src/utils/targets.js +104 -3
  114. package/src/utils/toolchainNotices.js +5 -0
  115. package/src/utils/wasiToolchain.js +14 -1
  116. package/src/utils/windowsToolchain.js +18 -0
  117. package/vitest.config.js +11 -0
  118. package/src/utils/cppFieldBindings.js +0 -23
@@ -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,39 @@ 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
+
20
27
  function registerEmbindObject(obj) {
21
28
  const id = nextEmbindId++;
22
29
  embindRegistry.set(id, obj);
23
30
  return id;
24
31
  }
25
32
 
33
+ // Comlink answers every member with a remote one: JSON.stringify and String() called toJSON and Symbol.toPrimitive
34
+ // on the worker's object, which has neither, and a member below the module is thenable. A handle answers them as a
35
+ // direct-mode object does.
36
+ const LOCAL_MEMBERS = {
37
+ then: undefined,
38
+ toJSON: () => ({}),
39
+ [Symbol.toPrimitive]: (hint) => (hint === 'number' ? NaN : '[object Object]'),
40
+ };
41
+
42
+ function handleFor(id) {
43
+ const handle = new Proxy(remoteModule[HANDLES][id], {
44
+ get: (target, prop) => (Object.hasOwn(LOCAL_MEMBERS, prop) ? LOCAL_MEMBERS[prop] : target[prop]),
45
+ });
46
+ embindProxyIds.set(handle, id);
47
+ return handle;
48
+ }
49
+
26
50
  // Reorder transfer handlers for correct priority
27
51
  const _proxyHandler = Comlink.transferHandlers.get('proxy');
28
52
  const _throwHandler = Comlink.transferHandlers.get('throw');
@@ -47,22 +71,16 @@ Comlink.transferHandlers.set('proxy', {
47
71
  canHandle: _proxyHandler.canHandle,
48
72
  serialize(obj) {
49
73
  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];
74
+ // CONSTRUCT results (Comlink marks them with proxy()) are handed out as
75
+ // method-returned objects are (embindObject handler), and the registry keeps
76
+ // the RAW object so arguments resolve back to real embind identities.
77
+ return [{ __embindId: registerEmbindObject(unwrapCoercionProxy(obj)) }, []];
58
78
  }
59
79
  return _proxyHandler.serialize(obj);
60
80
  },
61
81
  deserialize(data) {
62
82
  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;
83
+ return handleFor(data.__embindId);
66
84
  }
67
85
  return _proxyHandler.deserialize(data);
68
86
  },
@@ -102,40 +120,18 @@ Comlink.transferHandlers.set('embindVector', {
102
120
  serialize(obj) {
103
121
  const len = obj.size();
104
122
  const elements = new Array(len);
105
- let hasObjects = false;
106
123
  for (let i = 0; i < len; i++) {
107
124
  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
- }
125
+ elements[i] = elem !== null && typeof elem === 'object'
126
+ ? { __comlinkProxy: true, __embindId: registerEmbindObject(elem) }
127
+ : elem;
126
128
  }
127
- return [elements, transferables];
129
+ return [elements, []];
128
130
  },
129
131
  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
- });
132
+ return elements.map((elem) => (elem && typeof elem === 'object' && elem.__comlinkProxy
133
+ ? handleFor(elem.__embindId)
134
+ : elem));
139
135
  },
140
136
  });
141
137
 
@@ -210,16 +206,13 @@ Comlink.transferHandlers.set('embindObject', {
210
206
  && typeof obj.isDeleted === 'function';
211
207
  },
212
208
  serialize(obj) {
213
- const id = registerEmbindObject(obj);
214
- const { port1, port2 } = new MessageChannel();
215
- Comlink.expose(wrapWithVectorCoercion(obj), port1);
216
- return [{ __embindId: id, port: port2 }, [port2]];
209
+ // An object a property returns comes wrapped by its owner's coercion proxy. Registering the raw object,
210
+ // as the proxy handler does, keeps objects read from it one wrapper deep, which an argument's unwrapping
211
+ // undoes.
212
+ return [{ __embindId: registerEmbindObject(unwrapCoercionProxy(obj)) }, []];
217
213
  },
218
214
  deserialize(data) {
219
- data.port.start();
220
- const proxy = Comlink.wrap(data.port);
221
- embindProxyIds.set(proxy, data.__embindId);
222
- return proxy;
215
+ return handleFor(data.__embindId);
223
216
  },
224
217
  });
225
218
 
@@ -247,16 +240,28 @@ Comlink.transferHandlers.set('embindProxyArray', {
247
240
 
248
241
  let _worker = null;
249
242
 
243
+ // Configurable, so the coercion wrapper wraps the objects read through it as it does every other member.
244
+ export function moduleRoot(m) {
245
+ Object.defineProperty(m, HANDLES, {
246
+ value: new Proxy({}, { get: (_, id) => embindRegistry.get(Number(id)) }),
247
+ configurable: true,
248
+ });
249
+ return wrapWithVectorCoercion(m);
250
+ }
251
+
252
+ export function adoptModule(remote) {
253
+ remoteModule = remote;
254
+ return remote;
255
+ }
256
+
250
257
  function resolveScriptUrl(config) {
251
258
  const fileName = config.paths.js || config.paths.worker;
252
- let prefix = '';
259
+ let prefix = urlPath.getDefaultPathPrefix();
253
260
  if (config.path) {
254
261
  prefix = config.path;
255
262
  if (prefix.slice(-1) !== '/') prefix += '/';
256
263
  }
257
- let output = prefix + fileName;
258
- if (output.substring(0, 4) !== 'http' && output[0] !== '/') output = `/${output}`;
259
- return output;
264
+ return urlPath.finalizePath(prefix + fileName);
260
265
  }
261
266
 
262
267
  function exposeWorker(systemConfig, createModule) {
@@ -269,7 +274,7 @@ function exposeWorker(systemConfig, createModule) {
269
274
  // comlink throw handler, a raw WebAssembly.Exception does not.
270
275
  patchModuleForExceptionDecode(m);
271
276
  registerModuleEnums(m);
272
- return Comlink.proxy(wrapWithVectorCoercion(m));
277
+ return Comlink.proxy(moduleRoot(m));
273
278
  },
274
279
  };
275
280
  Comlink.expose(workerApi);
@@ -284,10 +289,35 @@ async function initWithWorker(config, userConfig) {
284
289
  logHandler, errorHandler, onRuntimeInitialized, getWasmFunction, useWorker, workerUrl,
285
290
  ...serializableConfig
286
291
  } = userConfig;
287
- const module = await workerApi.init(serializableConfig);
292
+ // The worker resolves a relative URL against its own script, so it gets the path resolved against the page.
293
+ const workerConfig = config.path ? { ...serializableConfig, path: urlPath.finalizePath(config.path) } : serializableConfig;
294
+ return moduleOnMainThread(adoptModule(await workerApi.init(workerConfig)));
295
+ }
296
+
297
+ // A call the worker cannot make, because the module lacks the function, fails there with a TypeError from inside comlink
298
+ // that names nothing: the failed call asks for the member, and names it when the module has none.
299
+ function namedCall(member, name, module) {
300
+ return new Proxy(member, {
301
+ // A member still needs Comlink's then to read its value (including an absent export).
302
+ get(target, prop) {
303
+ // Console formatters inspect function names and constructors synchronously.
304
+ if (prop === 'name') return name;
305
+ if (prop === 'constructor') return Function;
306
+ return prop !== 'then' && Object.hasOwn(LOCAL_MEMBERS, prop) ? LOCAL_MEMBERS[prop] : target[prop];
307
+ },
308
+ apply: (call, thisArg, args) => Reflect.apply(call, thisArg, args).catch(async (error) => {
309
+ const isBound = await Promise.resolve(module[name]).then((value) => value !== undefined, () => true);
310
+ if (isBound) throw error;
311
+ throw new Error(`crossbind: ${name} ${UNBOUND_MESSAGE}`);
312
+ }),
313
+ });
314
+ }
288
315
 
316
+ export function moduleOnMainThread(module) {
289
317
  return new Proxy(module, {
290
318
  get(target, prop) {
319
+ if (Object.hasOwn(LOCAL_MEMBERS, prop)) return LOCAL_MEMBERS[prop];
320
+ if (prop === 'constructor') return Object;
291
321
  if (prop === 'toArray') {
292
322
  return function toArray(vector) {
293
323
  if (Array.isArray(vector)) return vector;
@@ -302,7 +332,8 @@ async function initWithWorker(config, userConfig) {
302
332
  return target.toVector(classOrName, array);
303
333
  };
304
334
  }
305
- return target[prop];
335
+ const member = target[prop];
336
+ return typeof prop === 'string' && typeof member === 'function' ? namedCall(member, prop, target) : member;
306
337
  },
307
338
  });
308
339
  }
@@ -73,6 +73,16 @@ 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.
78
+ function replaceOnWriteFile(FS) {
79
+ const { writeFile } = FS;
80
+ FS.writeFile = (path, data) => {
81
+ FS.close(FS.open(path, 'w'));
82
+ return writeFile(path, data);
83
+ };
84
+ }
85
+
76
86
  export function createBaseModule(Module, config, adapter) {
77
87
  return new Promise((resolve, reject) => {
78
88
  const locateFile = buildLocateFile(config, adapter);
@@ -137,6 +147,7 @@ export function createBaseModule(Module, config, adapter) {
137
147
  // fs-browser), which emscripten's onRuntimeInitialized callback cannot, so
138
148
  // both hooks run after the factory settles - adapter first, then the user's.
139
149
  Module(m).then(async (mod) => {
150
+ if (mod.FS) replaceOnWriteFile(mod.FS);
140
151
  if (adapter.onModuleReady) await adapter.onModuleReady(mod, config);
141
152
  if (config.onRuntimeInitialized) config.onRuntimeInitialized(mod);
142
153
  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,35 +1,49 @@
1
1
  {
2
- "version": "1.0.6",
2
+ "version": "1.0.12",
3
3
  "registry": "ghcr.io/crossbind",
4
4
  "toolchains": {
5
- "rust": "1.98.1"
5
+ "rust": "1.99.0"
6
6
  },
7
7
  "images": {
8
8
  "rust-sysroot": {
9
- "index": "sha256:b82285c6c74f0f2f21bd4f3d07af8a005b16b9e8faaecbe672f6e9b139616eb7",
9
+ "index": "sha256:fcaddb9514956c80c28152679d11817599b3c544e2bb56568d79dbd7699af805",
10
10
  "platforms": {
11
- "linux/arm64": "sha256:50f2bc41d084157bb2d63caa8a91486ea15d3aff248e0d84013a20c7b8cf0c57",
12
- "linux/amd64": "sha256:65c63d0b3df31406c514a6ed9dbb5ba0940b27c94e2e89b3ceafedaee339bca8"
11
+ "linux/arm64": "sha256:70dca2248666ee712ad5cb11fd013274ca4bb1efdafee732a0ec15492889f622",
12
+ "linux/amd64": "sha256:a3377c70598ef4e1918a73feeebb0ec1b80b3cde956daac9b2b62f9d742b88dc"
13
13
  }
14
14
  },
15
15
  "base": {
16
- "index": "sha256:3a742a779a68e0180ca43232b9e28047e469462c575f1cdef8a3af3ba984263b",
16
+ "index": "sha256:86c6559bd51c10f8fc1ff6cbc0cf5386115fd7f0491a59f30f0d26a0203c200d",
17
17
  "platforms": {
18
- "linux/amd64": "sha256:ff2a42fe5cdff75a9ce89fde340dbde5655f639de2e1288baeb61f2e82d1d095",
19
- "linux/arm64": "sha256:2571179e6597369544a887da197e280ad9d15075719c04113cc2fd243358f56d"
18
+ "linux/amd64": "sha256:16056b4bd85ba1792791401003db0325c31d4aa4821ff40f6f2eb1e0fff1d3ce",
19
+ "linux/arm64": "sha256:8d213f83ddd1ad9101b74b2b071f304ac8924ffbe68d79ece50668076e0172c0"
20
20
  }
21
21
  },
22
22
  "web": {
23
- "index": "sha256:cd9065909b8e4aea584fbe6f6e0215773feaed97ab46e5ce265a487171f87757",
23
+ "index": "sha256:ffa089b83160e380e0b4447f483bf66cb87324f89b8e80b5776ed9346275a5ca",
24
24
  "platforms": {
25
- "linux/arm64": "sha256:224665ea653e6ff6e483fc24b226fa68bf45173a2598439665d827780cbe5534",
26
- "linux/amd64": "sha256:862b197c13dbdf163d480d35e8b17f96b23b447701cb8e79f0252773d7a48a42"
25
+ "linux/arm64": "sha256:1baf14ee0f1a821c1562d12902fe362d3639f57acb31f969402f2a2d8486647f",
26
+ "linux/amd64": "sha256:d3bd3aa785447f1679fdcb898921f614579d9eaad2aa9e828675ba7f82dc11ec"
27
27
  }
28
28
  },
29
29
  "android": {
30
- "index": "sha256:0d0b56a76cff5b16f52eba3a4d2b17bc888fb2ca502bfd0ae029bf837c6c354d",
30
+ "index": "sha256:830286886d8bb259e94e112e4f1bab5f153106365b17859d20c9ef714349e520",
31
31
  "platforms": {
32
- "linux/amd64": "sha256:77fb9007369c5c8003024aade231642afb798664f82aada1a8ac26b931a15fbf"
32
+ "linux/amd64": "sha256:1004144243fea89a8915417954073e8ffabb20d8a9aed31045ce2a95c1abe1da"
33
+ }
34
+ },
35
+ "linux": {
36
+ "index": "sha256:8d4cc55d2ef8ea40845fd13708575e06f6aecdc035e7200deddf8c662d5c910f",
37
+ "platforms": {
38
+ "linux/arm64": "sha256:0de59cc6ce7b381f213af0d26ccc83e0c41545a91eb0b36c51832c7fb660566c",
39
+ "linux/amd64": "sha256:3b3bc24d8f5dffe3a2408193265a807764fcf2744243c0a5e07f312f608fc6c0"
40
+ }
41
+ },
42
+ "windows": {
43
+ "index": "sha256:a31110874a9b6b9e3fec177edc8a8093905902bcc6abc6a85e45eb0927d4f070",
44
+ "platforms": {
45
+ "linux/arm64": "sha256:bd9ec6ba60952f929e09777374a3bb05512bbe796325c0e8a0b84e1a12d1b86c",
46
+ "linux/amd64": "sha256:01f4bd8c4d1044bde68fe758d2632c510f8efb76aa4bad28696f9e9b199c8f4e"
33
47
  }
34
48
  }
35
49
  }
@@ -7,12 +7,32 @@
7
7
 
8
8
  #include <netinet/in.h>
9
9
  #include <sys/socket.h>
10
+ #include <sys/stat.h>
11
+ #include <unistd.h>
10
12
 
11
13
  /* Compiled by the C++ driver at the command link; keep C linkage. */
12
14
  #ifdef __cplusplus
13
15
  extern "C" {
14
16
  #endif
15
17
 
18
+ /* wasi-libc p2/p3 leaves its output stream at the old offset after SEEK_END. SEEK_SET resets
19
+ that stream, so resolve the end offset through fstat and perform an absolute seek instead. */
20
+ off_t __real_lseek(int fd, off_t offset, int whence);
21
+ off_t __wrap_lseek(int fd, off_t offset, int whence)
22
+ {
23
+ if (whence == SEEK_END) {
24
+ struct stat status;
25
+ if (fstat(fd, &status) != 0) return (off_t)-1;
26
+ off_t absolute;
27
+ if (__builtin_add_overflow(status.st_size, offset, &absolute)) {
28
+ errno = EOVERFLOW;
29
+ return (off_t)-1;
30
+ }
31
+ return __real_lseek(fd, absolute, SEEK_SET);
32
+ }
33
+ return __real_lseek(fd, offset, whence);
34
+ }
35
+
16
36
  /* No dynamic loading on WASI; loaders treat NULL as "plugin unavailable". */
17
37
  void *dlopen(const char *file, int mode)
18
38
  {
@@ -79,6 +99,15 @@ int getpeername(int fd, struct sockaddr *addr, socklen_t *len)
79
99
  return crossbind_zeroed_inet(addr, len);
80
100
  }
81
101
 
102
+ /* The WASI SQLite leaves extension loading out, yet SpatiaLite's stored procedures switch it off; this answers
103
+ SQLITE_ERROR as such a build does. Weak, so a SQLite that has the function keeps its own. */
104
+ __attribute__((weak)) int sqlite3_enable_load_extension(void *db, int onoff)
105
+ {
106
+ (void)db;
107
+ (void)onoff;
108
+ return 1;
109
+ }
110
+
82
111
  #ifdef __cplusplus
83
112
  }
84
113
  #endif