@mikrojs/native 0.0.7

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 (109) hide show
  1. package/CMakeLists.txt +198 -0
  2. package/LICENSE +21 -0
  3. package/README.md +49 -0
  4. package/cmake/mikrojs_bytecode.cmake +146 -0
  5. package/cmake.js +22 -0
  6. package/dist/index.d.ts +52 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +132 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/types.d.ts +43 -0
  11. package/dist/types.d.ts.map +1 -0
  12. package/dist/types.js +2 -0
  13. package/dist/types.js.map +1 -0
  14. package/include/byteorder_apple.h +11 -0
  15. package/include/byteorder_windows.h +12 -0
  16. package/include/mikrojs/cbor_helpers.h +24 -0
  17. package/include/mikrojs/cutils_wrap.h +59 -0
  18. package/include/mikrojs/errors.h +144 -0
  19. package/include/mikrojs/mem.h +11 -0
  20. package/include/mikrojs/mik_color.h +32 -0
  21. package/include/mikrojs/mikrojs.h +331 -0
  22. package/include/mikrojs/platform.h +82 -0
  23. package/include/mikrojs/private.h +281 -0
  24. package/include/mikrojs/utils.h +125 -0
  25. package/package.json +100 -0
  26. package/prebuilds/darwin-arm64/mikrojs.napi.node +0 -0
  27. package/prebuilds/linux-arm64/mikrojs.napi.node +0 -0
  28. package/prebuilds/linux-x64/mikrojs.napi.node +0 -0
  29. package/runtime/ble/ble.ts +231 -0
  30. package/runtime/ble/types.ts +194 -0
  31. package/runtime/ble/uuid.ts +89 -0
  32. package/runtime/ble/validators.ts +61 -0
  33. package/runtime/cbor/cbor.ts +1 -0
  34. package/runtime/cbor/types.ts +8 -0
  35. package/runtime/console/types.ts +50 -0
  36. package/runtime/env/env.ts +17 -0
  37. package/runtime/env/types.ts +12 -0
  38. package/runtime/format/types.ts +4 -0
  39. package/runtime/fs/fs.ts +93 -0
  40. package/runtime/fs/types.ts +92 -0
  41. package/runtime/globals.d.ts +87 -0
  42. package/runtime/http/helpers.ts +222 -0
  43. package/runtime/http/native.ts +151 -0
  44. package/runtime/http/request.ts +25 -0
  45. package/runtime/i2c/i2c.ts +35 -0
  46. package/runtime/i2c/types.ts +55 -0
  47. package/runtime/inspect/types.ts +10 -0
  48. package/runtime/internal.d.ts +456 -0
  49. package/runtime/kv/nvs.ts +17 -0
  50. package/runtime/kv/rtc.ts +17 -0
  51. package/runtime/kv/shared.ts +107 -0
  52. package/runtime/kv/types.ts +150 -0
  53. package/runtime/neopixel/neopixel.ts +38 -0
  54. package/runtime/neopixel/types.ts +27 -0
  55. package/runtime/pin/pin.ts +51 -0
  56. package/runtime/pin/types.ts +49 -0
  57. package/runtime/pwm/pwm.ts +32 -0
  58. package/runtime/pwm/types.ts +29 -0
  59. package/runtime/reader/reader.ts +167 -0
  60. package/runtime/reader/types.ts +34 -0
  61. package/runtime/result/native-result.node-shim.ts +44 -0
  62. package/runtime/result/result.ts +26 -0
  63. package/runtime/result/types.ts +60 -0
  64. package/runtime/schema/schema.ts +321 -0
  65. package/runtime/schema/types.ts +152 -0
  66. package/runtime/sleep/sleep.ts +14 -0
  67. package/runtime/sleep/types.ts +44 -0
  68. package/runtime/sntp/sntp.ts +54 -0
  69. package/runtime/sntp/types.ts +38 -0
  70. package/runtime/spi/spi.ts +31 -0
  71. package/runtime/spi/types.ts +42 -0
  72. package/runtime/stdio/stdio.ts +44 -0
  73. package/runtime/stdio/types.ts +22 -0
  74. package/runtime/stream/stream.ts +150 -0
  75. package/runtime/stream/types.ts +47 -0
  76. package/runtime/sys/sys.ts +90 -0
  77. package/runtime/sys/types.ts +131 -0
  78. package/runtime/test/test.ts +595 -0
  79. package/runtime/test/types.ts +97 -0
  80. package/runtime/uart/types.ts +75 -0
  81. package/runtime/uart/uart.ts +51 -0
  82. package/runtime/wifi/types.ts +156 -0
  83. package/runtime/wifi/wifi.ts +208 -0
  84. package/scripts/bundle-runtime.js +149 -0
  85. package/scripts/compare-minifiers.js +189 -0
  86. package/scripts/compile-bytecode.sh +38 -0
  87. package/scripts/copy-prebuild.js +20 -0
  88. package/scripts/generate-symbol-map.js +146 -0
  89. package/src/builtins.cpp +82 -0
  90. package/src/cutils_compat.c +38 -0
  91. package/src/eval_bytecode.cpp +42 -0
  92. package/src/fs.cpp +878 -0
  93. package/src/mem.cpp +63 -0
  94. package/src/mik_abort.cpp +160 -0
  95. package/src/mik_app_config.cpp +358 -0
  96. package/src/mik_cbor.cpp +334 -0
  97. package/src/mik_color.cpp +46 -0
  98. package/src/mik_console.cpp +422 -0
  99. package/src/mik_inspect.cpp +850 -0
  100. package/src/mik_repl.cpp +1122 -0
  101. package/src/mik_result.cpp +344 -0
  102. package/src/mik_stdio.cpp +147 -0
  103. package/src/mik_sys.cpp +239 -0
  104. package/src/mik_text_encoding.cpp +443 -0
  105. package/src/mikrojs.cpp +942 -0
  106. package/src/modules.cpp +944 -0
  107. package/src/platform_posix.cpp +134 -0
  108. package/src/timers.cpp +208 -0
  109. package/src/utils.cpp +173 -0
@@ -0,0 +1,422 @@
1
+ #include <cinttypes>
2
+ #include <cstdio>
3
+ #include <cstring>
4
+ #include <string>
5
+ #include <unistd.h>
6
+ #include <vector>
7
+
8
+ #include <quickjs.h>
9
+
10
+ #include "mikrojs/platform.h"
11
+ #include "mikrojs/private.h"
12
+ #include "mikrojs/utils.h"
13
+
14
+ /* ── Printf-style format (port of format.ts) ────────────────────── */
15
+
16
+ static std::string mik__format(JSContext* ctx, int argc, JSValue* argv, bool colors) {
17
+ if (argc == 0) return "";
18
+
19
+ /* If first arg is not a string, just inspect all args */
20
+ if (!JS_IsString(argv[0])) {
21
+ std::string out;
22
+ for (int i = 0; i < argc; i++) {
23
+ if (i > 0) out += ", ";
24
+ out += mik_inspect(ctx, argv[i], 2, colors);
25
+ }
26
+ return out;
27
+ }
28
+
29
+ const char* fmt = JS_ToCString(ctx, argv[0]);
30
+ if (!fmt) return "";
31
+
32
+ std::string result;
33
+ int arg_idx = 1; /* next arg to consume */
34
+
35
+ for (const char* p = fmt; *p; p++) {
36
+ if (*p != '%') {
37
+ result += *p;
38
+ continue;
39
+ }
40
+
41
+ /* Look at next char */
42
+ char next = *(p + 1);
43
+ if (next == '\0') {
44
+ result += '%';
45
+ break;
46
+ }
47
+
48
+ if (next == '%') {
49
+ result += '%';
50
+ p++;
51
+ continue;
52
+ }
53
+
54
+ if (arg_idx >= argc) {
55
+ /* No more args — keep the format specifier literal */
56
+ result += '%';
57
+ result += next;
58
+ p++;
59
+ continue;
60
+ }
61
+
62
+ switch (next) {
63
+ case 's': {
64
+ const char* s = JS_ToCString(ctx, argv[arg_idx]);
65
+ if (s) {
66
+ result += s;
67
+ JS_FreeCString(ctx, s);
68
+ }
69
+ arg_idx++;
70
+ p++;
71
+ break;
72
+ }
73
+ case 'd': {
74
+ const char* s = JS_ToCString(ctx, argv[arg_idx]);
75
+ if (s) {
76
+ result += s;
77
+ JS_FreeCString(ctx, s);
78
+ }
79
+ arg_idx++;
80
+ p++;
81
+ break;
82
+ }
83
+ case 'f': {
84
+ double d;
85
+ JS_ToFloat64(ctx, &d, argv[arg_idx]);
86
+ char buf[64];
87
+ snprintf(buf, sizeof(buf), "%f", d);
88
+ result += buf;
89
+ arg_idx++;
90
+ p++;
91
+ break;
92
+ }
93
+ case 'o':
94
+ case 'O': {
95
+ result += mik_inspect(ctx, argv[arg_idx], 2, colors);
96
+ arg_idx++;
97
+ p++;
98
+ break;
99
+ }
100
+ case 'c': {
101
+ /* CSS colors not supported — consume arg, output nothing */
102
+ arg_idx++;
103
+ p++;
104
+ break;
105
+ }
106
+ default:
107
+ result += '%';
108
+ result += next;
109
+ p++;
110
+ break;
111
+ }
112
+ }
113
+
114
+ JS_FreeCString(ctx, fmt);
115
+
116
+ /* Append remaining args */
117
+ for (int i = arg_idx; i < argc; i++) {
118
+ result += ' ';
119
+ result += mik_inspect(ctx, argv[i], 2, colors);
120
+ }
121
+
122
+ return result;
123
+ }
124
+
125
+ /* ── Error formatting (port of console.ts formatError) ───────────── */
126
+
127
+ static std::string mik__format_error(JSContext* ctx, JSValue error) {
128
+ JSValue stack_val = JS_GetPropertyStr(ctx, error, "stack");
129
+ JSValue msg_val = JS_GetPropertyStr(ctx, error, "message");
130
+
131
+ const char* stack = JS_ToCString(ctx, stack_val);
132
+ const char* msg = JS_ToCString(ctx, msg_val);
133
+
134
+ std::string result;
135
+ if (stack && msg && strstr(stack, msg)) {
136
+ /* Stack already includes message */
137
+ result = stack;
138
+ } else {
139
+ JSValue name_val = JS_GetPropertyStr(ctx, error, "name");
140
+ const char* name = JS_ToCString(ctx, name_val);
141
+ result = name ? name : "Error";
142
+ result += ": ";
143
+ result += msg ? msg : "";
144
+ if (stack) {
145
+ result += "\n";
146
+ result += stack;
147
+ }
148
+ if (name) JS_FreeCString(ctx, name);
149
+ JS_FreeValue(ctx, name_val);
150
+ }
151
+
152
+ if (stack) JS_FreeCString(ctx, stack);
153
+ if (msg) JS_FreeCString(ctx, msg);
154
+ JS_FreeValue(ctx, stack_val);
155
+ JS_FreeValue(ctx, msg_val);
156
+
157
+ return result;
158
+ }
159
+
160
+ /* ── Console methods ─────────────────────────────────────────────── */
161
+
162
+ static JSValue mik__console_log(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
163
+ std::string output = mik__format(ctx, argc, argv, true);
164
+
165
+ if (mik__repl_is_protocol_mode()) {
166
+ mik__repl_proto_send_output(MIK_MSG_LOG, output.c_str(), output.size());
167
+ return JS_UNDEFINED;
168
+ }
169
+
170
+ output += "\r\n";
171
+ MIK_GetPlatform()->stdout_write(output.c_str(), output.size());
172
+ fsync(fileno(stdout));
173
+ return JS_UNDEFINED;
174
+ }
175
+
176
+ static JSValue mik__console_info(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
177
+ std::string output = mik__format(ctx, argc, argv, true);
178
+
179
+ if (mik__repl_is_protocol_mode()) {
180
+ mik__repl_proto_send_output(MIK_MSG_INFO, output.c_str(), output.size());
181
+ return JS_UNDEFINED;
182
+ }
183
+
184
+ output += "\r\n";
185
+ MIK_GetPlatform()->stdout_write(output.c_str(), output.size());
186
+ fsync(fileno(stdout));
187
+ return JS_UNDEFINED;
188
+ }
189
+
190
+ static JSValue mik__console_debug(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
191
+ std::string output = mik__format(ctx, argc, argv, true);
192
+
193
+ if (mik__repl_is_protocol_mode()) {
194
+ mik__repl_proto_send_output(MIK_MSG_DEBUG, output.c_str(), output.size());
195
+ return JS_UNDEFINED;
196
+ }
197
+
198
+ output += "\r\n";
199
+ MIK_GetPlatform()->stdout_write(output.c_str(), output.size());
200
+ fsync(fileno(stdout));
201
+ return JS_UNDEFINED;
202
+ }
203
+
204
+ static JSValue mik__console_error_warn(JSContext* ctx, JSValue this_val, int argc, JSValue* argv,
205
+ int magic) {
206
+ uint8_t msg_type = magic == 0 ? MIK_MSG_ERROR : MIK_MSG_WARN;
207
+
208
+ /* Map Error objects to their stack traces */
209
+ std::vector<JSValue> mapped;
210
+ mapped.reserve(argc);
211
+
212
+ for (int i = 0; i < argc; i++) {
213
+ if (JS_IsError(argv[i])) {
214
+ std::string err_str = mik__format_error(ctx, argv[i]);
215
+ mapped.push_back(JS_NewString(ctx, err_str.c_str()));
216
+ } else {
217
+ mapped.push_back(JS_DupValue(ctx, argv[i]));
218
+ }
219
+ }
220
+
221
+ std::string output = mik__format(ctx, argc, mapped.data(), true);
222
+
223
+ for (auto& v : mapped) {
224
+ JS_FreeValue(ctx, v);
225
+ }
226
+
227
+ if (mik__repl_is_protocol_mode()) {
228
+ mik__repl_proto_send_output(msg_type, output.c_str(), output.size());
229
+ return JS_UNDEFINED;
230
+ }
231
+
232
+ output += "\r\n";
233
+ MIK_GetPlatform()->stderr_write(output.c_str(), output.size());
234
+ return JS_UNDEFINED;
235
+ }
236
+
237
+ /* ── Uncaught error reporting ─────────────────────────────────────── */
238
+
239
+ /* Dedup: the rejection tracker can fire multiple times for the same
240
+ * error (e.g. inner Promise.reject + async wrapper rejection). Skip
241
+ * if the same object pointer was just reported. */
242
+ static void* last_reported_ptr = nullptr;
243
+ static int64_t last_reported_time = 0;
244
+
245
+ void mik__report_uncaught_reset(void) {
246
+ last_reported_ptr = nullptr;
247
+ }
248
+
249
+ void mik__report_uncaught(JSContext* ctx, JSValue exc, bool in_promise) {
250
+ if (JS_IsObject(exc)) {
251
+ void* ptr = JS_VALUE_GET_PTR(exc);
252
+ int64_t now = MIK_GetPlatform()->get_boot_us();
253
+ /* Deduplicate only within 1ms — the rejection tracker can fire
254
+ * twice for the same error (inner + async wrapper) essentially
255
+ * instantly, but a new error 1s later at a recycled address
256
+ * must not be suppressed. */
257
+ if (ptr == last_reported_ptr && (now - last_reported_time) < 1000) {
258
+ return;
259
+ }
260
+ last_reported_ptr = ptr;
261
+ last_reported_time = now;
262
+ } else {
263
+ last_reported_ptr = nullptr;
264
+ }
265
+
266
+ /* Fixed-size stack buffer so this function never allocates from the
267
+ * C++ heap. Previously we used std::string + mik_inspect here; both
268
+ * can throw std::bad_alloc under memory pressure, and with
269
+ * CONFIG_COMPILER_CXX_EXCEPTIONS=n on ESP32 that turns into
270
+ * abort() — masking the real OOM the reporter was trying to surface.
271
+ * Messages longer than the buffer get truncated, which beats
272
+ * aborting. */
273
+ char buf[512];
274
+ size_t pos = 0;
275
+ auto append = [&](const char* s, size_t len) {
276
+ if (pos >= sizeof(buf) - 1) return;
277
+ size_t avail = sizeof(buf) - 1 - pos;
278
+ size_t n = len < avail ? len : avail;
279
+ memcpy(buf + pos, s, n);
280
+ pos += n;
281
+ };
282
+ auto append_cstr = [&](const char* s) {
283
+ if (s) append(s, strlen(s));
284
+ };
285
+
286
+ append_cstr(in_promise ? "Uncaught (in promise) " : "Uncaught ");
287
+
288
+ if (JS_IsObject(exc)) {
289
+ JSValue name_val = JS_GetPropertyStr(ctx, exc, "name");
290
+ JSValue msg_val = JS_GetPropertyStr(ctx, exc, "message");
291
+ const char* name = JS_ToCString(ctx, name_val);
292
+ const char* emsg = JS_ToCString(ctx, msg_val);
293
+
294
+ if (name && name[0]) {
295
+ append_cstr(name);
296
+ if (emsg && emsg[0]) {
297
+ append_cstr(": ");
298
+ append_cstr(emsg);
299
+ }
300
+ } else if (emsg && emsg[0]) {
301
+ append_cstr(emsg);
302
+ } else {
303
+ append_cstr("[object]");
304
+ }
305
+
306
+ if (name) JS_FreeCString(ctx, name);
307
+ if (emsg) JS_FreeCString(ctx, emsg);
308
+ JS_FreeValue(ctx, name_val);
309
+ JS_FreeValue(ctx, msg_val);
310
+
311
+ JSValue stack_val = JS_GetPropertyStr(ctx, exc, "stack");
312
+ if (JS_IsString(stack_val)) {
313
+ const char* stack = JS_ToCString(ctx, stack_val);
314
+ if (stack && stack[0]) {
315
+ append_cstr("\n");
316
+ append_cstr(stack);
317
+ }
318
+ if (stack) JS_FreeCString(ctx, stack);
319
+ }
320
+ JS_FreeValue(ctx, stack_val);
321
+ } else {
322
+ /* Describe primitive rejections by type without going through
323
+ * mik_inspect (which allocates a std::string). */
324
+ if (JS_IsNull(exc)) {
325
+ append_cstr("null");
326
+ } else if (JS_IsUndefined(exc)) {
327
+ append_cstr("undefined");
328
+ } else if (JS_IsBool(exc)) {
329
+ append_cstr(JS_ToBool(ctx, exc) ? "true" : "false");
330
+ } else if (JS_IsNumber(exc)) {
331
+ double d = 0;
332
+ JS_ToFloat64(ctx, &d, exc);
333
+ char num[32];
334
+ int n = snprintf(num, sizeof(num), "%g", d);
335
+ if (n > 0) append(num, (size_t)n);
336
+ } else if (JS_IsString(exc)) {
337
+ const char* s = JS_ToCString(ctx, exc);
338
+ if (s) {
339
+ append_cstr(s);
340
+ JS_FreeCString(ctx, s);
341
+ }
342
+ } else {
343
+ append_cstr("[primitive]");
344
+ }
345
+
346
+ /* Primitive rejections lose the throw-site stack (the async
347
+ * chain has already unwound). Attach current system-heap free
348
+ * bytes so logs hint at whether this was an allocation failure
349
+ * — QuickJS surfaces OOM as a null rejection when it can't
350
+ * allocate an Error object for the real throw. */
351
+ const MIKPlatform* plat = MIK_GetPlatform();
352
+ if (plat && plat->get_free_system_mem) {
353
+ char note[96];
354
+ int n = snprintf(note, sizeof(note),
355
+ "\n (primitive rejection, systemFree=%zuB at report time)",
356
+ plat->get_free_system_mem());
357
+ if (n > 0) append(note, (size_t)n);
358
+ }
359
+ }
360
+
361
+ if (mik__repl_is_protocol_mode()) {
362
+ mik__repl_proto_send_output(MIK_MSG_ERROR, buf, pos);
363
+ return;
364
+ }
365
+
366
+ append_cstr("\r\n");
367
+ MIK_GetPlatform()->stderr_write(buf, pos);
368
+ }
369
+
370
+ /* ── Test emit ────────────────────────────────────────────────────── */
371
+
372
+ static JSValue mik__test_emit(JSContext* ctx, JSValue /*this_val*/, int argc, JSValue* argv) {
373
+ if (argc < 1) return JS_UNDEFINED;
374
+ size_t len;
375
+ const char* str = JS_ToCStringLen(ctx, &len, argv[0]);
376
+ if (!str) return JS_EXCEPTION;
377
+ if (mik__repl_is_protocol_mode()) {
378
+ mik__repl_proto_send_output(MIK_MSG_TEST, str, len);
379
+ }
380
+ JS_FreeCString(ctx, str);
381
+ return JS_UNDEFINED;
382
+ }
383
+
384
+ /* Signals the current ServeLoop to return so a supervisor driving multiple
385
+ * test files through one transport session can tear down the runtime and
386
+ * move to the next file. Called from the test runtime after emitting the
387
+ * final run_done event. No-op when not running under a supervisor. */
388
+ static JSValue mik__test_file_done(JSContext* /*ctx*/, JSValue /*this_val*/, int /*argc*/,
389
+ JSValue* /*argv*/) {
390
+ MIK_ProtocolExit();
391
+ return JS_UNDEFINED;
392
+ }
393
+
394
+ /* ── Init ────────────────────────────────────────────────────────── */
395
+
396
+ void mik__console_init(JSContext* ctx, JSValue global_obj) {
397
+ JSValue console = JS_NewObject(ctx);
398
+ JS_SetPropertyStr(ctx, console, "log", JS_NewCFunction(ctx, mik__console_log, "log", 0));
399
+ JS_SetPropertyStr(ctx, console, "info", JS_NewCFunction(ctx, mik__console_info, "info", 0));
400
+ JS_SetPropertyStr(ctx, console, "debug", JS_NewCFunction(ctx, mik__console_debug, "debug", 0));
401
+ JS_SetPropertyStr(ctx, console, "warn",
402
+ JS_NewCFunctionMagic(ctx, mik__console_error_warn, "warn", 0,
403
+ JS_CFUNC_generic_magic, 1));
404
+ JS_SetPropertyStr(ctx, console, "error",
405
+ JS_NewCFunctionMagic(ctx, mik__console_error_warn, "error", 0,
406
+ JS_CFUNC_generic_magic, 0));
407
+ JS_SetPropertyStr(ctx, global_obj, "console", console);
408
+ }
409
+
410
+ /* Public: opt-in test helper installation. Skipped for ordinary runtimes so
411
+ * they don't pay the cost of two JSCFunction allocations + global property
412
+ * bindings they'll never use. The mikrojs/test built-in has a console.log
413
+ * fallback for when these globals aren't present. */
414
+ void MIK_EnableTestHelpers(MIKRuntime* mik_rt) {
415
+ JSContext* ctx = mik_rt->ctx;
416
+ JSValue global_obj = JS_GetGlobalObject(ctx);
417
+ JS_SetPropertyStr(ctx, global_obj, "__testEmit",
418
+ JS_NewCFunction(ctx, mik__test_emit, "__testEmit", 1));
419
+ JS_SetPropertyStr(ctx, global_obj, "__testFileDone",
420
+ JS_NewCFunction(ctx, mik__test_file_done, "__testFileDone", 0));
421
+ JS_FreeValue(ctx, global_obj);
422
+ }