diff --git a/crates/backend/src/typegen.rs b/crates/backend/src/typegen.rs index 1e94f9a2..f7eeb174 100644 --- a/crates/backend/src/typegen.rs +++ b/crates/backend/src/typegen.rs @@ -180,6 +180,7 @@ static KNOWN_TYPES: Lazy> = La ("Either25", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)), ("Either26", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)), ("external", ("object", false, false)), + ("Promise", ("Promise<{}>", false, false)), ("AbortSignal", ("AbortSignal", false, false)), ("JsGlobal", ("typeof global", false, false)), ("External", ("ExternalObject<{}>", false, false)), @@ -333,11 +334,20 @@ pub fn ty_to_ts_type(ty: &Type, is_return_ty: bool, is_struct_field: bool) -> (S .with(|c| c.borrow_mut().get(rust_ty.as_str()).cloned()) { ts_ty = Some((t, false)); - } else if rust_ty == "Promise" { - ts_ty = Some(( - format!("Promise<{}>", args.first().map(|(arg, _)| arg).unwrap()), - false, - )); + } else if rust_ty == "ThreadsafeFunction" { + let fatal_tsfn = match args.get(1) { + Some((arg, _)) => arg == "Fatal", + _ => false, + }; + let first_arg = args.first().map(|(arg, _)| arg).unwrap(); + ts_ty = if fatal_tsfn { + Some((format!("(value: {}) => any", first_arg), false)) + } else { + Some(( + format!("(err: Error | null, value: {}) => any", first_arg), + false, + )) + }; } else { // there should be runtime registered type in else let type_alias = ALIAS.with(|aliases| { diff --git a/examples/napi/__test__/typegen.spec.ts.md b/examples/napi/__test__/typegen.spec.ts.md index 76385fc9..7e9a0429 100644 --- a/examples/napi/__test__/typegen.spec.ts.md +++ b/examples/napi/__test__/typegen.spec.ts.md @@ -200,6 +200,8 @@ Generated by [AVA](https://avajs.dev). export function threadsafeFunctionClosureCapture(func: (...args: any[]) => any): void␊ export function tsfnCallWithCallback(func: (...args: any[]) => any): void␊ export function tsfnAsyncCall(func: (...args: any[]) => any): Promise␊ + export function acceptThreadsafeFunction(func: (err: Error | null, value: number) => any): void␊ + export function acceptThreadsafeFunctionFatal(func: (value: number) => any): void␊ export function getBuffer(): Buffer␊ export function appendBuffer(buf: Buffer): Buffer␊ export function getEmptyBuffer(): Buffer␊ diff --git a/examples/napi/__test__/typegen.spec.ts.snap b/examples/napi/__test__/typegen.spec.ts.snap index 375b4dee..fc83e0b5 100644 Binary files a/examples/napi/__test__/typegen.spec.ts.snap and b/examples/napi/__test__/typegen.spec.ts.snap differ diff --git a/examples/napi/__test__/values.spec.ts b/examples/napi/__test__/values.spec.ts index 1747067c..87ad5b3b 100644 --- a/examples/napi/__test__/values.spec.ts +++ b/examples/napi/__test__/values.spec.ts @@ -116,6 +116,8 @@ import { captureErrorInCallback, bigintFromI128, bigintFromI64, + acceptThreadsafeFunction, + acceptThreadsafeFunctionFatal, } from '../' test('export const', (t) => { @@ -792,6 +794,28 @@ Napi4Test('async call ThreadsafeFunction', async (t) => { ) }) +Napi4Test('accept ThreadsafeFunction', async (t) => { + await new Promise((resolve, reject) => { + acceptThreadsafeFunction((err, value) => { + if (err) { + reject(err) + } else { + t.is(value, 1) + resolve() + } + }) + }) +}) + +Napi4Test('accept ThreadsafeFunction Fatal', async (t) => { + await new Promise((resolve) => { + acceptThreadsafeFunctionFatal((value) => { + t.is(value, 1) + resolve() + }) + }) +}) + const Napi5Test = Number(process.versions.napi) >= 5 ? test : test.skip Napi5Test('Date test', (t) => { diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index aab94c85..df0dcced 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -190,6 +190,8 @@ export function threadsafeFunctionFatalModeError(cb: (...args: any[]) => any): v export function threadsafeFunctionClosureCapture(func: (...args: any[]) => any): void export function tsfnCallWithCallback(func: (...args: any[]) => any): void export function tsfnAsyncCall(func: (...args: any[]) => any): Promise +export function acceptThreadsafeFunction(func: (err: Error | null, value: number) => any): void +export function acceptThreadsafeFunctionFatal(func: (value: number) => any): void export function getBuffer(): Buffer export function appendBuffer(buf: Buffer): Buffer export function getEmptyBuffer(): Buffer diff --git a/examples/napi/src/threadsafe_function.rs b/examples/napi/src/threadsafe_function.rs index fc762e88..fe88ab64 100644 --- a/examples/napi/src/threadsafe_function.rs +++ b/examples/napi/src/threadsafe_function.rs @@ -102,3 +102,17 @@ pub fn tsfn_async_call(env: Env, func: JsFunction) -> napi::Result { Ok(()) }) } + +#[napi] +pub fn accept_threadsafe_function(func: ThreadsafeFunction) { + thread::spawn(move || { + func.call(Ok(1), ThreadsafeFunctionCallMode::NonBlocking); + }); +} + +#[napi] +pub fn accept_threadsafe_function_fatal(func: ThreadsafeFunction) { + thread::spawn(move || { + func.call(1, ThreadsafeFunctionCallMode::NonBlocking); + }); +}