feat: integrate with emnapi (#1669)

* Integrate with emnapi

* resolve conflict

* ignore wasm

* generate wasi file

* Add wasi test to workflow

* Fix wasi template

* emnapi new initialize api

* Finish test

* Purne tsconfig

* Generate wasi worker

* Fix electron test

* Finalize check

* Noop adjust_external_memory

* Apply cr suggestions
This commit is contained in:
LongYinan 2023-11-02 12:57:11 +08:00 committed by GitHub
parent 69c0223b9b
commit 13d0ce075e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
49 changed files with 1387 additions and 313 deletions

2
.cargo/config.toml Normal file
View file

@ -0,0 +1,2 @@
[target.'cfg(target_os = "wasi")']
rustflags = ["-C", "target-feature=+atomics,+bulk-memory"]

View file

@ -14,4 +14,6 @@ target
scripts
triples/index.js
rollup.config.js
crates/cli/index.js
crates/cli/index.js
examples/napi/index.wasi.mjs
examples/napi/wasi-worker.mjs

View file

@ -5,7 +5,8 @@ parserOptions:
jsx: true
ecmaVersion: latest
sourceType: module
extraFileExtensions: ['.cjs', '.mjs']
project:
- ./tsconfig.root-lint.json
env:
browser: true
@ -23,6 +24,7 @@ extends:
globals:
BigInt: 'readonly'
NodeJS: 'readonly'
globalThis: true
rules:
# 0 = off, 1 = warn, 2 = error
@ -205,6 +207,15 @@ overrides:
project:
- ./examples/tsconfig.json
rules:
'import/order':
[
2,
{
'newlines-between': 'always',
'alphabetize': { 'order': 'asc', 'caseInsensitive': true },
},
]
'import/no-extraneous-dependencies': 0
- files:
@ -223,6 +234,8 @@ overrides:
- files:
- ./*.js
- ./*.cjs
- ./*.mjs
plugins:
- '@typescript-eslint'
parserOptions:

View file

@ -63,7 +63,7 @@ jobs:
strategy:
fail-fast: false
matrix:
node: ['16', '18', '20']
node: ['18', '20']
settings:
- host: ubuntu-latest
target: x86_64-unknown-linux-gnu
@ -71,7 +71,7 @@ jobs:
test: |
yarn test:cli
yarn test --verbose
yarn tsc -p examples/napi/tsconfig.json --noEmit
yarn tsc -p examples/napi/tsconfig.json --noEmit --skipLibCheck
yarn test:macro
toolchain: stable
- host: ubuntu-latest
@ -80,7 +80,7 @@ jobs:
test: |
yarn test:cli
yarn test --verbose
yarn tsc -p examples/napi/tsconfig.json --noEmit
yarn tsc -p examples/napi/tsconfig.json --noEmit --skipLibCheck
yarn test:macro
toolchain: 1.65.0
- host: macos-latest
@ -89,7 +89,7 @@ jobs:
test: |
yarn test:cli
yarn test --verbose
yarn tsc -p examples/napi/tsconfig.json --noEmit
yarn tsc -p examples/napi/tsconfig.json --noEmit --skipLibCheck
yarn test:macro
toolchain: stable
- host: windows-latest
@ -98,7 +98,7 @@ jobs:
test: |
yarn test:cli
yarn test --verbose
yarn tsc -p examples/napi/tsconfig.json --noEmit
yarn tsc -p examples/napi/tsconfig.json --noEmit --skipLibCheck
yarn test:macro
toolchain: stable
- host: windows-latest
@ -118,9 +118,6 @@ jobs:
- settings:
toolchain: 1.65.0
node: 20
- settings:
target: i686-pc-windows-msvc
node: 16
- settings:
target: i686-pc-windows-msvc
node: 18
@ -311,7 +308,7 @@ jobs:
strategy:
fail-fast: false
matrix:
node: [16, 18, 20]
node: [18, 20]
settings:
- image: 'node:{:version}-slim'
target: x86_64-unknown-linux-gnu
@ -486,6 +483,42 @@ jobs:
- name: Check build
run: cargo check -p ${{ matrix.settings.package }} -F ${{ matrix.settings.features }}
test-node-wasi:
runs-on: ubuntu-latest
name: Test node wasi target
timeout-minutes: 10
continue-on-error: true
steps:
- uses: actions/checkout@v4
- name: Setup node
uses: actions/setup-node@v3
with:
node-version: 18
cache: 'yarn'
- name: Install
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
targets: wasm32-wasi-preview1-threads
- name: Cache cargo
uses: actions/cache@v3
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: stable-wasm32-wasi-preview1-threads-node@18-cargo-cache
- name: Install dependencies
run: yarn install --immutable --mode=skip-build
- name: Build
run: |
yarn build
yarn workspace @examples/napi build --target wasm32-wasi-preview1-threads
- name: Test
run: yarn workspace @examples/napi test -s
env:
WASI_TEST: 'true'
test-latest-bun:
runs-on: ubuntu-latest
name: Test latest bun

View file

@ -53,7 +53,7 @@ jobs:
- name: Install ziglang
uses: goto-bus-stop/setup-zig@v2
with:
version: 0.10.1
version: 0.11.0
- name: Install dependencies
run: yarn install --immutable --mode=skip-build
- name: install MacOS SDK

View file

@ -68,24 +68,48 @@
},
"dependencies": {
"@octokit/rest": "^20.0.1",
"@tybys/wasm-util": "0.8.0",
"clipanion": "^3.2.1",
"colorette": "^2.0.20",
"debug": "^4.3.4",
"emnapi": "0.43.1",
"inquirer": "^9.2.8",
"js-yaml": "^4.1.0",
"lodash-es": "^4.17.21",
"toml": "^3.0.0",
"typanion": "^3.13.0"
},
"devDependencies": {
"@emnapi/core": "0.43.1",
"@emnapi/runtime": "0.43.1",
"@types/debug": "^4.1.7",
"@types/inquirer": "^9.0.3",
"@types/js-yaml": "^4.0.5",
"@types/lodash-es": "^4.17.8",
"ava": "^5.3.1",
"esbuild": "^0.19.0",
"env-paths": "^3.0.0",
"esbuild": "^0.19.5",
"prettier": "^3.0.0",
"ts-node": "^10.9.1",
"tslib": "^2.6.1",
"typescript": "^5.1.6"
},
"peerDependencies": {
"@emnapi/runtime": "0.43.1",
"@tybys/wasm-util": "0.8.0",
"emnapi": "0.43.1"
},
"peerDependenciesMeta": {
"@emnapi/runtime": {
"optional": true
},
"@tybys/wasm-util": {
"optional": true
},
"emnapi": {
"optional": true
}
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Brooooooklyn"

View file

@ -1,4 +1,4 @@
import { join, parse, resolve } from 'path'
import { join, parse, resolve } from 'node:path'
import * as colors from 'colorette'

View file

@ -1,7 +1,8 @@
import { spawn } from 'child_process'
import { createHash } from 'crypto'
import { tmpdir } from 'os'
import { parse, join, resolve } from 'path'
import { spawn } from 'node:child_process'
import { createHash } from 'node:crypto'
import { createRequire } from 'node:module'
import { tmpdir } from 'node:os'
import { parse, join, resolve } from 'node:path'
import * as colors from 'colorette'
@ -20,6 +21,7 @@ import {
parseMetadata,
parseTriple,
processTypeDef,
readFileAsync,
readNapiConfig,
Target,
targetToEnvVar,
@ -28,11 +30,14 @@ import {
writeFileAsync,
} from '../utils/index.js'
import { createWasiBinding } from './load-wasi-template.js'
import { createCjsBinding } from './templates/index.js'
import { WASI_WORKER_TEMPLATE } from './wasi-worker-template.js'
const debug = debugFactory('build')
const require = createRequire(import.meta.url)
type OutputKind = 'js' | 'dts' | 'node' | 'exe'
type OutputKind = 'js' | 'dts' | 'node' | 'exe' | 'wasm'
type Output = {
kind: OutputKind
path: string
@ -383,6 +388,16 @@ class Builder {
}
// END LINKER
if (this.target.platform === 'wasi') {
const emnapi = join(
require.resolve('emnapi'),
'..',
'lib',
'wasm32-wasi-threads',
)
this.envs.EMNAPI_LINK_DIR = emnapi
}
debug('Set envs: ')
Object.entries(this.envs).forEach(([k, v]) => {
debug(' %i', `${k}=${v}`)
@ -471,7 +486,34 @@ class Builder {
// only for cdylib
if (this.cdyLibName) {
const idents = await this.generateTypeDef()
await this.writeJsBinding(idents)
const intermediateWasiRegisterFile = this.envs.WASI_REGISTER_TMP_PATH
const wasiRegisterFunctions =
this.target.arch === 'wasm32'
? JSON.parse(
await readFileAsync(intermediateWasiRegisterFile, 'utf8').catch(
(err) => {
console.warn(
`Read ${colors.yellowBright(
intermediateWasiRegisterFile,
)} failed, reason: ${err.message}`,
)
return `[]`
},
),
)
: []
const jsOutput = await this.writeJsBinding(idents)
const wasmOutput = await this.writeWasiBinding(
wasiRegisterFunctions,
jsOutput?.path ?? 'index',
idents,
)
if (jsOutput) {
this.outputs.push(jsOutput)
}
if (wasmOutput) {
this.outputs.push(wasmOutput)
}
}
return this.outputs
@ -517,6 +559,8 @@ class Builder {
? `lib${cdyLib}.dylib`
: this.target.platform === 'win32'
? `${cdyLib}.dll`
: this.target.platform === 'wasi' || this.target.platform === 'wasm'
? `${cdyLib}.wasm`
: `lib${cdyLib}.so`
let destName = this.config.binaryName
@ -526,7 +570,11 @@ class Builder {
if (this.options.platform) {
destName += `.${this.target.platformArchABI}`
}
destName += '.node'
if (srcName.endsWith('.wasm')) {
destName += '.wasi-wasm32.wasm'
} else {
destName += '.node'
}
return [srcName, destName]
} else if (this.binName) {
@ -592,12 +640,42 @@ class Builder {
debug('Writing js binding to:')
debug(' %i', dest)
await writeFileAsync(dest, cjs, 'utf-8')
this.outputs.push({
return {
kind: 'js',
path: dest,
})
} satisfies Output
} catch (e) {
throw new Error('Failed to write js binding file', { cause: e })
}
}
private async writeWasiBinding(
wasiRegisterFunctions: string[],
distFileName: string | undefined,
idents: string[],
) {
if (distFileName && wasiRegisterFunctions.length) {
const { name, dir } = parse(distFileName)
const newPath = join(dir, `${name}.wasi.mjs`)
const workerPath = join(dir, 'wasi-worker.mjs')
const declareCodes = `const { ${idents.join(', ')} } = binding\n`
const exportsCode = `export {\n${idents
.map((ident) => ` ${ident}`)
.join(',\n')}\n}`
await writeFileAsync(
newPath,
createWasiBinding(this.config.binaryName, wasiRegisterFunctions) +
declareCodes +
exportsCode +
'\n',
'utf8',
)
await writeFileAsync(workerPath, WASI_WORKER_TEMPLATE, 'utf8')
return {
kind: 'wasm',
path: newPath,
} satisfies Output
}
return null
}
}

View file

@ -0,0 +1,65 @@
export const createWasiBinding = (
localName: string,
wasiRegisterFunctions: string[],
) => `/* eslint-disable */
/* prettier-ignore */
/* auto-generated by NAPI-RS */
import * as __nodeFsPromises from 'node:fs/promises'
import * as __nodePath from 'node:path'
import { WASI as __nodeWASI } from 'node:wasi'
import { Worker } from 'node:worker_threads'
import * as __nodeURL from 'node:url'
import { instantiateNapiModule as __emnapiInstantiateNapiModule } from '@emnapi/core'
import { getDefaultContext as __emnapiGetDefaultContext } from '@emnapi/runtime'
const __wasi = new __nodeWASI({
env: process.env,
preopens: {
'/': __nodePath.join(__nodeURL.fileURLToPath(import.meta.url), '..'),
}
})
const __dirname = __nodePath.join(__nodeURL.fileURLToPath(import.meta.url), '..')
const __emnapiContext = __emnapiGetDefaultContext()
const __sharedMemory = new WebAssembly.Memory({
initial: 1024,
maximum: 10240,
shared: true,
})
const { instance: __napiInstance, module: __wasiModule, napiModule: __napiModule } = await __emnapiInstantiateNapiModule(__nodeFsPromises.readFile(__nodePath.join(__dirname, '${localName}.wasi-wasm32.wasm')), {
context: __emnapiContext,
asyncWorkPoolSize: 4,
wasi: __wasi,
onCreateWorker() {
return new Worker(__nodePath.join(__dirname, 'wasi-worker.mjs'), {
env: process.env,
execArgv: ['--experimental-wasi-unstable-preview1'],
})
},
overwriteImports(importObject) {
importObject.env = {
...importObject.env,
...importObject.napi,
...importObject.emnapi,
memory: __sharedMemory,
}
},
beforeInit({ instance }) {
__napi_rs_initialize_modules(instance)
}
})
function __napi_rs_initialize_modules(__napiInstance) {
${wasiRegisterFunctions
.map((name) => ` __napiInstance.exports['${name}']()`)
.join('\n')}
}
const binding = __napiModule.exports
`

View file

@ -157,10 +157,7 @@ function generateGithubWorkflow(options: NewOptions): Output | null {
return {
target: './.github/workflows/ci.yml',
content: createGithubActionsCIYml(
getBinaryName(options.name),
options.targets,
),
content: createGithubActionsCIYml(options.targets),
}
}

View file

@ -195,4 +195,5 @@ Cargo.lock
!.yarn/versions
*.node
*.wasm
`

View file

@ -17,10 +17,7 @@ const TEST_LINUX_AARCH64_MUSL = 'test-linux-aarch64-musl-binding'
const TEST_LINUX_ARM_GNUEABIHF = 'test-linux-arm-gnueabihf-binding'
const UNIVERSAL_MACOS = 'universal-macOS'
export const createGithubActionsCIYml = (
binaryName: string,
targets: string[],
) => {
export const createGithubActionsCIYml = (targets: string[]) => {
const allTargets = new Set(
targets.flatMap((t) => {
const platform = parseTriple(t)

View file

@ -0,0 +1,52 @@
export const WASI_WORKER_TEMPLATE = `import fs from "node:fs";
import { createRequire } from "node:module";
import { parentPort, Worker } from "node:worker_threads";
import { instantiateNapiModuleSync, MessageHandler } from "@emnapi/core";
import { WASI } from "@tybys/wasm-util";
const require = createRequire(import.meta.url);
if (parentPort) {
parentPort.on("message", (data) => {
globalThis.onmessage({ data });
});
}
Object.assign(globalThis, {
self: globalThis,
require,
Worker,
importScripts: function (f) {
;(0, eval)(fs.readFileSync(f, "utf8") + "//# sourceURL=" + f);
},
postMessage: function (msg) {
if (parentPort) {
parentPort.postMessage(msg);
}
},
});
const handler = new MessageHandler({
onLoad({ wasmModule, wasmMemory }) {
const wasi = new WASI({ fs });
return instantiateNapiModuleSync(wasmModule, {
childThread: true,
wasi,
overwriteImports(importObject) {
importObject.env = {
...importObject.env,
...importObject.napi,
...importObject.emnapi,
memory: wasmMemory
};
},
});
},
});
globalThis.onmessage = function (e) {
handler.handle(e);
};
`

View file

@ -1,5 +1,7 @@
import { execSync } from 'child_process'
export type Platform = NodeJS.Platform | 'wasm' | 'wasi'
export const AVAILABLE_TARGETS = [
'aarch64-apple-darwin',
'aarch64-linux-android',
@ -63,24 +65,22 @@ export const NodeArchToCpu: Record<string, string> = {
riscv64: 'riscv64gc',
}
const SysToNodePlatform: Record<string, NodeJS.Platform> = {
const SysToNodePlatform: Record<string, Platform> = {
linux: 'linux',
freebsd: 'freebsd',
darwin: 'darwin',
windows: 'win32',
}
export const UniArchsByPlatform: Partial<
Record<NodeJS.Platform, NodeJSArch[]>
> = {
export const UniArchsByPlatform: Partial<Record<Platform, NodeJSArch[]>> = {
darwin: ['x64', 'arm64'],
}
export interface Target {
triple: string
platformArchABI: string
platform: NodeJS.Platform
arch: NodeJSArch
platform: Platform
arch: NodeJSArch | 'wasm32'
abi: string | null
}
@ -95,6 +95,15 @@ export interface Target {
* - `abi` = The ABI, for example `gnu`, `android`, `eabi`, etc.
*/
export function parseTriple(rawTriple: string): Target {
if (rawTriple === 'wasm32-wasi-preview1-threads') {
return {
triple: rawTriple,
platformArchABI: rawTriple,
platform: 'wasi',
arch: 'wasm32',
abi: 'wasi',
}
}
const triple = rawTriple.endsWith('eabi')
? `${rawTriple.slice(0, -4)}-eabi`
: rawTriple
@ -114,7 +123,7 @@ export function parseTriple(rawTriple: string): Target {
;[cpu, , sys, abi = null] = triples
}
const platform = SysToNodePlatform[sys] ?? (sys as NodeJS.Platform)
const platform = SysToNodePlatform[sys] ?? (sys as Platform)
const arch = CpuToNodeArch[cpu] ?? (cpu as NodeJSArch)
return {
triple: rawTriple,

View file

@ -1,4 +1,5 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"strict": true,
"target": "ES2022",

View file

@ -1,5 +1,6 @@
mod android;
mod macos;
mod wasi;
pub fn setup() {
println!("cargo:rerun-if-env-changed=DEBUG_GENERATED_CODE");
@ -9,6 +10,9 @@ pub fn setup() {
macos::setup();
}
Ok("android") => if android::setup().is_ok() {},
Ok("wasi") => {
wasi::setup();
}
_ => {}
}
}

22
crates/build/src/wasi.rs Normal file
View file

@ -0,0 +1,22 @@
use std::env;
pub fn setup() {
let link_dir = env::var("EMNAPI_LINK_DIR").expect("EMNAPI_LINK_DIR must be set");
println!("cargo:rerun-if-env-changed=EMNAPI_LINK_DIR");
println!("cargo:rerun-if-env-changed=WASI_REGISTER_TMP_PATH");
println!("cargo:rustc-link-search={}", link_dir);
println!("cargo:rustc-link-lib=static=emnapi-basic-mt");
println!("cargo:rustc-link-arg=--export-dynamic");
println!("cargo:rustc-link-arg=--export=malloc");
println!("cargo:rustc-link-arg=--export=free");
println!("cargo:rustc-link-arg=--export=napi_register_wasm_v1");
println!("cargo:rustc-link-arg=--export-if-defined=node_api_module_get_api_version_v1");
println!("cargo:rustc-link-arg=--export-table");
println!("cargo:rustc-link-arg=--export=emnapi_async_worker_create");
println!("cargo:rustc-link-arg=--export=emnapi_async_worker_init");
println!("cargo:rustc-link-arg=--import-memory");
println!("cargo:rustc-link-arg=--import-undefined");
println!("cargo:rustc-link-arg=--shared-memory");
println!("cargo:rustc-link-arg=--max-memory=2147483648");
println!("cargo:rustc-link-arg=--no-check-features");
}

View file

@ -75,11 +75,11 @@ version = "0.8"
optional = true
version = "0.4"
[target.'cfg(target_arch = "wasm32")'.dependencies]
[target.'cfg(target_os = "wasi")'.dependencies]
tokio = { version = "1", optional = true, features = ["rt", "sync"] }
napi-derive = { path = "../macro", version = "2.10.1", default-features = false }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
[target.'cfg(not(target_os = "wasi"))'.dependencies]
tokio = { version = "1", optional = true, features = [
"rt",
"rt-multi-thread",

View file

@ -7,13 +7,24 @@ use std::sync::{
Arc,
};
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
#[cfg(all(feature = "napi4", not(target_os = "wasi")))]
use crate::bindgen_prelude::{CUSTOM_GC_TSFN, THREADS_CAN_ACCESS_ENV, THREAD_DESTROYED};
pub use crate::js_values::TypedArrayType;
use crate::{check_status, sys, Error, Result, Status};
use super::{FromNapiValue, ToNapiValue, TypeName, ValidateNapiValue};
#[cfg(target_os = "wasi")]
extern "C" {
fn emnapi_sync_memory(
env: crate::sys::napi_env,
js_to_wasm: bool,
arraybuffer_or_view: crate::sys::napi_value,
byte_offset: usize,
length: usize,
) -> crate::sys::napi_status;
}
trait Finalizer {
type RustType;
@ -122,6 +133,49 @@ macro_rules! impl_typed_array {
impl $name {
fn noop_finalize(_data: *mut $rust_type, _length: usize) {}
#[cfg(target_os = "wasi")]
pub fn sync(&mut self, env: &crate::Env) {
if let Some((reference, _)) = self.raw {
let mut value = ptr::null_mut();
let mut array_buffer = ptr::null_mut();
crate::check_status_or_throw!(
env.raw(),
unsafe { crate::sys::napi_get_reference_value(env.raw(), reference, &mut value) },
"Failed to get reference value from TypedArray while syncing"
);
crate::check_status_or_throw!(
env.raw(),
unsafe {
crate::sys::napi_get_typedarray_info(
env.raw(),
value,
&mut ($typed_array_type as i32) as *mut i32,
&mut self.length as *mut usize,
ptr::null_mut(),
&mut array_buffer,
&mut self.byte_offset as *mut usize,
)
},
"Failed to get ArrayBuffer under the TypedArray while syncing"
);
crate::check_status_or_throw!(
env.raw(),
unsafe {
emnapi_sync_memory(
env.raw(),
false,
array_buffer,
self.byte_offset,
self.length,
)
},
"Failed to sync memory"
);
} else {
return;
}
}
pub fn new(mut data: Vec<$rust_type>) -> Self {
data.shrink_to_fit();
let ret = $name {

View file

@ -34,17 +34,18 @@ pub unsafe extern "C" fn raw_finalize_unchecked<T: ObjectFinalize>(
finalize_data: *mut c_void,
_finalize_hint: *mut c_void,
) {
let data = *unsafe { Box::from_raw(finalize_data as *mut T) };
let data: Box<T> = unsafe { Box::from_raw(finalize_data.cast()) };
if let Err(err) = data.finalize(unsafe { Env::from_raw(env) }) {
let e: JsError = err.into();
unsafe { e.throw_into(env) };
return;
}
if let Some((_, ref_val, finalize_callbacks_ptr)) =
REFERENCE_MAP.with(|reference_map| reference_map.borrow_mut().remove(&finalize_data))
{
let finalize_callbacks_rc = unsafe { Rc::from_raw(finalize_callbacks_ptr) };
#[cfg(debug_assertions)]
#[cfg(all(debug_assertions, not(target_os = "wasi")))]
{
let rc_strong_count = Rc::strong_count(&finalize_callbacks_rc);
// If `Rc` strong count is 2, it means the finalize of referenced `Object` is called before the `fn drop` of the `Reference`

View file

@ -374,6 +374,7 @@ impl Env {
))
}
#[cfg(not(target_os = "wasi"))]
/// This function gives V8 an indication of the amount of externally allocated memory that is kept alive by JavaScript objects (i.e. a JavaScript object that points to its own memory allocated by a native module).
///
/// Registering externally allocated memory will trigger global garbage collections more often than it would otherwise.
@ -385,6 +386,12 @@ impl Env {
Ok(changed)
}
#[cfg(target_os = "wasi")]
#[allow(unused_variables)]
pub fn adjust_external_memory(&mut self, size: i64) -> Result<i64> {
Ok(0)
}
/// This API allocates a node::Buffer object and initializes it with data copied from the passed-in buffer.
///
/// While this is still a fully-supported data structure, in most cases using a TypedArray will suffice.

View file

@ -3,7 +3,9 @@ use std::marker::PhantomData;
use std::os::raw::c_void;
use std::ptr;
use crate::bindgen_runtime::{ToNapiValue, THREAD_DESTROYED};
#[cfg(all(feature = "napi4", not(target_arch = "wasm32")))]
use crate::bindgen_prelude::THREAD_DESTROYED;
use crate::bindgen_runtime::ToNapiValue;
use crate::{check_status, JsObject, Value};
use crate::{sys, Env, Error, Result};
#[cfg(feature = "deferred_trace")]

View file

@ -6,13 +6,13 @@ use tokio::runtime::Runtime;
use crate::{sys, JsDeferred, JsUnknown, NapiValue, Result};
fn create_runtime() -> Option<Runtime> {
#[cfg(not(target_arch = "wasm32"))]
#[cfg(not(target_os = "wasi"))]
{
let runtime = tokio::runtime::Runtime::new().expect("Create tokio runtime failed");
Some(runtime)
}
#[cfg(target_arch = "wasm32")]
#[cfg(target_os = "wasi")]
{
tokio::runtime::Builder::new_current_thread()
.enable_all()

View file

@ -10,9 +10,7 @@ crate-type = ["cdylib", "lib"]
[dependencies]
napi = { path = "../../crates/napi", default-features = false, features = [
"tokio_fs",
"napi8",
"tokio_rt",
"serde-json",
"async",
"experimental",

View file

@ -1,2 +1,3 @@
*.node
wip/
*.wasm

View file

@ -14,23 +14,43 @@ snmalloc = ["snmalloc-rs"]
[dependencies]
chrono = "0.4"
futures = "0.3"
napi = { path = "../../crates/napi", default-features = false, features = [
"tokio_fs",
"napi9",
"tokio_rt",
"serde-json",
"async",
"experimental",
"latin1",
"chrono_date",
"deferred_trace",
] }
napi-derive = { path = "../../crates/macro", features = ["type-def"] }
napi-shared = { path = "../napi-shared" }
serde = "1"
serde_derive = "1"
serde_json = "1"
tokio = { version = "1.20.0", features = ["full"] }
[target.'cfg(not(target_os = "wasi"))'.dependencies]
napi = { path = "../../crates/napi", default-features = false, features = [
"napi9",
"serde-json",
"experimental",
"latin1",
"chrono_date",
"tokio",
"async",
"tokio_rt",
"tokio_fs",
"tokio_macros",
"deferred_trace",
] }
tokio = { version = "1", features = ["rt", "time"] }
[target.'cfg(target_os = "wasi")'.dependencies]
napi = { path = "../../crates/napi", default-features = false, features = [
"napi9",
"serde-json",
"experimental",
"latin1",
"chrono_date",
"tokio",
"async",
"tokio_rt",
"tokio_macros",
"tokio_sync",
"deferred_trace",
] }
tokio = { version = "1", default-features = false, features = ["rt", "time"] }
[dependencies.snmalloc-rs]
version = "0.3"

View file

@ -1,10 +1,6 @@
import { createRequire } from 'node:module'
import test from 'ava'
const require = createRequire(import.meta.url)
const { receiveString }: typeof import('../index.js') = require('../index.node')
const { receiveString } = (await import('../index.js')).default
test('Function message', (t) => {
// @ts-expect-error

View file

@ -1,14 +1,8 @@
import { createRequire } from 'node:module'
import ava from 'ava'
import test from 'ava'
const { Fib, Fib2, Fib3 } = (await import('../index.js')).default
const require = createRequire(import.meta.url)
const {
Fib,
Fib2,
Fib3,
}: typeof import('../index.js') = require('../index.node')
const test = process.env.WASI_TEST ? ava.skip : ava
for (const [index, factory] of [
() => new Fib(),

View file

@ -1,12 +1,8 @@
import { createRequire } from 'node:module'
import ava from 'ava'
import test from 'ava'
const { NotWritableClass } = (await import('../index.js')).default
const require = createRequire(import.meta.url)
const {
NotWritableClass,
}: typeof import('../index.js') = require('../index.node')
const test = process.env.WASI_TEST ? ava.skip : ava
test('Not Writable Class', (t) => {
const obj = new NotWritableClass('1')

View file

@ -1,9 +1,5 @@
import { createRequire } from 'node:module'
import test from 'ava'
const require = createRequire(import.meta.url)
const {
validateArray,
validateTypedArray,
@ -24,7 +20,7 @@ const {
returnUndefinedIfInvalid,
returnUndefinedIfInvalidPromise,
validateOptional,
}: typeof import('../index.d.ts') = require('../index.node')
} = (await import('../index.js')).default
test('should validate array', (t) => {
t.is(validateArray([1, 2, 3]), 3)
@ -127,8 +123,21 @@ test('should validate Map', (t) => {
})
})
test('should validate promise', async (t) => {
t.is(await validatePromise(Promise.resolve(1)), 2)
test.only('should validate promise', async (t) => {
if (process.env.WASI_TEST) {
t.pass()
return
}
t.is(
await validatePromise(
new Promise((resolve) => {
setTimeout(() => {
resolve(1)
}, 100)
}),
),
2,
)
// @ts-expect-error
await t.throwsAsync(() => validatePromise(1), {
code: 'InvalidArg',

View file

@ -1,3 +1,3 @@
const { threadsafeFunctionFatalModeError } = require('../index.node')
threadsafeFunctionFatalModeError(() => {})
import('../index.js').then(({ default: { threadsafeFunctionFatalModeError } }) => {
return threadsafeFunctionFatalModeError(() => {})
})

View file

@ -7,5 +7,9 @@ import test from 'ava'
const __dirname = join(fileURLToPath(import.meta.url), '..')
test('should generate correct type def file', (t) => {
t.snapshot(readFileSync(join(__dirname, '..', 'index.d.ts'), 'utf8'))
if (process.env.WASI_TEST) {
t.pass()
} else {
t.snapshot(readFileSync(join(__dirname, '..', 'index.d.ts'), 'utf8'))
}
})

View file

@ -1,5 +1,4 @@
import { exec } from 'node:child_process'
import { createRequire } from 'node:module'
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
@ -9,7 +8,6 @@ import type { AliasedStruct } from '../index.js'
import { test } from './test.framework.js'
const require = createRequire(import.meta.url)
const __dirname = join(fileURLToPath(import.meta.url), '..')
const {
@ -141,7 +139,10 @@ const {
chronoNativeDateTime,
chronoNativeDateTimeReturn,
throwAsyncError,
}: typeof import('../index.d.ts') = require('../index.node')
} = (await import('../index.js')).default
const Napi4Test = Number(process.versions.napi) >= 4 ? test : test.skip
const isWasiTest = !!process.env.WASI_TEST
test('export const', (t) => {
t.is(DEFAULT_COST, 12)
@ -310,7 +311,7 @@ test('should be able to into_reference', (t) => {
test('callback', (t) => {
getCwd((cwd) => {
t.is(cwd, process.cwd())
t.is(cwd, process.env.WASI_TEST ? '/' : process.cwd())
})
t.throws(
@ -345,7 +346,11 @@ test('return function', (t) => {
})
})
test('callback function return Promise', async (t) => {
Napi4Test('callback function return Promise', async (t) => {
if (isWasiTest) {
t.pass()
return
}
const cbSpy = spy()
await callbackReturnPromise<string>(() => '1', spy)
t.is(cbSpy.callCount, 0)
@ -360,7 +365,11 @@ test('callback function return Promise', async (t) => {
t.deepEqual(cbSpy.args, [['42']])
})
test('callback function return Promise and spawn', async (t) => {
Napi4Test('callback function return Promise and spawn', async (t) => {
if (isWasiTest) {
t.pass()
return
}
const finalReturn = await callbackReturnPromiseAndSpawn((input) =>
Promise.resolve(`${input} world`),
)
@ -433,7 +442,9 @@ test('Async error with stack trace', async (t) => {
const err = await t.throwsAsync(() => throwAsyncError())
t.not(err?.stack, undefined)
t.deepEqual(err!.message, 'Async Error')
t.regex(err!.stack!, /.+at .+values\.spec\.ts:\d+:\d+.+/gm)
if (!process.env.WASI_TEST) {
t.regex(err!.stack!, /.+at .+values\.spec\.ts:\d+:\d+.+/gm)
}
})
test('custom status code in Error', (t) => {
@ -488,10 +499,13 @@ test('aliased rust struct and enum', (t) => {
})
test('serde-json', (t) => {
if (process.env.WASI_TEST) {
t.pass()
return
}
const packageJson = readPackageJson()
t.is(packageJson.name, '@examples/napi')
t.is(packageJson.version, '0.0.0')
t.is(packageJson.dependencies, undefined)
t.snapshot(Object.keys(packageJson.devDependencies!).sort())
t.is(getPackageJsonName(packageJson), '@examples/napi')
@ -574,6 +588,10 @@ test('create external TypedArray', (t) => {
})
test('mutate TypedArray', (t) => {
if (isWasiTest) {
t.pass()
return
}
const input = new Float32Array([1, 2, 3, 4, 5])
mutateTypedArray(input)
t.deepEqual(input, new Float32Array([2.0, 4.0, 6.0, 8.0, 10.0]))
@ -587,6 +605,10 @@ test('deref uint8 array', (t) => {
})
test('async', async (t) => {
if (process.env.WASI_TEST) {
t.pass()
return
}
const bufPromise = readFileAsync(join(__dirname, '../package.json'))
await t.notThrowsAsync(bufPromise)
const buf = await bufPromise
@ -639,6 +661,11 @@ test('receive class reference in either', (t) => {
})
test('receive different class', (t) => {
// TODO: fix the napi_unwrap error from the emnapi
if (isWasiTest) {
t.pass()
return
}
const a = new JsClassForEither()
const b = new AnotherClassForEither()
t.is(receiveDifferentClass(a), 42)
@ -764,8 +791,6 @@ BigIntTest('from i128 i64', (t) => {
t.is(bigintFromI128(), BigInt('-100'))
})
const Napi4Test = Number(process.versions.napi) >= 4 ? test : test.skip
Napi4Test('call thread safe function', (t) => {
let i = 0
let value = 0
@ -820,13 +845,19 @@ Napi4Test('throw error from thread safe function fatal mode', (t) => {
return new Promise<void>((resolve) => {
p.on('exit', (code) => {
t.is(code, 1)
t.true(stderr.toString('utf8').includes(`[Error: Generic tsfn error]`))
const stderrMsg = stderr.toString('utf8')
console.info(stderrMsg)
t.true(stderrMsg.includes(`Error: Generic tsfn error`))
resolve()
})
})
})
Napi4Test('await Promise in rust', async (t) => {
if (isWasiTest) {
t.pass()
return
}
const fx = 20
const result = await asyncPlus100(
new Promise((resolve) => {
@ -837,6 +868,10 @@ Napi4Test('await Promise in rust', async (t) => {
})
Napi4Test('Promise should reject raw error in rust', async (t) => {
if (isWasiTest) {
t.pass()
return
}
const fxError = new Error('What is Happy Planet')
const err = await t.throwsAsync(() => asyncPlus100(Promise.reject(fxError)))
t.is(err, fxError)
@ -855,6 +890,10 @@ Napi4Test('call ThreadsafeFunction with callback', async (t) => {
})
Napi4Test('async call ThreadsafeFunction', async (t) => {
if (isWasiTest) {
t.pass()
return
}
await t.notThrowsAsync(() =>
tsfnAsyncCall((err, arg1, arg2, arg3) => {
t.is(err, null)
@ -867,6 +906,10 @@ Napi4Test('async call ThreadsafeFunction', async (t) => {
})
test('Throw from ThreadsafeFunction JavaScript callback', async (t) => {
if (isWasiTest) {
t.pass()
return
}
const errMsg = 'ThrowFromJavaScriptRawCallback'
await t.throwsAsync(
() =>
@ -915,7 +958,11 @@ Napi4Test('accept ThreadsafeFunction tuple args', async (t) => {
})
})
test('threadsafe function return Promise and await in Rust', async (t) => {
Napi4Test('threadsafe function return Promise and await in Rust', async (t) => {
if (isWasiTest) {
t.pass()
return
}
const value = await tsfnReturnPromise((err, value) => {
if (err) {
throw err
@ -959,7 +1006,11 @@ Napi4Test('object only from js', (t) => {
})
})
test('promise in either', async (t) => {
Napi4Test('promise in either', async (t) => {
if (isWasiTest) {
t.pass()
return
}
t.is(await promiseInEither(1), false)
t.is(await promiseInEither(20), true)
t.is(await promiseInEither(Promise.resolve(1)), false)

View file

@ -1,13 +1,19 @@
const { join } = require('node:path')
const { Worker } = require('node:worker_threads')
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { Worker } from 'node:worker_threads'
const test = require('ava').default
import test from 'ava'
const { Animal, Kind, DEFAULT_COST } = require('../index.node')
const { Animal, Kind, DEFAULT_COST } = (await import('../index.js')).default
const __dirname = join(fileURLToPath(import.meta.url), '..')
// aarch64-unknown-linux-gnu is extremely slow in CI, skip it or it will timeout
const t =
process.arch === 'arm64' && process.platform === 'linux' ? test.skip : test
(process.arch === 'arm64' && process.platform === 'linux') ||
process.env.WASI_TEST
? test.skip
: test
const concurrency = process.platform === 'win32' || process.platform === 'darwin' || (process.platform === 'linux' && process.arch === 'x64') ? 50 : 10
@ -15,9 +21,9 @@ t('should be able to require in worker thread', async (t) => {
await Promise.all(
Array.from({ length: concurrency }).map(() => {
const w = new Worker(join(__dirname, 'worker.cjs'), {
execArgv: []
execArgv: [],
})
return new Promise((resolve, reject) => {
return new Promise<void>((resolve, reject) => {
w.postMessage({ type: 'require' })
w.on('message', (msg) => {
t.is(msg, Animal.withKind(Kind.Cat).whoami() + DEFAULT_COST)
@ -39,9 +45,9 @@ t('custom GC works on worker_threads', async (t) => {
await Promise.all(
Array.from({ length: concurrency }).map(() =>
Promise.all([
new Promise((resolve, reject) => {
new Promise<Worker>((resolve, reject) => {
const w = new Worker(join(__dirname, 'worker.cjs'), {
execArgv: []
execArgv: [],
})
w.postMessage({
type: 'async:buffer',
@ -56,9 +62,9 @@ t('custom GC works on worker_threads', async (t) => {
}).then((w) => {
return w.terminate()
}),
new Promise((resolve, reject) => {
new Promise<Worker>((resolve, reject) => {
const w = new Worker(join(__dirname, 'worker.cjs'), {
execArgv: []
execArgv: [],
})
w.postMessage({
type: 'async:arraybuffer',
@ -82,9 +88,9 @@ t('should be able to new Class in worker thread concurrently', async (t) => {
await Promise.all(
Array.from({ length: concurrency }).map(() => {
const w = new Worker(join(__dirname, 'worker.cjs'), {
execArgv: []
execArgv: [],
})
return new Promise((resolve, reject) => {
return new Promise<void>((resolve, reject) => {
w.postMessage({ type: 'constructor' })
w.on('message', (msg) => {
t.is(msg, 'Ellie')

View file

@ -1,13 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Electron test</title>
</head>
<body>
<div>Electron test</div>
<script src="./index.js"></script>
</body>
</html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Electron test</title>
</head>
<body>
<div>Electron test</div>
<script src="./index.js"></script>
</body>
</html>

View file

@ -1,6 +1,6 @@
const { ipcRenderer } = require('electron')
const { callLongThreadsafeFunction } = require('../index')
const { callLongThreadsafeFunction } = require('../index.node')
callLongThreadsafeFunction(() => {})

View file

@ -1,15 +1,8 @@
const assert = require('assert')
const { readFileSync } = require('fs')
const assert = require('node:assert')
const { readFileSync } = require('node:fs')
const { app, BrowserWindow, ipcMain } = require('electron')
const {
readFileAsync,
callThreadsafeFunction,
withAbortController,
createExternalTypedArray,
} = require('./index')
const FILE_CONTENT = readFileSync(__filename, 'utf8')
const createWindowAndReload = async () => {
@ -39,16 +32,30 @@ const createWindowAndReload = async () => {
// reload to check if there is any crash
win.reload()
// make sure the renderer process is still alive
ipcMain.once('pong', () => {
console.info('pong')
resolve()
})
// Wait for a while to make sure if a crash happens, the 'resolve' function should be called after the crash
setTimeout(() => {
// make sure the renderer process is still alive
ipcMain.once('pong', () => resolve())
win.webContents.send('ping')
}, 500)
console.info('ping')
}, 1000)
})
}
async function main() {
const {
default: {
readFileAsync,
callThreadsafeFunction,
withAbortController,
createExternalTypedArray,
},
} = await import('./index.js')
const ctrl = new AbortController()
const promise = withAbortController(1, 2, ctrl.signal)
try {

13
examples/napi/index.js Normal file
View file

@ -0,0 +1,13 @@
import { createRequire } from 'node:module'
const require = createRequire(import.meta.url)
let exports
if (process.env.WASI_TEST) {
exports = await import('./index.wasi.mjs')
} else {
exports = require('./index.node')
}
export default exports

View file

@ -0,0 +1,448 @@
/* tslint:disable */
/* eslint-disable */
/* prettier-ignore */
/* auto-generated by NAPI-RS */
import * as __nodeFsPromises from 'node:fs/promises'
import * as __nodePath from 'node:path'
import { WASI as __nodeWASI } from 'node:wasi'
import { Worker } from 'node:worker_threads'
import * as __nodeURL from 'node:url'
import { instantiateNapiModule as __emnapiInstantiateNapiModule } from '@emnapi/core'
import { getDefaultContext as __emnapiGetDefaultContext } from '@emnapi/runtime'
const __wasi = new __nodeWASI({
env: process.env,
preopens: {
'/': __nodePath.join(__nodeURL.fileURLToPath(import.meta.url), '..'),
}
})
const __dirname = __nodePath.join(__nodeURL.fileURLToPath(import.meta.url), '..')
const __emnapiContext = __emnapiGetDefaultContext()
const __sharedMemory = new WebAssembly.Memory({
initial: 1024,
maximum: 10240,
shared: true,
})
const { instance: __napiInstance, module: __wasiModule, napiModule: __napiModule } = await __emnapiInstantiateNapiModule(__nodeFsPromises.readFile(__nodePath.join(__dirname, 'index.wasi-wasm32.wasm')), {
context: __emnapiContext,
asyncWorkPoolSize: 4,
wasi: __wasi,
onCreateWorker() {
return new Worker(__nodePath.join(__dirname, 'wasi-worker.mjs'), {
env: process.env,
execArgv: ['--experimental-wasi-unstable-preview1'],
})
},
overwriteImports(importObject) {
importObject.env = {
...importObject.env,
...importObject.napi,
...importObject.emnapi,
memory: __sharedMemory,
}
},
beforeInit({ instance }) {
__napi_rs_initialize_modules(instance)
}
})
function __napi_rs_initialize_modules(__napiInstance) {
__napiInstance.exports['__napi_register__DEFAULT_COST_0']()
__napiInstance.exports['__napi_register__TYPE_SKIPPED_CONST_1']()
__napiInstance.exports['__napi_register__get_words_2']()
__napiInstance.exports['__napi_register__get_nums_3']()
__napiInstance.exports['__napi_register__sum_nums_4']()
__napiInstance.exports['__napi_register__to_js_obj_5']()
__napiInstance.exports['__napi_register__get_num_arr_6']()
__napiInstance.exports['__napi_register__get_nested_num_arr_7']()
__napiInstance.exports['__napi_register__read_file_async_8']()
__napiInstance.exports['__napi_register__async_multi_two_9']()
__napiInstance.exports['__napi_register__bigint_add_10']()
__napiInstance.exports['__napi_register__create_big_int_11']()
__napiInstance.exports['__napi_register__create_big_int_i64_12']()
__napiInstance.exports['__napi_register__bigint_get_u64_as_string_13']()
__napiInstance.exports['__napi_register__bigint_from_i64_14']()
__napiInstance.exports['__napi_register__bigint_from_i128_15']()
__napiInstance.exports['__napi_register__get_cwd_16']()
__napiInstance.exports['__napi_register__option_end_17']()
__napiInstance.exports['__napi_register__option_start_18']()
__napiInstance.exports['__napi_register__option_start_end_19']()
__napiInstance.exports['__napi_register__option_only_20']()
__napiInstance.exports['__napi_register__read_file_21']()
__napiInstance.exports['__napi_register__return_js_function_22']()
__napiInstance.exports['__napi_register__callback_return_promise_23']()
__napiInstance.exports['__napi_register__callback_return_promise_and_spawn_24']()
__napiInstance.exports['__napi_register__capture_error_in_callback_25']()
__napiInstance.exports['__napi_register__Animal_struct_26']()
__napiInstance.exports['__napi_register__Animal_impl_27']()
__napiInstance.exports['__napi_register__Dog_struct_28']()
__napiInstance.exports['__napi_register__Bird_struct_29']()
__napiInstance.exports['__napi_register__Bird_impl_30']()
__napiInstance.exports['__napi_register__Blake2bHasher_struct_31']()
__napiInstance.exports['__napi_register__Blake2bHasher_impl_32']()
__napiInstance.exports['__napi_register__Blake2bHasher_impl_33']()
__napiInstance.exports['__napi_register__Blake2bKey_struct_34']()
__napiInstance.exports['__napi_register__Context_struct_35']()
__napiInstance.exports['__napi_register__Context_impl_36']()
__napiInstance.exports['__napi_register__AnimalWithDefaultConstructor_struct_37']()
__napiInstance.exports['__napi_register__NinjaTurtle_struct_38']()
__napiInstance.exports['__napi_register__NinjaTurtle_impl_39']()
__napiInstance.exports['__napi_register__JsAssets_struct_40']()
__napiInstance.exports['__napi_register__JsAssets_impl_41']()
__napiInstance.exports['__napi_register__JsAsset_struct_42']()
__napiInstance.exports['__napi_register__JsAsset_impl_43']()
__napiInstance.exports['__napi_register__Optional_struct_44']()
__napiInstance.exports['__napi_register__Optional_impl_45']()
__napiInstance.exports['__napi_register__create_object_with_class_field_46']()
__napiInstance.exports['__napi_register__receive_object_with_class_field_47']()
__napiInstance.exports['__napi_register__NotWritableClass_struct_48']()
__napiInstance.exports['__napi_register__NotWritableClass_impl_49']()
__napiInstance.exports['__napi_register__CustomFinalize_struct_50']()
__napiInstance.exports['__napi_register__CustomFinalize_impl_51']()
__napiInstance.exports['__napi_register__Width_struct_52']()
__napiInstance.exports['__napi_register__plus_one_53']()
__napiInstance.exports['__napi_register__GetterSetterWithClosures_struct_54']()
__napiInstance.exports['__napi_register__GetterSetterWithClosures_impl_55']()
__napiInstance.exports['__napi_register__ClassWithFactory_struct_56']()
__napiInstance.exports['__napi_register__ClassWithFactory_impl_57']()
__napiInstance.exports['__napi_register__Selector_struct_58']()
__napiInstance.exports['__napi_register__date_to_number_59']()
__napiInstance.exports['__napi_register__chrono_date_to_millis_60']()
__napiInstance.exports['__napi_register__chrono_date_add_1_minute_61']()
__napiInstance.exports['__napi_register__chrono_native_date_time_62']()
__napiInstance.exports['__napi_register__chrono_native_date_time_return_63']()
__napiInstance.exports['__napi_register__either_string_or_number_64']()
__napiInstance.exports['__napi_register__return_either_65']()
__napiInstance.exports['__napi_register__either3_66']()
__napiInstance.exports['__napi_register__either4_67']()
__napiInstance.exports['__napi_register__JsClassForEither_struct_68']()
__napiInstance.exports['__napi_register__JsClassForEither_impl_69']()
__napiInstance.exports['__napi_register__AnotherClassForEither_struct_70']()
__napiInstance.exports['__napi_register__AnotherClassForEither_impl_71']()
__napiInstance.exports['__napi_register__receive_class_or_number_72']()
__napiInstance.exports['__napi_register__receive_mut_class_or_number_73']()
__napiInstance.exports['__napi_register__receive_different_class_74']()
__napiInstance.exports['__napi_register__return_either_class_75']()
__napiInstance.exports['__napi_register__either_from_option_76']()
__napiInstance.exports['__napi_register__either_from_objects_77']()
__napiInstance.exports['__napi_register__either_bool_or_function_78']()
__napiInstance.exports['__napi_register__promise_in_either_79']()
__napiInstance.exports['__napi_register__Kind_80']()
__napiInstance.exports['__napi_register__Empty_81']()
__napiInstance.exports['__napi_register__Status_82']()
__napiInstance.exports['__napi_register__CustomNumEnum_83']()
__napiInstance.exports['__napi_register__enum_to_i32_84']()
__napiInstance.exports['__napi_register__SkippedEnums_85']()
__napiInstance.exports['__napi_register__throw_error_86']()
__napiInstance.exports['__napi_register__panic_87']()
__napiInstance.exports['__napi_register__receive_string_88']()
__napiInstance.exports['__napi_register__custom_status_code_89']()
__napiInstance.exports['__napi_register__throw_async_error_90']()
__napiInstance.exports['__napi_register__create_external_91']()
__napiInstance.exports['__napi_register__create_external_string_92']()
__napiInstance.exports['__napi_register__get_external_93']()
__napiInstance.exports['__napi_register__mutate_external_94']()
__napiInstance.exports['__napi_register__validate_array_95']()
__napiInstance.exports['__napi_register__validate_buffer_96']()
__napiInstance.exports['__napi_register__validate_typed_array_97']()
__napiInstance.exports['__napi_register__validate_bigint_98']()
__napiInstance.exports['__napi_register__validate_boolean_99']()
__napiInstance.exports['__napi_register__validate_date_100']()
__napiInstance.exports['__napi_register__validate_date_time_101']()
__napiInstance.exports['__napi_register__validate_external_102']()
__napiInstance.exports['__napi_register__validate_function_103']()
__napiInstance.exports['__napi_register__validate_hash_map_104']()
__napiInstance.exports['__napi_register__validate_null_105']()
__napiInstance.exports['__napi_register__validate_undefined_106']()
__napiInstance.exports['__napi_register__validate_number_107']()
__napiInstance.exports['__napi_register__validate_promise_108']()
__napiInstance.exports['__napi_register__validate_string_109']()
__napiInstance.exports['__napi_register__validate_symbol_110']()
__napiInstance.exports['__napi_register__validate_optional_111']()
__napiInstance.exports['__napi_register__return_undefined_if_invalid_112']()
__napiInstance.exports['__napi_register__return_undefined_if_invalid_promise_113']()
__napiInstance.exports['__napi_register__ts_rename_114']()
__napiInstance.exports['__napi_register__override_individual_arg_on_function_115']()
__napiInstance.exports['__napi_register__override_individual_arg_on_function_with_cb_arg_116']()
__napiInstance.exports['__napi_register__Fib_struct_117']()
__napiInstance.exports['__napi_register__Fib_impl_118']()
__napiInstance.exports['__napi_register__Fib_impl_119']()
__napiInstance.exports['__napi_register__Fib2_struct_120']()
__napiInstance.exports['__napi_register__Fib2_impl_121']()
__napiInstance.exports['__napi_register__Fib2_impl_122']()
__napiInstance.exports['__napi_register__Fib3_struct_123']()
__napiInstance.exports['__napi_register__Fib3_impl_124']()
__napiInstance.exports['__napi_register__ALIGNMENT_125']()
__napiInstance.exports['__napi_register__xxh64_126']()
__napiInstance.exports['__napi_register__xxh128_127']()
__napiInstance.exports['__napi_register__Xxh3_struct_128']()
__napiInstance.exports['__napi_register__Xxh3_impl_129']()
__napiInstance.exports['__napi_register__xxh2_plus_130']()
__napiInstance.exports['__napi_register__xxh3_xxh64_alias_131']()
__napiInstance.exports['__napi_register__xxh64_alias_132']()
__napiInstance.exports['__napi_register__get_mapping_133']()
__napiInstance.exports['__napi_register__sum_mapping_134']()
__napiInstance.exports['__napi_register__map_option_135']()
__napiInstance.exports['__napi_register__return_null_136']()
__napiInstance.exports['__napi_register__return_undefined_137']()
__napiInstance.exports['__napi_register__add_138']()
__napiInstance.exports['__napi_register__fibonacci_139']()
__napiInstance.exports['__napi_register__list_obj_keys_140']()
__napiInstance.exports['__napi_register__create_obj_141']()
__napiInstance.exports['__napi_register__get_global_142']()
__napiInstance.exports['__napi_register__get_undefined_143']()
__napiInstance.exports['__napi_register__get_null_144']()
__napiInstance.exports['__napi_register__receive_all_optional_object_145']()
__napiInstance.exports['__napi_register__AliasedEnum_146']()
__napiInstance.exports['__napi_register__fn_received_aliased_147']()
__napiInstance.exports['__napi_register__receive_strict_object_148']()
__napiInstance.exports['__napi_register__get_str_from_object_149']()
__napiInstance.exports['__napi_register__create_obj_with_property_150']()
__napiInstance.exports['__napi_register__getter_from_obj_151']()
__napiInstance.exports['__napi_register__receive_object_only_from_js_152']()
__napiInstance.exports['__napi_register__async_plus_100_153']()
__napiInstance.exports['__napi_register__JsRepo_struct_154']()
__napiInstance.exports['__napi_register__JsRepo_impl_155']()
__napiInstance.exports['__napi_register__JsRemote_struct_156']()
__napiInstance.exports['__napi_register__JsRemote_impl_157']()
__napiInstance.exports['__napi_register__CSSRuleList_struct_158']()
__napiInstance.exports['__napi_register__CSSRuleList_impl_159']()
__napiInstance.exports['__napi_register__CSSStyleSheet_struct_160']()
__napiInstance.exports['__napi_register__AnotherCSSStyleSheet_struct_161']()
__napiInstance.exports['__napi_register__AnotherCSSStyleSheet_impl_162']()
__napiInstance.exports['__napi_register__CSSStyleSheet_impl_163']()
__napiInstance.exports['__napi_register__read_package_json_164']()
__napiInstance.exports['__napi_register__get_package_json_name_165']()
__napiInstance.exports['__napi_register__test_serde_roundtrip_166']()
__napiInstance.exports['__napi_register__test_serde_big_number_precision_167']()
__napiInstance.exports['__napi_register__return_from_shared_crate_168']()
__napiInstance.exports['__napi_register__contains_169']()
__napiInstance.exports['__napi_register__concat_str_170']()
__napiInstance.exports['__napi_register__concat_utf16_171']()
__napiInstance.exports['__napi_register__concat_latin1_172']()
__napiInstance.exports['__napi_register__roundtrip_str_173']()
__napiInstance.exports['__napi_register__set_symbol_in_obj_174']()
__napiInstance.exports['__napi_register__create_symbol_175']()
__napiInstance.exports['__napi_register__create_symbol_for_176']()
__napiInstance.exports['__napi_register__DelaySum_impl_177']()
__napiInstance.exports['__napi_register__without_abort_controller_178']()
__napiInstance.exports['__napi_register__with_abort_controller_179']()
__napiInstance.exports['__napi_register__call_threadsafe_function_180']()
__napiInstance.exports['__napi_register__call_long_threadsafe_function_181']()
__napiInstance.exports['__napi_register__threadsafe_function_throw_error_182']()
__napiInstance.exports['__napi_register__threadsafe_function_fatal_mode_183']()
__napiInstance.exports['__napi_register__threadsafe_function_fatal_mode_error_184']()
__napiInstance.exports['__napi_register__threadsafe_function_closure_capture_185']()
__napiInstance.exports['__napi_register__tsfn_call_with_callback_186']()
__napiInstance.exports['__napi_register__tsfn_async_call_187']()
__napiInstance.exports['__napi_register__accept_threadsafe_function_188']()
__napiInstance.exports['__napi_register__accept_threadsafe_function_fatal_189']()
__napiInstance.exports['__napi_register__accept_threadsafe_function_tuple_args_190']()
__napiInstance.exports['__napi_register__tsfn_return_promise_191']()
__napiInstance.exports['__napi_register__tsfn_return_promise_timeout_192']()
__napiInstance.exports['__napi_register__tsfn_throw_from_js_193']()
__napiInstance.exports['__napi_register__get_buffer_194']()
__napiInstance.exports['__napi_register__append_buffer_195']()
__napiInstance.exports['__napi_register__get_empty_buffer_196']()
__napiInstance.exports['__napi_register__convert_u32_array_197']()
__napiInstance.exports['__napi_register__create_external_typed_array_198']()
__napiInstance.exports['__napi_register__mutate_typed_array_199']()
__napiInstance.exports['__napi_register__deref_uint8_array_200']()
__napiInstance.exports['__napi_register__buffer_pass_through_201']()
__napiInstance.exports['__napi_register__array_buffer_pass_through_202']()
__napiInstance.exports['__napi_register__AsyncBuffer_impl_203']()
__napiInstance.exports['__napi_register__async_reduce_buffer_204']()
__napiInstance.exports['__napi_register__run_script_205']()
}
const binding = __napiModule.exports
const { Animal, AnimalWithDefaultConstructor, AnotherClassForEither, AnotherCssStyleSheet, AnotherCSSStyleSheet, Asset, JsAsset, Assets, JsAssets, Bird, Blake2BHasher, Blake2bHasher, Blake2BKey, Blake2bKey, ClassWithFactory, Context, CssRuleList, CSSRuleList, CssStyleSheet, CSSStyleSheet, CustomFinalize, Dog, Fib, Fib2, Fib3, GetterSetterWithClosures, JsClassForEither, JsRemote, JsRepo, NinjaTurtle, NotWritableClass, Optional, Selector, Width, acceptThreadsafeFunction, acceptThreadsafeFunctionFatal, acceptThreadsafeFunctionTupleArgs, add, ALIAS, AliasedEnum, appendBuffer, arrayBufferPassThrough, asyncMultiTwo, asyncPlus100, asyncReduceBuffer, bigintAdd, bigintFromI128, bigintFromI64, bigintGetU64AsString, bufferPassThrough, callbackReturnPromise, callbackReturnPromiseAndSpawn, callLongThreadsafeFunction, callThreadsafeFunction, captureErrorInCallback, chronoDateAdd1Minute, chronoDateToMillis, chronoNativeDateTime, chronoNativeDateTimeReturn, concatLatin1, concatStr, concatUtf16, contains, convertU32Array, createBigInt, createBigIntI64, createExternal, createExternalString, createExternalTypedArray, createObj, createObjectWithClassField, createObjWithProperty, createSymbol, createSymbolFor, CustomNumEnum, customStatusCode, dateToNumber, DEFAULT_COST, derefUint8Array, either3, either4, eitherBoolOrFunction, eitherFromObjects, eitherFromOption, eitherStringOrNumber, Empty, enumToI32, fibonacci, fnReceivedAliased, getBuffer, getCwd, getEmptyBuffer, getExternal, getGlobal, getMapping, getNestedNumArr, getNull, getNumArr, getNums, getPackageJsonName, getStrFromObject, getterFromObj, getUndefined, getWords, Kind, listObjKeys, mapOption, mutateExternal, mutateTypedArray, optionEnd, optionOnly, optionStart, optionStartEnd, overrideIndividualArgOnFunction, overrideIndividualArgOnFunctionWithCbArg, panic, plusOne, promiseInEither, readFile, readFileAsync, readPackageJson, receiveAllOptionalObject, receiveClassOrNumber, receiveDifferentClass, receiveMutClassOrNumber, receiveObjectOnlyFromJs, receiveObjectWithClassField, receiveStrictObject, receiveString, returnEither, returnEitherClass, returnFromSharedCrate, returnJsFunction, returnNull, returnUndefined, returnUndefinedIfInvalid, returnUndefinedIfInvalidPromise, roundtripStr, runScript, setSymbolInObj, Status, sumMapping, sumNums, testSerdeBigNumberPrecision, testSerdeRoundtrip, threadsafeFunctionClosureCapture, threadsafeFunctionFatalMode, threadsafeFunctionFatalModeError, threadsafeFunctionThrowError, throwAsyncError, throwError, toJsObj, tsfnAsyncCall, tsfnCallWithCallback, tsfnReturnPromise, tsfnReturnPromiseTimeout, tsfnThrowFromJs, tsRename, validateArray, validateBigint, validateBoolean, validateBuffer, validateDate, validateDateTime, validateExternal, validateFunction, validateHashMap, validateNull, validateNumber, validateOptional, validatePromise, validateString, validateSymbol, validateTypedArray, validateUndefined, withAbortController, withoutAbortController, xxh64Alias, xxh2, xxh3 } = binding
export {
Animal,
AnimalWithDefaultConstructor,
AnotherClassForEither,
AnotherCssStyleSheet,
AnotherCSSStyleSheet,
Asset,
JsAsset,
Assets,
JsAssets,
Bird,
Blake2BHasher,
Blake2bHasher,
Blake2BKey,
Blake2bKey,
ClassWithFactory,
Context,
CssRuleList,
CSSRuleList,
CssStyleSheet,
CSSStyleSheet,
CustomFinalize,
Dog,
Fib,
Fib2,
Fib3,
GetterSetterWithClosures,
JsClassForEither,
JsRemote,
JsRepo,
NinjaTurtle,
NotWritableClass,
Optional,
Selector,
Width,
acceptThreadsafeFunction,
acceptThreadsafeFunctionFatal,
acceptThreadsafeFunctionTupleArgs,
add,
ALIAS,
AliasedEnum,
appendBuffer,
arrayBufferPassThrough,
asyncMultiTwo,
asyncPlus100,
asyncReduceBuffer,
bigintAdd,
bigintFromI128,
bigintFromI64,
bigintGetU64AsString,
bufferPassThrough,
callbackReturnPromise,
callbackReturnPromiseAndSpawn,
callLongThreadsafeFunction,
callThreadsafeFunction,
captureErrorInCallback,
chronoDateAdd1Minute,
chronoDateToMillis,
chronoNativeDateTime,
chronoNativeDateTimeReturn,
concatLatin1,
concatStr,
concatUtf16,
contains,
convertU32Array,
createBigInt,
createBigIntI64,
createExternal,
createExternalString,
createExternalTypedArray,
createObj,
createObjectWithClassField,
createObjWithProperty,
createSymbol,
createSymbolFor,
CustomNumEnum,
customStatusCode,
dateToNumber,
DEFAULT_COST,
derefUint8Array,
either3,
either4,
eitherBoolOrFunction,
eitherFromObjects,
eitherFromOption,
eitherStringOrNumber,
Empty,
enumToI32,
fibonacci,
fnReceivedAliased,
getBuffer,
getCwd,
getEmptyBuffer,
getExternal,
getGlobal,
getMapping,
getNestedNumArr,
getNull,
getNumArr,
getNums,
getPackageJsonName,
getStrFromObject,
getterFromObj,
getUndefined,
getWords,
Kind,
listObjKeys,
mapOption,
mutateExternal,
mutateTypedArray,
optionEnd,
optionOnly,
optionStart,
optionStartEnd,
overrideIndividualArgOnFunction,
overrideIndividualArgOnFunctionWithCbArg,
panic,
plusOne,
promiseInEither,
readFile,
readFileAsync,
readPackageJson,
receiveAllOptionalObject,
receiveClassOrNumber,
receiveDifferentClass,
receiveMutClassOrNumber,
receiveObjectOnlyFromJs,
receiveObjectWithClassField,
receiveStrictObject,
receiveString,
returnEither,
returnEitherClass,
returnFromSharedCrate,
returnJsFunction,
returnNull,
returnUndefined,
returnUndefinedIfInvalid,
returnUndefinedIfInvalidPromise,
roundtripStr,
runScript,
setSymbolInObj,
Status,
sumMapping,
sumNums,
testSerdeBigNumberPrecision,
testSerdeRoundtrip,
threadsafeFunctionClosureCapture,
threadsafeFunctionFatalMode,
threadsafeFunctionFatalModeError,
threadsafeFunctionThrowError,
throwAsyncError,
throwError,
toJsObj,
tsfnAsyncCall,
tsfnCallWithCallback,
tsfnReturnPromise,
tsfnReturnPromiseTimeout,
tsfnThrowFromJs,
tsRename,
validateArray,
validateBigint,
validateBoolean,
validateBuffer,
validateDate,
validateDateTime,
validateExternal,
validateFunction,
validateHashMap,
validateNull,
validateNumber,
validateOptional,
validatePromise,
validateString,
validateSymbol,
validateTypedArray,
validateUndefined,
withAbortController,
withoutAbortController,
xxh64Alias,
xxh2,
xxh3
}

View file

@ -3,11 +3,11 @@
"private": true,
"version": "0.0.0",
"type": "module",
"main": "./index.node",
"main": "./index.js",
"types": "./index.d.ts",
"scripts": {
"build": "napi-raw build --no-js",
"test": "cross-env TS_NODE_PROJECT=./tsconfig.json node --es-module-specifier-resolution=node --loader ts-node/esm/transpile-only ../../node_modules/ava/entrypoints/cli.mjs"
"build": "napi-raw build",
"test": "cross-env TS_NODE_PROJECT=./tsconfig.json node --es-module-specifier-resolution=node --loader ts-node/esm/transpile-only --experimental-wasi-unstable-preview1 ../../node_modules/ava/entrypoints/cli.mjs"
},
"devDependencies": {
"@napi-rs/cli": "workspace:*",
@ -25,11 +25,15 @@
"cjs": true
},
"files": [
"__tests__/**/*.spec.ts",
"__tests__/**/*.spec.cts",
"__tests__/**/*.spec.js",
"__tests__/**/*.spec.cjs"
"__tests__/**/*.spec.{ts,cts,js,cjs,mjs}"
],
"timeout": "10m"
},
"dependencies": {
"@emnapi/core": "0.43.1",
"@tybys/wasm-util": "^0.8.0",
"@types/lodash": "^4.14.191",
"lodash": "^4.17.21",
"sinon": "^15.0.1"
}
}

View file

@ -1,18 +1,29 @@
#[cfg(not(target_arch = "wasm32"))]
use futures::prelude::*;
use napi::bindgen_prelude::*;
use napi::tokio::{self, fs};
use napi::tokio;
#[cfg(not(target_arch = "wasm32"))]
use napi::tokio::fs;
#[napi]
async fn read_file_async(path: String) -> Result<Buffer> {
fs::read(path)
.map(|r| match r {
Ok(content) => Ok(content.into()),
Err(e) => Err(Error::new(
Status::GenericFailure,
format!("failed to read file, {}", e),
)),
})
.await
#[cfg(not(target_arch = "wasm32"))]
{
fs::read(path)
.map(|r| match r {
Ok(content) => Ok(content.into()),
Err(e) => Err(Error::new(
Status::GenericFailure,
format!("failed to read file, {}", e),
)),
})
.await
}
#[cfg(target_arch = "wasm32")]
{
let conetent = std::fs::read(path)?;
Ok(conetent.into())
}
}
#[napi]

View file

@ -0,0 +1,6 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "ESNext"
}
}

View file

@ -2,6 +2,7 @@
"extends": "../tsconfig.json",
"include": ["."],
"compilerOptions": {
"moduleResolution": "node",
"outDir": "./dist",
"rootDir": ".",
"target": "ESNext",
@ -11,5 +12,11 @@
"types": ["bun-types"],
"importHelpers": false
},
"exclude": ["dist", "electron.cjs", "electron-renderer", "node_modules"]
"exclude": [
"dist",
"electron.js",
"electron-renderer",
"index.js",
"index.wasi.mjs"
]
}

View file

@ -0,0 +1,51 @@
import fs from "node:fs";
import { createRequire } from "node:module";
import { parentPort, Worker } from "node:worker_threads";
import { instantiateNapiModuleSync, MessageHandler } from "@emnapi/core";
import { WASI } from "@tybys/wasm-util";
const require = createRequire(import.meta.url);
if (parentPort) {
parentPort.on("message", (data) => {
globalThis.onmessage({ data });
});
}
Object.assign(globalThis, {
self: globalThis,
require,
Worker,
importScripts: function (f) {
;(0, eval)(fs.readFileSync(f, "utf8") + "//# sourceURL=" + f);
},
postMessage: function (msg) {
if (parentPort) {
parentPort.postMessage(msg);
}
},
});
const handler = new MessageHandler({
onLoad({ wasmModule, wasmMemory }) {
const wasi = new WASI({ fs });
return instantiateNapiModuleSync(wasmModule, {
childThread: true,
wasi,
overwriteImports(importObject) {
importObject.env = {
...importObject.env,
...importObject.napi,
...importObject.emnapi,
memory: wasmMemory
};
},
});
},
});
globalThis.onmessage = function (e) {
handler.handle(e);
};

View file

@ -80,7 +80,7 @@
"c8": "^8.0.0",
"cross-env": "^7.0.3",
"electron": "^27.0.0",
"esbuild": "^0.19.0",
"esbuild": "^0.19.5",
"eslint": "^8.45.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-import": "^2.27.5",

View file

@ -23,7 +23,7 @@ const tripleLists: { [key: string]: { platform?: string } } = RAW_LIST.trim()
.split('\n')
.filter((line) => !line.startsWith('wasm') && line.trim().length)
.map(parseTriple)
.reduce((acc, cur) => {
.reduce((acc: Record<string, { platform?: string }>, cur) => {
acc[cur.triple] = cur
return acc
}, {})

8
triples/tsconfig.json Normal file
View file

@ -0,0 +1,8 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"composite": true,
"noEmit": true
},
"include": ["."]
}

View file

@ -5,6 +5,9 @@
"./triples/index.js",
"./memory-testing/*.js",
"./memory-testing/*.mjs",
"./crates/cli/index.js"
"./crates/cli/index.js",
"./examples/**/*.js",
"./examples/**/*.mjs",
"./examples/**/*.cjs"
]
}

374
yarn.lock
View file

@ -125,156 +125,174 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/android-arm64@npm:0.19.0":
version: 0.19.0
resolution: "@esbuild/android-arm64@npm:0.19.0"
"@emnapi/core@npm:0.43.1":
version: 0.43.1
resolution: "@emnapi/core@npm:0.43.1"
dependencies:
tslib: ^2.4.0
checksum: 5bd6bce73883ef4db9347c853dfecb7851cd8b60de21099ffe8be5fec7383df0f73c3e702a1f15f37e43493a766320a1d506296925b02fe88b265031d820a94a
languageName: node
linkType: hard
"@emnapi/runtime@npm:0.43.1":
version: 0.43.1
resolution: "@emnapi/runtime@npm:0.43.1"
dependencies:
tslib: ^2.4.0
checksum: 61ebf7346d745f87648b5a4373aa80aed7c731526615544253cbbcb50f1d0cc1159ca6504b6c67ce0df55d6afdd65ae272f988d03d01d568308ffc9f1f7dbfbb
languageName: node
linkType: hard
"@esbuild/android-arm64@npm:0.19.5":
version: 0.19.5
resolution: "@esbuild/android-arm64@npm:0.19.5"
conditions: os=android & cpu=arm64
languageName: node
linkType: hard
"@esbuild/android-arm@npm:0.19.0":
version: 0.19.0
resolution: "@esbuild/android-arm@npm:0.19.0"
"@esbuild/android-arm@npm:0.19.5":
version: 0.19.5
resolution: "@esbuild/android-arm@npm:0.19.5"
conditions: os=android & cpu=arm
languageName: node
linkType: hard
"@esbuild/android-x64@npm:0.19.0":
version: 0.19.0
resolution: "@esbuild/android-x64@npm:0.19.0"
"@esbuild/android-x64@npm:0.19.5":
version: 0.19.5
resolution: "@esbuild/android-x64@npm:0.19.5"
conditions: os=android & cpu=x64
languageName: node
linkType: hard
"@esbuild/darwin-arm64@npm:0.19.0":
version: 0.19.0
resolution: "@esbuild/darwin-arm64@npm:0.19.0"
"@esbuild/darwin-arm64@npm:0.19.5":
version: 0.19.5
resolution: "@esbuild/darwin-arm64@npm:0.19.5"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
"@esbuild/darwin-x64@npm:0.19.0":
version: 0.19.0
resolution: "@esbuild/darwin-x64@npm:0.19.0"
"@esbuild/darwin-x64@npm:0.19.5":
version: 0.19.5
resolution: "@esbuild/darwin-x64@npm:0.19.5"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
"@esbuild/freebsd-arm64@npm:0.19.0":
version: 0.19.0
resolution: "@esbuild/freebsd-arm64@npm:0.19.0"
"@esbuild/freebsd-arm64@npm:0.19.5":
version: 0.19.5
resolution: "@esbuild/freebsd-arm64@npm:0.19.5"
conditions: os=freebsd & cpu=arm64
languageName: node
linkType: hard
"@esbuild/freebsd-x64@npm:0.19.0":
version: 0.19.0
resolution: "@esbuild/freebsd-x64@npm:0.19.0"
"@esbuild/freebsd-x64@npm:0.19.5":
version: 0.19.5
resolution: "@esbuild/freebsd-x64@npm:0.19.5"
conditions: os=freebsd & cpu=x64
languageName: node
linkType: hard
"@esbuild/linux-arm64@npm:0.19.0":
version: 0.19.0
resolution: "@esbuild/linux-arm64@npm:0.19.0"
"@esbuild/linux-arm64@npm:0.19.5":
version: 0.19.5
resolution: "@esbuild/linux-arm64@npm:0.19.5"
conditions: os=linux & cpu=arm64
languageName: node
linkType: hard
"@esbuild/linux-arm@npm:0.19.0":
version: 0.19.0
resolution: "@esbuild/linux-arm@npm:0.19.0"
"@esbuild/linux-arm@npm:0.19.5":
version: 0.19.5
resolution: "@esbuild/linux-arm@npm:0.19.5"
conditions: os=linux & cpu=arm
languageName: node
linkType: hard
"@esbuild/linux-ia32@npm:0.19.0":
version: 0.19.0
resolution: "@esbuild/linux-ia32@npm:0.19.0"
"@esbuild/linux-ia32@npm:0.19.5":
version: 0.19.5
resolution: "@esbuild/linux-ia32@npm:0.19.5"
conditions: os=linux & cpu=ia32
languageName: node
linkType: hard
"@esbuild/linux-loong64@npm:0.19.0":
version: 0.19.0
resolution: "@esbuild/linux-loong64@npm:0.19.0"
"@esbuild/linux-loong64@npm:0.19.5":
version: 0.19.5
resolution: "@esbuild/linux-loong64@npm:0.19.5"
conditions: os=linux & cpu=loong64
languageName: node
linkType: hard
"@esbuild/linux-mips64el@npm:0.19.0":
version: 0.19.0
resolution: "@esbuild/linux-mips64el@npm:0.19.0"
"@esbuild/linux-mips64el@npm:0.19.5":
version: 0.19.5
resolution: "@esbuild/linux-mips64el@npm:0.19.5"
conditions: os=linux & cpu=mips64el
languageName: node
linkType: hard
"@esbuild/linux-ppc64@npm:0.19.0":
version: 0.19.0
resolution: "@esbuild/linux-ppc64@npm:0.19.0"
"@esbuild/linux-ppc64@npm:0.19.5":
version: 0.19.5
resolution: "@esbuild/linux-ppc64@npm:0.19.5"
conditions: os=linux & cpu=ppc64
languageName: node
linkType: hard
"@esbuild/linux-riscv64@npm:0.19.0":
version: 0.19.0
resolution: "@esbuild/linux-riscv64@npm:0.19.0"
"@esbuild/linux-riscv64@npm:0.19.5":
version: 0.19.5
resolution: "@esbuild/linux-riscv64@npm:0.19.5"
conditions: os=linux & cpu=riscv64
languageName: node
linkType: hard
"@esbuild/linux-s390x@npm:0.19.0":
version: 0.19.0
resolution: "@esbuild/linux-s390x@npm:0.19.0"
"@esbuild/linux-s390x@npm:0.19.5":
version: 0.19.5
resolution: "@esbuild/linux-s390x@npm:0.19.5"
conditions: os=linux & cpu=s390x
languageName: node
linkType: hard
"@esbuild/linux-x64@npm:0.19.0":
version: 0.19.0
resolution: "@esbuild/linux-x64@npm:0.19.0"
"@esbuild/linux-x64@npm:0.19.5":
version: 0.19.5
resolution: "@esbuild/linux-x64@npm:0.19.5"
conditions: os=linux & cpu=x64
languageName: node
linkType: hard
"@esbuild/netbsd-x64@npm:0.19.0":
version: 0.19.0
resolution: "@esbuild/netbsd-x64@npm:0.19.0"
"@esbuild/netbsd-x64@npm:0.19.5":
version: 0.19.5
resolution: "@esbuild/netbsd-x64@npm:0.19.5"
conditions: os=netbsd & cpu=x64
languageName: node
linkType: hard
"@esbuild/openbsd-x64@npm:0.19.0":
version: 0.19.0
resolution: "@esbuild/openbsd-x64@npm:0.19.0"
"@esbuild/openbsd-x64@npm:0.19.5":
version: 0.19.5
resolution: "@esbuild/openbsd-x64@npm:0.19.5"
conditions: os=openbsd & cpu=x64
languageName: node
linkType: hard
"@esbuild/sunos-x64@npm:0.19.0":
version: 0.19.0
resolution: "@esbuild/sunos-x64@npm:0.19.0"
"@esbuild/sunos-x64@npm:0.19.5":
version: 0.19.5
resolution: "@esbuild/sunos-x64@npm:0.19.5"
conditions: os=sunos & cpu=x64
languageName: node
linkType: hard
"@esbuild/win32-arm64@npm:0.19.0":
version: 0.19.0
resolution: "@esbuild/win32-arm64@npm:0.19.0"
"@esbuild/win32-arm64@npm:0.19.5":
version: 0.19.5
resolution: "@esbuild/win32-arm64@npm:0.19.5"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
"@esbuild/win32-ia32@npm:0.19.0":
version: 0.19.0
resolution: "@esbuild/win32-ia32@npm:0.19.0"
"@esbuild/win32-ia32@npm:0.19.5":
version: 0.19.5
resolution: "@esbuild/win32-ia32@npm:0.19.5"
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
"@esbuild/win32-x64@npm:0.19.0":
version: 0.19.0
resolution: "@esbuild/win32-x64@npm:0.19.0"
"@esbuild/win32-x64@npm:0.19.5":
version: 0.19.5
resolution: "@esbuild/win32-x64@npm:0.19.5"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
@ -335,7 +353,9 @@ __metadata:
version: 0.0.0-use.local
resolution: "@examples/napi@workspace:examples/napi"
dependencies:
"@emnapi/core": 0.43.1
"@napi-rs/cli": "workspace:*"
"@tybys/wasm-util": ^0.8.0
"@types/lodash": ^4.14.195
ava: ^5.3.1
cross-env: 7.0.3
@ -498,7 +518,11 @@ __metadata:
version: 0.0.0-use.local
resolution: "@napi-rs/cli@workspace:cli"
dependencies:
"@emnapi/core": 0.43.1
"@emnapi/runtime": 0.43.1
"@octokit/rest": ^20.0.1
"@tybys/wasm-util": 0.8.0
"@types/debug": ^4.1.7
"@types/inquirer": ^9.0.3
"@types/js-yaml": ^4.0.5
"@types/lodash-es": ^4.17.8
@ -506,14 +530,29 @@ __metadata:
clipanion: ^3.2.1
colorette: ^2.0.20
debug: ^4.3.4
esbuild: ^0.19.0
emnapi: 0.43.1
env-paths: ^3.0.0
esbuild: ^0.19.5
inquirer: ^9.2.8
js-yaml: ^4.1.0
lodash-es: ^4.17.21
prettier: ^3.0.0
toml: ^3.0.0
ts-node: ^10.9.1
tslib: ^2.6.1
typanion: ^3.13.0
typescript: ^5.1.6
peerDependencies:
"@emnapi/runtime": 0.43.1
"@tybys/wasm-util": 0.8.0
emnapi: 0.43.1
peerDependenciesMeta:
"@emnapi/runtime":
optional: true
"@tybys/wasm-util":
optional: true
emnapi:
optional: true
bin:
napi: ./dist/cli.js
napi-raw: ./cli.mjs
@ -1281,6 +1320,15 @@ __metadata:
languageName: node
linkType: hard
"@tybys/wasm-util@npm:0.8.0, @tybys/wasm-util@npm:^0.8.0":
version: 0.8.0
resolution: "@tybys/wasm-util@npm:0.8.0"
dependencies:
tslib: ^2.4.0
checksum: ce110aa721abe0c31f93c25c0abcc621bcc03a30f1927fe940e13456ef30438f140ec436dfe6d2a288814299d68215b071e986bca8ed97dfc3108f80ad214093
languageName: node
linkType: hard
"@types/cacheable-request@npm:^6.0.1":
version: 6.0.3
resolution: "@types/cacheable-request@npm:6.0.3"
@ -1293,7 +1341,7 @@ __metadata:
languageName: node
linkType: hard
"@types/debug@npm:^4.1.8":
"@types/debug@npm:^4.1.7, @types/debug@npm:^4.1.8":
version: 4.1.8
resolution: "@types/debug@npm:4.1.8"
dependencies:
@ -1421,16 +1469,16 @@ __metadata:
linkType: hard
"@types/node@npm:*, @types/node@npm:^20.4.2":
version: 20.4.5
resolution: "@types/node@npm:20.4.5"
checksum: 36a0304a8dc346a1b2d2edac4c4633eecf70875793d61a5274d0df052d7a7af7a8e34f29884eac4fbd094c4f0201477dcb39c0ecd3307ca141688806538d1138
version: 20.4.4
resolution: "@types/node@npm:20.4.4"
checksum: 43f3c4a8acc38ae753e15a0e79bae0447d255b3742fa87f8e065d7b9d20ecb0e03d6c5b46c00d5d26f4552160381a00255f49205595a8ee48c2423e00263c930
languageName: node
linkType: hard
"@types/node@npm:^18.11.18":
version: 18.17.1
resolution: "@types/node@npm:18.17.1"
checksum: 56201bda9a2d05d68602df63b4e67b0545ac8c6d0280bd5fb31701350a978a577a027501fbf49db99bf177f2242ebd1244896bfd35e89042d5bd7dfebff28d4e
version: 18.17.0
resolution: "@types/node@npm:18.17.0"
checksum: 3a43c5c5541342751b514485144818a515fac5427f663066068eaacbe8a108cbe1207aae75ec89d34c3b32414c334aad84e9083cf7fcf3ebfd970adc871314a4
languageName: node
linkType: hard
@ -1508,14 +1556,14 @@ __metadata:
linkType: hard
"@typescript-eslint/eslint-plugin@npm:^6.0.0":
version: 6.2.1
resolution: "@typescript-eslint/eslint-plugin@npm:6.2.1"
version: 6.2.0
resolution: "@typescript-eslint/eslint-plugin@npm:6.2.0"
dependencies:
"@eslint-community/regexpp": ^4.5.1
"@typescript-eslint/scope-manager": 6.2.1
"@typescript-eslint/type-utils": 6.2.1
"@typescript-eslint/utils": 6.2.1
"@typescript-eslint/visitor-keys": 6.2.1
"@typescript-eslint/scope-manager": 6.2.0
"@typescript-eslint/type-utils": 6.2.0
"@typescript-eslint/utils": 6.2.0
"@typescript-eslint/visitor-keys": 6.2.0
debug: ^4.3.4
graphemer: ^1.4.0
ignore: ^5.2.4
@ -1529,44 +1577,44 @@ __metadata:
peerDependenciesMeta:
typescript:
optional: true
checksum: e73f3fe36519d895037d223f3ddf200b97e17bcde9390984118c38733add1edf996357c809ec2db92cec61bc7c9e5a3d9a583e0d0f92fa9c3919b68716a27b37
checksum: 1ef46b1c2e3e2013f66b4982dcfb9e198a3824cc1503b843e553201a108a3cb6e4adfb2c486158c89d993e5e4b9d99aeb2af28297e43da98c4750dae8f5131b5
languageName: node
linkType: hard
"@typescript-eslint/parser@npm:^6.0.0":
version: 6.2.1
resolution: "@typescript-eslint/parser@npm:6.2.1"
version: 6.2.0
resolution: "@typescript-eslint/parser@npm:6.2.0"
dependencies:
"@typescript-eslint/scope-manager": 6.2.1
"@typescript-eslint/types": 6.2.1
"@typescript-eslint/typescript-estree": 6.2.1
"@typescript-eslint/visitor-keys": 6.2.1
"@typescript-eslint/scope-manager": 6.2.0
"@typescript-eslint/types": 6.2.0
"@typescript-eslint/typescript-estree": 6.2.0
"@typescript-eslint/visitor-keys": 6.2.0
debug: ^4.3.4
peerDependencies:
eslint: ^7.0.0 || ^8.0.0
peerDependenciesMeta:
typescript:
optional: true
checksum: cf4768cbfc696ce1d4b15ae55b3d2b52761e91a4a80e738cf3a75c501c2257d735cd6e462567965069d0d693a8cf5463ab9e8b97c36c6ed1fccd3c1c09855bdb
checksum: ba79674f2d4599a24c7afa8f18ec28243b80df39f82a4a6b7a4ce7c584ec37d4ade40a3aa058d597a5cbf71647a40d0995866748d14cf4b52d8ad4420d10f669
languageName: node
linkType: hard
"@typescript-eslint/scope-manager@npm:6.2.1":
version: 6.2.1
resolution: "@typescript-eslint/scope-manager@npm:6.2.1"
"@typescript-eslint/scope-manager@npm:6.2.0":
version: 6.2.0
resolution: "@typescript-eslint/scope-manager@npm:6.2.0"
dependencies:
"@typescript-eslint/types": 6.2.1
"@typescript-eslint/visitor-keys": 6.2.1
checksum: 3bb461678c7e729895c5ac16781ec7d66efc6ffa944bb49693ce8e9560f9a6cac70929157c0fc0875b2829ae19a5cdabb97973ddcfb7e81c16e22cdd5d39e3fd
"@typescript-eslint/types": 6.2.0
"@typescript-eslint/visitor-keys": 6.2.0
checksum: 75a650a3ede78bf841a3bf3f4880b94a06aa4c420f399a6fb9faee19a2e5998f7e330a13f78e07c4958413345bab58b0593f09fa163a77e8f6353012e795660c
languageName: node
linkType: hard
"@typescript-eslint/type-utils@npm:6.2.1":
version: 6.2.1
resolution: "@typescript-eslint/type-utils@npm:6.2.1"
"@typescript-eslint/type-utils@npm:6.2.0":
version: 6.2.0
resolution: "@typescript-eslint/type-utils@npm:6.2.0"
dependencies:
"@typescript-eslint/typescript-estree": 6.2.1
"@typescript-eslint/utils": 6.2.1
"@typescript-eslint/typescript-estree": 6.2.0
"@typescript-eslint/utils": 6.2.0
debug: ^4.3.4
ts-api-utils: ^1.0.1
peerDependencies:
@ -1574,23 +1622,23 @@ __metadata:
peerDependenciesMeta:
typescript:
optional: true
checksum: 7f8d80f03e6ddc1838307a2a4df61dc4bd8400efb9dcc7316063ae293fce54afad238404a0c25cd2cdaceee73ae514f254b850bd7ff11e2def700d5d6b90af05
checksum: 9adb542fb3c49bf5c1fecca98549bee3fcfd28a0ceee5227817a1ceb0841b912e322f58ba1b3ca98a47fc998cbec0a3d69cacb9cf9ac4be1d133b11bb9d53aae
languageName: node
linkType: hard
"@typescript-eslint/types@npm:6.2.1":
version: 6.2.1
resolution: "@typescript-eslint/types@npm:6.2.1"
checksum: 388d32f15a9db8ad5d80794caf9ab280d6e5a428efdf4f6a6dfc4069afe4d19da32d628acf638e4c5b92ee77a9a18eecf728a778a3b91cc8a24484af579fc9cf
"@typescript-eslint/types@npm:6.2.0":
version: 6.2.0
resolution: "@typescript-eslint/types@npm:6.2.0"
checksum: 81878866cf7f49dbc335cce05adfbd994f348e2ebe9538fd6e934fa82e44186c16b2112b8d5f9f4c528ea127be157185be5e35e4913db4880d20ac495785baaf
languageName: node
linkType: hard
"@typescript-eslint/typescript-estree@npm:6.2.1":
version: 6.2.1
resolution: "@typescript-eslint/typescript-estree@npm:6.2.1"
"@typescript-eslint/typescript-estree@npm:6.2.0":
version: 6.2.0
resolution: "@typescript-eslint/typescript-estree@npm:6.2.0"
dependencies:
"@typescript-eslint/types": 6.2.1
"@typescript-eslint/visitor-keys": 6.2.1
"@typescript-eslint/types": 6.2.0
"@typescript-eslint/visitor-keys": 6.2.0
debug: ^4.3.4
globby: ^11.1.0
is-glob: ^4.0.3
@ -1599,34 +1647,34 @@ __metadata:
peerDependenciesMeta:
typescript:
optional: true
checksum: 3d9beeb5e36b8827de5c160ed8e5c111dd66ca00671b183409b051e242b291480679b900bb74aaf4895dcae49497037567d3fcbbe67fa9930786ddd01c685f04
checksum: 5bfd5bf09feff6c4807cfa65cf407dd0249f7d487d6820941dd05999ee35cacdabaacadf23c92b90b57920025e93088e93924bc8df41f393ac0366538eb2902f
languageName: node
linkType: hard
"@typescript-eslint/utils@npm:6.2.1":
version: 6.2.1
resolution: "@typescript-eslint/utils@npm:6.2.1"
"@typescript-eslint/utils@npm:6.2.0":
version: 6.2.0
resolution: "@typescript-eslint/utils@npm:6.2.0"
dependencies:
"@eslint-community/eslint-utils": ^4.4.0
"@types/json-schema": ^7.0.12
"@types/semver": ^7.5.0
"@typescript-eslint/scope-manager": 6.2.1
"@typescript-eslint/types": 6.2.1
"@typescript-eslint/typescript-estree": 6.2.1
"@typescript-eslint/scope-manager": 6.2.0
"@typescript-eslint/types": 6.2.0
"@typescript-eslint/typescript-estree": 6.2.0
semver: ^7.5.4
peerDependencies:
eslint: ^7.0.0 || ^8.0.0
checksum: d16356a633f39d988a9af159da15e28c6a28fa47abce372061c79cf186d193d148e1c32862c9702ff87e2a06f7a2f82773e4b56320a39f432f4b1a989f8005ad
checksum: 54f062412a8ce23554ca4063d275327981640426b1ecd1073d30dd8b9464ff7af68b8f9f6272033bad9307815d56f2f922faa8a995421efdccd6165dd62557e1
languageName: node
linkType: hard
"@typescript-eslint/visitor-keys@npm:6.2.1":
version: 6.2.1
resolution: "@typescript-eslint/visitor-keys@npm:6.2.1"
"@typescript-eslint/visitor-keys@npm:6.2.0":
version: 6.2.0
resolution: "@typescript-eslint/visitor-keys@npm:6.2.0"
dependencies:
"@typescript-eslint/types": 6.2.1
"@typescript-eslint/types": 6.2.0
eslint-visitor-keys: ^3.4.1
checksum: c05a1c45129f2cf9a8c49dadc3da10b675232e59b69dfe9fdc0bfb45d3be077ceff78097baf50e502dab3e71ce9fd799d2015e356a4be2787ee10c6c7a44ea8a
checksum: b400c657c7e5c65b289304f6f5cee6536f23b3441306f82aff2d2e047e13770330715d4f7b29e734b0b2dab6030e41028894d5cd441696115bfea43ad18b2c54
languageName: node
linkType: hard
@ -3357,6 +3405,18 @@ __metadata:
languageName: node
linkType: hard
"emnapi@npm:0.43.1":
version: 0.43.1
resolution: "emnapi@npm:0.43.1"
peerDependencies:
node-addon-api: ">= 6.1.0"
peerDependenciesMeta:
node-addon-api:
optional: true
checksum: 0b5940adaa8f165fdd3cdbc155d6514bd32b449732a0746bfb0dbd190816f16f942a59d32d3d9a978b391bf86df941496ef0c9502de94e10bd8ba79ea5afa730
languageName: node
linkType: hard
"emoji-regex@npm:^8.0.0":
version: 8.0.0
resolution: "emoji-regex@npm:8.0.0"
@ -3405,6 +3465,13 @@ __metadata:
languageName: node
linkType: hard
"env-paths@npm:^3.0.0":
version: 3.0.0
resolution: "env-paths@npm:3.0.0"
checksum: b2b0a0d0d9931a13d279c22ed94d78648a1cc5f408f05d47ff3e0c1616f0aa0c38fb33deec5e5be50497225d500607d57f9c8652c4d39c2f2b7608cd45768128
languageName: node
linkType: hard
"envinfo@npm:7.8.1":
version: 7.8.1
resolution: "envinfo@npm:7.8.1"
@ -3515,32 +3582,32 @@ __metadata:
languageName: node
linkType: hard
"esbuild@npm:^0.19.0":
version: 0.19.0
resolution: "esbuild@npm:0.19.0"
"esbuild@npm:^0.19.5":
version: 0.19.5
resolution: "esbuild@npm:0.19.5"
dependencies:
"@esbuild/android-arm": 0.19.0
"@esbuild/android-arm64": 0.19.0
"@esbuild/android-x64": 0.19.0
"@esbuild/darwin-arm64": 0.19.0
"@esbuild/darwin-x64": 0.19.0
"@esbuild/freebsd-arm64": 0.19.0
"@esbuild/freebsd-x64": 0.19.0
"@esbuild/linux-arm": 0.19.0
"@esbuild/linux-arm64": 0.19.0
"@esbuild/linux-ia32": 0.19.0
"@esbuild/linux-loong64": 0.19.0
"@esbuild/linux-mips64el": 0.19.0
"@esbuild/linux-ppc64": 0.19.0
"@esbuild/linux-riscv64": 0.19.0
"@esbuild/linux-s390x": 0.19.0
"@esbuild/linux-x64": 0.19.0
"@esbuild/netbsd-x64": 0.19.0
"@esbuild/openbsd-x64": 0.19.0
"@esbuild/sunos-x64": 0.19.0
"@esbuild/win32-arm64": 0.19.0
"@esbuild/win32-ia32": 0.19.0
"@esbuild/win32-x64": 0.19.0
"@esbuild/android-arm": 0.19.5
"@esbuild/android-arm64": 0.19.5
"@esbuild/android-x64": 0.19.5
"@esbuild/darwin-arm64": 0.19.5
"@esbuild/darwin-x64": 0.19.5
"@esbuild/freebsd-arm64": 0.19.5
"@esbuild/freebsd-x64": 0.19.5
"@esbuild/linux-arm": 0.19.5
"@esbuild/linux-arm64": 0.19.5
"@esbuild/linux-ia32": 0.19.5
"@esbuild/linux-loong64": 0.19.5
"@esbuild/linux-mips64el": 0.19.5
"@esbuild/linux-ppc64": 0.19.5
"@esbuild/linux-riscv64": 0.19.5
"@esbuild/linux-s390x": 0.19.5
"@esbuild/linux-x64": 0.19.5
"@esbuild/netbsd-x64": 0.19.5
"@esbuild/openbsd-x64": 0.19.5
"@esbuild/sunos-x64": 0.19.5
"@esbuild/win32-arm64": 0.19.5
"@esbuild/win32-ia32": 0.19.5
"@esbuild/win32-x64": 0.19.5
dependenciesMeta:
"@esbuild/android-arm":
optional: true
@ -3588,7 +3655,7 @@ __metadata:
optional: true
bin:
esbuild: bin/esbuild
checksum: 77ef2e57a94d1b88657fb6cd79c3ad6e3161823fd1b615f2ce4b71f163755b85e98cb5b565ed6c38db1f788c52c092b5534caa6800178de99a25de0671f92186
checksum: 5a0227cf6ffffa3076714d88230af1dfdd2fc363d91bd712a81fb91230c315a395e2c9b7588eee62986aeebf4999804b9b1b59eeab8e2457184eb0056bfe20c8
languageName: node
linkType: hard
@ -6530,7 +6597,7 @@ __metadata:
c8: ^8.0.0
cross-env: ^7.0.3
electron: ^27.0.0
esbuild: ^0.19.0
esbuild: ^0.19.5
eslint: ^8.45.0
eslint-config-prettier: ^9.0.0
eslint-plugin-import: ^2.27.5
@ -8903,6 +8970,13 @@ __metadata:
languageName: node
linkType: hard
"toml@npm:^3.0.0":
version: 3.0.0
resolution: "toml@npm:3.0.0"
checksum: 5d7f1d8413ad7780e9bdecce8ea4c3f5130dd53b0a4f2e90b93340979a137739879d7b9ce2ce05c938b8cc828897fe9e95085197342a1377dd8850bf5125f15f
languageName: node
linkType: hard
"tr46@npm:~0.0.3":
version: 0.0.3
resolution: "tr46@npm:0.0.3"
@ -8987,7 +9061,7 @@ __metadata:
languageName: node
linkType: hard
"tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0, tslib@npm:^2.6.0":
"tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0, tslib@npm:^2.6.0, tslib@npm:^2.6.1":
version: 2.6.1
resolution: "tslib@npm:2.6.1"
checksum: b0d176d176487905b66ae4d5856647df50e37beea7571c53b8d10ba9222c074b81f1410fb91da13debaf2cbc970663609068bdebafa844ea9d69b146527c38fe