objcjs-types 0.2.1 → 0.3.0

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.
Files changed (194) hide show
  1. package/bin/objcjs-types.ts +31 -0
  2. package/dist/AVFoundation/functions.d.ts +21 -0
  3. package/dist/AVFoundation/functions.js +32 -0
  4. package/dist/AVFoundation/index.d.ts +1 -0
  5. package/dist/AVFoundation/index.js +1 -0
  6. package/dist/Accessibility/functions.d.ts +16 -0
  7. package/dist/Accessibility/functions.js +35 -0
  8. package/dist/Accessibility/index.d.ts +1 -0
  9. package/dist/Accessibility/index.js +1 -0
  10. package/dist/AddressBook/functions.d.ts +98 -0
  11. package/dist/AddressBook/functions.js +290 -0
  12. package/dist/AddressBook/index.d.ts +1 -0
  13. package/dist/AddressBook/index.js +1 -0
  14. package/dist/AppKit/functions.d.ts +112 -0
  15. package/dist/AppKit/functions.js +272 -0
  16. package/dist/AppKit/index.d.ts +1 -0
  17. package/dist/AppKit/index.js +1 -0
  18. package/dist/AudioToolbox/functions.d.ts +377 -0
  19. package/dist/AudioToolbox/functions.js +1124 -0
  20. package/dist/AudioToolbox/index.d.ts +1 -0
  21. package/dist/AudioToolbox/index.js +1 -0
  22. package/dist/AuthenticationServices/functions.d.ts +2 -0
  23. package/dist/AuthenticationServices/functions.js +5 -0
  24. package/dist/AuthenticationServices/index.d.ts +1 -0
  25. package/dist/AuthenticationServices/index.js +1 -0
  26. package/dist/BrowserEngineCore/functions.d.ts +3 -0
  27. package/dist/BrowserEngineCore/functions.js +11 -0
  28. package/dist/BrowserEngineCore/index.d.ts +1 -0
  29. package/dist/BrowserEngineCore/index.js +1 -0
  30. package/dist/CoreAudio/functions.d.ts +60 -0
  31. package/dist/CoreAudio/functions.js +173 -0
  32. package/dist/CoreAudio/index.d.ts +1 -0
  33. package/dist/CoreAudio/index.js +1 -0
  34. package/dist/CoreMIDI/functions.d.ts +96 -0
  35. package/dist/CoreMIDI/functions.js +287 -0
  36. package/dist/CoreMIDI/index.d.ts +1 -0
  37. package/dist/CoreMIDI/index.js +1 -0
  38. package/dist/CoreML/functions.d.ts +2 -0
  39. package/dist/CoreML/functions.js +5 -0
  40. package/dist/CoreML/index.d.ts +1 -0
  41. package/dist/CoreML/index.js +1 -0
  42. package/dist/CoreMediaIO/functions.d.ts +38 -0
  43. package/dist/CoreMediaIO/functions.js +107 -0
  44. package/dist/CoreMediaIO/index.d.ts +1 -0
  45. package/dist/CoreMediaIO/index.js +1 -0
  46. package/dist/CoreText/functions.d.ts +209 -0
  47. package/dist/CoreText/functions.js +611 -0
  48. package/dist/CoreText/index.d.ts +1 -0
  49. package/dist/CoreText/index.js +1 -0
  50. package/dist/CoreWLAN/functions.d.ts +23 -0
  51. package/dist/CoreWLAN/functions.js +56 -0
  52. package/dist/CoreWLAN/index.d.ts +1 -0
  53. package/dist/CoreWLAN/index.js +1 -0
  54. package/dist/DeviceDiscoveryExtension/functions.d.ts +11 -0
  55. package/dist/DeviceDiscoveryExtension/functions.js +17 -0
  56. package/dist/DeviceDiscoveryExtension/index.d.ts +1 -0
  57. package/dist/DeviceDiscoveryExtension/index.js +1 -0
  58. package/dist/DiscRecording/functions.d.ts +97 -0
  59. package/dist/DiscRecording/functions.js +290 -0
  60. package/dist/DiscRecording/index.d.ts +1 -0
  61. package/dist/DiscRecording/index.js +1 -0
  62. package/dist/DiscRecordingUI/functions.d.ts +13 -0
  63. package/dist/DiscRecordingUI/functions.js +38 -0
  64. package/dist/DiscRecordingUI/index.d.ts +1 -0
  65. package/dist/DiscRecordingUI/index.js +1 -0
  66. package/dist/ExceptionHandling/functions.d.ts +1 -0
  67. package/dist/ExceptionHandling/functions.js +5 -0
  68. package/dist/ExceptionHandling/index.d.ts +1 -0
  69. package/dist/ExceptionHandling/index.js +1 -0
  70. package/dist/FSKit/functions.d.ts +4 -0
  71. package/dist/FSKit/functions.js +11 -0
  72. package/dist/FSKit/index.d.ts +1 -0
  73. package/dist/FSKit/index.js +1 -0
  74. package/dist/Foundation/functions.d.ts +145 -0
  75. package/dist/Foundation/functions.js +386 -0
  76. package/dist/Foundation/index.d.ts +1 -0
  77. package/dist/Foundation/index.js +1 -0
  78. package/dist/GLKit/functions.d.ts +51 -0
  79. package/dist/GLKit/functions.js +146 -0
  80. package/dist/GLKit/index.d.ts +1 -0
  81. package/dist/GLKit/index.js +1 -0
  82. package/dist/GameController/functions.d.ts +18 -0
  83. package/dist/GameController/functions.js +44 -0
  84. package/dist/GameController/index.d.ts +1 -0
  85. package/dist/GameController/index.js +1 -0
  86. package/dist/HealthKit/functions.d.ts +19 -0
  87. package/dist/HealthKit/functions.js +35 -0
  88. package/dist/HealthKit/index.d.ts +1 -0
  89. package/dist/HealthKit/index.js +1 -0
  90. package/dist/IOSurface/functions.d.ts +53 -0
  91. package/dist/IOSurface/functions.js +155 -0
  92. package/dist/IOSurface/index.d.ts +1 -0
  93. package/dist/IOSurface/index.js +1 -0
  94. package/dist/IOUSBHost/functions.d.ts +44 -0
  95. package/dist/IOUSBHost/functions.js +131 -0
  96. package/dist/IOUSBHost/index.d.ts +1 -0
  97. package/dist/IOUSBHost/index.js +1 -0
  98. package/dist/InstantMessage/functions.d.ts +1 -0
  99. package/dist/InstantMessage/functions.js +5 -0
  100. package/dist/InstantMessage/index.d.ts +1 -0
  101. package/dist/InstantMessage/index.js +1 -0
  102. package/dist/JavaRuntimeSupport/functions.d.ts +40 -0
  103. package/dist/JavaRuntimeSupport/functions.js +113 -0
  104. package/dist/JavaRuntimeSupport/index.d.ts +1 -0
  105. package/dist/JavaRuntimeSupport/index.js +1 -0
  106. package/dist/JavaScriptCore/functions.d.ts +120 -0
  107. package/dist/JavaScriptCore/functions.js +359 -0
  108. package/dist/JavaScriptCore/index.d.ts +1 -0
  109. package/dist/JavaScriptCore/index.js +1 -0
  110. package/dist/MLCompute/functions.d.ts +27 -0
  111. package/dist/MLCompute/functions.js +41 -0
  112. package/dist/MLCompute/index.d.ts +1 -0
  113. package/dist/MLCompute/index.js +1 -0
  114. package/dist/MapKit/functions.d.ts +23 -0
  115. package/dist/MapKit/functions.js +56 -0
  116. package/dist/MapKit/index.d.ts +1 -0
  117. package/dist/MapKit/index.js +1 -0
  118. package/dist/Matter/functions.d.ts +17 -0
  119. package/dist/Matter/functions.js +26 -0
  120. package/dist/Matter/index.d.ts +1 -0
  121. package/dist/Matter/index.js +1 -0
  122. package/dist/MediaAccessibility/functions.d.ts +28 -0
  123. package/dist/MediaAccessibility/functions.js +83 -0
  124. package/dist/MediaAccessibility/index.d.ts +1 -0
  125. package/dist/MediaAccessibility/index.js +1 -0
  126. package/dist/MediaPlayer/functions.d.ts +3 -0
  127. package/dist/MediaPlayer/functions.js +11 -0
  128. package/dist/MediaPlayer/index.d.ts +1 -0
  129. package/dist/MediaPlayer/index.js +1 -0
  130. package/dist/Metal/functions.d.ts +14 -0
  131. package/dist/Metal/functions.js +26 -0
  132. package/dist/Metal/index.d.ts +1 -0
  133. package/dist/Metal/index.js +1 -0
  134. package/dist/MetalKit/functions.d.ts +11 -0
  135. package/dist/MetalKit/functions.js +20 -0
  136. package/dist/MetalKit/index.d.ts +1 -0
  137. package/dist/MetalKit/index.js +1 -0
  138. package/dist/MetalPerformanceShaders/functions.d.ts +7 -0
  139. package/dist/MetalPerformanceShaders/functions.js +14 -0
  140. package/dist/MetalPerformanceShaders/index.d.ts +1 -0
  141. package/dist/MetalPerformanceShaders/index.js +1 -0
  142. package/dist/NearbyInteraction/functions.d.ts +3 -0
  143. package/dist/NearbyInteraction/functions.js +5 -0
  144. package/dist/NearbyInteraction/index.d.ts +1 -0
  145. package/dist/NearbyInteraction/index.js +1 -0
  146. package/dist/ParavirtualizedGraphics/functions.d.ts +7 -0
  147. package/dist/ParavirtualizedGraphics/functions.js +14 -0
  148. package/dist/ParavirtualizedGraphics/index.d.ts +1 -0
  149. package/dist/ParavirtualizedGraphics/index.js +1 -0
  150. package/dist/QuartzCore/functions.d.ts +19 -0
  151. package/dist/QuartzCore/functions.js +50 -0
  152. package/dist/QuartzCore/index.d.ts +1 -0
  153. package/dist/QuartzCore/index.js +1 -0
  154. package/dist/SceneKit/functions.d.ts +17 -0
  155. package/dist/SceneKit/functions.js +38 -0
  156. package/dist/SceneKit/index.d.ts +1 -0
  157. package/dist/SceneKit/index.js +1 -0
  158. package/dist/SensorKit/functions.d.ts +4 -0
  159. package/dist/SensorKit/functions.js +14 -0
  160. package/dist/SensorKit/index.d.ts +1 -0
  161. package/dist/SensorKit/index.js +1 -0
  162. package/dist/ServiceManagement/functions.d.ts +7 -0
  163. package/dist/ServiceManagement/functions.js +20 -0
  164. package/dist/ServiceManagement/index.d.ts +1 -0
  165. package/dist/ServiceManagement/index.js +1 -0
  166. package/dist/StoreKit/functions.d.ts +1 -0
  167. package/dist/StoreKit/functions.js +5 -0
  168. package/dist/StoreKit/index.d.ts +1 -0
  169. package/dist/StoreKit/index.js +1 -0
  170. package/dist/VideoToolbox/functions.d.ts +81 -0
  171. package/dist/VideoToolbox/functions.js +236 -0
  172. package/dist/VideoToolbox/index.d.ts +1 -0
  173. package/dist/VideoToolbox/index.js +1 -0
  174. package/dist/Vision/functions.d.ts +16 -0
  175. package/dist/Vision/functions.js +38 -0
  176. package/dist/Vision/index.d.ts +1 -0
  177. package/dist/Vision/index.js +1 -0
  178. package/generator/ast-parser.ts +1368 -0
  179. package/generator/clang.ts +167 -0
  180. package/generator/custom.ts +936 -0
  181. package/generator/discover.ts +111 -0
  182. package/generator/emitter.ts +2026 -0
  183. package/generator/frameworks.ts +135 -0
  184. package/generator/index.ts +1334 -0
  185. package/generator/parse-worker.ts +263 -0
  186. package/generator/resolve-strings.ts +121 -0
  187. package/generator/struct-fields.ts +46 -0
  188. package/generator/templates/bind.ts +100 -0
  189. package/generator/templates/helpers.ts +70 -0
  190. package/generator/templates/nsdata.ts +97 -0
  191. package/generator/templates/osversion.ts +91 -0
  192. package/generator/type-mapper.ts +615 -0
  193. package/generator/worker-pool.ts +309 -0
  194. package/package.json +13 -4
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Worker thread for parallel header parsing.
3
+ * Receives parse tasks via postMessage, runs clang AST dump + parsing,
4
+ * and returns parsed results to the main thread.
5
+ *
6
+ * Each worker handles one task at a time. The WorkerPool dispatches
7
+ * tasks to idle workers for automatic load balancing.
8
+ */
9
+
10
+ declare var self: Worker;
11
+
12
+ import { clangASTDump, clangASTDumpWithPreIncludes, clangBatchASTDump } from "./clang.ts";
13
+ import {
14
+ parseAST,
15
+ parseProtocols,
16
+ parseIntegerEnums,
17
+ parseStringEnums,
18
+ parseStructs,
19
+ parseTypedefs,
20
+ parseFunctions
21
+ } from "./ast-parser.ts";
22
+
23
+ /**
24
+ * Read a header file and split it into lines for deprecation scanning.
25
+ */
26
+ async function readHeaderLines(headerPath: string): Promise<string[] | undefined> {
27
+ try {
28
+ const content = await Bun.file(headerPath).text();
29
+ return content.split("\n");
30
+ } catch {
31
+ return undefined;
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Read multiple header files and build a map of file path → lines
37
+ * for batched deprecation scanning.
38
+ */
39
+ async function readHeaderLinesMap(headerPaths: string[]): Promise<Map<string, string[]>> {
40
+ const map = new Map<string, string[]>();
41
+ const results = await Promise.all(
42
+ headerPaths.map(async (p) => {
43
+ try {
44
+ const content = await Bun.file(p).text();
45
+ return { path: p, lines: content.split("\n") };
46
+ } catch {
47
+ return { path: p, lines: undefined };
48
+ }
49
+ })
50
+ );
51
+ for (const { path, lines } of results) {
52
+ if (lines) map.set(path, lines);
53
+ }
54
+ return map;
55
+ }
56
+
57
+ self.onmessage = async (event: MessageEvent) => {
58
+ const msg = event.data;
59
+
60
+ try {
61
+ if (msg.type === "parse-batch") {
62
+ // Batched framework task: parse all classes, protocols, and enums from
63
+ // a single clang invocation that includes ALL framework headers at once.
64
+ // This dramatically reduces clang process overhead.
65
+ const classTargetSet = new Set<string>(msg.classTargets ?? []);
66
+ const protocolTargetSet = new Set<string>(msg.protocolTargets ?? []);
67
+ const integerTargetSet = new Set<string>(msg.integerEnumTargets ?? []);
68
+ const stringTargetSet = new Set<string>(msg.stringEnumTargets ?? []);
69
+ const headerPaths: string[] = msg.headerPaths ?? [];
70
+ const preIncludes: string[] = msg.preIncludes ?? [];
71
+
72
+ // Read all header files for deprecation scanning concurrently with clang.
73
+ const [headerLinesMap, ast] = await Promise.all([
74
+ readHeaderLinesMap(headerPaths),
75
+ clangBatchASTDump(headerPaths, preIncludes)
76
+ ]);
77
+
78
+ const classes = classTargetSet.size > 0 ? parseAST(ast, classTargetSet, undefined, headerLinesMap) : new Map();
79
+ const protocols =
80
+ protocolTargetSet.size > 0 ? parseProtocols(ast, protocolTargetSet, undefined, headerLinesMap) : new Map();
81
+ const integerEnums = integerTargetSet.size > 0 ? parseIntegerEnums(ast, integerTargetSet) : new Map();
82
+ const stringEnums = stringTargetSet.size > 0 ? parseStringEnums(ast, stringTargetSet) : new Map();
83
+ const structResult = parseStructs(ast);
84
+ const typedefs = parseTypedefs(ast);
85
+
86
+ // Parse C function declarations from this framework's headers
87
+ const frameworkHeaderPathSet = new Set<string>(headerPaths);
88
+ const functions = parseFunctions(ast, frameworkHeaderPathSet, msg.frameworkName ?? "");
89
+
90
+ postMessage({
91
+ id: msg.id,
92
+ type: "batch-result",
93
+ classes: [...classes.entries()],
94
+ protocols: [...protocols.entries()],
95
+ integerEnums: [...integerEnums.entries()],
96
+ stringEnums: [...stringEnums.entries()],
97
+ structs: [...structResult.structs.entries()],
98
+ structAliases: structResult.aliases,
99
+ typedefs: [...typedefs.entries()],
100
+ functions: [...functions.entries()],
101
+ // Report what was found vs expected for logging
102
+ foundClasses: classes.size,
103
+ foundProtocols: protocols.size,
104
+ foundIntegerEnums: integerEnums.size,
105
+ foundStringEnums: stringEnums.size
106
+ });
107
+ } else if (msg.type === "parse-all") {
108
+ // Unified task: parse classes, protocols, and enums from a single clang AST.
109
+ // This avoids running clang multiple times on the same header file.
110
+ const classTargetSet = new Set<string>(msg.classTargets ?? []);
111
+ const protocolTargetSet = new Set<string>(msg.protocolTargets ?? []);
112
+ const integerTargetSet = new Set<string>(msg.integerEnumTargets ?? []);
113
+ const stringTargetSet = new Set<string>(msg.stringEnumTargets ?? []);
114
+
115
+ const headerLines = await readHeaderLines(msg.headerPath);
116
+ let ast = await clangASTDump(msg.headerPath);
117
+
118
+ let classes = classTargetSet.size > 0 ? parseAST(ast, classTargetSet, headerLines) : new Map();
119
+ let protocols = protocolTargetSet.size > 0 ? parseProtocols(ast, protocolTargetSet, headerLines) : new Map();
120
+ let integerEnums = integerTargetSet.size > 0 ? parseIntegerEnums(ast, integerTargetSet) : new Map();
121
+ let stringEnums = stringTargetSet.size > 0 ? parseStringEnums(ast, stringTargetSet) : new Map();
122
+ let structResult = parseStructs(ast);
123
+ let typedefs = parseTypedefs(ast);
124
+
125
+ // Fallback: retry without -fmodules using pre-includes if any expected
126
+ // target type has missing results. Some headers contain both classes and
127
+ // enums, where the class parses fine with -fmodules but the enum does not.
128
+ // The old all-or-nothing check would skip the fallback if any target type
129
+ // succeeded, causing enums (or other types) to be silently dropped.
130
+ const missingClasses = classTargetSet.size > 0 && classes.size < classTargetSet.size;
131
+ const missingProtocols = protocolTargetSet.size > 0 && protocols.size < protocolTargetSet.size;
132
+ const missingIntEnums = integerTargetSet.size > 0 && integerEnums.size < integerTargetSet.size;
133
+ const missingStrEnums = stringTargetSet.size > 0 && stringEnums.size < stringTargetSet.size;
134
+ const hasMissing = missingClasses || missingProtocols || missingIntEnums || missingStrEnums;
135
+
136
+ if (hasMissing && msg.fallbackPreIncludes) {
137
+ const fallbackAst = await clangASTDumpWithPreIncludes(msg.headerPath, msg.fallbackPreIncludes);
138
+
139
+ // Merge fallback results: only fill in targets that were missing from the
140
+ // primary parse. Keep primary results where they succeeded to avoid
141
+ // losing data that only -fmodules can provide.
142
+ if (missingClasses) {
143
+ const fallbackClasses = parseAST(fallbackAst, classTargetSet, headerLines);
144
+ for (const [name, cls] of fallbackClasses) {
145
+ if (!classes.has(name)) classes.set(name, cls);
146
+ }
147
+ }
148
+ if (missingProtocols) {
149
+ const fallbackProtocols = parseProtocols(fallbackAst, protocolTargetSet, headerLines);
150
+ for (const [name, proto] of fallbackProtocols) {
151
+ if (!protocols.has(name)) protocols.set(name, proto);
152
+ }
153
+ }
154
+ if (missingIntEnums) {
155
+ const fallbackIntEnums = parseIntegerEnums(fallbackAst, integerTargetSet);
156
+ for (const [name, enumDef] of fallbackIntEnums) {
157
+ if (!integerEnums.has(name)) integerEnums.set(name, enumDef);
158
+ }
159
+ }
160
+ if (missingStrEnums) {
161
+ const fallbackStrEnums = parseStringEnums(fallbackAst, stringTargetSet);
162
+ for (const [name, enumDef] of fallbackStrEnums) {
163
+ if (!stringEnums.has(name)) stringEnums.set(name, enumDef);
164
+ }
165
+ }
166
+ // Re-parse structs from fallback AST and merge
167
+ const fallbackStructResult = parseStructs(fallbackAst);
168
+ for (const [name, structDef] of fallbackStructResult.structs) {
169
+ if (!structResult.structs.has(name)) structResult.structs.set(name, structDef);
170
+ }
171
+ for (const alias of fallbackStructResult.aliases) {
172
+ if (!structResult.aliases.some((a) => a.name === alias.name)) {
173
+ structResult.aliases.push(alias);
174
+ }
175
+ }
176
+ // Merge fallback typedefs
177
+ const fallbackTypedefs = parseTypedefs(fallbackAst);
178
+ for (const [name, qualType] of fallbackTypedefs) {
179
+ if (!typedefs.has(name)) typedefs.set(name, qualType);
180
+ }
181
+ }
182
+
183
+ postMessage({
184
+ id: msg.id,
185
+ type: "all-result",
186
+ classes: [...classes.entries()],
187
+ protocols: [...protocols.entries()],
188
+ integerEnums: [...integerEnums.entries()],
189
+ stringEnums: [...stringEnums.entries()],
190
+ structs: [...structResult.structs.entries()],
191
+ structAliases: structResult.aliases,
192
+ typedefs: [...typedefs.entries()],
193
+ functions: [] // parse-all is used for extra headers which don't need function parsing
194
+ });
195
+ } else if (msg.type === "parse-classes") {
196
+ const targetSet = new Set<string>(msg.targets);
197
+ const headerLines = await readHeaderLines(msg.headerPath);
198
+ let ast = await clangASTDump(msg.headerPath);
199
+ let parsed = parseAST(ast, targetSet, headerLines);
200
+
201
+ // Fallback: retry without -fmodules using pre-includes.
202
+ // Some headers (e.g., WebKit) need Foundation macros pre-loaded.
203
+ if (parsed.size === 0 && msg.fallbackPreIncludes) {
204
+ ast = await clangASTDumpWithPreIncludes(msg.headerPath, msg.fallbackPreIncludes);
205
+ parsed = parseAST(ast, targetSet, headerLines);
206
+ }
207
+
208
+ postMessage({
209
+ id: msg.id,
210
+ type: "classes-result",
211
+ // Convert Map to entries array for structured clone fast path
212
+ classes: [...parsed.entries()],
213
+ targets: msg.targets
214
+ });
215
+ } else if (msg.type === "parse-protocols") {
216
+ const targetSet = new Set<string>(msg.targets);
217
+ const headerLines = await readHeaderLines(msg.headerPath);
218
+ let ast = await clangASTDump(msg.headerPath);
219
+ let parsed = parseProtocols(ast, targetSet, headerLines);
220
+
221
+ // Fallback
222
+ if (parsed.size === 0 && msg.fallbackPreIncludes) {
223
+ ast = await clangASTDumpWithPreIncludes(msg.headerPath, msg.fallbackPreIncludes);
224
+ parsed = parseProtocols(ast, targetSet, headerLines);
225
+ }
226
+
227
+ postMessage({
228
+ id: msg.id,
229
+ type: "protocols-result",
230
+ protocols: [...parsed.entries()],
231
+ targets: msg.targets
232
+ });
233
+ } else if (msg.type === "parse-enums") {
234
+ const integerTargetSet = new Set<string>(msg.integerTargets ?? []);
235
+ const stringTargetSet = new Set<string>(msg.stringTargets ?? []);
236
+ let ast = await clangASTDump(msg.headerPath);
237
+ let integerEnums = parseIntegerEnums(ast, integerTargetSet);
238
+ let stringEnums = parseStringEnums(ast, stringTargetSet);
239
+
240
+ // Fallback: retry without -fmodules using pre-includes
241
+ if (integerEnums.size === 0 && stringEnums.size === 0 && msg.fallbackPreIncludes) {
242
+ ast = await clangASTDumpWithPreIncludes(msg.headerPath, msg.fallbackPreIncludes);
243
+ integerEnums = parseIntegerEnums(ast, integerTargetSet);
244
+ stringEnums = parseStringEnums(ast, stringTargetSet);
245
+ }
246
+
247
+ postMessage({
248
+ id: msg.id,
249
+ type: "enums-result",
250
+ integerEnums: [...integerEnums.entries()],
251
+ stringEnums: [...stringEnums.entries()],
252
+ integerTargets: msg.integerTargets ?? [],
253
+ stringTargets: msg.stringTargets ?? []
254
+ });
255
+ }
256
+ } catch (error) {
257
+ postMessage({
258
+ id: msg.id,
259
+ type: "error",
260
+ error: String(error)
261
+ });
262
+ }
263
+ };
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Resolves extern NSString * constant values at generation time.
3
+ *
4
+ * String enums (NS_TYPED_EXTENSIBLE_ENUM) declare their values as extern
5
+ * NSString * constants in the framework binary. Since objc-js doesn't expose
6
+ * dlsym, we can't read these at runtime — but we CAN read them during
7
+ * generation using Bun's FFI to call dlopen/dlsym/objc_msgSend directly.
8
+ *
9
+ * This avoids compiling a helper binary (which fails on macOS Sequoia due to
10
+ * Gatekeeper blocking unsigned locally-compiled executables).
11
+ */
12
+
13
+ import { dlopen as bunDlopen, FFIType, read, CString } from "bun:ffi";
14
+ import type { Pointer } from "bun:ffi";
15
+
16
+ /** Lazily initialized FFI bindings for dlopen/dlsym and ObjC runtime. */
17
+ let ffi: {
18
+ dlopen: (path: Buffer, flags: number) => Pointer;
19
+ dlsym: (handle: Pointer, name: Buffer) => Pointer;
20
+ dlclose: (handle: Pointer) => number;
21
+ objc_msgSend: (obj: Pointer, sel: Pointer) => Pointer;
22
+ utf8Sel: Pointer;
23
+ } | null = null;
24
+
25
+ /** Convert a JS string to a null-terminated C string buffer. */
26
+ function cstr(s: string): Buffer {
27
+ const buf = Buffer.alloc(s.length + 1);
28
+ buf.write(s, "utf-8");
29
+ return buf;
30
+ }
31
+
32
+ /** Initialize the FFI bindings once. */
33
+ function ensureFFI() {
34
+ if (ffi) return ffi;
35
+
36
+ const libdl = bunDlopen("/usr/lib/libdl.dylib", {
37
+ dlopen: { args: [FFIType.ptr, FFIType.i32], returns: FFIType.ptr },
38
+ dlsym: { args: [FFIType.ptr, FFIType.ptr], returns: FFIType.ptr },
39
+ dlclose: { args: [FFIType.ptr], returns: FFIType.i32 }
40
+ });
41
+
42
+ const libobjc = bunDlopen("/usr/lib/libobjc.A.dylib", {
43
+ sel_registerName: { args: [FFIType.ptr], returns: FFIType.ptr },
44
+ objc_msgSend: { args: [FFIType.ptr, FFIType.ptr], returns: FFIType.ptr }
45
+ });
46
+
47
+ const utf8Sel = libobjc.symbols.sel_registerName(cstr("UTF8String")) as Pointer;
48
+
49
+ ffi = {
50
+ dlopen: (path, flags) => libdl.symbols.dlopen(path, flags) as Pointer,
51
+ dlsym: (handle, name) => libdl.symbols.dlsym(handle, name) as Pointer,
52
+ dlclose: (handle) => libdl.symbols.dlclose(handle) as number,
53
+ objc_msgSend: (obj, sel) => libobjc.symbols.objc_msgSend(obj, sel) as Pointer,
54
+ utf8Sel
55
+ };
56
+
57
+ return ffi;
58
+ }
59
+
60
+ /**
61
+ * Resolve extern NSString * constant values from a framework binary.
62
+ *
63
+ * Uses Bun's FFI to call dlopen/dlsym directly within the Bun process,
64
+ * avoiding the need to compile and execute a separate helper binary.
65
+ *
66
+ * @param frameworkBinaryPath - Path to the framework binary
67
+ * (e.g., "/System/Library/Frameworks/Foundation.framework/Foundation")
68
+ * @param symbolNames - Array of extern symbol names to resolve
69
+ * @returns Map from symbol name to its string value
70
+ */
71
+ export async function resolveStringConstants(
72
+ frameworkBinaryPath: string,
73
+ symbolNames: string[]
74
+ ): Promise<Map<string, string>> {
75
+ if (symbolNames.length === 0) {
76
+ return new Map();
77
+ }
78
+
79
+ const f = ensureFFI();
80
+
81
+ // Open the framework binary via dlopen (RTLD_LAZY = 1)
82
+ const handle = f.dlopen(cstr(frameworkBinaryPath), 1);
83
+ if (Number(handle) === 0) {
84
+ return new Map();
85
+ }
86
+
87
+ const result = new Map<string, string>();
88
+
89
+ try {
90
+ for (const symbolName of symbolNames) {
91
+ // dlsym returns the address of the global variable (NSString **)
92
+ const symAddr = f.dlsym(handle, cstr(symbolName));
93
+ if (Number(symAddr) === 0) continue;
94
+
95
+ // Dereference: read the NSString * pointer from the global variable
96
+ // read.ptr returns number; cast to Pointer for objc_msgSend
97
+ const nsStringPtr = read.ptr(symAddr, 0) as unknown as Pointer;
98
+ if (Number(nsStringPtr) === 0) continue;
99
+
100
+ // Call [nsString UTF8String] via objc_msgSend -> returns const char *
101
+ const utf8Ptr = f.objc_msgSend(nsStringPtr, f.utf8Sel);
102
+ if (Number(utf8Ptr) === 0) continue;
103
+
104
+ // Read the C string value
105
+ const value = new CString(utf8Ptr).toString();
106
+ result.set(symbolName, value);
107
+ }
108
+ } finally {
109
+ f.dlclose(handle);
110
+ }
111
+
112
+ return result;
113
+ }
114
+
115
+ /**
116
+ * Clean up resources. No-op in the FFI implementation since we don't
117
+ * compile a helper binary, but kept for API compatibility.
118
+ */
119
+ export async function cleanupResolver(): Promise<void> {
120
+ // No temp files to clean up in the FFI implementation
121
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Parses the KNOWN_STRUCT_FIELDS table from the objc-js native runtime.
3
+ * This table defines the field names that the bridge assigns to struct properties
4
+ * at the JS level. Structs not in this table get positional names (field0, field1, ...).
5
+ */
6
+
7
+ import { join } from "path";
8
+
9
+ /** Path to the objc-js struct-utils.h header containing KNOWN_STRUCT_FIELDS. */
10
+ const STRUCT_UTILS_PATH = join(import.meta.dir, "..", "node_modules", "objc-js", "src", "native", "struct-utils.h");
11
+
12
+ /**
13
+ * Parse the KNOWN_STRUCT_FIELDS C++ map from struct-utils.h.
14
+ * Returns a map of struct name → field names array.
15
+ *
16
+ * The C++ source looks like:
17
+ * ```cpp
18
+ * {"CGPoint", {"x", "y"}},
19
+ * {"NSRange", {"location", "length"}},
20
+ * ```
21
+ */
22
+ export async function parseKnownStructFields(): Promise<Map<string, string[]>> {
23
+ const content = await Bun.file(STRUCT_UTILS_PATH).text();
24
+ const result = new Map<string, string[]>();
25
+
26
+ // Match each entry: {"StructName", {"field1", "field2", ...}},
27
+ const entryRe = /\{"(\w+)",\s*\{([^}]+)\}\}/g;
28
+ let match: RegExpExecArray | null;
29
+
30
+ while ((match = entryRe.exec(content)) !== null) {
31
+ const structName = match[1]!;
32
+ const fieldsStr = match[2]!;
33
+ // Extract individual field name strings
34
+ const fields: string[] = [];
35
+ const fieldRe = /"(\w+)"/g;
36
+ let fieldMatch: RegExpExecArray | null;
37
+ while ((fieldMatch = fieldRe.exec(fieldsStr)) !== null) {
38
+ fields.push(fieldMatch[1]!);
39
+ }
40
+ if (fields.length > 0) {
41
+ result.set(structName, fields);
42
+ }
43
+ }
44
+
45
+ return result;
46
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Internal helper for binding ObjC class constants with `instanceof` support.
3
+ *
4
+ * Separated from helpers.ts to avoid circular imports — framework index files
5
+ * import this, and helpers.ts imports from Foundation's index.
6
+ *
7
+ * @internal
8
+ */
9
+
10
+ import type { NobjcObject } from "objc-js";
11
+
12
+ /**
13
+ * Cache of `isKindOfClass$` results: obj → (cls → result).
14
+ *
15
+ * After the first check for a given (obj, cls) pair crosses the JS↔ObjC
16
+ * boundary, subsequent checks on the same object return the cached result
17
+ * with zero native calls. This matters for if/else chains that are hit
18
+ * repeatedly (e.g. in event handlers or render loops).
19
+ */
20
+ const _kindCache = new WeakMap<object, Map<object, boolean>>();
21
+
22
+ /**
23
+ * Cached `isKindOfClass:` check — the single path used by both
24
+ * `instanceof` (via `Symbol.hasInstance`) and the `isKindOfClass()` helper.
25
+ */
26
+ function _isKindOfClass(obj: any, cls: any): boolean {
27
+ if (typeof obj !== "object" || obj === null) return false;
28
+ let m = _kindCache.get(obj);
29
+ if (m) {
30
+ const v = m.get(cls);
31
+ if (v !== undefined) return v;
32
+ }
33
+ try {
34
+ if (!m) {
35
+ m = new Map();
36
+ _kindCache.set(obj, m);
37
+ }
38
+ const r = obj.isKindOfClass$(cls);
39
+ m.set(cls, r);
40
+ return r;
41
+ } catch {
42
+ return false;
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Type guard that narrows an ObjC object to a specific class type using
48
+ * the Objective-C runtime's `-[NSObject isKindOfClass:]` check.
49
+ *
50
+ * Results are cached per (object, class) pair so repeated checks skip
51
+ * the native boundary crossing entirely.
52
+ *
53
+ * Returns `true` if `obj` is an instance of `cls` or any of its subclasses,
54
+ * and narrows the TypeScript type accordingly.
55
+ *
56
+ * @param obj - The object to check
57
+ * @param cls - The runtime class constant (e.g. `NSWindow`, `NSString`)
58
+ * @returns `true` if `obj` is a kind of `cls`
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * import {
63
+ * ASAuthorizationAppleIDCredential,
64
+ * type _ASAuthorizationAppleIDCredential,
65
+ * ASPasswordCredential,
66
+ * type _ASPasswordCredential,
67
+ * } from "./AuthenticationServices";
68
+ * import { isKindOfClass } from "./helpers";
69
+ *
70
+ * const cred = authorization.credential();
71
+ *
72
+ * if (isKindOfClass<_ASAuthorizationAppleIDCredential>(cred, ASAuthorizationAppleIDCredential)) {
73
+ * cred.user(); // narrowed to _ASAuthorizationAppleIDCredential
74
+ * cred.email();
75
+ * } else if (isKindOfClass<_ASPasswordCredential>(cred, ASPasswordCredential)) {
76
+ * cred.user();
77
+ * cred.password();
78
+ * }
79
+ * ```
80
+ */
81
+ export function isKindOfClass<T extends NobjcObject>(obj: NobjcObject, cls: any): obj is T {
82
+ return _isKindOfClass(obj, cls);
83
+ }
84
+
85
+ /**
86
+ * Load a class constant from a framework library and patch
87
+ * `Symbol.hasInstance` so that `obj instanceof ClassName` works at runtime
88
+ * by delegating to the ObjC runtime's `-[NSObject isKindOfClass:]`.
89
+ *
90
+ * Uses the same cached check as `isKindOfClass()`.
91
+ *
92
+ * @internal
93
+ */
94
+ export function _bindClass<T>(lib: any, name: string): T {
95
+ const cls = lib[name];
96
+ Object.defineProperty(cls, Symbol.hasInstance, {
97
+ value: (obj: any) => _isKindOfClass(obj, cls)
98
+ });
99
+ return cls as unknown as T;
100
+ }
@@ -0,0 +1,70 @@
1
+ import type { NobjcObject } from "objc-js";
2
+ import {
3
+ NSArray,
4
+ NSDictionary,
5
+ NSString,
6
+ type _NSArray,
7
+ type _NSDictionary,
8
+ type _NSString
9
+ } from "./Foundation/index.js";
10
+
11
+ /**
12
+ * Create NSString from a JavaScript string
13
+ * @param str The string object
14
+ * @returns An NSString object
15
+ */
16
+ export function NSStringFromString(str: string): _NSString {
17
+ const nsString = NSString.stringWithUTF8String$(str);
18
+ if (!nsString) {
19
+ throw new Error(`Failed to create NSString from string: ${str}`);
20
+ }
21
+ return nsString;
22
+ }
23
+
24
+ /**
25
+ * Combine NS_OPTIONS values using bitwise OR.
26
+ *
27
+ * Accepts any number of option flag values and returns their bitwise
28
+ * combination, preserving the NS_OPTIONS type for type safety.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * import { NSWindowStyleMask } from "./AppKit";
33
+ * import { options } from "./helpers";
34
+ *
35
+ * const mask = options(
36
+ * NSWindowStyleMask.Titled,
37
+ * NSWindowStyleMask.Closable,
38
+ * NSWindowStyleMask.Resizable,
39
+ * );
40
+ * ```
41
+ */
42
+ export function options<T extends number>(...values: T[]): T {
43
+ let result = 0;
44
+ for (const v of values) {
45
+ result |= v;
46
+ }
47
+ return result as T;
48
+ }
49
+
50
+ export function NSArrayFromObjects(objects: NobjcObject[]): _NSArray {
51
+ if (objects.length === 0) {
52
+ return NSArray.array();
53
+ }
54
+ let array = NSArray.arrayWithObject$(objects[0]!);
55
+ for (let i = 1; i < objects.length; i++) {
56
+ array = array.arrayByAddingObject$(objects[i]!);
57
+ }
58
+ return array;
59
+ }
60
+
61
+ export function NSDictionaryFromKeysAndValues(keys: NobjcObject[], values: NobjcObject[]): _NSDictionary {
62
+ if (keys.length !== values.length) {
63
+ throw new Error("Keys and values arrays must have the same length");
64
+ }
65
+ const keysArray = NSArrayFromObjects(keys);
66
+ const valuesArray = NSArrayFromObjects(values);
67
+ return NSDictionary.dictionaryWithObjects$forKeys$(valuesArray, keysArray);
68
+ }
69
+
70
+ export { isKindOfClass } from "./bind.js";
@@ -0,0 +1,97 @@
1
+ import { NSData, type _NSData } from "./Foundation/index.js";
2
+ import { NSStringFromString } from "./helpers.js";
3
+
4
+ /**
5
+ * Create NSData from a JavaScript Buffer
6
+ * @param buffer The Buffer object
7
+ * @returns An NSData object
8
+ */
9
+ export function NSDataFromBuffer(buffer: Buffer): _NSData {
10
+ return NSData.dataWithBytes$length$(buffer, buffer.length);
11
+ }
12
+
13
+ /**
14
+ * Convert NSData to a JavaScript Buffer using base64 encoding
15
+ * This is the most reliable method for data conversion.
16
+ * @param data The NSData object
17
+ * @returns A Buffer containing the data
18
+ */
19
+ export function bufferFromNSData(data: _NSData): Buffer {
20
+ const nsData = data as unknown as typeof NSData.prototype;
21
+ const length = nsData.length();
22
+
23
+ if (length === 0) {
24
+ return Buffer.alloc(0);
25
+ }
26
+
27
+ // Use base64 encoding as a reliable bridge between NSData and JS Buffer
28
+ const base64String = nsData.base64EncodedStringWithOptions$(0);
29
+ const base64Str = base64String.UTF8String();
30
+ return Buffer.from(base64Str, "base64");
31
+ }
32
+
33
+ /**
34
+ * Convert NSData to a JavaScript Buffer using direct memory copy
35
+ * This method uses getBytes:length: for direct memory access.
36
+ * May be more efficient for large data, but requires proper buffer allocation.
37
+ * @param data The NSData object
38
+ * @returns A Buffer containing the data
39
+ */
40
+ export function bufferFromNSDataDirect(data: _NSData): Buffer {
41
+ const nsData = data as unknown as typeof NSData.prototype;
42
+ const length = nsData.length();
43
+
44
+ if (length === 0) {
45
+ return Buffer.alloc(0);
46
+ }
47
+
48
+ // Allocate a buffer and copy bytes directly
49
+ const buffer = Buffer.alloc(length);
50
+ nsData.getBytes$length$(buffer, length);
51
+ return buffer;
52
+ }
53
+
54
+ /**
55
+ * Convert NSData to a JavaScript Uint8Array
56
+ * @param data The NSData object
57
+ * @returns A Uint8Array containing the data
58
+ */
59
+ export function uint8ArrayFromNSData(data: _NSData): Uint8Array {
60
+ const buffer = bufferFromNSData(data);
61
+ return new Uint8Array(buffer);
62
+ }
63
+
64
+ /**
65
+ * Convert NSData to a base64 string
66
+ * @param data The NSData object
67
+ * @returns A base64-encoded string
68
+ */
69
+ export function base64FromNSData(data: _NSData): string {
70
+ const nsString = data.base64EncodedStringWithOptions$(0);
71
+ return nsString.UTF8String();
72
+ }
73
+
74
+ /**
75
+ * Create NSData from a base64 string
76
+ * @param base64String The base64-encoded string
77
+ * @returns An NSData object
78
+ */
79
+ export function NSDataFromBase64(base64String: string): _NSData {
80
+ const nsString = NSStringFromString(base64String);
81
+ const nsDataAlloc = NSData.alloc();
82
+ const nsData = nsDataAlloc.initWithBase64EncodedString$options$(nsString, 0);
83
+ if (!nsData) {
84
+ throw new Error(`Failed to create NSData from base64 string: ${base64String}`);
85
+ }
86
+ return nsData;
87
+ }
88
+
89
+ /**
90
+ * Get the length of NSData
91
+ * @param data The NSData object
92
+ * @returns The length in bytes
93
+ */
94
+ export function NSDataLength(data: _NSData): number {
95
+ const nsData = data as unknown as typeof NSData.prototype;
96
+ return nsData.length();
97
+ }