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.
- package/LICENSE +1 -1
- package/README.md +1574 -597
- package/bin/novac +468 -171
- package/bin/nvc +522 -0
- package/bin/nvml +78 -17
- package/demo.nv +0 -0
- package/demo_builtins.nv +0 -0
- package/demo_http.nv +0 -0
- package/examples/bf.nv +69 -0
- package/examples/math.nv +21 -0
- package/kits/birdAPI/kitdef.js +954 -0
- package/kits/kitRNG/kitdef.js +740 -0
- package/kits/kitSSH/kitdef.js +1272 -0
- package/kits/kitadb/kitdef.js +606 -0
- package/kits/kitai/kitdef.js +2185 -0
- package/kits/kitansi/kitdef.js +1402 -0
- package/kits/kitcanvas/kitdef.js +914 -0
- package/kits/kitclippy/kitdef.js +925 -0
- package/kits/kitformat/kitdef.js +1485 -0
- package/kits/kitgps/kitdef.js +1862 -0
- package/kits/kitlibproc/kitdef.js +3 -2
- package/kits/kitmatrix/ex.js +19 -0
- package/kits/kitmatrix/kitdef.js +960 -0
- package/kits/kitmorse/kitdef.js +229 -0
- package/kits/kitmpatch/kitdef.js +906 -0
- package/kits/kitnet/kitdef.js +1401 -0
- package/kits/kitnovacweb/README.md +1416 -143
- package/kits/kitnovacweb/kitdef.js +92 -2
- package/kits/kitnovacweb/nvml/executor.js +578 -176
- package/kits/kitnovacweb/nvml/index.js +2 -2
- package/kits/kitnovacweb/nvml/lexer.js +72 -69
- package/kits/kitnovacweb/nvml/parser.js +328 -159
- package/kits/kitnovacweb/nvml/renderer.js +770 -270
- package/kits/kitparse/kitdef.js +1688 -0
- package/kits/kitproto/kitdef.js +613 -0
- package/kits/kitqr/kitdef.js +637 -0
- package/kits/kitregex++/kitdef.js +1353 -0
- package/kits/kitrequire/kitdef.js +1599 -0
- package/kits/kitx11/kitdef.js +1 -0
- package/kits/kitx11/kitx11.js +2472 -0
- package/kits/kitx11/kitx11_conn.js +948 -0
- package/kits/kitx11/kitx11_worker.js +121 -0
- package/kits/libtea/kitdef.js +2691 -0
- package/kits/libterm/ex.js +285 -0
- package/kits/libterm/kitdef.js +1927 -0
- package/novac/LICENSE +21 -0
- package/novac/README.md +1823 -0
- package/novac/bin/novac +950 -0
- package/novac/bin/nvc +522 -0
- package/novac/bin/nvml +542 -0
- package/novac/demo.nv +245 -0
- package/novac/demo_builtins.nv +209 -0
- package/novac/demo_http.nv +62 -0
- package/novac/examples/bf.nv +69 -0
- package/novac/examples/math.nv +21 -0
- package/novac/kits/kitai/kitdef.js +2185 -0
- package/novac/kits/kitansi/kitdef.js +1402 -0
- package/novac/kits/kitformat/kitdef.js +1485 -0
- package/novac/kits/kitgps/kitdef.js +1862 -0
- package/novac/kits/kitlibfs/kitdef.js +231 -0
- package/{examples/example-project/nova_modules → novac/kits}/kitlibproc/kitdef.js +3 -2
- package/novac/kits/kitmatrix/ex.js +19 -0
- package/novac/kits/kitmatrix/kitdef.js +960 -0
- package/novac/kits/kitmpatch/kitdef.js +906 -0
- package/novac/kits/kitnovacweb/README.md +1572 -0
- package/novac/kits/kitnovacweb/demo.nv +12 -0
- package/novac/kits/kitnovacweb/demo.nvml +71 -0
- package/novac/kits/kitnovacweb/index.nova +12 -0
- package/novac/kits/kitnovacweb/kitdef.js +692 -0
- package/novac/kits/kitnovacweb/nova.kit.json +8 -0
- package/novac/kits/kitnovacweb/nvml/executor.js +739 -0
- package/novac/kits/kitnovacweb/nvml/index.js +67 -0
- package/novac/kits/kitnovacweb/nvml/lexer.js +263 -0
- package/novac/kits/kitnovacweb/nvml/parser.js +508 -0
- package/novac/kits/kitnovacweb/nvml/renderer.js +924 -0
- package/novac/kits/kitparse/kitdef.js +1688 -0
- package/novac/kits/kitregex++/kitdef.js +1353 -0
- package/novac/kits/kitrequire/kitdef.js +1599 -0
- package/novac/kits/kitx11/kitdef.js +1 -0
- package/novac/kits/kitx11/kitx11.js +2472 -0
- package/novac/kits/kitx11/kitx11_conn.js +948 -0
- package/novac/kits/kitx11/kitx11_worker.js +121 -0
- package/novac/kits/libtea/tf.js +2691 -0
- package/novac/kits/libterm/ex.js +285 -0
- package/novac/kits/libterm/kitdef.js +1927 -0
- package/novac/node_modules/chalk/license +9 -0
- package/novac/node_modules/chalk/package.json +83 -0
- package/novac/node_modules/chalk/readme.md +297 -0
- package/novac/node_modules/chalk/source/index.d.ts +325 -0
- package/novac/node_modules/chalk/source/index.js +225 -0
- package/novac/node_modules/chalk/source/utilities.js +33 -0
- package/novac/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +236 -0
- package/novac/node_modules/chalk/source/vendor/ansi-styles/index.js +223 -0
- package/novac/node_modules/chalk/source/vendor/supports-color/browser.d.ts +1 -0
- package/novac/node_modules/chalk/source/vendor/supports-color/browser.js +34 -0
- package/novac/node_modules/chalk/source/vendor/supports-color/index.d.ts +55 -0
- package/novac/node_modules/chalk/source/vendor/supports-color/index.js +190 -0
- package/novac/node_modules/commander/LICENSE +22 -0
- package/novac/node_modules/commander/Readme.md +1176 -0
- package/novac/node_modules/commander/esm.mjs +16 -0
- package/novac/node_modules/commander/index.js +24 -0
- package/novac/node_modules/commander/lib/argument.js +150 -0
- package/novac/node_modules/commander/lib/command.js +2777 -0
- package/novac/node_modules/commander/lib/error.js +39 -0
- package/novac/node_modules/commander/lib/help.js +747 -0
- package/novac/node_modules/commander/lib/option.js +380 -0
- package/novac/node_modules/commander/lib/suggestSimilar.js +101 -0
- package/novac/node_modules/commander/package-support.json +19 -0
- package/novac/node_modules/commander/package.json +82 -0
- package/novac/node_modules/commander/typings/esm.d.mts +3 -0
- package/novac/node_modules/commander/typings/index.d.ts +1113 -0
- package/novac/node_modules/node-addon-api/LICENSE.md +9 -0
- package/novac/node_modules/node-addon-api/README.md +95 -0
- package/novac/node_modules/node-addon-api/common.gypi +21 -0
- package/novac/node_modules/node-addon-api/except.gypi +25 -0
- package/novac/node_modules/node-addon-api/index.js +14 -0
- package/novac/node_modules/node-addon-api/napi-inl.deprecated.h +186 -0
- package/novac/node_modules/node-addon-api/napi-inl.h +7165 -0
- package/novac/node_modules/node-addon-api/napi.h +3364 -0
- package/novac/node_modules/node-addon-api/node_addon_api.gyp +42 -0
- package/novac/node_modules/node-addon-api/node_api.gyp +9 -0
- package/novac/node_modules/node-addon-api/noexcept.gypi +26 -0
- package/novac/node_modules/node-addon-api/package-support.json +21 -0
- package/novac/node_modules/node-addon-api/package.json +480 -0
- package/novac/node_modules/node-addon-api/tools/README.md +73 -0
- package/novac/node_modules/node-addon-api/tools/check-napi.js +99 -0
- package/novac/node_modules/node-addon-api/tools/clang-format.js +71 -0
- package/novac/node_modules/node-addon-api/tools/conversion.js +301 -0
- package/novac/node_modules/serialize-javascript/LICENSE +27 -0
- package/novac/node_modules/serialize-javascript/README.md +149 -0
- package/novac/node_modules/serialize-javascript/index.js +297 -0
- package/novac/node_modules/serialize-javascript/package.json +33 -0
- package/novac/package.json +27 -0
- package/novac/scripts/update-bin.js +24 -0
- package/novac/src/core/bstd.js +1035 -0
- package/novac/src/core/config.js +155 -0
- package/novac/src/core/describe.js +187 -0
- package/novac/src/core/emitter.js +499 -0
- package/novac/src/core/error.js +86 -0
- package/novac/src/core/executor.js +5606 -0
- package/novac/src/core/formatter.js +686 -0
- package/novac/src/core/lexer.js +1026 -0
- package/novac/src/core/nova_builtins.js +717 -0
- package/novac/src/core/nova_thread_worker.js +166 -0
- package/novac/src/core/parser.js +2181 -0
- package/novac/src/core/types.js +112 -0
- package/novac/src/index.js +28 -0
- package/novac/src/runtime/stdlib.js +244 -0
- package/package.json +6 -3
- package/scripts/update-bin.js +0 -0
- package/src/core/bstd.js +838 -362
- package/src/core/executor.js +2578 -170
- package/src/core/lexer.js +502 -54
- package/src/core/nova_builtins.js +21 -3
- package/src/core/parser.js +413 -72
- package/src/core/types.js +30 -2
- package/src/index.js +0 -0
- package/examples/example-project/README.md +0 -3
- package/examples/example-project/src/main.nova +0 -3
- package/src/core/environment.js +0 -0
- /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
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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
|
|
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
|