jiren 1.0.6 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,4 @@
1
- import { CString, type Pointer } from "bun:ffi";
2
- import { lib } from "./native";
1
+ import { lib, readCString } from "./native";
3
2
  import type {
4
3
  RequestOptions,
5
4
  GetRequestOptions,
@@ -7,8 +6,10 @@ import type {
7
6
  HttpResponse,
8
7
  } from "../types";
9
8
 
9
+ // Runtime-agnostic pointer type
10
+ type Pointer = any;
10
11
  export class JirenClient {
11
- private ptr: Pointer | null;
12
+ private ptr: unknown | null;
12
13
 
13
14
  constructor() {
14
15
  this.ptr = lib.symbols.zclient_new();
@@ -21,7 +22,7 @@ export class JirenClient {
21
22
  */
22
23
  public close(): void {
23
24
  if (this.ptr) {
24
- lib.symbols.zclient_free(this.ptr);
25
+ lib.symbols.zclient_free(this.ptr as Pointer);
25
26
  this.ptr = null;
26
27
  }
27
28
  }
@@ -59,7 +60,7 @@ export class JirenClient {
59
60
  }
60
61
 
61
62
  const respPtr = lib.symbols.zclient_request(
62
- this.ptr,
63
+ this.ptr as Pointer,
63
64
  methodBuffer,
64
65
  urlBuffer,
65
66
  headersBuffer,
@@ -132,7 +133,7 @@ export class JirenClient {
132
133
 
133
134
  for (const url of urls) {
134
135
  const urlBuffer = Buffer.from(url + "\0");
135
- lib.symbols.zclient_prefetch(this.ptr, urlBuffer);
136
+ lib.symbols.zclient_prefetch(this.ptr as Pointer, urlBuffer);
136
137
  }
137
138
  }
138
139
 
@@ -151,15 +152,12 @@ export class JirenClient {
151
152
 
152
153
  let bodyString = "";
153
154
  if (bodyLen > 0 && bodyPtr) {
154
- // CString uses the full length if not null-terminated or we can just read the ptr
155
- // Since we know the length, we can be more precise, but CString assumes null-terminated
156
- // for now we trust it is null terminated from Zig
157
- bodyString = new CString(bodyPtr).toString();
155
+ bodyString = readCString(bodyPtr);
158
156
  }
159
157
 
160
158
  const headers: Record<string, string> = {};
161
159
  if (headersLen > 0 && headersPtr) {
162
- const headersStr = new CString(headersPtr).toString();
160
+ const headersStr = readCString(headersPtr);
163
161
  const lines = headersStr.split("\r\n");
164
162
  for (const line of lines) {
165
163
  if (!line) continue;
@@ -1,64 +1,182 @@
1
- import { dlopen, FFIType, suffix } from "bun:ffi";
2
- import { join } from "path";
1
+ import { isBun } from "./runtime";
2
+ import { join, dirname } from "path";
3
+ import { fileURLToPath } from "url";
3
4
 
4
- // Resolve library path relative to this module
5
- const libPath = join(import.meta.dir, `../lib/libhttpclient.${suffix}`);
5
+ // Helper to get library path
6
+ function getLibPath(): string {
7
+ if (isBun) {
8
+ // @ts-ignore - Bun-specific
9
+ const { suffix } = await import("bun:ffi");
10
+ return join(import.meta.dir, `../lib/libhttpclient.${suffix}`);
11
+ } else {
12
+ // Node.js
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = dirname(__filename);
15
+ const platform = process.platform;
16
+ const ext =
17
+ platform === "darwin" ? "dylib" : platform === "win32" ? "dll" : "so";
18
+ return join(__dirname, `../lib/libhttpclient.${ext}`);
19
+ }
20
+ }
6
21
 
7
- export const ffiDef = {
8
- zclient_new: {
9
- args: [],
10
- returns: FFIType.ptr,
11
- },
12
- zclient_free: {
13
- args: [FFIType.ptr],
14
- returns: FFIType.void,
15
- },
16
- zclient_get: {
17
- args: [FFIType.ptr, FFIType.cstring],
18
- returns: FFIType.ptr,
19
- },
20
- zclient_post: {
21
- args: [FFIType.ptr, FFIType.cstring, FFIType.cstring],
22
- returns: FFIType.ptr,
23
- },
24
- zclient_prefetch: {
25
- args: [FFIType.ptr, FFIType.cstring],
26
- returns: FFIType.void,
27
- },
28
- zclient_response_status: {
29
- args: [FFIType.ptr],
30
- returns: FFIType.u16,
31
- },
32
- zclient_response_body: {
33
- args: [FFIType.ptr],
34
- returns: FFIType.ptr,
35
- },
36
- zclient_response_body_len: {
37
- args: [FFIType.ptr],
38
- returns: FFIType.u64,
39
- },
40
- zclient_response_headers: {
41
- args: [FFIType.ptr],
42
- returns: FFIType.ptr,
43
- },
44
- zclient_response_headers_len: {
45
- args: [FFIType.ptr],
46
- returns: FFIType.u64,
47
- },
48
- zclient_response_free: {
49
- args: [FFIType.ptr],
50
- returns: FFIType.void,
51
- },
52
- zclient_request: {
53
- args: [
54
- FFIType.ptr, // client
55
- FFIType.cstring, // method
56
- FFIType.cstring, // url
57
- FFIType.cstring, // headers (nullable)
58
- FFIType.cstring, // body (nullable)
59
- ],
60
- returns: FFIType.ptr,
61
- },
62
- } as const;
22
+ // Type definitions for our FFI symbols
23
+ export type NativeLib = {
24
+ symbols: {
25
+ zclient_new: () => any;
26
+ zclient_free: (ptr: any) => void;
27
+ zclient_get: (client: any, url: any) => any;
28
+ zclient_post: (client: any, url: any, body: any) => any;
29
+ zclient_prefetch: (client: any, url: any) => void;
30
+ zclient_response_status: (resp: any) => number;
31
+ zclient_response_body: (resp: any) => any;
32
+ zclient_response_body_len: (resp: any) => bigint | number;
33
+ zclient_response_headers: (resp: any) => any;
34
+ zclient_response_headers_len: (resp: any) => bigint | number;
35
+ zclient_response_free: (resp: any) => void;
36
+ zclient_request: (
37
+ client: any,
38
+ method: any,
39
+ url: any,
40
+ headers: any,
41
+ body: any
42
+ ) => any;
43
+ };
44
+ };
63
45
 
64
- export const lib = dlopen(libPath, ffiDef);
46
+ // Load the appropriate FFI library
47
+ async function loadLibrary(): Promise<NativeLib> {
48
+ const libPath = getLibPath();
49
+
50
+ if (isBun) {
51
+ // Use Bun FFI
52
+ const { dlopen, FFIType } = await import("bun:ffi");
53
+
54
+ const ffiDef = {
55
+ zclient_new: { args: [], returns: FFIType.ptr },
56
+ zclient_free: { args: [FFIType.ptr], returns: FFIType.void },
57
+ zclient_get: {
58
+ args: [FFIType.ptr, FFIType.cstring],
59
+ returns: FFIType.ptr,
60
+ },
61
+ zclient_post: {
62
+ args: [FFIType.ptr, FFIType.cstring, FFIType.cstring],
63
+ returns: FFIType.ptr,
64
+ },
65
+ zclient_prefetch: {
66
+ args: [FFIType.ptr, FFIType.cstring],
67
+ returns: FFIType.void,
68
+ },
69
+ zclient_response_status: { args: [FFIType.ptr], returns: FFIType.u16 },
70
+ zclient_response_body: { args: [FFIType.ptr], returns: FFIType.ptr },
71
+ zclient_response_body_len: { args: [FFIType.ptr], returns: FFIType.u64 },
72
+ zclient_response_headers: { args: [FFIType.ptr], returns: FFIType.ptr },
73
+ zclient_response_headers_len: {
74
+ args: [FFIType.ptr],
75
+ returns: FFIType.u64,
76
+ },
77
+ zclient_response_free: { args: [FFIType.ptr], returns: FFIType.void },
78
+ zclient_request: {
79
+ args: [
80
+ FFIType.ptr,
81
+ FFIType.cstring,
82
+ FFIType.cstring,
83
+ FFIType.cstring,
84
+ FFIType.cstring,
85
+ ],
86
+ returns: FFIType.ptr,
87
+ },
88
+ };
89
+
90
+ return dlopen(libPath, ffiDef) as NativeLib;
91
+ } else {
92
+ // Use koffi for Node.js
93
+ const koffi = await import("koffi");
94
+
95
+ const nativeLib = koffi.load(libPath);
96
+
97
+ return {
98
+ symbols: {
99
+ zclient_new: nativeLib.func("zclient_new", "void*", []),
100
+ zclient_free: nativeLib.func("zclient_free", "void", ["void*"]),
101
+ zclient_get: nativeLib.func("zclient_get", "void*", ["void*", "str"]),
102
+ zclient_post: nativeLib.func("zclient_post", "void*", [
103
+ "void*",
104
+ "str",
105
+ "str",
106
+ ]),
107
+ zclient_prefetch: nativeLib.func("zclient_prefetch", "void", [
108
+ "void*",
109
+ "str",
110
+ ]),
111
+ zclient_response_status: nativeLib.func(
112
+ "zclient_response_status",
113
+ "uint16",
114
+ ["void*"]
115
+ ),
116
+ zclient_response_body: nativeLib.func(
117
+ "zclient_response_body",
118
+ "void*",
119
+ ["void*"]
120
+ ),
121
+ zclient_response_body_len: nativeLib.func(
122
+ "zclient_response_body_len",
123
+ "uint64",
124
+ ["void*"]
125
+ ),
126
+ zclient_response_headers: nativeLib.func(
127
+ "zclient_response_headers",
128
+ "void*",
129
+ ["void*"]
130
+ ),
131
+ zclient_response_headers_len: nativeLib.func(
132
+ "zclient_response_headers_len",
133
+ "uint64",
134
+ ["void*"]
135
+ ),
136
+ zclient_response_free: nativeLib.func("zclient_response_free", "void", [
137
+ "void*",
138
+ ]),
139
+ zclient_request: nativeLib.func("zclient_request", "void*", [
140
+ "void*",
141
+ "str",
142
+ "str",
143
+ "str",
144
+ "str",
145
+ ]),
146
+ },
147
+ };
148
+ }
149
+ }
150
+
151
+ // Export the loaded library (initialized on first import)
152
+ export const lib: NativeLib = await loadLibrary();
153
+
154
+ // Helper to read C string from pointer (runtime-agnostic)
155
+ export function readCString(ptr: any): string {
156
+ if (!ptr) return "";
157
+
158
+ if (isBun) {
159
+ // For Bun, we need to dynamically get CString
160
+ // Use a simple approach: read bytes until null terminator
161
+ const view = new DataView(new ArrayBuffer(0));
162
+ try {
163
+ // In Bun, pointers can be read as buffers
164
+ // @ts-ignore - Bun specific
165
+ const buf = Buffer.from(ptr as any);
166
+ const nullIdx = buf.indexOf(0);
167
+ return buf.toString("utf8", 0, nullIdx >= 0 ? nullIdx : undefined);
168
+ } catch {
169
+ // Fallback: try to read as string directly
170
+ return String(ptr);
171
+ }
172
+ } else {
173
+ // Node.js with koffi
174
+ try {
175
+ const koffi = require("koffi");
176
+ return koffi.decode(ptr, "string");
177
+ } catch (e) {
178
+ console.error("Failed to decode C string:", e);
179
+ return "";
180
+ }
181
+ }
182
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Runtime detection utility for Jiren
3
+ * Detects whether we're running in Bun or Node.js
4
+ */
5
+
6
+ export const isBun = typeof Bun !== "undefined";
7
+ export const isNode =
8
+ !isBun &&
9
+ typeof process !== "undefined" &&
10
+ process.versions &&
11
+ process.versions.node;
12
+
13
+ export function getRuntime(): "bun" | "node" | "unknown" {
14
+ if (isBun) return "bun";
15
+ if (isNode) return "node";
16
+ return "unknown";
17
+ }
@@ -1,101 +1,18 @@
1
- import { FFIType } from "bun:ffi";
2
- export declare const ffiDef: {
3
- readonly zclient_new: {
4
- readonly args: readonly [];
5
- readonly returns: FFIType.ptr;
6
- };
7
- readonly zclient_free: {
8
- readonly args: readonly [FFIType.ptr];
9
- readonly returns: FFIType.void;
10
- };
11
- readonly zclient_get: {
12
- readonly args: readonly [FFIType.ptr, FFIType.cstring];
13
- readonly returns: FFIType.ptr;
14
- };
15
- readonly zclient_post: {
16
- readonly args: readonly [FFIType.ptr, FFIType.cstring, FFIType.cstring];
17
- readonly returns: FFIType.ptr;
18
- };
19
- readonly zclient_prefetch: {
20
- readonly args: readonly [FFIType.ptr, FFIType.cstring];
21
- readonly returns: FFIType.void;
22
- };
23
- readonly zclient_response_status: {
24
- readonly args: readonly [FFIType.ptr];
25
- readonly returns: FFIType.uint16_t;
26
- };
27
- readonly zclient_response_body: {
28
- readonly args: readonly [FFIType.ptr];
29
- readonly returns: FFIType.ptr;
30
- };
31
- readonly zclient_response_body_len: {
32
- readonly args: readonly [FFIType.ptr];
33
- readonly returns: FFIType.uint64_t;
34
- };
35
- readonly zclient_response_headers: {
36
- readonly args: readonly [FFIType.ptr];
37
- readonly returns: FFIType.ptr;
38
- };
39
- readonly zclient_response_headers_len: {
40
- readonly args: readonly [FFIType.ptr];
41
- readonly returns: FFIType.uint64_t;
42
- };
43
- readonly zclient_response_free: {
44
- readonly args: readonly [FFIType.ptr];
45
- readonly returns: FFIType.void;
46
- };
47
- readonly zclient_request: {
48
- readonly args: readonly [FFIType.ptr, FFIType.cstring, FFIType.cstring, FFIType.cstring, FFIType.cstring];
49
- readonly returns: FFIType.ptr;
1
+ export type NativeLib = {
2
+ symbols: {
3
+ zclient_new: () => any;
4
+ zclient_free: (ptr: any) => void;
5
+ zclient_get: (client: any, url: any) => any;
6
+ zclient_post: (client: any, url: any, body: any) => any;
7
+ zclient_prefetch: (client: any, url: any) => void;
8
+ zclient_response_status: (resp: any) => number;
9
+ zclient_response_body: (resp: any) => any;
10
+ zclient_response_body_len: (resp: any) => bigint | number;
11
+ zclient_response_headers: (resp: any) => any;
12
+ zclient_response_headers_len: (resp: any) => bigint | number;
13
+ zclient_response_free: (resp: any) => void;
14
+ zclient_request: (client: any, method: any, url: any, headers: any, body: any) => any;
50
15
  };
51
16
  };
52
- export declare const lib: import("bun:ffi").Library<{
53
- readonly zclient_new: {
54
- readonly args: readonly [];
55
- readonly returns: FFIType.ptr;
56
- };
57
- readonly zclient_free: {
58
- readonly args: readonly [FFIType.ptr];
59
- readonly returns: FFIType.void;
60
- };
61
- readonly zclient_get: {
62
- readonly args: readonly [FFIType.ptr, FFIType.cstring];
63
- readonly returns: FFIType.ptr;
64
- };
65
- readonly zclient_post: {
66
- readonly args: readonly [FFIType.ptr, FFIType.cstring, FFIType.cstring];
67
- readonly returns: FFIType.ptr;
68
- };
69
- readonly zclient_prefetch: {
70
- readonly args: readonly [FFIType.ptr, FFIType.cstring];
71
- readonly returns: FFIType.void;
72
- };
73
- readonly zclient_response_status: {
74
- readonly args: readonly [FFIType.ptr];
75
- readonly returns: FFIType.uint16_t;
76
- };
77
- readonly zclient_response_body: {
78
- readonly args: readonly [FFIType.ptr];
79
- readonly returns: FFIType.ptr;
80
- };
81
- readonly zclient_response_body_len: {
82
- readonly args: readonly [FFIType.ptr];
83
- readonly returns: FFIType.uint64_t;
84
- };
85
- readonly zclient_response_headers: {
86
- readonly args: readonly [FFIType.ptr];
87
- readonly returns: FFIType.ptr;
88
- };
89
- readonly zclient_response_headers_len: {
90
- readonly args: readonly [FFIType.ptr];
91
- readonly returns: FFIType.uint64_t;
92
- };
93
- readonly zclient_response_free: {
94
- readonly args: readonly [FFIType.ptr];
95
- readonly returns: FFIType.void;
96
- };
97
- readonly zclient_request: {
98
- readonly args: readonly [FFIType.ptr, FFIType.cstring, FFIType.cstring, FFIType.cstring, FFIType.cstring];
99
- readonly returns: FFIType.ptr;
100
- };
101
- }>;
17
+ export declare const lib: NativeLib;
18
+ export declare function readCString(ptr: any): string;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Runtime detection utility for Jiren
3
+ * Detects whether we're running in Bun or Node.js
4
+ */
5
+ export declare const isBun: boolean;
6
+ export declare const isNode: string | false;
7
+ export declare function getRuntime(): "bun" | "node" | "unknown";
package/package.json CHANGED
@@ -1,15 +1,17 @@
1
1
  {
2
2
  "name": "jiren",
3
- "version": "1.0.6",
3
+ "version": "1.0.9",
4
4
  "author": "",
5
5
  "main": "index.ts",
6
6
  "module": "index.ts",
7
7
  "types": "dist/index.d.ts",
8
- "dependencies": {
9
- "@types/bun": "^1.3.4"
8
+ "dependencies": {},
9
+ "optionalDependencies": {
10
+ "koffi": "^2.9.0"
10
11
  },
11
12
  "devDependencies": {
12
- "typescript": "^5"
13
+ "typescript": "^5",
14
+ "@types/bun": "^1.3.4"
13
15
  },
14
16
  "peerDependencies": {
15
17
  "typescript": "^5"