use wasm-runtime from NPM instead of patched sources

This commit is contained in:
Nikita Sivukhin
2025-09-23 12:28:01 +04:00
parent 9d395a5a52
commit 1faafdb57e
15 changed files with 48 additions and 24928 deletions

View File

@@ -5,7 +5,7 @@ import {
WASI as __WASI,
instantiateNapiModuleSync,
MessageHandler
} from '@tursodatabase/wasm-runtime'
} from '@napi-rs/wasm-runtime'
function getUint8ArrayFromMemory(memory: WebAssembly.Memory, ptr: number, len: number): Uint8Array {
ptr = ptr >>> 0;

View File

@@ -23,6 +23,6 @@
"test": "echo 'no tests'"
},
"dependencies": {
"@tursodatabase/wasm-runtime": "^0.2.0-pre.7"
"@napi-rs/wasm-runtime": "^1.0.5"
}
}

View File

@@ -1 +0,0 @@
!dist

View File

@@ -1,44 +0,0 @@
MIT License
Copyright (c) 2020-present LongYinan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
MIT License
Copyright (c) 2018 GitHub
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,264 +0,0 @@
'use strict';
// @ts-check
/**
* @param {unknown} value
*/
const getType = (value) => {
if (value === undefined) return 0
if (value === null) return 1
const t = typeof value;
if (t === 'boolean') return 2
if (t === 'number') return 3
if (t === 'string') return 4
if (t === 'object') return 6
if (t === 'bigint') return 9
return -1
};
/**
* @param {import('memfs').IFs} memfs
* @param {any} value
* @param {ReturnType<typeof getType>} type
* @returns {Uint8Array}
*/
const encodeValue = (memfs, value, type) => {
switch (type) {
case 0:
case 1:
return new Uint8Array(0)
case 2: {
const view = new Int32Array(1);
view[0] = value ? 1 : 0;
return new Uint8Array(view.buffer)
}
case 3: {
const view = new Float64Array(1);
view[0] = value;
return new Uint8Array(view.buffer)
}
case 4: {
const view = new TextEncoder().encode(value);
return view
}
case 6: {
function storeConstructor(obj, memfs, processed = new WeakSet()) {
if (!obj || typeof obj !== 'object') {
return
}
if (processed.has(obj)) {
return
}
processed.add(obj);
const [entry] =
Object.entries(memfs).filter(([_, v]) => v === obj.constructor)[0] ??
[];
if (entry) {
Object.defineProperty(obj, '__constructor__', {
configurable: true,
writable: true,
enumerable: true,
value: entry,
});
}
for (const value of Object.values(obj)) {
storeConstructor(value, memfs, processed);
}
}
storeConstructor(value, memfs);
const json = JSON.stringify(value, (_, value) => {
if (typeof value === 'bigint') {
return `BigInt(${String(value)})`
}
if (value instanceof Error) {
return {
...value,
message: value.message,
stack: value.stack,
__error__: value.constructor.name,
}
}
return value
});
const view = new TextEncoder().encode(json);
return view
}
case 9: {
const view = new BigInt64Array(1);
view[0] = value;
return new Uint8Array(view.buffer)
}
case -1:
default:
throw new Error('unsupported data')
}
};
/**
* @param {typeof import('memfs')} memfs
* @param {Uint8Array} payload
* @param {number} type
* @returns {any}
*/
const decodeValue = (memfs, payload, type) => {
if (type === 0) return undefined
if (type === 1) return null
if (type === 2)
return Boolean(new Int32Array(payload.buffer, payload.byteOffset, 1)[0])
if (type === 3)
return new Float64Array(payload.buffer, payload.byteOffset, 1)[0]
if (type === 4) return new TextDecoder().decode(payload.slice())
if (type === 6) {
const obj = JSON.parse(
new TextDecoder().decode(payload.slice()),
(_key, value) => {
if (typeof value === 'string') {
const matched = value.match(/^BigInt\((-?\d+)\)$/);
if (matched && matched[1]) {
return BigInt(matched[1])
}
}
return value
},
);
function loadConstructor(obj, memfs, processed = new WeakSet()) {
if (!obj || typeof obj !== 'object') {
return
}
if (processed.has(obj)) {
return
}
processed.add(obj);
if (obj.__constructor__) {
const ctor = obj.__constructor__;
delete obj.__constructor__;
Object.setPrototypeOf(obj, memfs[ctor].prototype);
}
for (const value of Object.values(obj)) {
loadConstructor(value, memfs, processed);
}
}
loadConstructor(obj, memfs);
if (obj.__error__) {
const name = obj.__error__;
const ErrorConstructor = globalThis[name] || Error;
delete obj.__error__;
const err = new ErrorConstructor(obj.message);
Object.defineProperty(err, 'stack', {
configurable: true,
enumerable: false,
writable: true,
value: err.stack,
});
Object.defineProperty(err, Symbol.toStringTag, {
configurable: true,
enumerable: false,
writable: true,
value: name,
});
for (const [k, v] of Object.entries(obj)) {
if (k === 'message' || k === 'stack') continue
err[k] = v;
}
return err
}
return obj
}
if (type === 9)
return new BigInt64Array(payload.buffer, payload.byteOffset, 1)[0]
throw new Error('unsupported data')
};
/**
* @param {import('memfs').IFs} fs
* @returns {(e: { data: { __fs__: { sab: Int32Array, type: keyof import('memfs').IFs, payload: any[] } } }) => void}
*/
// oxlint-disable-next-line no-unused-vars -- fixed in an upcoming release
const createOnMessage = (fs) =>
function onMessage(e) {
if (e.data.__fs__) {
/**
* 0..4 status(int32_t): 21(waiting) 0(success) 1(error)
* 5..8 type(napi_valuetype): 0(undefined) 1(null) 2(boolean) 3(number) 4(string) 6(jsonstring) 9(bigint) -1(unsupported)
* 9..16 payload_size(uint32_t) <= 1024
* 16..16 + payload_size payload_content
*/
const { sab, type, payload } = e.data.__fs__;
const fn = fs[type];
try {
const ret = fn.apply(fs, payload);
const t = getType(ret);
Atomics.store(sab, 1, t);
const v = encodeValue(fs, ret, t);
Atomics.store(sab, 2, v.length);
new Uint8Array(sab.buffer).set(v, 16);
Atomics.store(sab, 0, 0); // success
} catch (/** @type {any} */ err) {
const t = getType(err);
Atomics.store(sab, 1, t);
const v = encodeValue(fs, err, t);
Atomics.store(sab, 2, v.length);
new Uint8Array(sab.buffer).set(v, 16);
Atomics.store(sab, 0, 1); // error
} finally {
Atomics.notify(sab, 0);
}
}
};
/**
* @param {typeof import('memfs')} memfs
*/
const createFsProxy = (memfs) =>
new Proxy(
{},
{
get(_target, p, _receiver) {
/**
* @param {any[]} args
*/
return function (...args) {
const sab = new SharedArrayBuffer(16 + 10240);
const i32arr = new Int32Array(sab);
Atomics.store(i32arr, 0, 21);
postMessage({
__fs__: {
sab: i32arr,
type: p,
payload: args,
},
});
Atomics.wait(i32arr, 0, 21);
const status = Atomics.load(i32arr, 0);
const type = Atomics.load(i32arr, 1);
const size = Atomics.load(i32arr, 2);
const content = new Uint8Array(sab, 16, size);
const value = decodeValue(memfs, content, type);
if (status === 1) {
throw value
}
return value
}
},
},
);
exports.createFsProxy = createFsProxy;
exports.createOnMessage = createOnMessage;

File diff suppressed because one or more lines are too long

View File

@@ -1,259 +0,0 @@
// @ts-check
/**
* @param {unknown} value
*/
const getType = (value) => {
if (value === undefined) return 0
if (value === null) return 1
const t = typeof value
if (t === 'boolean') return 2
if (t === 'number') return 3
if (t === 'string') return 4
if (t === 'object') return 6
if (t === 'bigint') return 9
return -1
}
/**
* @param {import('memfs').IFs} memfs
* @param {any} value
* @param {ReturnType<typeof getType>} type
* @returns {Uint8Array}
*/
const encodeValue = (memfs, value, type) => {
switch (type) {
case 0:
case 1:
return new Uint8Array(0)
case 2: {
const view = new Int32Array(1)
view[0] = value ? 1 : 0
return new Uint8Array(view.buffer)
}
case 3: {
const view = new Float64Array(1)
view[0] = value
return new Uint8Array(view.buffer)
}
case 4: {
const view = new TextEncoder().encode(value)
return view
}
case 6: {
function storeConstructor(obj, memfs, processed = new WeakSet()) {
if (!obj || typeof obj !== 'object') {
return
}
if (processed.has(obj)) {
return
}
processed.add(obj)
const [entry] =
Object.entries(memfs).filter(([_, v]) => v === obj.constructor)[0] ??
[]
if (entry) {
Object.defineProperty(obj, '__constructor__', {
configurable: true,
writable: true,
enumerable: true,
value: entry,
})
}
for (const value of Object.values(obj)) {
storeConstructor(value, memfs, processed)
}
}
storeConstructor(value, memfs)
const json = JSON.stringify(value, (_, value) => {
if (typeof value === 'bigint') {
return `BigInt(${String(value)})`
}
if (value instanceof Error) {
return {
...value,
message: value.message,
stack: value.stack,
__error__: value.constructor.name,
}
}
return value
})
const view = new TextEncoder().encode(json)
return view
}
case 9: {
const view = new BigInt64Array(1)
view[0] = value
return new Uint8Array(view.buffer)
}
case -1:
default:
throw new Error('unsupported data')
}
}
/**
* @param {typeof import('memfs')} memfs
* @param {Uint8Array} payload
* @param {number} type
* @returns {any}
*/
const decodeValue = (memfs, payload, type) => {
if (type === 0) return undefined
if (type === 1) return null
if (type === 2)
return Boolean(new Int32Array(payload.buffer, payload.byteOffset, 1)[0])
if (type === 3)
return new Float64Array(payload.buffer, payload.byteOffset, 1)[0]
if (type === 4) return new TextDecoder().decode(payload.slice())
if (type === 6) {
const obj = JSON.parse(
new TextDecoder().decode(payload.slice()),
(_key, value) => {
if (typeof value === 'string') {
const matched = value.match(/^BigInt\((-?\d+)\)$/)
if (matched && matched[1]) {
return BigInt(matched[1])
}
}
return value
},
)
function loadConstructor(obj, memfs, processed = new WeakSet()) {
if (!obj || typeof obj !== 'object') {
return
}
if (processed.has(obj)) {
return
}
processed.add(obj)
if (obj.__constructor__) {
const ctor = obj.__constructor__
delete obj.__constructor__
Object.setPrototypeOf(obj, memfs[ctor].prototype)
}
for (const value of Object.values(obj)) {
loadConstructor(value, memfs, processed)
}
}
loadConstructor(obj, memfs)
if (obj.__error__) {
const name = obj.__error__
const ErrorConstructor = globalThis[name] || Error
delete obj.__error__
const err = new ErrorConstructor(obj.message)
Object.defineProperty(err, 'stack', {
configurable: true,
enumerable: false,
writable: true,
value: err.stack,
})
Object.defineProperty(err, Symbol.toStringTag, {
configurable: true,
enumerable: false,
writable: true,
value: name,
})
for (const [k, v] of Object.entries(obj)) {
if (k === 'message' || k === 'stack') continue
err[k] = v
}
return err
}
return obj
}
if (type === 9)
return new BigInt64Array(payload.buffer, payload.byteOffset, 1)[0]
throw new Error('unsupported data')
}
/**
* @param {import('memfs').IFs} fs
* @returns {(e: { data: { __fs__: { sab: Int32Array, type: keyof import('memfs').IFs, payload: any[] } } }) => void}
*/
// oxlint-disable-next-line no-unused-vars -- fixed in an upcoming release
export const createOnMessage = (fs) =>
function onMessage(e) {
if (e.data.__fs__) {
/**
* 0..4 status(int32_t): 21(waiting) 0(success) 1(error)
* 5..8 type(napi_valuetype): 0(undefined) 1(null) 2(boolean) 3(number) 4(string) 6(jsonstring) 9(bigint) -1(unsupported)
* 9..16 payload_size(uint32_t) <= 1024
* 16..16 + payload_size payload_content
*/
const { sab, type, payload } = e.data.__fs__
const fn = fs[type]
try {
const ret = fn.apply(fs, payload)
const t = getType(ret)
Atomics.store(sab, 1, t)
const v = encodeValue(fs, ret, t)
Atomics.store(sab, 2, v.length)
new Uint8Array(sab.buffer).set(v, 16)
Atomics.store(sab, 0, 0) // success
} catch (/** @type {any} */ err) {
const t = getType(err)
Atomics.store(sab, 1, t)
const v = encodeValue(fs, err, t)
Atomics.store(sab, 2, v.length)
new Uint8Array(sab.buffer).set(v, 16)
Atomics.store(sab, 0, 1) // error
} finally {
Atomics.notify(sab, 0)
}
}
}
/**
* @param {typeof import('memfs')} memfs
*/
export const createFsProxy = (memfs) =>
new Proxy(
{},
{
get(_target, p, _receiver) {
/**
* @param {any[]} args
*/
return function (...args) {
const sab = new SharedArrayBuffer(16 + 10240)
const i32arr = new Int32Array(sab)
Atomics.store(i32arr, 0, 21)
postMessage({
__fs__: {
sab: i32arr,
type: p,
payload: args,
},
})
Atomics.wait(i32arr, 0, 21)
const status = Atomics.load(i32arr, 0)
const type = Atomics.load(i32arr, 1)
const size = Atomics.load(i32arr, 2)
const content = new Uint8Array(sab, 16, size)
const value = decodeValue(memfs, content, type)
if (status === 1) {
throw value
}
return value
}
},
},
)

View File

@@ -1,42 +0,0 @@
{
"name": "@tursodatabase/wasm-runtime",
"version": "0.2.0-pre.7",
"type": "module",
"description": "Runtime and polyfill for wasm targets",
"author": {
"name": "LongYinan",
"url": "https://github.com/Brooooooklyn"
},
"repository": {
"type": "git",
"url": "https://github.com/tursodatabase/turso"
},
"license": "MIT",
"files": [
"runtime.cjs",
"runtime.js",
"fs-proxy.js",
"dist/*.cjs",
"dist/*.js",
"LICENSE"
],
"dependencies": {
"@emnapi/core": "^1.4.5",
"@emnapi/runtime": "^1.4.5",
"@tybys/wasm-util": "^0.10.1"
},
"scripts": {
"build": "echo 'nothing to build'",
"tsc-build": "echo 'nothing to tsc-build'",
"test": "echo 'nothing to test'"
},
"exports": {
".": {
"import": "./runtime.js",
"require": "./runtime.cjs"
},
"./fs": {
"import": "./dist/fs.js"
}
}
}

View File

@@ -1,15 +0,0 @@
const { MessageHandler, instantiateNapiModuleSync, instantiateNapiModule } = require('@emnapi/core')
const { getDefaultContext } = require('@emnapi/runtime')
const { WASI } = require('@tybys/wasm-util')
const { createFsProxy, createOnMessage } = require('./dist/fs-proxy.cjs')
module.exports = {
MessageHandler,
instantiateNapiModule,
instantiateNapiModuleSync,
getDefaultContext,
WASI,
createFsProxy,
createOnMessage,
}

View File

@@ -1,8 +0,0 @@
export {
instantiateNapiModuleSync,
instantiateNapiModule,
MessageHandler,
} from '@emnapi/core'
export { getDefaultContext } from '@emnapi/runtime'
export * from '@tybys/wasm-util'
export { createOnMessage, createFsProxy } from './fs-proxy.js'