@mikrojs/firmware 0.16.0 → 0.17.0-next.20260722220623

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 (37) hide show
  1. package/components/mikrojs/CMakeLists.txt +9 -2
  2. package/components/mikrojs/include/mikrojs_esp32.h +23 -0
  3. package/components/mikrojs/mik_config.cpp +159 -5
  4. package/components/mikrojs/mik_deploy.cpp +54 -35
  5. package/components/mikrojs/mik_main.cpp +66 -5
  6. package/components/mikrojs/mik_nvs_kv.cpp +64 -18
  7. package/components/mikrojs/mik_ota.cpp +1791 -0
  8. package/components/mikrojs/platform_esp32.cpp +59 -0
  9. package/components/mikrojs/test/CMakeLists.txt +1 -0
  10. package/components/mikrojs/test/ble_test.cpp +2 -2
  11. package/components/mikrojs/test/fs_js_test.cpp +1 -1
  12. package/components/mikrojs/test/fs_pub_test.cpp +23 -23
  13. package/components/mikrojs/test/http_server_test.cpp +12 -2
  14. package/components/mikrojs/test/http_test.cpp +2 -2
  15. package/components/mikrojs/test/modules_extended_test.cpp +2 -2
  16. package/components/mikrojs/test/modules_test.cpp +2 -2
  17. package/components/mikrojs/test/nvs_kv_test.cpp +245 -0
  18. package/components/mikrojs/test/ota_host/gunzip_host_test.cpp +118 -0
  19. package/components/mikrojs/test/ota_host/run.sh +174 -0
  20. package/components/mikrojs/test/rtc_test.cpp +1 -1
  21. package/components/mikrojs/test/sleep_test.cpp +1 -1
  22. package/components/mikrojs/test/sntp_test.cpp +1 -1
  23. package/components/mikrojs/test/stdio_test.cpp +1 -1
  24. package/components/mikrojs/test/sys_test.cpp +1 -1
  25. package/components/mikrojs/test/udp_test.cpp +5 -5
  26. package/components/mikrojs/test/wifi_test.cpp +2 -2
  27. package/package.json +3 -3
  28. package/prebuilds/esp32/bootloader/bootloader.bin +0 -0
  29. package/prebuilds/esp32/mikrojs.bin +0 -0
  30. package/prebuilds/esp32c3/bootloader/bootloader.bin +0 -0
  31. package/prebuilds/esp32c3/mikrojs.bin +0 -0
  32. package/prebuilds/esp32c5/bootloader/bootloader.bin +0 -0
  33. package/prebuilds/esp32c5/mikrojs.bin +0 -0
  34. package/prebuilds/esp32c6/bootloader/bootloader.bin +0 -0
  35. package/prebuilds/esp32c6/mikrojs.bin +0 -0
  36. package/prebuilds/esp32s3/bootloader/bootloader.bin +0 -0
  37. package/prebuilds/esp32s3/mikrojs.bin +0 -0
@@ -0,0 +1,118 @@
1
+ // Host regression test for the pure unpack path in ../../mik_ota.cpp:
2
+ // unpack_tgz() (streaming gunzip -> untar) and sha256_file().
3
+ //
4
+ // Two things this guards:
5
+ //
6
+ // 1. The esp32c6 ROM tinfl over-reads the gzip trailer. On the chip the tinfl
7
+ // implementation comes from the mask ROM (an old miniz) which, at the end of
8
+ // the deflate stream, draws the 8-byte gzip trailer into its bit-buffer
9
+ // lookahead and reports it consumed. Code that recovers the trailer from
10
+ // tinfl's *leftover input* then sees < 8 bytes and rejects a good build.
11
+ // rom_tinfl_decompress() below models that behaviour; the real unpack_tgz
12
+ // must still succeed because it reads the trailer from the file's last 8
13
+ // bytes (a rolling tail). Revert to buffer-based trailer reading and this
14
+ // fails.
15
+ //
16
+ // 2. Streaming gunzip -> untar must be byte-identical to the archive. We unpack
17
+ // into a dir and run.sh diffs it against `tar -xzf`.
18
+ #include <cstdint>
19
+ #include <cstdio>
20
+ #include <cstdlib>
21
+ #include <cstring>
22
+
23
+ #define MIK_OTA_HOST_TEST 1
24
+ #include "miniz.h"
25
+
26
+ // Non-zero after a run means the modelled over-read actually fired (so the test
27
+ // isn't passing vacuously).
28
+ static size_t g_rom_ate = 0;
29
+
30
+ static tinfl_status rom_tinfl_decompress(tinfl_decompressor* r, const mz_uint8* pin,
31
+ size_t* pin_size, mz_uint8* pout_start,
32
+ mz_uint8* pout_next, size_t* pout_size,
33
+ const mz_uint32 flags) {
34
+ size_t avail_before = *pin_size;
35
+ tinfl_status st = tinfl_decompress(r, pin, pin_size, pout_start, pout_next, pout_size, flags);
36
+ if (st == TINFL_STATUS_DONE) {
37
+ size_t leftover = avail_before - *pin_size; // unconsumed input still in buffer
38
+ size_t eat = leftover < 4 ? leftover : 4; // ROM swallows a few trailer bytes
39
+ *pin_size += eat;
40
+ g_rom_ate += eat;
41
+ }
42
+ return st;
43
+ }
44
+ // From here on, unpack_tgz()'s tinfl_decompress(...) call hits the shim.
45
+ #define tinfl_decompress rom_tinfl_decompress
46
+
47
+ #ifndef MIK_OTA_SRC
48
+ #define MIK_OTA_SRC "../../mik_ota.cpp"
49
+ #endif
50
+ #include MIK_OTA_SRC // brings unpack_tgz/sha256_file into this TU
51
+
52
+ static int cmd_unpack(const char* build, const char* dest, bool expect_ok) {
53
+ const char* err = "(none)";
54
+ OtaErr kind = OtaErr::kTransient;
55
+ bool ok = unpack_tgz(build, dest, &err, &kind);
56
+ printf("unpack_tgz(%s) -> %s (err=%s kind=%s) rom_shim_ate=%zu\n", build,
57
+ ok ? "OK" : "FAIL", err, err_kind(kind), g_rom_ate);
58
+ if (expect_ok) return ok ? 0 : 1;
59
+ return ok ? 1 : 0; // expect-fail: rejection is success
60
+ }
61
+
62
+ static int count_open_fds() {
63
+ DIR* d = opendir("/dev/fd");
64
+ if (!d) return -1;
65
+ int n = 0;
66
+ while (readdir(d)) n++;
67
+ closedir(d);
68
+ return n;
69
+ }
70
+
71
+ // Regression: a failed unpack must not strand the open member handle. Only
72
+ // untar_finish closes Untar::cur, and it is skipped when the unpack already
73
+ // failed — so without an unconditional close, every aborted attempt leaks one
74
+ // FILE. On device that is an lfs_file_t plus its caches on a ~200 KB heap,
75
+ // pointing into a tree install_build is about to rmtree. Needs a fixture that
76
+ // fails *mid-member*: the corrupt.tgz fixture dies before any file is opened.
77
+ static int cmd_leak(const char* build, const char* dest) {
78
+ const char* err = "(none)";
79
+ OtaErr kind = OtaErr::kTransient;
80
+ if (unpack_tgz(build, dest, &err, &kind)) {
81
+ printf("fixture unpacked cleanly — it must fail mid-member to test this\n");
82
+ return 1;
83
+ }
84
+ int before = count_open_fds();
85
+ for (int i = 0; i < 5; i++) unpack_tgz(build, dest, &err, &kind);
86
+ int after = count_open_fds();
87
+ printf("failed mid-member unpack (err=%s); open fds before=%d after=%d\n", err, before, after);
88
+ if (before < 0 || after < 0) {
89
+ printf(" (cannot enumerate /dev/fd on this host — check skipped)\n");
90
+ return 0;
91
+ }
92
+ return after > before ? 1 : 0;
93
+ }
94
+
95
+ static int cmd_sha256(const char* file) {
96
+ char hex[65];
97
+ if (!sha256_file(file, hex)) {
98
+ fprintf(stderr, "sha256_file: cannot read %s\n", file);
99
+ return 1;
100
+ }
101
+ printf("%s\n", hex);
102
+ return 0;
103
+ }
104
+
105
+ int main(int argc, char** argv) {
106
+ if (argc >= 4 && strcmp(argv[1], "unpack") == 0) return cmd_unpack(argv[2], argv[3], true);
107
+ if (argc >= 4 && strcmp(argv[1], "expect-fail") == 0) return cmd_unpack(argv[2], argv[3], false);
108
+ if (argc >= 4 && strcmp(argv[1], "leak") == 0) return cmd_leak(argv[2], argv[3]);
109
+ if (argc >= 3 && strcmp(argv[1], "sha256") == 0) return cmd_sha256(argv[2]);
110
+ fprintf(stderr,
111
+ "usage:\n"
112
+ " %s unpack <build.tgz> <destdir>\n"
113
+ " %s expect-fail <build.tgz> <destdir>\n"
114
+ " %s leak <build.tgz> <destdir>\n"
115
+ " %s sha256 <file>\n",
116
+ argv[0], argv[0], argv[0], argv[0]);
117
+ return 2;
118
+ }
@@ -0,0 +1,174 @@
1
+ #!/usr/bin/env bash
2
+ # Host test for the pure OTA unpack path in ../../mik_ota.cpp. Compiles the REAL
3
+ # unpack_tgz()/sha256_file() on the host, runs them through a tinfl shim that
4
+ # models the esp32c6 ROM over-reading the gzip trailer, and asserts:
5
+ # - a real `tar -czf` build unpacks byte-identical to `tar -xzf`
6
+ # - the ROM over-read actually fires (test isn't vacuous)
7
+ # - truncated and corrupt builds are rejected (kind=corrupt)
8
+ # - a member that escapes dest_dir is rejected and writes nothing
9
+ # - sha256_file matches a known vector
10
+ #
11
+ # Needs: a C/C++ compiler, tar, gzip, and miniz (auto-fetched into .cache/, or
12
+ # set MINIZ_DIR to a dir containing miniz.c + miniz.h).
13
+ set -euo pipefail
14
+
15
+ # Stop macOS tar from injecting AppleDouble (._*) resource-fork entries, so the
16
+ # fixture archive matches what `mikro app pack` produces on any platform.
17
+ export COPYFILE_DISABLE=1
18
+
19
+ DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
20
+ SRC="$DIR/../../mik_ota.cpp"
21
+ CACHE="${MINIZ_DIR:-$DIR/.cache}"
22
+ BUILD="$DIR/.build"
23
+ CXX="${CXX:-c++}"
24
+ CC="${CC:-cc}"
25
+ MINIZ_URL="https://github.com/richgel999/miniz/releases/download/3.0.2/miniz-3.0.2.zip"
26
+ # Pinned so a test that hardens decompression does not itself run an unverified
27
+ # decompression library. This is the digest of the 3.0.2 release asset as first
28
+ # fetched; re-pin deliberately when bumping the version.
29
+ MINIZ_SHA256="ada38db0b703a56d3dd6d57bf84a9c5d664921d870d8fea4db153979fb5332c5"
30
+
31
+ mkdir -p "$BUILD"
32
+
33
+ # ---- miniz ----
34
+ if [ ! -f "$CACHE/miniz.c" ] || [ ! -f "$CACHE/miniz.h" ]; then
35
+ echo "miniz not found in $CACHE — fetching $MINIZ_URL"
36
+ mkdir -p "$CACHE"
37
+ curl -sSL --max-time 60 -o "$CACHE/miniz.zip" "$MINIZ_URL"
38
+ GOT_ZIP="$(shasum -a 256 "$CACHE/miniz.zip" | cut -d' ' -f1)"
39
+ if [ "$GOT_ZIP" != "$MINIZ_SHA256" ]; then
40
+ rm -f "$CACHE/miniz.zip"
41
+ echo "FAIL: miniz checksum mismatch (got $GOT_ZIP, want $MINIZ_SHA256)"; exit 1
42
+ fi
43
+ (cd "$CACHE" && unzip -o miniz.zip >/dev/null)
44
+ [ -f "$CACHE/miniz.c" ] || { echo "FAIL: could not obtain miniz (set MINIZ_DIR)"; exit 1; }
45
+ fi
46
+
47
+ # miniz is C — build it once as a C object (it isn't valid C++), then link.
48
+ "$CC" -O2 -I"$CACHE" -c "$CACHE/miniz.c" -o "$BUILD/miniz.o"
49
+ "$CXX" -std=c++17 -O2 -Wall -Wno-unused-function -Wno-unneeded-internal-declaration \
50
+ -I"$CACHE" "$DIR/gunzip_host_test.cpp" "$BUILD/miniz.o" -o "$BUILD/test"
51
+
52
+ TEST="$BUILD/test"
53
+
54
+ # ---- fixtures, packed exactly like `mikro app pack` (tar -C build app) ----
55
+ make_fixture() { # <name> <payload-bytes>
56
+ rm -rf "$BUILD/src_$1" "$BUILD/ref_$1" "$BUILD/out_$1"
57
+ mkdir -p "$BUILD/src_$1/app/nested"
58
+ head -c "$2" /dev/urandom | base64 > "$BUILD/src_$1/app/blob.txt"
59
+ printf 'export const x = 42\n' > "$BUILD/src_$1/app/index.js"
60
+ printf 'nested\n' > "$BUILD/src_$1/app/nested/n.txt"
61
+ tar -czf "$BUILD/$1.tgz" -C "$BUILD/src_$1" app
62
+ mkdir -p "$BUILD/ref_$1"
63
+ tar -xzf "$BUILD/$1.tgz" -C "$BUILD/ref_$1" # reference extraction
64
+ }
65
+ # small: compresses to one read so the trailer sits in tinfl's final buffer —
66
+ # the over-read definitely fires.
67
+ make_fixture small 256
68
+ # large: multiple 4 KiB reads, exercising the window-wrapping decode path and
69
+ # the 512-byte tar-block reassembly across inflate-output boundaries.
70
+ make_fixture large 60000
71
+
72
+ run_unpack() { # <name> <require-overread:0|1>
73
+ local name="$1" require="$2"
74
+ echo "== $name fixture ($(wc -c < "$BUILD/$name.tgz" | tr -d ' ') byte .tgz) =="
75
+ local out
76
+ out="$("$TEST" unpack "$BUILD/$name.tgz" "$BUILD/out_$name")" || {
77
+ echo " $out"; echo "FAIL: unpack_tgz rejected a valid build under the ROM shim"; exit 1; }
78
+ echo " $out"
79
+ diff -r "$BUILD/out_$name/app" "$BUILD/ref_$name/app" || {
80
+ echo "FAIL: streaming unpack differs from tar -xzf"; exit 1; }
81
+ if [ "$require" = 1 ] && ! printf '%s' "$out" | grep -qE 'rom_shim_ate=[1-9]'; then
82
+ echo "FAIL: the ROM over-read never fired — test would be vacuous"; exit 1
83
+ fi
84
+ echo " output matches tar -xzf (byte-identical) ✓"
85
+ }
86
+
87
+ run_unpack small 1
88
+ run_unpack large 0
89
+
90
+ # ---- rejection cases ----
91
+ echo "== corrupt/truncated rejection =="
92
+ # Truncated: drop the last 16 bytes (trailer + tail of deflate).
93
+ head -c "$(( $(wc -c < "$BUILD/small.tgz") - 16 ))" "$BUILD/small.tgz" > "$BUILD/trunc.tgz"
94
+ "$TEST" expect-fail "$BUILD/trunc.tgz" "$BUILD/out_trunc" >/dev/null && echo " truncated rejected ✓" \
95
+ || { echo "FAIL: truncated build was accepted"; exit 1; }
96
+ # Corrupt: flip a byte in the deflate body.
97
+ cp "$BUILD/large.tgz" "$BUILD/corrupt.tgz"
98
+ printf '\xff' | dd of="$BUILD/corrupt.tgz" bs=1 seek=64 count=1 conv=notrunc 2>/dev/null
99
+ "$TEST" expect-fail "$BUILD/corrupt.tgz" "$BUILD/out_corrupt" >/dev/null && echo " corrupt rejected ✓" \
100
+ || { echo "FAIL: corrupt build was accepted"; exit 1; }
101
+ # Not gzip at all.
102
+ head -c 200 /dev/urandom > "$BUILD/garbage.tgz"
103
+ "$TEST" expect-fail "$BUILD/garbage.tgz" "$BUILD/out_garbage" >/dev/null && echo " non-gzip rejected ✓" \
104
+ || { echo "FAIL: non-gzip build was accepted"; exit 1; }
105
+
106
+ # ---- no leaked handle on a mid-member failure ----
107
+ # Half a .tgz: enough deflate output to open app/blob.txt, then the input runs
108
+ # out while it is still open. That is the shape that strands the handle; the
109
+ # corrupt.tgz above fails at byte 64, before any member exists.
110
+ echo "== no fd leak on mid-member failure =="
111
+ head -c "$(( $(wc -c < "$BUILD/large.tgz") / 2 ))" "$BUILD/large.tgz" > "$BUILD/midtrunc.tgz"
112
+ LEAK_OUT="$("$TEST" leak "$BUILD/midtrunc.tgz" "$BUILD/out_midtrunc")" || {
113
+ echo " $LEAK_OUT"; echo "FAIL: aborted unpack leaks the open member handle"; exit 1; }
114
+ echo " $LEAK_OUT"
115
+ echo " aborted unpack releases the member handle ✓"
116
+
117
+ # ---- path traversal rejection ----
118
+ # The checksum is no defence here: it ships in the same offer as the URL, so
119
+ # whoever serves the offer controls both. A member that escapes dest_dir would
120
+ # land on the live app or the rollback baseline during boot reconcile.
121
+ echo "== path traversal rejection =="
122
+ rm -rf "$BUILD/src_evil" "$BUILD/out_evil" "$BUILD/zz.txt"
123
+ mkdir -p "$BUILD/src_evil/app"
124
+ printf 'pwned\n' > "$BUILD/src_evil/app/zz.txt"
125
+ tar -cf "$BUILD/evil.tar" -C "$BUILD/src_evil" app
126
+ # Rewrite the member name in place: `app/zz.txt` -> `..//zz.txt`, same length so
127
+ # every other header field (including the size) stays valid. tar itself refuses
128
+ # to create such a member, hence the patch. The header checksum is left stale,
129
+ # which is fine: untar_block doesn't verify it, and if it ever does this test
130
+ # will fail loudly rather than silently stop testing traversal.
131
+ OFF="$(grep -abo 'app/zz.txt' "$BUILD/evil.tar" | head -1 | cut -d: -f1)"
132
+ [ -n "$OFF" ] || { echo "FAIL: could not locate the member name to patch"; exit 1; }
133
+ printf '../' | dd of="$BUILD/evil.tar" bs=1 seek="$OFF" count=3 conv=notrunc 2>/dev/null
134
+ gzip -c "$BUILD/evil.tar" > "$BUILD/evil.tgz"
135
+ "$TEST" expect-fail "$BUILD/evil.tgz" "$BUILD/out_evil" >/dev/null && echo " ../ member rejected ✓" \
136
+ || { echo "FAIL: a build with a ../ member was accepted"; exit 1; }
137
+ [ ! -e "$BUILD/zz.txt" ] || { echo "FAIL: the ../ member escaped dest_dir"; exit 1; }
138
+ echo " nothing written outside dest_dir ✓"
139
+
140
+ # ---- deep nesting rejection ----
141
+ # Every level created here is a level rmtree recurses over during boot reconcile,
142
+ # where a stack overflow reboots into the same call and never clears the tree.
143
+ # `a/` twice per level fits ~49 levels in the 100-byte name field, so the cap is
144
+ # what keeps a hostile build from bricking the device rather than just failing.
145
+ echo "== deep nesting rejection =="
146
+ rm -rf "$BUILD/src_deep" "$BUILD/out_deep"
147
+ DEEP="$BUILD/src_deep/app"; for _ in $(seq 1 12); do DEEP="$DEEP/a"; done
148
+ mkdir -p "$DEEP"
149
+ printf 'deep\n' > "$DEEP/x.txt"
150
+ tar -czf "$BUILD/deep.tgz" -C "$BUILD/src_deep" app
151
+ "$TEST" expect-fail "$BUILD/deep.tgz" "$BUILD/out_deep" >/dev/null && echo " deep member rejected ✓" \
152
+ || { echo "FAIL: a build nested past the depth cap was accepted"; exit 1; }
153
+ # A shallow build must still unpack, or the cap has just broken every real build.
154
+ rm -rf "$BUILD/src_ok" "$BUILD/out_ok"
155
+ mkdir -p "$BUILD/src_ok/app/a/b"
156
+ printf 'ok\n' > "$BUILD/src_ok/app/a/b/x.txt"
157
+ tar -czf "$BUILD/okdepth.tgz" -C "$BUILD/src_ok" app
158
+ "$TEST" unpack "$BUILD/okdepth.tgz" "$BUILD/out_ok" >/dev/null \
159
+ && [ -f "$BUILD/out_ok/app/a/b/x.txt" ] && echo " ordinary nesting still accepted ✓" \
160
+ || { echo "FAIL: the depth cap rejected an ordinary build"; exit 1; }
161
+
162
+ # ---- SHA-256 known vector ----
163
+ echo "== sha256 vector =="
164
+ # echo -n "abc" | sha256sum -> ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
165
+ printf 'abc' > "$BUILD/abc.bin"
166
+ GOT="$("$TEST" sha256 "$BUILD/abc.bin")"
167
+ WANT="ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
168
+ [ "$GOT" = "$WANT" ] && echo " sha256('abc') = $GOT ✓" \
169
+ || { echo "FAIL: sha256 mismatch: got $GOT want $WANT"; exit 1; }
170
+
171
+ echo
172
+ echo "PASS: unpack_tgz unpacks valid builds byte-identical (even when tinfl eats the"
173
+ echo " gzip trailer), rejects corrupt/truncated builds and path traversal, and"
174
+ echo " SHA-256 is correct."
@@ -16,7 +16,7 @@ static void setup() {
16
16
  static void teardown() { MIK_FreeRuntime(rt); }
17
17
 
18
18
  static JSValue eval_module(const char* code) {
19
- JSValue ret = MIK_EvalModuleContent(ctx, "mikrojs/test", code, strlen(code));
19
+ JSValue ret = MIK_EvalModuleContent(ctx, "mikro/test", code, strlen(code));
20
20
  if (!JS_IsException(ret)) {
21
21
  JS_FreeValue(ctx, ret);
22
22
  mik__execute_jobs(ctx);
@@ -17,7 +17,7 @@ static void setup() {
17
17
  static void teardown() { MIK_FreeRuntime(rt); }
18
18
 
19
19
  static JSValue eval_module(const char* code) {
20
- JSValue ret = MIK_EvalModuleContent(ctx, "mikrojs/test", code, strlen(code));
20
+ JSValue ret = MIK_EvalModuleContent(ctx, "mikro/test", code, strlen(code));
21
21
  if (!JS_IsException(ret)) {
22
22
  JS_FreeValue(ctx, ret);
23
23
  mik__execute_jobs(ctx);
@@ -20,7 +20,7 @@ static void setup() {
20
20
  static void teardown() { MIK_FreeRuntime(rt); }
21
21
 
22
22
  static JSValue eval_module(const char* code) {
23
- JSValue ret = MIK_EvalModuleContent(ctx, "mikrojs/test", code, strlen(code));
23
+ JSValue ret = MIK_EvalModuleContent(ctx, "mikro/test", code, strlen(code));
24
24
  if (!JS_IsException(ret)) {
25
25
  JS_FreeValue(ctx, ret);
26
26
  mik__execute_jobs(ctx);
@@ -7,7 +7,7 @@ static MIKRuntime* rt;
7
7
  static JSContext* ctx;
8
8
 
9
9
  static JSValue eval_module(const char* code) {
10
- JSValue ret = MIK_EvalModuleContent(ctx, "mikrojs/test", code, strlen(code));
10
+ JSValue ret = MIK_EvalModuleContent(ctx, "mikro/test", code, strlen(code));
11
11
  if (!JS_IsException(ret)) {
12
12
  JS_FreeValue(ctx, ret);
13
13
  mik__execute_jobs(ctx);
@@ -7,7 +7,7 @@ static MIKRuntime* rt;
7
7
  static JSContext* ctx;
8
8
 
9
9
  static JSValue eval_module(const char* code) {
10
- JSValue ret = MIK_EvalModuleContent(ctx, "mikrojs/test", code, strlen(code));
10
+ JSValue ret = MIK_EvalModuleContent(ctx, "mikro/test", code, strlen(code));
11
11
  if (!JS_IsException(ret)) {
12
12
  JS_FreeValue(ctx, ret);
13
13
  mik__execute_jobs(ctx);
@@ -19,7 +19,7 @@ static void setup() {
19
19
  static void teardown() { MIK_FreeRuntime(rt); }
20
20
 
21
21
  static JSValue eval_module(const char* code) {
22
- JSValue ret = MIK_EvalModuleContent(ctx, "mikrojs/test", code, strlen(code));
22
+ JSValue ret = MIK_EvalModuleContent(ctx, "mikro/test", code, strlen(code));
23
23
  if (!JS_IsException(ret)) {
24
24
  JS_FreeValue(ctx, ret);
25
25
  mik__execute_jobs(ctx);
@@ -27,11 +27,11 @@ static JSValue eval_module(const char* code) {
27
27
  return ret;
28
28
  }
29
29
 
30
- TEST_CASE("mikrojs/udp module is importable", "[modules]") {
30
+ TEST_CASE("mikro/udp module is importable", "[modules]") {
31
31
  setup();
32
32
 
33
33
  JSValue ret = eval_module(R"(
34
- import {bind} from "mikrojs/udp";
34
+ import {bind} from "mikro/udp";
35
35
  globalThis.__hasBind = typeof bind === "function";
36
36
  )");
37
37
  TEST_ASSERT_FALSE_MESSAGE(JS_IsException(ret), "Module eval should not throw");
@@ -45,13 +45,13 @@ TEST_CASE("mikrojs/udp module is importable", "[modules]") {
45
45
  teardown();
46
46
  }
47
47
 
48
- TEST_CASE("mikrojs/udp bind and close work on device", "[modules]") {
48
+ TEST_CASE("mikro/udp bind and close work on device", "[modules]") {
49
49
  setup();
50
50
 
51
51
  /* Drives a bind+close, holds the socket on globalThis to keep it alive
52
52
  * past the IIFE, then verifies port assignment and idempotent close. */
53
53
  JSValue ret = eval_module(R"(
54
- import {bind} from "mikrojs/udp";
54
+ import {bind} from "mikro/udp";
55
55
  globalThis.__phase = "init";
56
56
  (async () => {
57
57
  const r = await bind({port: 0, family: 'ipv4'});
@@ -37,7 +37,7 @@ static void setup() {
37
37
  /* WiFi module is self-registered and lazily initialized.
38
38
  * The import below triggers init + loop consumer registration. */
39
39
  const char* init_code = "import { Wifi } from \"native:mikro/wifi\"; new Wifi();";
40
- JSValue ret = MIK_EvalModuleContent(ctx, "mikrojs/test-setup", init_code, strlen(init_code));
40
+ JSValue ret = MIK_EvalModuleContent(ctx, "mikro/test-setup", init_code, strlen(init_code));
41
41
  if (!JS_IsException(ret)) {
42
42
  JS_FreeValue(ctx, ret);
43
43
  mik__execute_jobs(ctx);
@@ -56,7 +56,7 @@ static void teardown() {
56
56
  }
57
57
 
58
58
  static JSValue eval_module(const char* code) {
59
- JSValue ret = MIK_EvalModuleContent(ctx, "mikrojs/test", code, strlen(code));
59
+ JSValue ret = MIK_EvalModuleContent(ctx, "mikro/test", code, strlen(code));
60
60
  if (!JS_IsException(ret)) {
61
61
  JS_FreeValue(ctx, ret);
62
62
  mik__execute_jobs(ctx);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikrojs/firmware",
3
- "version": "0.16.0",
3
+ "version": "0.17.0-next.20260722220623",
4
4
  "description": "Mikro.js ESP32 firmware: ESP-IDF component, build tools, and project template",
5
5
  "keywords": [
6
6
  "esp-idf",
@@ -47,8 +47,8 @@
47
47
  },
48
48
  "dependencies": {
49
49
  "esbuild": "^0.28.0",
50
- "@mikrojs/quickjs": "0.16.0",
51
- "@mikrojs/native": "0.16.0"
50
+ "@mikrojs/native": "0.17.0-next.20260722220623+e03f4a6",
51
+ "@mikrojs/quickjs": "0.17.0-next.20260722220623+e03f4a6"
52
52
  },
53
53
  "engines": {
54
54
  "node": ">=24.0.0"
Binary file
Binary file
Binary file
Binary file
Binary file