objc-js 0.0.5 → 0.0.6

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.
package/README.md CHANGED
@@ -142,3 +142,116 @@ NobjcProtocol.implement(
142
142
  - `methodImplementations`: An object mapping method names (using `$` notation) to JavaScript functions
143
143
 
144
144
  **Returns:** A `NobjcObject` that can be passed to Objective-C APIs expecting the protocol
145
+
146
+ #### `getPointer()`
147
+
148
+ Get the raw native pointer for a NobjcObject as a Node Buffer. This is useful for passing Objective-C objects to native APIs that expect raw pointers, such as Electron's native window handles.
149
+
150
+ ```typescript
151
+ function getPointer(obj: NobjcObject): Buffer;
152
+ ```
153
+
154
+ **Parameters:**
155
+
156
+ - `obj`: The NobjcObject to get the pointer from
157
+
158
+ **Returns:** A Buffer containing the pointer address in little-endian format (8 bytes on 64-bit macOS)
159
+
160
+ **Example: Getting an NSView pointer**
161
+
162
+ ```typescript
163
+ import { NobjcLibrary, getPointer } from "objc-js";
164
+
165
+ // Load AppKit framework
166
+ const appKit = new NobjcLibrary("/System/Library/Frameworks/AppKit.framework/AppKit");
167
+
168
+ // Get an NSWindow and its content view
169
+ const NSApplication = appKit["NSApplication"];
170
+ const app = NSApplication.sharedApplication();
171
+ const window = app.mainWindow();
172
+ const view = window.contentView();
173
+
174
+ // Get the raw pointer as a Buffer
175
+ const pointerBuffer = getPointer(view);
176
+
177
+ // Read the pointer as a BigInt (64-bit unsigned integer)
178
+ const pointer = pointerBuffer.readBigUInt64LE(0);
179
+ console.log(`NSView pointer: 0x${pointer.toString(16)}`);
180
+
181
+ // Use with Electron or other native APIs
182
+ // const { BrowserWindow } = require('electron');
183
+ // const win = BrowserWindow.fromId(pointer);
184
+ ```
185
+
186
+ **Note:** The pointer is returned as a Buffer in little-endian format. Use `readBigUInt64LE(0)` to read it as a 64-bit unsigned integer, which is the standard pointer size on macOS.
187
+
188
+ #### `fromPointer()`
189
+
190
+ Create a NobjcObject from a raw native pointer. This is the inverse of `getPointer()` and allows you to reconstruct an Objective-C object from a pointer address.
191
+
192
+ ```typescript
193
+ function fromPointer(pointer: Buffer | bigint): NobjcObject;
194
+ ```
195
+
196
+ **Parameters:**
197
+
198
+ - `pointer`: A Buffer (8 bytes in little-endian format) or BigInt containing the pointer address
199
+
200
+ **Returns:** A NobjcObject wrapping the native Objective-C object
201
+
202
+ **Example: Round-trip pointer conversion**
203
+
204
+ ```typescript
205
+ import { NobjcLibrary, getPointer, fromPointer } from "objc-js";
206
+
207
+ const foundation = new NobjcLibrary("/System/Library/Frameworks/Foundation.framework/Foundation");
208
+ const NSString = foundation["NSString"];
209
+
210
+ // Create an original object
211
+ const original = NSString.stringWithUTF8String$("Hello, World!");
212
+ console.log(original.toString()); // "Hello, World!"
213
+
214
+ // Get the pointer
215
+ const pointerBuffer = getPointer(original);
216
+ const pointer = pointerBuffer.readBigUInt64LE(0);
217
+ console.log(`Pointer: 0x${pointer.toString(16)}`);
218
+
219
+ // Reconstruct the object from the pointer
220
+ const reconstructed = fromPointer(pointer);
221
+ // or: const reconstructed = fromPointer(pointerBuffer);
222
+
223
+ console.log(reconstructed.toString()); // "Hello, World!"
224
+
225
+ // Both objects reference the same underlying Objective-C object
226
+ const ptr1 = getPointer(original).readBigUInt64LE(0);
227
+ const ptr2 = getPointer(reconstructed).readBigUInt64LE(0);
228
+ console.log(ptr1 === ptr2); // true
229
+ ```
230
+
231
+ **Example: Using with external native APIs**
232
+
233
+ ```typescript
234
+ // Receive a pointer from an external API
235
+ const externalPointer = 0x12345678n; // Example pointer from native code
236
+
237
+ // Convert it to a NobjcObject
238
+ const nsObject = fromPointer(externalPointer);
239
+
240
+ // Now you can call Objective-C methods on it
241
+ console.log(nsObject.description());
242
+ ```
243
+
244
+ **⚠️ Safety Warning:**
245
+
246
+ This function is **inherently unsafe** and should be used with extreme caution:
247
+
248
+ - **Invalid pointers will crash your program**: The pointer must point to a valid Objective-C object
249
+ - **Dangling pointers**: The object must still be alive (not deallocated). Accessing a deallocated object will crash
250
+ - **No type checking**: There's no way to verify the pointer points to the expected type of object
251
+ - **Memory management**: Be aware of Objective-C reference counting. The object must remain valid for the lifetime of your usage
252
+
253
+ Only use this function when:
254
+
255
+ - You received the pointer from `getPointer()` and the object is still alive
256
+ - You received the pointer from a trusted native API that guarantees the object's validity
257
+ - You're interfacing with external native code that provides valid Objective-C object pointers
Binary file
package/dist/index.d.ts CHANGED
@@ -7,10 +7,55 @@ declare class NobjcObject {
7
7
  [key: string]: NobjcMethod;
8
8
  constructor(object: NobjcNative.ObjcObject);
9
9
  }
10
- declare class NobjcMethod {
11
- constructor(object: NobjcNative.ObjcObject, methodName: string);
10
+ interface NobjcMethod {
11
+ (...args: any[]): any;
12
12
  }
13
+ declare const NobjcMethod: (object: NobjcNative.ObjcObject, methodName: string) => NobjcMethod;
13
14
  declare class NobjcProtocol {
14
15
  static implement(protocolName: string, methodImplementations: Record<string, (...args: any[]) => any>): NobjcObject;
15
16
  }
16
- export { NobjcLibrary, NobjcObject, NobjcMethod, NobjcProtocol };
17
+ /**
18
+ * Get the raw native pointer for a NobjcObject as a Node Buffer.
19
+ * The pointer is stored in little-endian format (8 bytes on 64-bit macOS).
20
+ *
21
+ * @param obj - The NobjcObject to get the pointer from
22
+ * @returns A Buffer containing the pointer address
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * const view = window.contentView();
27
+ * const pointerBuffer = getPointer(view);
28
+ * const pointer = pointerBuffer.readBigUInt64LE(0);
29
+ * console.log(`NSView pointer: 0x${pointer.toString(16)}`);
30
+ * ```
31
+ */
32
+ declare function getPointer(obj: NobjcObject): Buffer;
33
+ /**
34
+ * Create a NobjcObject from a raw native pointer.
35
+ *
36
+ * @param pointer - A Buffer or BigInt containing the pointer address
37
+ * @returns A NobjcObject wrapping the native object
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * // From a Buffer
42
+ * const pointerBuffer = getPointer(originalObject);
43
+ * const reconstructed = fromPointer(pointerBuffer);
44
+ *
45
+ * // From a BigInt
46
+ * const pointer = 0x12345678n;
47
+ * const obj = fromPointer(pointer);
48
+ *
49
+ * // Round-trip example
50
+ * const original = NSString.stringWithUTF8String$("Hello");
51
+ * const ptr = getPointer(original).readBigUInt64LE(0);
52
+ * const restored = fromPointer(ptr);
53
+ * console.log(restored.toString()); // "Hello"
54
+ * ```
55
+ *
56
+ * @warning This is unsafe! The pointer must point to a valid Objective-C object.
57
+ * Using an invalid pointer will cause a crash. The object must still be alive
58
+ * (not deallocated) when you call this function.
59
+ */
60
+ declare function fromPointer(pointer: Buffer | bigint): NobjcObject;
61
+ export { NobjcLibrary, NobjcObject, NobjcMethod, NobjcProtocol, getPointer, fromPointer };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { LoadLibrary, GetClassObject, ObjcObject, CreateProtocolImplementation } from "./native.js";
1
+ import { LoadLibrary, GetClassObject, ObjcObject, GetPointer, FromPointer, CreateProtocolImplementation } from "./native.js";
2
2
  const NATIVE_OBJC_OBJECT = Symbol("nativeObjcObject");
3
3
  class NobjcLibrary {
4
4
  constructor(library) {
@@ -54,7 +54,7 @@ class NobjcObject {
54
54
  if (!(methodName in receiver)) {
55
55
  throw new Error(`Method ${methodName} not found on object ${receiver}`);
56
56
  }
57
- return new NobjcMethod(object, methodName);
57
+ return NobjcMethod(object, methodName);
58
58
  }
59
59
  };
60
60
  return new Proxy(object, handler);
@@ -66,22 +66,21 @@ function unwrapArg(arg) {
66
66
  }
67
67
  return arg;
68
68
  }
69
- class NobjcMethod {
70
- constructor(object, methodName) {
71
- const selector = NobjcMethodNameToObjcSelector(methodName);
72
- // This cannot be an arrow function because we need to access `arguments`.
73
- function methodFunc() {
74
- const unwrappedArgs = Array.from(arguments).map(unwrapArg);
75
- const result = object.$msgSend(selector, ...unwrappedArgs);
76
- if (typeof result == "object" && result instanceof ObjcObject) {
77
- return new NobjcObject(result);
78
- }
79
- return result;
69
+ // Note: This is actually a factory function that returns a callable Proxy
70
+ const NobjcMethod = function (object, methodName) {
71
+ const selector = NobjcMethodNameToObjcSelector(methodName);
72
+ // This cannot be an arrow function because we need to access `arguments`.
73
+ function methodFunc() {
74
+ const unwrappedArgs = Array.from(arguments).map(unwrapArg);
75
+ const result = object.$msgSend(selector, ...unwrappedArgs);
76
+ if (typeof result == "object" && result instanceof ObjcObject) {
77
+ return new NobjcObject(result);
80
78
  }
81
- const handler = {};
82
- return new Proxy(methodFunc, handler);
79
+ return result;
83
80
  }
84
- }
81
+ const handler = {};
82
+ return new Proxy(methodFunc, handler);
83
+ };
85
84
  class NobjcProtocol {
86
85
  static implement(protocolName, methodImplementations) {
87
86
  // Convert method names from $ notation to : notation
@@ -109,4 +108,61 @@ class NobjcProtocol {
109
108
  return new NobjcObject(nativeObj);
110
109
  }
111
110
  }
112
- export { NobjcLibrary, NobjcObject, NobjcMethod, NobjcProtocol };
111
+ /**
112
+ * Get the raw native pointer for a NobjcObject as a Node Buffer.
113
+ * The pointer is stored in little-endian format (8 bytes on 64-bit macOS).
114
+ *
115
+ * @param obj - The NobjcObject to get the pointer from
116
+ * @returns A Buffer containing the pointer address
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * const view = window.contentView();
121
+ * const pointerBuffer = getPointer(view);
122
+ * const pointer = pointerBuffer.readBigUInt64LE(0);
123
+ * console.log(`NSView pointer: 0x${pointer.toString(16)}`);
124
+ * ```
125
+ */
126
+ function getPointer(obj) {
127
+ // Unwrap the NobjcObject to get the native ObjcObject
128
+ if (obj && typeof obj === "object" && NATIVE_OBJC_OBJECT in obj) {
129
+ const nativeObj = obj[NATIVE_OBJC_OBJECT];
130
+ return GetPointer(nativeObj);
131
+ }
132
+ throw new TypeError("Argument must be a NobjcObject instance");
133
+ }
134
+ /**
135
+ * Create a NobjcObject from a raw native pointer.
136
+ *
137
+ * @param pointer - A Buffer or BigInt containing the pointer address
138
+ * @returns A NobjcObject wrapping the native object
139
+ *
140
+ * @example
141
+ * ```typescript
142
+ * // From a Buffer
143
+ * const pointerBuffer = getPointer(originalObject);
144
+ * const reconstructed = fromPointer(pointerBuffer);
145
+ *
146
+ * // From a BigInt
147
+ * const pointer = 0x12345678n;
148
+ * const obj = fromPointer(pointer);
149
+ *
150
+ * // Round-trip example
151
+ * const original = NSString.stringWithUTF8String$("Hello");
152
+ * const ptr = getPointer(original).readBigUInt64LE(0);
153
+ * const restored = fromPointer(ptr);
154
+ * console.log(restored.toString()); // "Hello"
155
+ * ```
156
+ *
157
+ * @warning This is unsafe! The pointer must point to a valid Objective-C object.
158
+ * Using an invalid pointer will cause a crash. The object must still be alive
159
+ * (not deallocated) when you call this function.
160
+ */
161
+ function fromPointer(pointer) {
162
+ const nativeObj = FromPointer(pointer);
163
+ if (nativeObj === null) {
164
+ throw new Error("Cannot create object from null pointer");
165
+ }
166
+ return new NobjcObject(nativeObj);
167
+ }
168
+ export { NobjcLibrary, NobjcObject, NobjcMethod, NobjcProtocol, getPointer, fromPointer };
package/dist/native.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  import * as _binding from "#nobjc_native";
2
- declare const LoadLibrary: typeof _binding.LoadLibrary, GetClassObject: typeof _binding.GetClassObject, ObjcObject: typeof _binding.ObjcObject, CreateProtocolImplementation: typeof _binding.CreateProtocolImplementation;
3
- export { LoadLibrary, GetClassObject, ObjcObject, CreateProtocolImplementation };
2
+ declare const LoadLibrary: typeof _binding.LoadLibrary, GetClassObject: typeof _binding.GetClassObject, ObjcObject: typeof _binding.ObjcObject, GetPointer: typeof _binding.GetPointer, FromPointer: typeof _binding.FromPointer, CreateProtocolImplementation: typeof _binding.CreateProtocolImplementation;
3
+ export { LoadLibrary, GetClassObject, ObjcObject, GetPointer, FromPointer, CreateProtocolImplementation };
4
4
  export type { _binding as NobjcNative };
package/dist/native.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
2
  const require = createRequire(import.meta.url);
3
3
  const binding = require("#nobjc_native");
4
- const { LoadLibrary, GetClassObject, ObjcObject, CreateProtocolImplementation } = binding;
5
- export { LoadLibrary, GetClassObject, ObjcObject, CreateProtocolImplementation };
4
+ const { LoadLibrary, GetClassObject, ObjcObject, GetPointer, FromPointer, CreateProtocolImplementation } = binding;
5
+ export { LoadLibrary, GetClassObject, ObjcObject, GetPointer, FromPointer, CreateProtocolImplementation };
package/package.json CHANGED
@@ -36,7 +36,7 @@
36
36
  "format": "prettier --write \"**/*.{ts,js,json,md}\"",
37
37
  "preinstall-disabled": "npm run build-scripts && npm run make-clangd-config"
38
38
  },
39
- "version": "0.0.5",
39
+ "version": "0.0.6",
40
40
  "description": "Objective-C bridge for Node.js",
41
41
  "main": "dist/index.js",
42
42
  "devDependencies": {