objc-js 0.0.13 → 0.0.15
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 +56 -1
- package/binding.gyp +7 -2
- package/build/Release/nobjc_native.node +0 -0
- package/dist/index.d.ts +116 -1
- package/dist/index.js +117 -2
- package/dist/native.d.ts +2 -2
- package/dist/native.js +2 -2
- package/package.json +1 -1
- package/src/native/bridge.h +20 -0
- package/src/native/debug.h +19 -0
- package/src/native/ffi-utils.h +342 -0
- package/src/native/method-forwarding.h +5 -0
- package/src/native/method-forwarding.mm +104 -73
- package/src/native/nobjc.mm +3 -0
- package/src/native/protocol-impl.mm +6 -7
- package/src/native/protocol-storage.h +59 -0
- package/src/native/subclass-impl.h +20 -0
- package/src/native/subclass-impl.mm +1001 -0
- package/src/native/type-conversion.h +0 -8
package/README.md
CHANGED
|
@@ -21,9 +21,39 @@ const str = NSString.stringWithUTF8String$("Hello, World!");
|
|
|
21
21
|
console.log(str.toString());
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
### Subclassing Objective-C Classes
|
|
25
|
+
|
|
26
|
+
**objc-js** supports defining new Objective-C classes and subclassing existing ones from JavaScript. This allows you to override methods, implement custom behavior, and integrate deeply with macOS frameworks.
|
|
27
|
+
|
|
28
|
+
See [Subclassing Documentation](./docs/subclassing.md) for detailed usage and examples.
|
|
29
|
+
|
|
30
|
+
#### Quick Example
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { NobjcClass } from "objc-js";
|
|
34
|
+
|
|
35
|
+
const MyClass = NobjcClass.define({
|
|
36
|
+
name: "MyCustomClass",
|
|
37
|
+
superclass: "NSObject",
|
|
38
|
+
methods: {
|
|
39
|
+
description: {
|
|
40
|
+
types: "@@:", // returns NSString
|
|
41
|
+
implementation: (self) => {
|
|
42
|
+
const superDesc = NobjcClass.super(self, "description");
|
|
43
|
+
const prefix = NSString.stringWithUTF8String$("Custom: ");
|
|
44
|
+
return prefix.stringByAppendingString$(superDesc);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const instance = MyClass.alloc().init();
|
|
51
|
+
console.log(instance.description().toString()); // "Custom: <MyCustomClass: 0x...>"
|
|
52
|
+
```
|
|
53
|
+
|
|
24
54
|
### Protocol Implementation
|
|
25
55
|
|
|
26
|
-
**objc-js**
|
|
56
|
+
**objc-js** supports creating Objective-C protocol implementations from JavaScript. This allows you to create delegate objects that can be passed to Objective-C APIs.
|
|
27
57
|
|
|
28
58
|
#### Creating a Protocol Implementation
|
|
29
59
|
|
|
@@ -125,6 +155,31 @@ Wrapper for Objective-C objects. Methods can be called using the `$` notation.
|
|
|
125
155
|
const result = object.methodName$arg1$arg2$(arg1, arg2);
|
|
126
156
|
```
|
|
127
157
|
|
|
158
|
+
#### `NobjcClass`
|
|
159
|
+
|
|
160
|
+
Static class for defining new Objective-C classes and subclassing existing ones.
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
NobjcClass.define(definition: ClassDefinition): NobjcObject
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Parameters:**
|
|
167
|
+
|
|
168
|
+
- `definition.name`: The name of the new Objective-C class (must be unique)
|
|
169
|
+
- `definition.superclass`: The name of the superclass (e.g., "NSObject", "NSView")
|
|
170
|
+
- `definition.protocols`: (Optional) Array of protocol names to conform to
|
|
171
|
+
- `definition.methods`: Object mapping selector names to method definitions
|
|
172
|
+
|
|
173
|
+
**Returns:** The new class object (can call `.alloc().init()` on it)
|
|
174
|
+
|
|
175
|
+
See [Subclassing Documentation](./docs/subclassing.md) for more details.
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
NobjcClass.super(self: NobjcObject, selector: string, ...args: any[]): any
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Call the superclass implementation of a method. Supports methods with any number of arguments, including methods with out-parameters (like `NSError**`).
|
|
182
|
+
|
|
128
183
|
#### `NobjcProtocol`
|
|
129
184
|
|
|
130
185
|
Static class for creating protocol implementations.
|
package/binding.gyp
CHANGED
|
@@ -6,18 +6,23 @@
|
|
|
6
6
|
"src/native/nobjc.mm",
|
|
7
7
|
"src/native/ObjcObject.mm",
|
|
8
8
|
"src/native/protocol-impl.mm",
|
|
9
|
-
"src/native/method-forwarding.mm"
|
|
9
|
+
"src/native/method-forwarding.mm",
|
|
10
|
+
"src/native/subclass-impl.mm"
|
|
10
11
|
],
|
|
11
12
|
"defines": [
|
|
12
13
|
"NODE_ADDON_API_CPP_EXCEPTIONS",
|
|
13
14
|
"NAPI_VERSION=6"
|
|
14
15
|
],
|
|
15
16
|
"include_dirs": [
|
|
16
|
-
"<!@(node -p \"require('node-addon-api').include\")"
|
|
17
|
+
"<!@(node -p \"require('node-addon-api').include\")",
|
|
18
|
+
"<!@(pkg-config --cflags-only-I libffi | sed 's/-I//g')"
|
|
17
19
|
],
|
|
18
20
|
"dependencies": [
|
|
19
21
|
"<!(node -p \"require('node-addon-api').gyp\")"
|
|
20
22
|
],
|
|
23
|
+
"libraries": [
|
|
24
|
+
"-lffi"
|
|
25
|
+
],
|
|
21
26
|
"xcode_settings": {
|
|
22
27
|
"MACOSX_DEPLOYMENT_TARGET": "13.3",
|
|
23
28
|
"CLANG_CXX_LIBRARY": "libc++",
|
|
Binary file
|
package/dist/index.d.ts
CHANGED
|
@@ -58,4 +58,119 @@ declare function getPointer(obj: NobjcObject): Buffer;
|
|
|
58
58
|
* (not deallocated) when you call this function.
|
|
59
59
|
*/
|
|
60
60
|
declare function fromPointer(pointer: Buffer | bigint): NobjcObject;
|
|
61
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Method definition for defining a class method.
|
|
63
|
+
*/
|
|
64
|
+
interface MethodDefinition {
|
|
65
|
+
/**
|
|
66
|
+
* Objective-C type encoding string.
|
|
67
|
+
* Common encodings:
|
|
68
|
+
* - @ = id (object)
|
|
69
|
+
* - : = SEL (selector)
|
|
70
|
+
* - v = void
|
|
71
|
+
* - B = BOOL
|
|
72
|
+
* - q = long long / NSInteger (64-bit)
|
|
73
|
+
* - Q = unsigned long long / NSUInteger (64-bit)
|
|
74
|
+
* - ^@ = id* (pointer to object, e.g., NSError**)
|
|
75
|
+
* - @? = block
|
|
76
|
+
*
|
|
77
|
+
* Example: "@@:@^@" means:
|
|
78
|
+
* - Return: @ (id)
|
|
79
|
+
* - self: @ (id)
|
|
80
|
+
* - _cmd: : (SEL)
|
|
81
|
+
* - arg1: @ (NSArray*)
|
|
82
|
+
* - arg2: ^@ (NSError**)
|
|
83
|
+
*/
|
|
84
|
+
types: string;
|
|
85
|
+
/**
|
|
86
|
+
* The JavaScript implementation.
|
|
87
|
+
* Receives (self, ...args) where self is the instance.
|
|
88
|
+
* For NSError** out-params, the arg is an object with { set(error), get() } methods.
|
|
89
|
+
*/
|
|
90
|
+
implementation: (self: NobjcObject, ...args: any[]) => any;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Class definition for creating a new Objective-C class.
|
|
94
|
+
*/
|
|
95
|
+
interface ClassDefinition {
|
|
96
|
+
/** Name of the new Objective-C class (must be unique in the runtime) */
|
|
97
|
+
name: string;
|
|
98
|
+
/** Superclass - either a class name string or a NobjcObject representing a Class */
|
|
99
|
+
superclass: string | NobjcObject;
|
|
100
|
+
/** Optional: protocols to conform to */
|
|
101
|
+
protocols?: string[];
|
|
102
|
+
/** Instance methods to implement/override */
|
|
103
|
+
methods?: Record<string, MethodDefinition>;
|
|
104
|
+
/** Optional: class methods */
|
|
105
|
+
classMethods?: Record<string, MethodDefinition>;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* API for defining new Objective-C classes at runtime.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```typescript
|
|
112
|
+
* // Define a subclass of NSObject
|
|
113
|
+
* const MyClass = NobjcClass.define({
|
|
114
|
+
* name: "MyClass",
|
|
115
|
+
* superclass: "NSObject",
|
|
116
|
+
* protocols: ["NSCopying"],
|
|
117
|
+
* methods: {
|
|
118
|
+
* "init": {
|
|
119
|
+
* types: "@@:",
|
|
120
|
+
* implementation: (self) => {
|
|
121
|
+
* return NobjcClass.super(self, "init");
|
|
122
|
+
* }
|
|
123
|
+
* },
|
|
124
|
+
* "myMethod:": {
|
|
125
|
+
* types: "@@:@",
|
|
126
|
+
* implementation: (self, arg) => {
|
|
127
|
+
* console.log("myMethod called with", arg);
|
|
128
|
+
* return arg;
|
|
129
|
+
* }
|
|
130
|
+
* }
|
|
131
|
+
* }
|
|
132
|
+
* });
|
|
133
|
+
*
|
|
134
|
+
* // Create an instance
|
|
135
|
+
* const instance = MyClass.alloc().init();
|
|
136
|
+
* instance.myMethod$(someArg);
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
declare class NobjcClass {
|
|
140
|
+
/**
|
|
141
|
+
* Define a new Objective-C class at runtime.
|
|
142
|
+
*
|
|
143
|
+
* @param definition The class definition
|
|
144
|
+
* @returns A NobjcObject representing the new Class (can be used to alloc/init instances)
|
|
145
|
+
*
|
|
146
|
+
* @warning For private methods (like _requestContextWithRequests:error:), you must provide
|
|
147
|
+
* the correct type encoding manually since it cannot be introspected from protocols.
|
|
148
|
+
*/
|
|
149
|
+
static define(definition: ClassDefinition): NobjcObject;
|
|
150
|
+
/**
|
|
151
|
+
* Call the superclass implementation of a method.
|
|
152
|
+
* Use this inside a method implementation to invoke super.
|
|
153
|
+
*
|
|
154
|
+
* @param self The instance (the first argument to your implementation)
|
|
155
|
+
* @param selector The selector string (e.g., "init" or "_requestContextWithRequests:error:")
|
|
156
|
+
* @param args Additional arguments to pass to super
|
|
157
|
+
* @returns The result of the super call
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```typescript
|
|
161
|
+
* methods: {
|
|
162
|
+
* "init": {
|
|
163
|
+
* types: "@@:",
|
|
164
|
+
* implementation: (self) => {
|
|
165
|
+
* // Call [super init]
|
|
166
|
+
* const result = NobjcClass.super(self, "init");
|
|
167
|
+
* // Do additional setup...
|
|
168
|
+
* return result;
|
|
169
|
+
* }
|
|
170
|
+
* }
|
|
171
|
+
* }
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
static super(self: NobjcObject, selector: string, ...args: any[]): any;
|
|
175
|
+
}
|
|
176
|
+
export { NobjcLibrary, NobjcObject, NobjcMethod, NobjcProtocol, NobjcClass, getPointer, fromPointer };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LoadLibrary, GetClassObject, ObjcObject, GetPointer, FromPointer, CreateProtocolImplementation } from "./native.js";
|
|
1
|
+
import { LoadLibrary, GetClassObject, ObjcObject, GetPointer, FromPointer, CreateProtocolImplementation, DefineClass, CallSuper } from "./native.js";
|
|
2
2
|
const customInspectSymbol = Symbol.for("nodejs.util.inspect.custom");
|
|
3
3
|
const NATIVE_OBJC_OBJECT = Symbol("nativeObjcObject");
|
|
4
4
|
class NobjcLibrary {
|
|
@@ -198,4 +198,119 @@ function fromPointer(pointer) {
|
|
|
198
198
|
}
|
|
199
199
|
return new NobjcObject(nativeObj);
|
|
200
200
|
}
|
|
201
|
-
|
|
201
|
+
/**
|
|
202
|
+
* API for defining new Objective-C classes at runtime.
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```typescript
|
|
206
|
+
* // Define a subclass of NSObject
|
|
207
|
+
* const MyClass = NobjcClass.define({
|
|
208
|
+
* name: "MyClass",
|
|
209
|
+
* superclass: "NSObject",
|
|
210
|
+
* protocols: ["NSCopying"],
|
|
211
|
+
* methods: {
|
|
212
|
+
* "init": {
|
|
213
|
+
* types: "@@:",
|
|
214
|
+
* implementation: (self) => {
|
|
215
|
+
* return NobjcClass.super(self, "init");
|
|
216
|
+
* }
|
|
217
|
+
* },
|
|
218
|
+
* "myMethod:": {
|
|
219
|
+
* types: "@@:@",
|
|
220
|
+
* implementation: (self, arg) => {
|
|
221
|
+
* console.log("myMethod called with", arg);
|
|
222
|
+
* return arg;
|
|
223
|
+
* }
|
|
224
|
+
* }
|
|
225
|
+
* }
|
|
226
|
+
* });
|
|
227
|
+
*
|
|
228
|
+
* // Create an instance
|
|
229
|
+
* const instance = MyClass.alloc().init();
|
|
230
|
+
* instance.myMethod$(someArg);
|
|
231
|
+
* ```
|
|
232
|
+
*/
|
|
233
|
+
class NobjcClass {
|
|
234
|
+
/**
|
|
235
|
+
* Define a new Objective-C class at runtime.
|
|
236
|
+
*
|
|
237
|
+
* @param definition The class definition
|
|
238
|
+
* @returns A NobjcObject representing the new Class (can be used to alloc/init instances)
|
|
239
|
+
*
|
|
240
|
+
* @warning For private methods (like _requestContextWithRequests:error:), you must provide
|
|
241
|
+
* the correct type encoding manually since it cannot be introspected from protocols.
|
|
242
|
+
*/
|
|
243
|
+
static define(definition) {
|
|
244
|
+
// Convert method implementations to wrap args and unwrap returns
|
|
245
|
+
const nativeDefinition = {
|
|
246
|
+
name: definition.name,
|
|
247
|
+
superclass: typeof definition.superclass === "string" ? definition.superclass : unwrapArg(definition.superclass),
|
|
248
|
+
protocols: definition.protocols
|
|
249
|
+
};
|
|
250
|
+
if (definition.methods) {
|
|
251
|
+
nativeDefinition.methods = {};
|
|
252
|
+
for (const [selector, methodDef] of Object.entries(definition.methods)) {
|
|
253
|
+
const normalizedSelector = NobjcMethodNameToObjcSelector(selector);
|
|
254
|
+
nativeDefinition.methods[normalizedSelector] = {
|
|
255
|
+
types: methodDef.types,
|
|
256
|
+
implementation: (nativeSelf, ...nativeArgs) => {
|
|
257
|
+
// Wrap self
|
|
258
|
+
const wrappedSelf = wrapObjCObjectIfNeeded(nativeSelf);
|
|
259
|
+
// Wrap args, but preserve out-param objects as-is
|
|
260
|
+
const wrappedArgs = nativeArgs.map((arg) => {
|
|
261
|
+
// Check if it's an out-param object (has 'set' method)
|
|
262
|
+
if (arg && typeof arg === "object" && typeof arg.set === "function") {
|
|
263
|
+
// Keep out-param objects as-is, but wrap the error objects they handle
|
|
264
|
+
return {
|
|
265
|
+
set: (error) => arg.set(unwrapArg(error)),
|
|
266
|
+
get: () => wrapObjCObjectIfNeeded(arg.get())
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
return wrapObjCObjectIfNeeded(arg);
|
|
270
|
+
});
|
|
271
|
+
// Call the user's implementation
|
|
272
|
+
const result = methodDef.implementation(wrappedSelf, ...wrappedArgs);
|
|
273
|
+
// Unwrap the return value
|
|
274
|
+
return unwrapArg(result);
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// Call native DefineClass
|
|
280
|
+
const nativeClass = DefineClass(nativeDefinition);
|
|
281
|
+
// Return wrapped Class object
|
|
282
|
+
return new NobjcObject(nativeClass);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Call the superclass implementation of a method.
|
|
286
|
+
* Use this inside a method implementation to invoke super.
|
|
287
|
+
*
|
|
288
|
+
* @param self The instance (the first argument to your implementation)
|
|
289
|
+
* @param selector The selector string (e.g., "init" or "_requestContextWithRequests:error:")
|
|
290
|
+
* @param args Additional arguments to pass to super
|
|
291
|
+
* @returns The result of the super call
|
|
292
|
+
*
|
|
293
|
+
* @example
|
|
294
|
+
* ```typescript
|
|
295
|
+
* methods: {
|
|
296
|
+
* "init": {
|
|
297
|
+
* types: "@@:",
|
|
298
|
+
* implementation: (self) => {
|
|
299
|
+
* // Call [super init]
|
|
300
|
+
* const result = NobjcClass.super(self, "init");
|
|
301
|
+
* // Do additional setup...
|
|
302
|
+
* return result;
|
|
303
|
+
* }
|
|
304
|
+
* }
|
|
305
|
+
* }
|
|
306
|
+
* ```
|
|
307
|
+
*/
|
|
308
|
+
static super(self, selector, ...args) {
|
|
309
|
+
const normalizedSelector = NobjcMethodNameToObjcSelector(selector);
|
|
310
|
+
const nativeSelf = unwrapArg(self);
|
|
311
|
+
const unwrappedArgs = args.map(unwrapArg);
|
|
312
|
+
const result = CallSuper(nativeSelf, normalizedSelector, ...unwrappedArgs);
|
|
313
|
+
return wrapObjCObjectIfNeeded(result);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
export { NobjcLibrary, NobjcObject, NobjcMethod, NobjcProtocol, NobjcClass, 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, GetPointer: typeof _binding.GetPointer, FromPointer: typeof _binding.FromPointer, CreateProtocolImplementation: typeof _binding.CreateProtocolImplementation;
|
|
3
|
-
export { LoadLibrary, GetClassObject, ObjcObject, GetPointer, FromPointer, 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, DefineClass: typeof _binding.DefineClass, CallSuper: typeof _binding.CallSuper;
|
|
3
|
+
export { LoadLibrary, GetClassObject, ObjcObject, GetPointer, FromPointer, CreateProtocolImplementation, DefineClass, CallSuper };
|
|
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, GetPointer, FromPointer, CreateProtocolImplementation } = binding;
|
|
5
|
-
export { LoadLibrary, GetClassObject, ObjcObject, GetPointer, FromPointer, CreateProtocolImplementation };
|
|
4
|
+
const { LoadLibrary, GetClassObject, ObjcObject, GetPointer, FromPointer, CreateProtocolImplementation, DefineClass, CallSuper } = binding;
|
|
5
|
+
export { LoadLibrary, GetClassObject, ObjcObject, GetPointer, FromPointer, CreateProtocolImplementation, DefineClass, CallSuper };
|
package/package.json
CHANGED
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"format": "prettier --write \"**/*.{ts,js,json,md}\"",
|
|
40
40
|
"preinstall-disabled": "npm run build-scripts && npm run make-clangd-config"
|
|
41
41
|
},
|
|
42
|
-
"version": "0.0.
|
|
42
|
+
"version": "0.0.15",
|
|
43
43
|
"description": "Objective-C bridge for Node.js",
|
|
44
44
|
"main": "dist/index.js",
|
|
45
45
|
"dependencies": {
|
package/src/native/bridge.h
CHANGED
|
@@ -173,6 +173,10 @@ template <typename T>
|
|
|
173
173
|
T ConvertToNativeValue(const Napi::Value &value,
|
|
174
174
|
const ObjcArgumentContext &context) {
|
|
175
175
|
if constexpr (std::is_same_v<T, id>) {
|
|
176
|
+
// Handle null/undefined as nil
|
|
177
|
+
if (value.IsNull() || value.IsUndefined()) {
|
|
178
|
+
return nil;
|
|
179
|
+
}
|
|
176
180
|
// is value an ObjcObject instance?
|
|
177
181
|
if (value.IsObject()) {
|
|
178
182
|
Napi::Object obj = value.As<Napi::Object>();
|
|
@@ -183,6 +187,10 @@ T ConvertToNativeValue(const Napi::Value &value,
|
|
|
183
187
|
}
|
|
184
188
|
}
|
|
185
189
|
if constexpr (std::is_same_v<T, SEL>) {
|
|
190
|
+
// Handle null/undefined as NULL selector
|
|
191
|
+
if (value.IsNull() || value.IsUndefined()) {
|
|
192
|
+
return nullptr;
|
|
193
|
+
}
|
|
186
194
|
if (!value.IsString()) {
|
|
187
195
|
throw Napi::TypeError::New(value.Env(),
|
|
188
196
|
CONVERT_ARG_ERROR_MSG("Expected a string"));
|
|
@@ -191,18 +199,30 @@ T ConvertToNativeValue(const Napi::Value &value,
|
|
|
191
199
|
return sel_registerName(selName.c_str());
|
|
192
200
|
}
|
|
193
201
|
if constexpr (std::is_same_v<T, bool>) {
|
|
202
|
+
// Handle null/undefined as false
|
|
203
|
+
if (value.IsNull() || value.IsUndefined()) {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
194
206
|
if (!value.IsBoolean()) {
|
|
195
207
|
throw Napi::TypeError::New(value.Env(),
|
|
196
208
|
CONVERT_ARG_ERROR_MSG("Expected a boolean"));
|
|
197
209
|
}
|
|
198
210
|
return value.As<Napi::Boolean>().Value();
|
|
199
211
|
} else if constexpr (std::is_same_v<T, std::string>) {
|
|
212
|
+
// Handle null/undefined as empty string
|
|
213
|
+
if (value.IsNull() || value.IsUndefined()) {
|
|
214
|
+
return std::string();
|
|
215
|
+
}
|
|
200
216
|
if (!value.IsString()) {
|
|
201
217
|
throw Napi::TypeError::New(value.Env(),
|
|
202
218
|
CONVERT_ARG_ERROR_MSG("Expected a string"));
|
|
203
219
|
}
|
|
204
220
|
return value.As<Napi::String>().Utf8Value();
|
|
205
221
|
} else if constexpr (std::is_arithmetic_v<T>) {
|
|
222
|
+
// Handle null/undefined as 0
|
|
223
|
+
if (value.IsNull() || value.IsUndefined()) {
|
|
224
|
+
return static_cast<T>(0);
|
|
225
|
+
}
|
|
206
226
|
if (value.IsNumber()) {
|
|
207
227
|
return ConvertJSNumberToNativeValue<T>(value, context);
|
|
208
228
|
} else if (value.IsBigInt()) {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#ifndef DEBUG_H
|
|
2
|
+
#define DEBUG_H
|
|
3
|
+
|
|
4
|
+
#define NOBJC_DEBUG 0
|
|
5
|
+
|
|
6
|
+
// Conditional logging macro - only logs when NOBJC_DEBUG is 1
|
|
7
|
+
#if NOBJC_DEBUG
|
|
8
|
+
#define NOBJC_LOG(fmt, ...) NSLog(@"" fmt, ##__VA_ARGS__)
|
|
9
|
+
#else
|
|
10
|
+
#define NOBJC_LOG(fmt, ...) do { } while(0)
|
|
11
|
+
#endif
|
|
12
|
+
|
|
13
|
+
// Always-on error logging
|
|
14
|
+
#define NOBJC_ERROR(fmt, ...) NSLog(@"ERROR: " fmt, ##__VA_ARGS__)
|
|
15
|
+
|
|
16
|
+
// Always-on warning logging
|
|
17
|
+
#define NOBJC_WARN(fmt, ...) NSLog(@"WARNING: " fmt, ##__VA_ARGS__)
|
|
18
|
+
|
|
19
|
+
#endif // DEBUG_H
|