novac 2.0.1 → 2.2.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 (161) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +1574 -597
  3. package/bin/novac +468 -171
  4. package/bin/nvc +522 -0
  5. package/bin/nvml +78 -17
  6. package/demo.nv +0 -0
  7. package/demo_builtins.nv +0 -0
  8. package/demo_http.nv +0 -0
  9. package/examples/bf.nv +69 -0
  10. package/examples/math.nv +21 -0
  11. package/kits/birdAPI/kitdef.js +954 -0
  12. package/kits/kitRNG/kitdef.js +740 -0
  13. package/kits/kitSSH/kitdef.js +1272 -0
  14. package/kits/kitadb/kitdef.js +606 -0
  15. package/kits/kitai/kitdef.js +2185 -0
  16. package/kits/kitansi/kitdef.js +1402 -0
  17. package/kits/kitcanvas/kitdef.js +914 -0
  18. package/kits/kitclippy/kitdef.js +925 -0
  19. package/kits/kitformat/kitdef.js +1485 -0
  20. package/kits/kitgps/kitdef.js +1862 -0
  21. package/kits/kitlibproc/kitdef.js +3 -2
  22. package/kits/kitmatrix/ex.js +19 -0
  23. package/kits/kitmatrix/kitdef.js +960 -0
  24. package/kits/kitmorse/kitdef.js +229 -0
  25. package/kits/kitmpatch/kitdef.js +906 -0
  26. package/kits/kitnet/kitdef.js +1401 -0
  27. package/kits/kitnovacweb/README.md +1416 -143
  28. package/kits/kitnovacweb/kitdef.js +92 -2
  29. package/kits/kitnovacweb/nvml/executor.js +578 -176
  30. package/kits/kitnovacweb/nvml/index.js +2 -2
  31. package/kits/kitnovacweb/nvml/lexer.js +72 -69
  32. package/kits/kitnovacweb/nvml/parser.js +328 -159
  33. package/kits/kitnovacweb/nvml/renderer.js +770 -270
  34. package/kits/kitparse/kitdef.js +1688 -0
  35. package/kits/kitproto/kitdef.js +613 -0
  36. package/kits/kitqr/kitdef.js +637 -0
  37. package/kits/kitregex++/kitdef.js +1353 -0
  38. package/kits/kitrequire/kitdef.js +1599 -0
  39. package/kits/kitx11/kitdef.js +1 -0
  40. package/kits/kitx11/kitx11.js +2472 -0
  41. package/kits/kitx11/kitx11_conn.js +948 -0
  42. package/kits/kitx11/kitx11_worker.js +121 -0
  43. package/kits/libtea/kitdef.js +2691 -0
  44. package/kits/libterm/ex.js +285 -0
  45. package/kits/libterm/kitdef.js +1927 -0
  46. package/novac/LICENSE +21 -0
  47. package/novac/README.md +1823 -0
  48. package/novac/bin/novac +950 -0
  49. package/novac/bin/nvc +522 -0
  50. package/novac/bin/nvml +542 -0
  51. package/novac/demo.nv +245 -0
  52. package/novac/demo_builtins.nv +209 -0
  53. package/novac/demo_http.nv +62 -0
  54. package/novac/examples/bf.nv +69 -0
  55. package/novac/examples/math.nv +21 -0
  56. package/novac/kits/kitai/kitdef.js +2185 -0
  57. package/novac/kits/kitansi/kitdef.js +1402 -0
  58. package/novac/kits/kitformat/kitdef.js +1485 -0
  59. package/novac/kits/kitgps/kitdef.js +1862 -0
  60. package/novac/kits/kitlibfs/kitdef.js +231 -0
  61. package/{examples/example-project/nova_modules → novac/kits}/kitlibproc/kitdef.js +3 -2
  62. package/novac/kits/kitmatrix/ex.js +19 -0
  63. package/novac/kits/kitmatrix/kitdef.js +960 -0
  64. package/novac/kits/kitmpatch/kitdef.js +906 -0
  65. package/novac/kits/kitnovacweb/README.md +1572 -0
  66. package/novac/kits/kitnovacweb/demo.nv +12 -0
  67. package/novac/kits/kitnovacweb/demo.nvml +71 -0
  68. package/novac/kits/kitnovacweb/index.nova +12 -0
  69. package/novac/kits/kitnovacweb/kitdef.js +692 -0
  70. package/novac/kits/kitnovacweb/nova.kit.json +8 -0
  71. package/novac/kits/kitnovacweb/nvml/executor.js +739 -0
  72. package/novac/kits/kitnovacweb/nvml/index.js +67 -0
  73. package/novac/kits/kitnovacweb/nvml/lexer.js +263 -0
  74. package/novac/kits/kitnovacweb/nvml/parser.js +508 -0
  75. package/novac/kits/kitnovacweb/nvml/renderer.js +924 -0
  76. package/novac/kits/kitparse/kitdef.js +1688 -0
  77. package/novac/kits/kitregex++/kitdef.js +1353 -0
  78. package/novac/kits/kitrequire/kitdef.js +1599 -0
  79. package/novac/kits/kitx11/kitdef.js +1 -0
  80. package/novac/kits/kitx11/kitx11.js +2472 -0
  81. package/novac/kits/kitx11/kitx11_conn.js +948 -0
  82. package/novac/kits/kitx11/kitx11_worker.js +121 -0
  83. package/novac/kits/libtea/tf.js +2691 -0
  84. package/novac/kits/libterm/ex.js +285 -0
  85. package/novac/kits/libterm/kitdef.js +1927 -0
  86. package/novac/node_modules/chalk/license +9 -0
  87. package/novac/node_modules/chalk/package.json +83 -0
  88. package/novac/node_modules/chalk/readme.md +297 -0
  89. package/novac/node_modules/chalk/source/index.d.ts +325 -0
  90. package/novac/node_modules/chalk/source/index.js +225 -0
  91. package/novac/node_modules/chalk/source/utilities.js +33 -0
  92. package/novac/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +236 -0
  93. package/novac/node_modules/chalk/source/vendor/ansi-styles/index.js +223 -0
  94. package/novac/node_modules/chalk/source/vendor/supports-color/browser.d.ts +1 -0
  95. package/novac/node_modules/chalk/source/vendor/supports-color/browser.js +34 -0
  96. package/novac/node_modules/chalk/source/vendor/supports-color/index.d.ts +55 -0
  97. package/novac/node_modules/chalk/source/vendor/supports-color/index.js +190 -0
  98. package/novac/node_modules/commander/LICENSE +22 -0
  99. package/novac/node_modules/commander/Readme.md +1176 -0
  100. package/novac/node_modules/commander/esm.mjs +16 -0
  101. package/novac/node_modules/commander/index.js +24 -0
  102. package/novac/node_modules/commander/lib/argument.js +150 -0
  103. package/novac/node_modules/commander/lib/command.js +2777 -0
  104. package/novac/node_modules/commander/lib/error.js +39 -0
  105. package/novac/node_modules/commander/lib/help.js +747 -0
  106. package/novac/node_modules/commander/lib/option.js +380 -0
  107. package/novac/node_modules/commander/lib/suggestSimilar.js +101 -0
  108. package/novac/node_modules/commander/package-support.json +19 -0
  109. package/novac/node_modules/commander/package.json +82 -0
  110. package/novac/node_modules/commander/typings/esm.d.mts +3 -0
  111. package/novac/node_modules/commander/typings/index.d.ts +1113 -0
  112. package/novac/node_modules/node-addon-api/LICENSE.md +9 -0
  113. package/novac/node_modules/node-addon-api/README.md +95 -0
  114. package/novac/node_modules/node-addon-api/common.gypi +21 -0
  115. package/novac/node_modules/node-addon-api/except.gypi +25 -0
  116. package/novac/node_modules/node-addon-api/index.js +14 -0
  117. package/novac/node_modules/node-addon-api/napi-inl.deprecated.h +186 -0
  118. package/novac/node_modules/node-addon-api/napi-inl.h +7165 -0
  119. package/novac/node_modules/node-addon-api/napi.h +3364 -0
  120. package/novac/node_modules/node-addon-api/node_addon_api.gyp +42 -0
  121. package/novac/node_modules/node-addon-api/node_api.gyp +9 -0
  122. package/novac/node_modules/node-addon-api/noexcept.gypi +26 -0
  123. package/novac/node_modules/node-addon-api/package-support.json +21 -0
  124. package/novac/node_modules/node-addon-api/package.json +480 -0
  125. package/novac/node_modules/node-addon-api/tools/README.md +73 -0
  126. package/novac/node_modules/node-addon-api/tools/check-napi.js +99 -0
  127. package/novac/node_modules/node-addon-api/tools/clang-format.js +71 -0
  128. package/novac/node_modules/node-addon-api/tools/conversion.js +301 -0
  129. package/novac/node_modules/serialize-javascript/LICENSE +27 -0
  130. package/novac/node_modules/serialize-javascript/README.md +149 -0
  131. package/novac/node_modules/serialize-javascript/index.js +297 -0
  132. package/novac/node_modules/serialize-javascript/package.json +33 -0
  133. package/novac/package.json +27 -0
  134. package/novac/scripts/update-bin.js +24 -0
  135. package/novac/src/core/bstd.js +1035 -0
  136. package/novac/src/core/config.js +155 -0
  137. package/novac/src/core/describe.js +187 -0
  138. package/novac/src/core/emitter.js +499 -0
  139. package/novac/src/core/error.js +86 -0
  140. package/novac/src/core/executor.js +5606 -0
  141. package/novac/src/core/formatter.js +686 -0
  142. package/novac/src/core/lexer.js +1026 -0
  143. package/novac/src/core/nova_builtins.js +717 -0
  144. package/novac/src/core/nova_thread_worker.js +166 -0
  145. package/novac/src/core/parser.js +2181 -0
  146. package/novac/src/core/types.js +112 -0
  147. package/novac/src/index.js +28 -0
  148. package/novac/src/runtime/stdlib.js +244 -0
  149. package/package.json +6 -3
  150. package/scripts/update-bin.js +0 -0
  151. package/src/core/bstd.js +838 -362
  152. package/src/core/executor.js +2578 -170
  153. package/src/core/lexer.js +502 -54
  154. package/src/core/nova_builtins.js +21 -3
  155. package/src/core/parser.js +413 -72
  156. package/src/core/types.js +30 -2
  157. package/src/index.js +0 -0
  158. package/examples/example-project/README.md +0 -3
  159. package/examples/example-project/src/main.nova +0 -3
  160. package/src/core/environment.js +0 -0
  161. /package/{examples/example-project/bin/example-project.nv → novac/node_modules/node-addon-api/nothing.c} +0 -0
package/bin/nvc ADDED
@@ -0,0 +1,522 @@
1
+ #!/usr/bin/env node
2
+ // nvc — novac global binary: native addon kit generator
3
+ //
4
+ // Usage:
5
+ // nvc natives --name myaddon --sources win32:src/addon_win.cpp linux-x64:src/addon_lnx.cpp darwin:src/addon_mac.cpp
6
+ //
7
+ // Output:
8
+ // nkitmyaddon/
9
+ // bins/ ← compiled .node binaries per platform/arch
10
+ // src/ ← copied source files
11
+ // CMakeLists.txt ← auto-generated (no binding.gyp — node-gyp is never used)
12
+ // kitdef.js ← runtime loader (sniffs platform+arch)
13
+ // unsupported.cpp ← auto-generated stub for missing targets
14
+ // build.js ← the build orchestrator (cmake-js → cmake → manual gcc/clang)
15
+
16
+ 'use strict';
17
+
18
+ const fs = require('fs');
19
+ const path = require('path');
20
+ const os = require('os');
21
+ const { execSync, spawnSync } = require('child_process');
22
+
23
+ // ─── CONSTANTS ────────────────────────────────────────────────────────────────
24
+
25
+ const SUPPORTED_PLATFORMS = ['win32', 'linux', 'darwin', 'android', 'freebsd', 'openbsd'];
26
+ const SUPPORTED_ARCHS = ['x64', 'x86', 'arm64', 'arm', 'ia32', 'riscv64', 'mips'];
27
+
28
+ // Node-API version to target (stable across Node 12+)
29
+ const NAPI_VERSION = 6;
30
+
31
+ // ─── HELPERS ─────────────────────────────────────────────────────────────────
32
+
33
+ function run(cmd, cwd, silent = false) {
34
+ const result = spawnSync(cmd, { shell: true, cwd, stdio: silent ? 'pipe' : 'inherit' });
35
+ return result.status === 0;
36
+ }
37
+
38
+ function has(cmd) {
39
+ const r = spawnSync(cmd, ['--version'], { shell: true, stdio: 'pipe' });
40
+ return r.status === 0;
41
+ }
42
+
43
+ function mkdir(p) {
44
+ fs.mkdirSync(p, { recursive: true });
45
+ }
46
+
47
+ function write(p, content) {
48
+ fs.writeFileSync(p, content, 'utf8');
49
+ }
50
+
51
+ function copy(src, dest) {
52
+ fs.copyFileSync(src, dest);
53
+ }
54
+
55
+ // Parse "platform-arch:path" or "platform:path" source specs
56
+ // e.g. "linux-x64:src/addon.cpp" "win32:src/win.cpp" "darwin-arm64:src/mac.cpp"
57
+ function parseSourceSpec(spec) {
58
+ const colon = spec.indexOf(':');
59
+ if (colon === -1) throw new Error(`Invalid source spec (missing ':'): ${spec}`);
60
+ const tag = spec.slice(0, colon).trim();
61
+ const file = spec.slice(colon + 1).trim();
62
+ const dash = tag.indexOf('-');
63
+ let platform, arch;
64
+ if (dash === -1) {
65
+ platform = tag;
66
+ arch = '*'; // any arch for this platform
67
+ } else {
68
+ platform = tag.slice(0, dash);
69
+ arch = tag.slice(dash + 1);
70
+ }
71
+ return { platform, arch, file };
72
+ }
73
+
74
+ // Canonical binary name: addon-linux-x64.node
75
+ function binName(kitName, platform, arch) {
76
+ return `${kitName}-${platform}-${arch}.node`;
77
+ }
78
+
79
+ // ─── CODE GENERATORS ─────────────────────────────────────────────────────────
80
+
81
+ function genCMakeLists(kitName, srcFile, napiVersion) {
82
+ // Resolve paths at generation time — avoids CMake execute_process quoting bugs on all platforms
83
+ const { execSync: _exec } = require('child_process');
84
+ let nodeInclude = '';
85
+ let napiInclude = '';
86
+ let nodeBinDir = '';
87
+
88
+ try {
89
+ const _p = require('path');
90
+ nodeInclude = _p.join(_p.dirname(process.execPath), '..', 'include', 'node');
91
+ } catch(_) {}
92
+ // napi include: always use the local node_modules path — node-addon-api isn't
93
+ // installed globally, it gets installed by npm install inside the kit folder.
94
+ // CMakeLists.txt is generated before that happens, so we hardcode the relative path.
95
+ napiInclude = '${CMAKE_SOURCE_DIR}/node_modules/node-addon-api';
96
+ try {
97
+ nodeBinDir = require('path').dirname(process.execPath);
98
+ } catch(_) {}
99
+
100
+ return `cmake_minimum_required(VERSION 3.15)
101
+ project(${kitName})
102
+
103
+ set(CMAKE_CXX_STANDARD 17)
104
+
105
+ # Paths baked in at kit-generation time by nvc (no runtime execute_process needed)
106
+ set(NODE_INCLUDE_DIR "${nodeInclude}")
107
+ set(NAPI_INCLUDE "${napiInclude}")
108
+ set(NODE_BIN_DIR "${nodeBinDir}")
109
+
110
+ add_library(\${PROJECT_NAME} SHARED ${srcFile})
111
+ set_target_properties(\${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")
112
+
113
+ target_include_directories(\${PROJECT_NAME} PRIVATE
114
+ \${NODE_INCLUDE_DIR}
115
+ \${NAPI_INCLUDE}
116
+ )
117
+
118
+ target_compile_definitions(\${PROJECT_NAME} PRIVATE
119
+ NAPI_VERSION=${napiVersion}
120
+ NAPI_DISABLE_CPP_EXCEPTIONS
121
+ )
122
+
123
+ if(WIN32)
124
+ target_link_libraries(\${PROJECT_NAME} "\${NODE_BIN_DIR}/node.lib")
125
+ elseif(APPLE)
126
+ set_target_properties(\${PROJECT_NAME} PROPERTIES
127
+ LINK_FLAGS "-undefined dynamic_lookup"
128
+ )
129
+ endif()
130
+ `;
131
+ }
132
+
133
+
134
+ function genUnsupportedStub(kitName) {
135
+ return `// Auto-generated unsupported-arch stub for ${kitName}
136
+ #include <node_api.h>
137
+ #include <stdio.h>
138
+
139
+ napi_value Init(napi_env env, napi_value exports) {
140
+ const char* arch =
141
+ #if defined(__x86_64__) || defined(_M_X64)
142
+ "x64";
143
+ #elif defined(__i386__) || defined(_M_IX86)
144
+ "x86";
145
+ #elif defined(__aarch64__) || defined(_M_ARM64)
146
+ "arm64";
147
+ #elif defined(__arm__) || defined(_M_ARM)
148
+ "arm";
149
+ #elif defined(__riscv) && __riscv_xlen == 64
150
+ "riscv64";
151
+ #else
152
+ "unknown";
153
+ #endif
154
+
155
+ const char* plat =
156
+ #if defined(_WIN32)
157
+ "win32";
158
+ #elif defined(__APPLE__)
159
+ "darwin";
160
+ #elif defined(__ANDROID__)
161
+ "android";
162
+ #elif defined(__linux__)
163
+ "linux";
164
+ #elif defined(__FreeBSD__)
165
+ "freebsd";
166
+ #else
167
+ "unknown";
168
+ #endif
169
+
170
+ fprintf(stderr, "\\n[${kitName}] This native addon does not support your platform: %s-%s\\n", plat, arch);
171
+ fprintf(stderr, " Please check https://your-project/nkit${kitName} for supported targets.\\n\\n");
172
+
173
+ napi_throw_error(env, "UNSUPPORTED_PLATFORM",
174
+ "This native addon does not support your current platform/architecture.");
175
+ return exports;
176
+ }
177
+
178
+ NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
179
+ `;
180
+ }
181
+
182
+ function genKitdef(kitName, sources) {
183
+ const entries = sources.map(s => {
184
+ const archClause = s.arch === '*'
185
+ ? `platform === '${s.platform}'`
186
+ : `platform === '${s.platform}' && arch === '${s.arch}'`;
187
+ return ` if (${archClause}) return require('./bins/${binName(kitName, s.platform, s.arch === '*' ? 'universal' : s.arch)}');`;
188
+ });
189
+
190
+ return `// kitdef.js — auto-generated by kitnatives for ${kitName}
191
+ // Do not edit manually; re-run kitnatives to regenerate.
192
+
193
+ 'use strict';
194
+
195
+ const path = require('path');
196
+ const platform = process.platform;
197
+ const arch = process.arch;
198
+
199
+ function loadAddon() {
200
+ ${entries.join('\n')}
201
+
202
+ // Fallback: unsupported platform/arch
203
+ try {
204
+ return require('./bins/${kitName}-unsupported.node');
205
+ } catch (_) {
206
+ throw new Error(
207
+ \`[${kitName}] Unsupported platform: \${platform}-\${arch}. \` +
208
+ \`No native binary available for this target.\`
209
+ );
210
+ }
211
+ }
212
+
213
+ const addon = loadAddon();
214
+
215
+ module.exports = {
216
+ kitdef: addon
217
+ };
218
+ `;
219
+ }
220
+
221
+ function genBuildScript(kitName, sources, outDir) {
222
+ const builds = sources.map((s, i) => {
223
+ const bname = binName(kitName, s.platform, s.arch === '*' ? 'universal' : s.arch);
224
+ const srcRel = path.basename(s.file);
225
+ return `
226
+ // ── Build ${i + 1}: ${s.platform}-${s.arch} ──────────────────────────────────
227
+ {
228
+ const srcFile = path.join(__dirname, 'src', '${srcRel}');
229
+ const outBin = path.join(__dirname, 'bins', '${bname}');
230
+ const cmake = path.join(__dirname, 'cmake_build_${i}');
231
+
232
+ console.log('[kitnatives] Building ${s.platform}-${s.arch}...');
233
+
234
+ if (fs.existsSync(outBin)) {
235
+ console.log(' → Already built, skipping.');
236
+ } else {
237
+ let built = false;
238
+
239
+ // Strategy 1: cmake-js (explicitly told to use CMake, never gyp)
240
+ if (!built && hasTool('cmake-js')) {
241
+ console.log(' → Trying cmake-js...');
242
+ fs.mkdirSync(cmake, { recursive: true });
243
+ // Pass --cmake-options to avoid any gyp fallback; point at our CMakeLists.txt
244
+ const r = run(
245
+ \`cmake-js compile --runtime node --cmake-options="-DCMAKE_BUILD_TYPE=Release" --directory "\${__dirname}" --out "\${cmake}"\`,
246
+ __dirname
247
+ );
248
+ if (r) {
249
+ const nodeFile = findNodeFile(cmake);
250
+ if (nodeFile) { fs.copyFileSync(nodeFile, outBin); built = true; }
251
+ }
252
+ }
253
+
254
+ // Strategy 2: cmake manually
255
+ if (!built && hasTool('cmake')) {
256
+ console.log(' → Trying cmake...');
257
+ fs.mkdirSync(cmake, { recursive: true });
258
+ const r1 = run(\`cmake .. -DSRC_FILE="${srcRel}" -DKIT_NAME="${kitName}"\`, cmake);
259
+ const r2 = r1 && run('cmake --build . --config Release', cmake);
260
+ if (r2) {
261
+ const nodeFile = findNodeFile(cmake);
262
+ if (nodeFile) { fs.copyFileSync(nodeFile, outBin); built = true; }
263
+ }
264
+ }
265
+
266
+ // Strategy 3: raw gcc/clang
267
+ if (!built) {
268
+ console.log(' → Trying manual gcc/clang compile...');
269
+ const compiler = hasTool('clang++') ? 'clang++' : hasTool('g++') ? 'g++' : null;
270
+ if (compiler) {
271
+ // Resolve include paths at build time
272
+ let nodeHead = '', napiHead = '';
273
+ try { const _p = require('path'); nodeHead = _p.join(_p.dirname(process.execPath), '..', 'include', 'node'); } catch(_) {}
274
+ napiHead = require('path').join(__dirname, 'node_modules', 'node-addon-api');
275
+ const isAndroid = process.platform === 'android' || process.env.TERMUX_VERSION;
276
+ const sharedFlag = isAndroid ? '-shared' : process.platform === 'darwin' ? '-dynamiclib' : '-shared';
277
+ const extraFlags = process.platform === 'darwin' ? '-undefined dynamic_lookup' : '';
278
+ const flags = [
279
+ sharedFlag, '-fPIC', extraFlags,
280
+ nodeHead ? \`-I"\${nodeHead}"\` : '',
281
+ napiHead ? \`-I"\${napiHead}"\` : '',
282
+ \`-DNAPI_VERSION=${NAPI_VERSION}\`,
283
+ '-DNAPI_DISABLE_CPP_EXCEPTIONS',
284
+ '-std=c++17',
285
+ \`"\${srcFile}"\`,
286
+ \`-o "\${outBin}"\`
287
+ ].filter(Boolean).join(' ');
288
+ const r = run(\`\${compiler} \${flags}\`, __dirname);
289
+ if (r) built = true;
290
+ }
291
+ }
292
+
293
+ if (!built) {
294
+ console.warn(\` ✗ Could not build ${s.platform}-${s.arch}. Skipping.\`);
295
+ } else {
296
+ console.log(\` ✓ Built → bins/${bname}\`);
297
+ }
298
+ }
299
+ }`;
300
+ });
301
+
302
+ return `#!/usr/bin/env node
303
+ // build.js — auto-generated by kitnatives for ${kitName}
304
+ // Run this to compile all native targets.
305
+
306
+ 'use strict';
307
+
308
+ const fs = require('fs');
309
+ const path = require('path');
310
+ const { execSync, spawnSync } = require('child_process');
311
+
312
+ function run(cmd, cwd) {
313
+ const r = spawnSync(cmd, { shell: true, cwd: cwd || __dirname, stdio: 'inherit' });
314
+ return r.status === 0;
315
+ }
316
+
317
+ function hasTool(name) {
318
+ const r = spawnSync(name, ['--version'], { shell: true, stdio: 'pipe' });
319
+ return r.status === 0;
320
+ }
321
+
322
+ function findNodeFile(dir) {
323
+ if (!fs.existsSync(dir)) return null;
324
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
325
+ for (const e of entries) {
326
+ const full = path.join(dir, e.name);
327
+ if (e.isDirectory()) { const r = findNodeFile(full); if (r) return r; }
328
+ else if (e.name.endsWith('.node')) return full;
329
+ }
330
+ return null;
331
+ }
332
+
333
+ fs.mkdirSync(path.join(__dirname, 'bins'), { recursive: true });
334
+
335
+ ${builds.join('\n')}
336
+
337
+ // Build unsupported stub
338
+ {
339
+ const stubSrc = path.join(__dirname, 'src', 'unsupported.cpp');
340
+ const stubOut = path.join(__dirname, 'bins', '${kitName}-unsupported.node');
341
+ if (!fs.existsSync(stubOut)) {
342
+ console.log('[kitnatives] Building unsupported-arch stub...');
343
+ const compiler = hasTool('clang++') ? 'clang++' : hasTool('g++') ? 'g++' : null;
344
+ if (compiler) {
345
+ const _p = require('path');
346
+ const nodeHead = _p.join(_p.dirname(process.execPath), '..', 'include', 'node');
347
+ const flags = [
348
+ '-shared', '-fPIC', '-undefined dynamic_lookup',
349
+ \`-I"\${nodeHead}"\`,
350
+ \`-DNAPI_VERSION=${NAPI_VERSION}\`,
351
+ '-std=c++17',
352
+ \`"\${stubSrc}"\`,
353
+ \`-o "\${stubOut}"\`
354
+ ].join(' ');
355
+ if (run(\`\${compiler} \${flags}\`, __dirname)) {
356
+ console.log(' ✓ Built unsupported stub');
357
+ }
358
+ } else {
359
+ console.warn(' ✗ No compiler found for stub. Missing-arch errors will throw JS exceptions.');
360
+ }
361
+ }
362
+ }
363
+
364
+ console.log('\\n[kitnatives] Build complete.');
365
+ `;
366
+ }
367
+
368
+ // ─── MAIN GENERATOR ──────────────────────────────────────────────────────────
369
+
370
+ function generate(opts) {
371
+ const { name, sources, outBase } = opts;
372
+
373
+ if (!name) throw new Error('--name is required');
374
+ if (!sources?.length) throw new Error('--sources is required (at least one platform:file)');
375
+
376
+ const kitName = name;
377
+ const outDir = path.resolve(outBase ?? '.', `nkit${kitName}`);
378
+
379
+ const srcDir = path.join(outDir, 'src');
380
+ const binDir = path.join(outDir, 'bins');
381
+
382
+ mkdir(outDir);
383
+ mkdir(srcDir);
384
+ mkdir(binDir);
385
+
386
+ const parsedSources = sources.map(parseSourceSpec);
387
+
388
+ // Copy source files into src/
389
+ for (const s of parsedSources) {
390
+ const dest = path.join(srcDir, path.basename(s.file));
391
+ if (fs.existsSync(s.file)) {
392
+ copy(s.file, dest);
393
+ } else {
394
+ // Create a placeholder if the file doesn't exist yet
395
+ write(dest, `// Placeholder for ${s.file} (file not found during generation)\n`);
396
+ console.warn(`[kitnatives] Warning: source file not found: ${s.file} — placeholder written`);
397
+ }
398
+ }
399
+
400
+ // Write unsupported stub
401
+ write(path.join(srcDir, 'unsupported.cpp'), genUnsupportedStub(kitName));
402
+
403
+ // Write CMakeLists.txt (no binding.gyp — node-gyp is never used)
404
+ const primarySrc = path.basename(parsedSources[0].file);
405
+ write(path.join(outDir, 'CMakeLists.txt'), genCMakeLists(kitName, `src/${primarySrc}`, NAPI_VERSION));
406
+
407
+ // Write kitdef.js
408
+ write(path.join(outDir, 'kitdef.js'), genKitdef(kitName, parsedSources));
409
+
410
+ // Write build.js
411
+ write(path.join(outDir, 'build.js'), genBuildScript(kitName, parsedSources, outDir));
412
+ try { fs.chmodSync(path.join(outDir, 'build.js'), 0o755); } catch (_) {}
413
+
414
+ // Write package.json
415
+ write(path.join(outDir, 'package.json'), JSON.stringify({
416
+ name: `nkit${kitName}`,
417
+ version: '1.0.0',
418
+ main: 'kitdef.js',
419
+ scripts: {
420
+ build: 'node build.js'
421
+ },
422
+ dependencies: {
423
+ 'node-addon-api': '^7.0.0'
424
+ },
425
+ description: `Native addon kit for ${kitName} (generated by kitnatives)`
426
+ }, null, 2));
427
+
428
+ // Write README
429
+ write(path.join(outDir, 'README.md'), `# nkit${kitName}
430
+
431
+ Native addon kit generated by **kitnatives**.
432
+
433
+ ## Building
434
+
435
+ \`\`\`sh
436
+ npm install # installs node-addon-api headers
437
+ node build.js # compiles all targets
438
+ \`\`\`
439
+
440
+ The build script tries three strategies in order:
441
+ 1. **cmake-js** — if available globally
442
+ 2. **cmake** — manual configure + build
443
+ 3. **gcc/clang** — raw compiler invocation with manual flags
444
+
445
+ If none succeed for a target, a warning is printed and that target is skipped.
446
+ A fallback \`unsupported.node\` stub is always compiled; it prints a descriptive
447
+ error message and throws if loaded on an unsupported platform.
448
+
449
+ ## Supported targets
450
+
451
+ | Platform | Arch | Source |
452
+ |----------|------|--------|
453
+ ${parsedSources.map(s => `| ${s.platform} | ${s.arch} | \`src/${path.basename(s.file)}\` |`).join('\n')}
454
+
455
+ ## Usage
456
+
457
+ \`\`\`js
458
+ const { kitdef } = require('./kitdef.js');
459
+ // kitdef === your addon's exports for this platform
460
+ \`\`\`
461
+ `);
462
+
463
+ console.log(`\n[nvc] Generated nkit${kitName} at: ${outDir}`);
464
+ console.log(` src/ ← your C/C++ sources`);
465
+ console.log(` bins/ ← compiled .node binaries (after build.js)`);
466
+ console.log(` CMakeLists.txt ← cmake build (no node-gyp, no binding.gyp)`);
467
+ console.log(` kitdef.js ← runtime loader`);
468
+ console.log(` build.js ← build orchestrator`);
469
+ console.log(`\nNext: cd nkit${kitName} && npm install && node build.js\n`);
470
+
471
+ return outDir;
472
+ }
473
+
474
+ // ─── CLI ─────────────────────────────────────────────────────────────────────
475
+
476
+ function parseCLI(argv) {
477
+ const args = argv.slice(2);
478
+ const opts = { sources: [] };
479
+
480
+ for (let i = 0; i < args.length; i++) {
481
+ if (args[i] === '--name') { opts.name = args[++i]; }
482
+ else if (args[i] === '--out') { opts.outBase = args[++i]; }
483
+ else if (args[i] === '--sources') {
484
+ while (args[i + 1] && !args[i + 1].startsWith('--')) {
485
+ opts.sources.push(args[++i]);
486
+ }
487
+ }
488
+ }
489
+ return opts;
490
+ }
491
+
492
+ if (require.main === module) {
493
+ const opts = parseCLI(process.argv);
494
+ try {
495
+ generate(opts);
496
+ } catch (e) {
497
+ console.error('[nvc] Error:', e.message);
498
+ console.error('\nUsage:');
499
+ console.error(' nvc natives --name <kitname> --sources <platform[-arch]>:<file> ...\n');
500
+ console.error('Examples:');
501
+ console.error(' nvc natives --name myaddon --sources linux-x64:src/addon.cpp darwin-arm64:src/addon_mac.cpp win32:src/addon_win.cpp');
502
+ console.error(' nvc natives --name myaddon --sources linux:src/addon.cpp # any arch on linux');
503
+ process.exit(1);
504
+ }
505
+ }
506
+
507
+ // ─── EXPORTS ─────────────────────────────────────────────────────────────────
508
+
509
+ module.exports = {
510
+ kitdef: {
511
+ generate,
512
+ parseSourceSpec,
513
+ binName,
514
+ genCMakeLists,
515
+ genUnsupportedStub,
516
+ genKitdef,
517
+ genBuildScript,
518
+ SUPPORTED_PLATFORMS,
519
+ SUPPORTED_ARCHS,
520
+ NAPI_VERSION,
521
+ }
522
+ };
package/bin/nvml CHANGED
@@ -212,24 +212,71 @@ function cmdServe(port, nvmlFile, route) {
212
212
  }
213
213
 
214
214
  // Build a mutation-capturing proxy for /_nvml/run
215
+ const _sseClients = new Set(); // Server-Sent Events connections
216
+
215
217
  function makeMutationProxy(mutations, live) {
216
218
  return {
217
- setConfig: (k, v) => mutations.push({ type: 'setConfig', key: String(k), value: v }),
218
- setTitle: (t) => mutations.push({ type: 'setConfig', key: 'title', value: t }),
219
- setMeta: (k, v) => mutations.push({ type: 'setConfig', key: String(k), value: v }),
220
- set: (id, val) => mutations.push({ type: 'setText', id: String(id), value: String(val) }),
221
- setHTML: (id, val) => mutations.push({ type: 'setHTML', id: String(id), value: String(val) }),
222
- setProp: (id, k, v)=> mutations.push({ type: 'setProp', id: String(id), key: String(k), value: String(v) }),
223
- addClass: (id, cls) => mutations.push({ type: 'addClass', id: String(id), value: String(cls) }),
224
- setClass: (id, cls) => mutations.push({ type: 'setClass', id: String(id), value: String(cls) }),
225
- addStyle: (css) => mutations.push({ type: 'addStyle', value: String(css) }),
226
- addElementStyle: (id, css) => mutations.push({ type: 'addStyle', id: String(id), value: String(css) }),
227
- hide: (id) => mutations.push({ type: 'hide', id: String(id) }),
228
- show: (id, d) => mutations.push({ type: 'show', id: String(id), value: d || 'block' }),
229
- alert: (msg) => mutations.push({ type: 'alert', value: String(msg) }),
230
- get: (id) => (live.elements && live.elements[id]) ? live.elements[id].text : null,
231
- getConfig: (k) => live[k] ?? null,
232
- config: live,
219
+ // config
220
+ setConfig: (k, v) => mutations.push({ type: 'setConfig', key: String(k), value: v }),
221
+ setTitle: (t) => mutations.push({ type: 'setConfig', key: 'title', value: t }),
222
+ setMeta: (k, v) => mutations.push({ type: 'setConfig', key: String(k), value: v }),
223
+ // text / html
224
+ set: (id, val) => mutations.push({ type: 'setText', id: String(id), value: String(val) }),
225
+ setHTML: (id, val) => mutations.push({ type: 'setHTML', id: String(id), value: String(val) }),
226
+ // props / attrs
227
+ setProp: (id, k, v) => mutations.push({ type: 'setProp', id: String(id), key: String(k), value: String(v) }),
228
+ setAttr: (id, k, v) => mutations.push({ type: 'setAttr', id: String(id), key: String(k), value: String(v) }),
229
+ removeAttr: (id, k) => mutations.push({ type: 'removeAttr', id: String(id), key: String(k) }),
230
+ // classes
231
+ addClass: (id, cls) => mutations.push({ type: 'addClass', id: String(id), value: String(cls) }),
232
+ removeClass: (id, cls) => mutations.push({ type: 'removeClass', id: String(id), value: String(cls) }),
233
+ toggleClass: (id, cls, f) => mutations.push({ type: 'toggleClass', id: String(id), value: String(cls), force: f }),
234
+ setClass: (id, cls) => mutations.push({ type: 'setClass', id: String(id), value: String(cls) }),
235
+ // style
236
+ addStyle: (css) => mutations.push({ type: 'addStyle', value: String(css) }),
237
+ addElementStyle:(id, css) => mutations.push({ type: 'addStyle', id: String(id), value: String(css) }),
238
+ setStyle: (id, k, v) => mutations.push({ type: 'setStyle', id: String(id), key: String(k), value: String(v) }),
239
+ setCSSVar: (name, val) => mutations.push({ type: 'setCSSVar', name: String(name), value: String(val) }),
240
+ // visibility
241
+ hide: (id, t) => mutations.push({ type: 'hide', id: String(id), transition: t || null }),
242
+ show: (id, d, t) => mutations.push({ type: 'show', id: String(id), value: d || 'block', transition: t || null }),
243
+ // DOM manipulation
244
+ remove: (id, t) => mutations.push({ type: 'remove', id: String(id), transition: t || null }),
245
+ appendChild: (id, html) => mutations.push({ type: 'appendChild', id: String(id), html: String(html) }),
246
+ insertBefore: (id, html) => mutations.push({ type: 'insertBefore', id: String(id), html: String(html) }),
247
+ insertAfter: (id, html) => mutations.push({ type: 'insertAfter', id: String(id), html: String(html) }),
248
+ // focus / scroll
249
+ focus: (id) => mutations.push({ type: 'focus', id: String(id) }),
250
+ blur: (id) => mutations.push({ type: 'blur', id: String(id) }),
251
+ scroll: (id, beh) => mutations.push({ type: 'scroll', id: String(id), behavior: beh || 'smooth' }),
252
+ // signals
253
+ setSignal: (name, val) => mutations.push({ type: 'setSignal', name: String(name), value: val }),
254
+ // list patch
255
+ patchList: (id, items, tpl, key) => mutations.push({ type: 'patchList', id: String(id), items, template: String(tpl), key: key || null }),
256
+ // navigation
257
+ navigate: (path) => mutations.push({ type: 'navigate', path: String(path) }),
258
+ reload: () => mutations.push({ type: 'reload' }),
259
+ redirect: (url) => mutations.push({ type: 'redirect', url: String(url) }),
260
+ // feedback
261
+ alert: (msg) => mutations.push({ type: 'alert', value: String(msg) }),
262
+ toast: (msg, d, t) => mutations.push({ type: 'toast', value: String(msg), duration: d || 3000, type: t || 'info' }),
263
+ console: (msg, lvl) => mutations.push({ type: 'console', value: String(msg), level: lvl || 'log' }),
264
+ // reads from live snapshot
265
+ get: (id) => (live.elements && live.elements[id]) ? live.elements[id].text : null,
266
+ getValue: (id) => (live.elements && live.elements[id]) ? live.elements[id].value : null,
267
+ getClass: (id) => (live.elements && live.elements[id]) ? live.elements[id].class : null,
268
+ getConfig: (k) => live[k] ?? null,
269
+ getSignal: (n) => (live.state && live.state[n]) ?? null,
270
+ config: live,
271
+ // SSE push (server → all connected clients)
272
+ push: (name, val) => {
273
+ const msg = `event: signal\ndata: ${JSON.stringify({ name, value: val })}\n\n`;
274
+ _sseClients.forEach(c => { try { c.write(msg); } catch(_) {} });
275
+ },
276
+ pushMutations: (muts) => {
277
+ const msg = `event: mutations\ndata: ${JSON.stringify(muts)}\n\n`;
278
+ _sseClients.forEach(c => { try { c.write(msg); } catch(_) {} });
279
+ },
233
280
  };
234
281
  }
235
282
 
@@ -237,12 +284,26 @@ function cmdServe(port, nvmlFile, route) {
237
284
  const parsed = url.parse(req.url || '/');
238
285
  const urlPath = parsed.pathname || '/';
239
286
 
240
- // CORS — lets nvml-browser previewer connect without issues
287
+ // CORS
241
288
  res.setHeader('Access-Control-Allow-Origin', '*');
242
289
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
243
290
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
244
291
  if (req.method === 'OPTIONS') { res.writeHead(204); res.end(); return; }
245
292
 
293
+ // ── /_nvml/sse — Server-Sent Events for server-push signal updates ──
294
+ if (urlPath === '/_nvml/sse' && req.method === 'GET') {
295
+ res.writeHead(200, {
296
+ 'Content-Type': 'text/event-stream',
297
+ 'Cache-Control': 'no-cache',
298
+ 'Connection': 'keep-alive',
299
+ 'X-Accel-Buffering': 'no',
300
+ });
301
+ res.write('retry: 3000\n\n'); // tell client to reconnect after 3s if disconnected
302
+ _sseClients.add(res);
303
+ req.on('close', () => _sseClients.delete(res));
304
+ return;
305
+ }
306
+
246
307
  // ── /_nvml/run — triggered server-side Nova scripts ──────
247
308
  if (urlPath === '/_nvml/run' && req.method === 'POST') {
248
309
  let rawBody = '';
package/demo.nv CHANGED
File without changes
package/demo_builtins.nv CHANGED
File without changes
package/demo_http.nv CHANGED
File without changes