aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xpackages/bun-types/bun.lockbbin37964 -> 38313 bytes
-rw-r--r--packages/bun-types/ffi.d.ts149
-rw-r--r--packages/bun-types/package.json5
-rw-r--r--packages/bun-types/tests/ffi.test-d.ts96
4 files changed, 222 insertions, 28 deletions
diff --git a/packages/bun-types/bun.lockb b/packages/bun-types/bun.lockb
index b95880249..e764f6a0c 100755
--- a/packages/bun-types/bun.lockb
+++ b/packages/bun-types/bun.lockb
Binary files differ
diff --git a/packages/bun-types/ffi.d.ts b/packages/bun-types/ffi.d.ts
index 19337fab1..3e7e91534 100644
--- a/packages/bun-types/ffi.d.ts
+++ b/packages/bun-types/ffi.d.ts
@@ -345,6 +345,74 @@ declare module "bun:ffi" {
*/
u64_fast = 16,
}
+
+ type UNTYPED = never;
+ export type Pointer = number & {};
+
+ interface FFITypeToType {
+ [FFIType.char]: number;
+ [FFIType.int8_t]: number;
+ [FFIType.i8]: number;
+ [FFIType.uint8_t]: number;
+ [FFIType.u8]: number;
+ [FFIType.int16_t]: number;
+ [FFIType.i16]: number;
+ [FFIType.uint16_t]: number;
+ [FFIType.u16]: number;
+ [FFIType.int32_t]: number;
+ [FFIType.i32]: number;
+ [FFIType.int]: number;
+ [FFIType.uint32_t]: number;
+ [FFIType.u32]: number;
+ [FFIType.int64_t]: UNTYPED;
+ [FFIType.i64]: UNTYPED;
+ [FFIType.uint64_t]: UNTYPED;
+ [FFIType.u64]: UNTYPED;
+ [FFIType.double]: UNTYPED;
+ [FFIType.f64]: UNTYPED;
+ [FFIType.float]: UNTYPED;
+ [FFIType.f32]: UNTYPED;
+ [FFIType.bool]: boolean;
+ [FFIType.ptr]: Pointer;
+ [FFIType.pointer]: Pointer;
+ [FFIType.void]: UNTYPED;
+ [FFIType.cstring]: CString;
+ [FFIType.i64_fast]: number | bigint;
+ [FFIType.u64_fast]: number | bigint;
+ }
+ interface FFITypeStringToType {
+ ["char"]: FFIType.char;
+ ["int8_t"]: FFIType.int8_t;
+ ["i8"]: FFIType.i8;
+ ["uint8_t"]: FFIType.uint8_t;
+ ["u8"]: FFIType.u8;
+ ["int16_t"]: FFIType.int16_t;
+ ["i16"]: FFIType.i16;
+ ["uint16_t"]: FFIType.uint16_t;
+ ["u16"]: FFIType.u16;
+ ["int32_t"]: FFIType.int32_t;
+ ["i32"]: FFIType.i32;
+ ["int"]: FFIType.int;
+ ["uint32_t"]: FFIType.uint32_t;
+ ["u32"]: FFIType.u32;
+ ["int64_t"]: FFIType.int64_t;
+ ["i64"]: FFIType.i64;
+ ["uint64_t"]: FFIType.uint64_t;
+ ["u64"]: FFIType.u64;
+ ["double"]: FFIType.double;
+ ["f64"]: FFIType.f64;
+ ["float"]: FFIType.float;
+ ["f32"]: FFIType.f32;
+ ["bool"]: FFIType.bool;
+ ["ptr"]: FFIType.ptr;
+ ["pointer"]: FFIType.pointer;
+ ["void"]: FFIType.void;
+ ["cstring"]: FFIType.cstring;
+ ["function"]: FFIType.pointer; // for now
+ ["usize"]: FFIType.uint64_t; // for now
+ ["callback"]: FFIType.pointer; // for now
+ }
+
export type FFITypeOrString =
| FFIType
| "char"
@@ -388,12 +456,16 @@ declare module "bun:ffi" {
*
* @example
* From JavaScript:
- * ```js
- * const lib = dlopen('add', {
- * // FFIType can be used or you can pass string labels.
- * args: [FFIType.i32, "i32"],
- * returns: "i32",
- * });
+ * ```ts
+ * import { dlopen, FFIType, suffix } from "bun:ffi"
+ *
+ * const lib = dlopen(`adder.${suffix}`, {
+ * add: {
+ * // FFIType can be used or you can pass string labels.
+ * args: [FFIType.i32, "i32"],
+ * returns: "i32",
+ * },
+ * })
* lib.symbols.add(1, 2)
* ```
* In C:
@@ -413,7 +485,9 @@ declare module "bun:ffi" {
*
* @example
* From JavaScript:
- * ```js
+ * ```ts
+ * import { dlopen, CString } from "bun:ffi"
+ *
* const lib = dlopen('z', {
* version: {
* returns: "ptr",
@@ -470,16 +544,8 @@ declare module "bun:ffi" {
// */
// export function callback(ffi: FFIFunction, cb: Function): number;
- export interface Library {
- symbols: Record<
- string,
- CallableFunction & {
- /**
- * The function without a wrapper
- */
- native: CallableFunction;
- }
- >;
+ export interface Library<Fns extends Record<string, Narrow<FFIFunction>>> {
+ symbols: ConvertFns<Fns>;
/**
* `dlclose` the library, unloading the symbols and freeing allocated memory.
@@ -491,6 +557,32 @@ declare module "bun:ffi" {
close(): void;
}
+ type ToFFIType<T extends FFITypeOrString> = T extends FFIType
+ ? T
+ : T extends string
+ ? FFITypeStringToType[T]
+ : never;
+
+ type _Narrow<T, U> = [U] extends [T] ? U : Extract<T, U>;
+ type Narrow<T = unknown> =
+ | _Narrow<T, 0 | (number & {})>
+ | _Narrow<T, 0n | (bigint & {})>
+ | _Narrow<T, "" | (string & {})>
+ | _Narrow<T, boolean>
+ | _Narrow<T, symbol>
+ | _Narrow<T, []>
+ | _Narrow<T, { [_: PropertyKey]: Narrow }>
+ | (T extends object ? { [K in keyof T]: Narrow<T[K]> } : never)
+ | Extract<{} | null | undefined, T>;
+
+ type ConvertFns<Fns extends Record<string, FFIFunction>> = {
+ [K in keyof Fns]: (
+ ...args: Fns[K]["args"] extends infer A extends FFITypeOrString[]
+ ? { [L in keyof A]: FFITypeToType[ToFFIType<A[L]>] }
+ : never
+ ) => FFITypeToType[ToFFIType<NonNullable<Fns[K]["returns"]>>];
+ };
+
/**
* Open a library using `"bun:ffi"`
*
@@ -518,7 +610,10 @@ declare module "bun:ffi" {
* goes to Fabrice Bellard and TinyCC maintainers for making this possible.
*
*/
- export function dlopen(name: string, symbols: Symbols): Library;
+ export function dlopen<Fns extends Record<string, Narrow<FFIFunction>>>(
+ name: string,
+ symbols: Fns,
+ ): Library<Fns>;
/**
* Turn a native library's function pointer into a JavaScript function
@@ -548,7 +643,7 @@ declare module "bun:ffi" {
*
*/
export function CFunction(
- fn: FFIFunction & { ptr: number | bigint },
+ fn: FFIFunction & { ptr: Pointer },
): CallableFunction & {
/**
* Free the memory allocated by the wrapping function
@@ -608,7 +703,9 @@ declare module "bun:ffi" {
* goes to Fabrice Bellard and TinyCC maintainers for making this possible.
*
*/
- export function linkSymbols(symbols: Symbols): Library;
+ export function linkSymbols<Fns extends Record<string, Narrow<FFIFunction>>>(
+ symbols: Fns,
+ ): Library<Fns>;
/**
* Read a pointer as a {@link Buffer}
@@ -626,7 +723,7 @@ declare module "bun:ffi" {
*
*/
export function toBuffer(
- ptr: number,
+ ptr: Pointer,
byteOffset?: number,
byteLength?: number,
): Buffer;
@@ -646,7 +743,7 @@ declare module "bun:ffi" {
* undefined behavior. Use with care!
*/
export function toArrayBuffer(
- ptr: number,
+ ptr: Pointer,
byteOffset?: number,
byteLength?: number,
): ArrayBuffer;
@@ -681,7 +778,7 @@ declare module "bun:ffi" {
export function ptr(
view: TypedArray | ArrayBufferLike | DataView,
byteOffset?: number,
- ): number;
+ ): Pointer;
/**
* Get a string from a UTF-8 encoded C string
@@ -734,7 +831,7 @@ declare module "bun:ffi" {
* reading beyond the bounds of the pointer will crash the program or cause
* undefined behavior. Use with care!
*/
- constructor(ptr: number, byteOffset?: number, byteLength?: number);
+ constructor(ptr: Pointer, byteOffset?: number, byteLength?: number);
/**
* The ptr to the C string
@@ -743,7 +840,7 @@ declare module "bun:ffi" {
* is safe to continue using this instance after the `ptr` has been
* freed.
*/
- ptr: number;
+ ptr: Pointer;
byteOffset?: number;
byteLength?: number;
@@ -772,7 +869,7 @@ declare module "bun:ffi" {
*
* Becomes `null` once {@link JSCallback.prototype.close} is called
*/
- readonly ptr: number | null;
+ readonly ptr: Pointer | null;
/**
* Can the callback be called from a different thread?
diff --git a/packages/bun-types/package.json b/packages/bun-types/package.json
index ada826752..2dbf1839d 100644
--- a/packages/bun-types/package.json
+++ b/packages/bun-types/package.json
@@ -10,8 +10,9 @@
"fmt": "prettier --write './**/*.{ts,tsx,js,jsx}'"
},
"devDependencies": {
- "tsd": "^0.22.0",
- "prettier": "^2.4.1"
+ "conditional-type-checks": "^1.0.6",
+ "prettier": "^2.4.1",
+ "tsd": "^0.22.0"
},
"tsd": {
"directory": "tests"
diff --git a/packages/bun-types/tests/ffi.test-d.ts b/packages/bun-types/tests/ffi.test-d.ts
new file mode 100644
index 000000000..105736134
--- /dev/null
+++ b/packages/bun-types/tests/ffi.test-d.ts
@@ -0,0 +1,96 @@
+import { dlopen, FFIType, suffix, CString, Pointer } from "bun:ffi";
+import * as tsd from "tsd";
+import * as tc from "conditional-type-checks";
+
+// `suffix` is either "dylib", "so", or "dll" depending on the platform
+// you don't have to use "suffix", it's just there for convenience
+const path = `libsqlite3.${suffix}`;
+
+const lib = dlopen(
+ path, // a library name or file path
+ {
+ sqlite3_libversion: {
+ // no arguments, returns a string
+ args: [],
+ returns: FFIType.cstring,
+ },
+ add: {
+ args: [FFIType.i32, FFIType.i32],
+ returns: FFIType.i32,
+ },
+ allArgs: {
+ args: [
+ FFIType.char, // string
+ FFIType.int8_t,
+ FFIType.i8,
+ FFIType.uint8_t,
+ FFIType.u8,
+ FFIType.int16_t,
+ FFIType.i16,
+ FFIType.uint16_t,
+ FFIType.u16,
+ FFIType.int32_t,
+ FFIType.i32,
+ FFIType.int,
+ FFIType.uint32_t,
+ FFIType.u32,
+ FFIType.int64_t,
+ FFIType.i64,
+ FFIType.uint64_t,
+ FFIType.u64,
+ FFIType.double,
+ FFIType.f64,
+ FFIType.float,
+ FFIType.f32,
+ FFIType.bool,
+ FFIType.ptr,
+ FFIType.pointer,
+ FFIType.void,
+ FFIType.cstring,
+ FFIType.i64_fast,
+ FFIType.u64_fast,
+ ],
+ returns: FFIType.void,
+ },
+ },
+);
+
+tsd.expectType<CString>(lib.symbols.sqlite3_libversion());
+tsd.expectType<number>(lib.symbols.add(1, 2));
+
+tc.assert<
+ tc.IsExact<
+ typeof lib["symbols"]["allArgs"],
+ [
+ number,
+ number,
+ number,
+ number,
+ number,
+ number,
+ number,
+ number,
+ number,
+ number,
+ number,
+ number,
+ number,
+ number,
+ never,
+ never,
+ never,
+ never,
+ never,
+ never,
+ never,
+ never,
+ boolean,
+ Pointer,
+ Pointer,
+ never,
+ CString,
+ number | bigint,
+ number | bigint,
+ ]
+ >
+>;