@mikrojs/native 0.21.0-pr-431.20260920213100 → 0.21.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.
@@ -21,3 +21,14 @@ MIKAppCommitResult mik__app_commit(const char* base, bool erased);
21
21
  /* Recover from an interrupted commit: restore the rollback copy if the live
22
22
  * app is missing, otherwise drop leftover staging/rollback dirs. */
23
23
  void mik__app_recover(const char* base);
24
+
25
+ /* Remove a directory tree. Returns true when nothing is left at `path` (also
26
+ * when nothing was there). On failure errno is that of the first entry that
27
+ * could not be removed. Descends at most 32 levels and feeds the watchdog. */
28
+ bool mik__rmdir_recursive(const char* path);
29
+
30
+ /* Create `path` and any missing parents. Returns true when `path` is a
31
+ * directory afterwards. On failure errno is that of the first mkdir that
32
+ * left no directory behind, so a full filesystem reads as ENOSPC here instead
33
+ * of as ENOENT from a later open. */
34
+ bool mik__mkdirs(const char* path);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikrojs/native",
3
- "version": "0.21.0-pr-431.20260920213100",
3
+ "version": "0.21.0",
4
4
  "description": "Mikro.js C++ runtime library and Node.js native addon",
5
5
  "keywords": [
6
6
  "esp32",
@@ -85,14 +85,14 @@
85
85
  "./runtime/wifi/types": "./runtime/wifi/types.ts"
86
86
  },
87
87
  "dependencies": {
88
- "@mikrojs/quickjs": "0.21.0-pr-431.20260920213100+4a26f8c",
88
+ "@mikrojs/quickjs": "0.21.0",
89
89
  "cmake-js": "^8.0.0",
90
90
  "node-addon-api": "^8.9.2",
91
91
  "node-gyp-build": "^4.8.4"
92
92
  },
93
93
  "devDependencies": {
94
- "@mikrojs/registry": "0.21.0-pr-431.20260920213100+4a26f8c",
95
- "@mikrojs/schema": "0.21.0-pr-431.20260920213100+4a26f8c",
94
+ "@mikrojs/registry": "0.21.0",
95
+ "@mikrojs/schema": "0.21.0",
96
96
  "@swc/core": "^1.16.2",
97
97
  "@types/node": "^24.12.2",
98
98
  "esbuild": "^0.28.2",
@@ -1,11 +1,14 @@
1
1
  #include "mikrojs/app_store.h"
2
2
 
3
3
  #include <dirent.h>
4
+ #include <errno.h>
4
5
  #include <stdio.h>
5
6
  #include <string.h>
6
7
  #include <sys/stat.h>
7
8
  #include <unistd.h>
8
9
 
10
+ #include "mikrojs/platform.h"
11
+
9
12
  namespace {
10
13
 
11
14
  bool path_exists(const char* path) {
@@ -33,43 +36,86 @@ constexpr int kMaxDepth = 32;
33
36
 
34
37
  /* `path` is a mutable buffer of `cap` bytes holding the directory to remove;
35
38
  * each level appends into it and truncates on the way out, so a frame costs a
36
- * few dozen bytes rather than a 512-byte path of its own. */
37
- void rmdir_recursive_at(char* path, size_t cap, int depth) {
38
- DIR* dir = opendir(path);
39
- if (!dir) return;
40
-
39
+ * few dozen bytes rather than a 512-byte path of its own. Returns true when
40
+ * the directory is gone; `first_errno` keeps the first failure. */
41
+ bool rmdir_recursive_at(char* path, size_t cap, int depth, int* first_errno) {
42
+ const MIKPlatform* platform = MIK_GetPlatform();
41
43
  const size_t base = strlen(path);
42
- struct dirent* entry;
43
- while ((entry = readdir(dir)) != NULL) {
44
- if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
45
-
46
- const int n = snprintf(path + base, cap - base, "/%s", entry->d_name);
47
- if (n < 0 || (size_t)n >= cap - base) {
48
- path[base] = 0; /* would truncate to a different path; skip it */
49
- continue;
50
- }
51
44
 
52
- struct stat st;
53
- if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
54
- if (depth < kMaxDepth) rmdir_recursive_at(path, cap, depth + 1);
55
- } else {
56
- unlink(path);
45
+ DIR* dir = opendir(path);
46
+ if (dir) {
47
+ struct dirent* entry;
48
+ while ((entry = readdir(dir)) != NULL) {
49
+ if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
50
+ if (platform->feed_watchdog) platform->feed_watchdog();
51
+
52
+ const int n = snprintf(path + base, cap - base, "/%s", entry->d_name);
53
+ if (n < 0 || (size_t)n >= cap - base) {
54
+ path[base] = 0; /* would truncate to a different path; skip it */
55
+ continue;
56
+ }
57
+
58
+ struct stat st;
59
+ if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
60
+ if (depth < kMaxDepth) rmdir_recursive_at(path, cap, depth + 1, first_errno);
61
+ } else if (unlink(path) != 0 && *first_errno == 0) {
62
+ *first_errno = errno;
63
+ }
64
+ path[base] = 0;
57
65
  }
58
- path[base] = 0;
66
+ closedir(dir);
59
67
  }
60
- closedir(dir);
61
- rmdir(path);
68
+
69
+ if (rmdir(path) == 0) return true;
70
+ if (*first_errno == 0) *first_errno = errno;
71
+ return false;
62
72
  }
63
73
 
64
- void rmdir_recursive(const char* path) {
74
+ } // namespace
75
+
76
+ bool mik__rmdir_recursive(const char* path) {
65
77
  char buf[512];
66
78
  const size_t n = strlen(path);
67
- if (n >= sizeof(buf)) return;
79
+ if (n >= sizeof(buf)) {
80
+ errno = ENAMETOOLONG;
81
+ return false;
82
+ }
83
+ if (!path_exists(path)) return true;
68
84
  memcpy(buf, path, n + 1);
69
- rmdir_recursive_at(buf, sizeof(buf), 0);
85
+ int first_errno = 0;
86
+ if (rmdir_recursive_at(buf, sizeof(buf), 0, &first_errno)) return true;
87
+ errno = first_errno;
88
+ return false;
70
89
  }
71
90
 
72
- } // namespace
91
+ bool mik__mkdirs(const char* path) {
92
+ char tmp[512];
93
+ const size_t n = strlen(path);
94
+ if (n == 0 || n >= sizeof(tmp)) {
95
+ errno = n == 0 ? EINVAL : ENAMETOOLONG;
96
+ return false;
97
+ }
98
+ memcpy(tmp, path, n + 1);
99
+
100
+ int first_errno = 0;
101
+ for (char* p = tmp + 1;; p++) {
102
+ if (*p != '/' && *p != '\0') continue;
103
+ const char saved = *p;
104
+ *p = '\0';
105
+ if (mkdir(tmp, 0755) != 0 && errno != EEXIST) {
106
+ /* A prefix that is there anyway (a mount point may not answer
107
+ * EEXIST) must not hide the errno of the one that is missing. */
108
+ const int e = errno;
109
+ if (first_errno == 0 && !dir_exists(tmp)) first_errno = e;
110
+ }
111
+ *p = saved;
112
+ if (saved == '\0') break;
113
+ }
114
+
115
+ if (dir_exists(path)) return true;
116
+ errno = first_errno ? first_errno : ENOTDIR;
117
+ return false;
118
+ }
73
119
 
74
120
  MIKAppCommitResult mik__app_commit(const char* base, bool erased) {
75
121
  char app[512];
@@ -85,14 +131,14 @@ MIKAppCommitResult mik__app_commit(const char* base, bool erased) {
85
131
  * leave the live app untouched and just clean up. The original logic
86
132
  * would have deleted the live app in this degenerate case. */
87
133
  if (!dir_exists(staged_app)) {
88
- rmdir_recursive(old);
89
- rmdir_recursive(tmp);
134
+ mik__rmdir_recursive(old);
135
+ mik__rmdir_recursive(tmp);
90
136
  return MIK_APP_COMMIT_OK;
91
137
  }
92
138
 
93
139
  /* Atomic swap: staging dir → app dir */
94
140
  if (path_exists(app) && !erased) {
95
- rmdir_recursive(old);
141
+ mik__rmdir_recursive(old);
96
142
  if (rename(app, old) != 0) {
97
143
  return MIK_APP_COMMIT_STASH_FAILED;
98
144
  }
@@ -105,8 +151,8 @@ MIKAppCommitResult mik__app_commit(const char* base, bool erased) {
105
151
  return MIK_APP_COMMIT_SWAP_FAILED;
106
152
  }
107
153
 
108
- rmdir_recursive(old);
109
- rmdir_recursive(tmp);
154
+ mik__rmdir_recursive(old);
155
+ mik__rmdir_recursive(tmp);
110
156
  return MIK_APP_COMMIT_OK;
111
157
  }
112
158
 
@@ -125,10 +171,10 @@ void mik__app_recover(const char* base) {
125
171
  if (!has_app && has_old) {
126
172
  rename(old, app);
127
173
  } else if (has_old) {
128
- rmdir_recursive(old);
174
+ mik__rmdir_recursive(old);
129
175
  }
130
176
 
131
177
  if (has_tmp) {
132
- rmdir_recursive(tmp);
178
+ mik__rmdir_recursive(tmp);
133
179
  }
134
180
  }