MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

base constructor for map/set, logging output fixes

+332 -62
+39 -26
examples/spa/server.js
··· 1 1 import { open } from 'ant:fs'; 2 - import { join } from 'ant:path'; 2 + import { join, extname } from 'ant:path'; 3 3 import { Radix3 } from '../server/radix3'; 4 4 5 5 const router = new Radix3(); 6 6 7 - router.get('*path', c => { 8 - const path = c.params.path === '/' ? 'index.html' : c.params.path; 9 - const file = open(join(import.meta.dirname, path)); 7 + const validPaths = new Set(); 8 + const invalidPaths = new Set(); 9 + 10 + const basePath = import.meta.dirname; 11 + const indexPath = join(basePath, 'index.html'); 12 + 13 + const mimeTypes = new Map([ 14 + ['.html', 'text/html'], 15 + ['.js', 'application/javascript'], 16 + ['.css', 'text/css'], 17 + ['.json', 'application/json'], 18 + ['.png', 'image/png'], 19 + ['.jpg', 'image/jpeg'], 20 + ['.jpeg', 'image/jpeg'], 21 + ['.gif', 'image/gif'], 22 + ['.svg', 'image/svg+xml'], 23 + ['.ico', 'image/x-icon'], 24 + ['.woff', 'font/woff'], 25 + ['.woff2', 'font/woff2'] 26 + ]); 10 27 11 - if (path.endsWith('.html')) { 12 - return c.res.html(file); 13 - } 28 + router.get('/api/version', async c => c.res.json({ version: Ant.version })); 14 29 15 - if (path.endsWith('.js')) { 16 - return c.res.body(file, 200, 'application/javascript'); 17 - } 30 + router.get('*path', c => { 31 + const reqPath = c.params.path; 32 + if (reqPath === '/') return c.res.body(open(indexPath), 200, 'text/html'); 18 33 19 - if (path.endsWith('.png')) { 20 - return c.res.body(file, 200, 'image/png'); 21 - } 34 + const filePath = reqPath === '/' ? indexPath : join(basePath, reqPath); 22 35 23 - if (path.endsWith('.jpg') || path.endsWith('.jpeg')) { 24 - return c.res.body(file, 200, 'image/jpeg'); 36 + if (validPaths.has(filePath)) { 37 + const ext = extname(reqPath) || '.html'; 38 + return c.res.body(open(filePath), 200, mimeTypes.get(ext) ?? 'application/octet-stream'); 25 39 } 26 40 27 - if (path.endsWith('.gif')) { 28 - return c.res.body(file, 200, 'image/gif'); 41 + if (invalidPaths.has(filePath)) { 42 + return c.res.body(open(indexPath), 200, 'text/html'); 29 43 } 30 44 31 - if (path.endsWith('.svg')) { 32 - return c.res.body(file, 200, 'image/svg+xml'); 33 - } 45 + try { 46 + const file = open(filePath); 47 + validPaths.add(filePath); 34 48 35 - if (path.endsWith('.ico')) { 36 - return c.res.body(file, 200, 'image/x-icon'); 49 + const ext = extname(reqPath) || '.html'; 50 + return c.res.body(file, 200, mimeTypes.get(ext) ?? 'application/octet-stream'); 51 + } catch { 52 + invalidPaths.add(filePath); 53 + return c.res.body(open(indexPath), 200, 'text/html'); 37 54 } 38 - 39 - return c.res.body(file); 40 55 }); 41 - 42 - router.get('/api/version', async c => c.res.json({ version: Ant.version })); 43 56 44 57 router.printTree(); 45 58 console.log('');
+1 -1
meson.build
··· 74 74 build_date = run_command('date', '+%Y-%m-%d', check: true).stdout().strip() 75 75 76 76 version_conf = configuration_data() 77 - version_conf.set('ANT_VERSION', '0.1.0.10') 77 + version_conf.set('ANT_VERSION', '0.1.0.11') 78 78 version_conf.set('ANT_GIT_HASH', git_hash) 79 79 version_conf.set('ANT_BUILD_DATE', build_date) 80 80
+224 -34
src/ant.c
··· 160 160 UT_hash_handle hh; 161 161 } obj_prop_cache_t; 162 162 163 + typedef struct map_entry { 164 + char *key; 165 + jsval_t value; 166 + UT_hash_handle hh; 167 + } map_entry_t; 168 + 169 + typedef struct set_entry { 170 + jsval_t value; 171 + char *key; 172 + UT_hash_handle hh; 173 + } set_entry_t; 174 + 163 175 static ant_library_t *library_registry = NULL; 164 176 static esm_module_cache_t global_module_cache = {NULL, 0}; 165 177 static obj_prop_cache_t *global_property_cache = NULL; ··· 1109 1121 n += (size_t) snprintf(buf + n, len - n, "<ref *%d> ", self_ref); 1110 1122 } 1111 1123 1112 - jsoff_t tag_off = lkp(js, obj, "@@toStringTag", 13); 1113 - if (tag_off != 0) { 1114 - jsval_t tag_val = resolveprop(js, mkval(T_PROP, tag_off)); 1115 - if (vtype(tag_val) == T_STR) { 1116 - jsoff_t tlen, toff = vstr(js, tag_val, &tlen); 1117 - n += cpy(buf + n, len - n, "Object [", 8); 1118 - n += cpy(buf + n, len - n, (const char *) &js->mem[toff], tlen); 1119 - n += cpy(buf + n, len - n, "] {\n", 4); 1124 + jsoff_t tag_off = lkp_proto(js, obj, "@@toStringTag", 13); 1125 + bool is_map = false, is_set = false; 1126 + jsoff_t tlen = 0, toff = 0; 1127 + const char *tag_str = NULL; 1128 + 1129 + if (tag_off == 0) goto print_plain_object; 1130 + 1131 + jsval_t tag_val = resolveprop(js, mkval(T_PROP, tag_off)); 1132 + if (vtype(tag_val) != T_STR) goto print_plain_object; 1133 + 1134 + toff = vstr(js, tag_val, &tlen); 1135 + tag_str = (const char *) &js->mem[toff]; 1136 + is_map = (tlen == 3 && memcmp(tag_str, "Map", 3) == 0); 1137 + is_set = (tlen == 3 && memcmp(tag_str, "Set", 3) == 0); 1138 + 1139 + if (is_map) { 1140 + jsoff_t map_off = lkp(js, obj, "__map", 5); 1141 + if (map_off == 0) goto print_tagged_object; 1142 + 1143 + jsval_t map_val = resolveprop(js, mkval(T_PROP, map_off)); 1144 + if (vtype(map_val) != T_NUM) goto print_tagged_object; 1145 + 1146 + map_entry_t **map_ptr = (map_entry_t**)(size_t)vdata(map_val); 1147 + n += cpy(buf + n, len - n, "Map(", 4); 1148 + 1149 + unsigned int count = 0; 1150 + if (map_ptr && *map_ptr) count = HASH_COUNT(*map_ptr); 1151 + n += (size_t) snprintf(buf + n, len - n, "%u", count); 1152 + n += cpy(buf + n, len - n, ") ", 2); 1153 + 1154 + if (count == 0) { 1155 + n += cpy(buf + n, len - n, "{}", 2); 1156 + } else { 1157 + n += cpy(buf + n, len - n, "{\n", 2); 1158 + stringify_indent++; 1159 + bool first = true; 1160 + if (map_ptr && *map_ptr) { 1161 + map_entry_t *entry, *tmp; 1162 + HASH_ITER(hh, *map_ptr, entry, tmp) { 1163 + if (!first) n += cpy(buf + n, len - n, ",\n", 2); 1164 + first = false; 1165 + n += add_indent(buf + n, len - n, stringify_indent); 1166 + 1167 + size_t key_len = strlen(entry->key); 1168 + n += cpy(buf + n, len - n, "'", 1); 1169 + n += cpy(buf + n, len - n, entry->key, key_len); 1170 + n += cpy(buf + n, len - n, "'", 1); 1171 + n += cpy(buf + n, len - n, " => ", 4); 1172 + n += tostr(js, entry->value, buf + n, len - n); 1173 + } 1174 + } 1175 + stringify_indent--; 1176 + n += cpy(buf + n, len - n, "\n", 1); 1177 + n += add_indent(buf + n, len - n, stringify_indent); 1178 + n += cpy(buf + n, len - n, "}", 1); 1179 + } 1180 + pop_stringify(); 1181 + return n; 1182 + } 1183 + 1184 + if (is_set) { 1185 + jsoff_t set_off = lkp(js, obj, "__set", 5); 1186 + if (set_off == 0) goto print_tagged_object; 1187 + 1188 + jsval_t set_val = resolveprop(js, mkval(T_PROP, set_off)); 1189 + if (vtype(set_val) != T_NUM) goto print_tagged_object; 1190 + 1191 + set_entry_t **set_ptr = (set_entry_t**)(size_t)vdata(set_val); 1192 + n += cpy(buf + n, len - n, "Set(", 4); 1193 + 1194 + unsigned int count = 0; 1195 + if (set_ptr && *set_ptr) count = HASH_COUNT(*set_ptr); 1196 + n += (size_t) snprintf(buf + n, len - n, "%u", count); 1197 + n += cpy(buf + n, len - n, ") ", 2); 1198 + 1199 + if (count == 0) { 1200 + n += cpy(buf + n, len - n, "{}", 2); 1120 1201 } else { 1121 1202 n += cpy(buf + n, len - n, "{\n", 2); 1203 + stringify_indent++; 1204 + bool first = true; 1205 + if (set_ptr && *set_ptr) { 1206 + set_entry_t *entry, *tmp; 1207 + HASH_ITER(hh, *set_ptr, entry, tmp) { 1208 + if (!first) n += cpy(buf + n, len - n, ",\n", 2); 1209 + first = false; 1210 + n += add_indent(buf + n, len - n, stringify_indent); 1211 + n += tostr(js, entry->value, buf + n, len - n); 1212 + } 1213 + } 1214 + stringify_indent--; 1215 + n += cpy(buf + n, len - n, "\n", 1); 1216 + n += add_indent(buf + n, len - n, stringify_indent); 1217 + n += cpy(buf + n, len - n, "}", 1); 1122 1218 } 1123 - } else { 1124 - n += cpy(buf + n, len - n, "{\n", 2); 1219 + pop_stringify(); 1220 + return n; 1125 1221 } 1222 + 1223 + print_tagged_object: 1224 + n += cpy(buf + n, len - n, "Object [", 8); 1225 + n += cpy(buf + n, len - n, (const char *) &js->mem[toff], tlen); 1226 + n += cpy(buf + n, len - n, "] {\n", 4); 1227 + goto continue_object_print; 1228 + 1229 + print_plain_object: 1230 + n += cpy(buf + n, len - n, "{\n", 2); 1231 + 1232 + continue_object_print: 1126 1233 1127 1234 stringify_indent++; 1128 1235 jsoff_t next = loadoff(js, (jsoff_t) vdata(obj)) & ~(3U | CONSTMASK); ··· 11349 11456 return js_mkerr(js, "Invalid export statement"); 11350 11457 } 11351 11458 11352 - typedef struct map_entry { 11353 - char *key; 11354 - jsval_t value; 11355 - UT_hash_handle hh; 11356 - } map_entry_t; 11357 - 11358 - typedef struct set_entry { 11359 - jsval_t value; 11360 - char *key; 11361 - UT_hash_handle hh; 11362 - } set_entry_t; 11363 - 11364 11459 typedef struct weakmap_entry { 11365 11460 jsval_t key_obj; 11366 11461 jsval_t value; ··· 11384 11479 jsval_t map_obj = mkobj(js, 0); 11385 11480 11386 11481 jsval_t map_proto = get_ctor_proto(js, "Map", 3); 11387 - if (vtype(map_proto) == T_OBJ) { 11388 - set_proto(js, map_obj, map_proto); 11389 - } 11482 + if (vtype(map_proto) == T_OBJ) set_proto(js, map_obj, map_proto); 11390 11483 11391 11484 map_entry_t **map_head = (map_entry_t **)ANT_GC_MALLOC(sizeof(map_entry_t *)); 11392 11485 if (!map_head) return js_mkerr(js, "out of memory"); ··· 11396 11489 jsval_t map_key = js_mkstr(js, "__map", 5); 11397 11490 setprop(js, map_obj, map_key, map_ptr); 11398 11491 11492 + if (nargs == 0 || vtype(args[0]) != T_ARR) return map_obj; 11493 + 11494 + jsval_t iterable = args[0]; 11495 + jsoff_t length = arr_length(js, iterable); 11496 + 11497 + for (jsoff_t i = 0; i < length; i++) { 11498 + jsval_t entry = arr_get(js, iterable, i); 11499 + if (vtype(entry) != T_ARR) continue; 11500 + 11501 + jsoff_t entry_len = arr_length(js, entry); 11502 + if (entry_len < 2) continue; 11503 + 11504 + jsval_t key = arr_get(js, entry, 0); 11505 + jsval_t value = arr_get(js, entry, 1); 11506 + const char *key_str = jsval_to_key(js, key); 11507 + 11508 + map_entry_t *map_entry; 11509 + HASH_FIND_STR(*map_head, key_str, map_entry); 11510 + if (map_entry) { 11511 + map_entry->value = value; 11512 + continue; 11513 + } 11514 + 11515 + map_entry = (map_entry_t *)ANT_GC_MALLOC(sizeof(map_entry_t)); 11516 + if (!map_entry) return js_mkerr(js, "out of memory"); 11517 + map_entry->key = strdup(key_str); 11518 + map_entry->value = value; 11519 + HASH_ADD_STR(*map_head, key, map_entry); 11520 + } 11521 + 11399 11522 return map_obj; 11400 11523 } 11401 11524 ··· 11403 11526 jsval_t set_obj = mkobj(js, 0); 11404 11527 11405 11528 jsval_t set_proto_val = get_ctor_proto(js, "Set", 3); 11406 - if (vtype(set_proto_val) == T_OBJ) { 11407 - set_proto(js, set_obj, set_proto_val); 11408 - } 11529 + if (vtype(set_proto_val) == T_OBJ) set_proto(js, set_obj, set_proto_val); 11409 11530 11410 11531 set_entry_t **set_head = (set_entry_t **)ANT_GC_MALLOC(sizeof(set_entry_t *)); 11411 11532 if (!set_head) return js_mkerr(js, "out of memory"); ··· 11415 11536 jsval_t set_key = js_mkstr(js, "__set", 5); 11416 11537 setprop(js, set_obj, set_key, set_ptr); 11417 11538 11539 + if (nargs == 0 || vtype(args[0]) != T_ARR) return set_obj; 11540 + 11541 + jsval_t iterable = args[0]; 11542 + jsoff_t length = arr_length(js, iterable); 11543 + 11544 + for (jsoff_t i = 0; i < length; i++) { 11545 + jsval_t value = arr_get(js, iterable, i); 11546 + const char *key_str = jsval_to_key(js, value); 11547 + 11548 + set_entry_t *entry; 11549 + HASH_FIND_STR(*set_head, key_str, entry); 11550 + if (entry) continue; 11551 + 11552 + entry = (set_entry_t *)ANT_GC_MALLOC(sizeof(set_entry_t)); 11553 + if (!entry) return js_mkerr(js, "out of memory"); 11554 + entry->value = value; 11555 + entry->key = strdup(key_str); 11556 + HASH_ADD_KEYPTR(hh, *set_head, entry->key, strlen(entry->key), entry); 11557 + } 11558 + 11418 11559 return set_obj; 11419 11560 } 11420 11561 ··· 11638 11779 jsval_t wm_obj = mkobj(js, 0); 11639 11780 11640 11781 jsval_t wm_proto = get_ctor_proto(js, "WeakMap", 7); 11641 - if (vtype(wm_proto) == T_OBJ) { 11642 - set_proto(js, wm_obj, wm_proto); 11643 - } 11782 + if (vtype(wm_proto) == T_OBJ) set_proto(js, wm_obj, wm_proto); 11644 11783 11645 11784 weakmap_entry_t **wm_head = (weakmap_entry_t **)ANT_GC_MALLOC(sizeof(weakmap_entry_t *)); 11646 11785 if (!wm_head) return js_mkerr(js, "out of memory"); ··· 11650 11789 jsval_t wm_key = js_mkstr(js, "__weakmap", 9); 11651 11790 setprop(js, wm_obj, wm_key, wm_ptr); 11652 11791 11792 + if (nargs == 0 || vtype(args[0]) != T_ARR) return wm_obj; 11793 + 11794 + jsval_t iterable = args[0]; 11795 + jsoff_t length = arr_length(js, iterable); 11796 + 11797 + for (jsoff_t i = 0; i < length; i++) { 11798 + jsval_t entry = arr_get(js, iterable, i); 11799 + if (vtype(entry) != T_ARR) continue; 11800 + 11801 + jsoff_t entry_len = arr_length(js, entry); 11802 + if (entry_len < 2) continue; 11803 + 11804 + jsval_t key = arr_get(js, entry, 0); 11805 + jsval_t value = arr_get(js, entry, 1); 11806 + 11807 + if (vtype(key) != T_OBJ) return js_mkerr(js, "WeakMap key must be an object"); 11808 + 11809 + weakmap_entry_t *wm_entry; 11810 + HASH_FIND(hh, *wm_head, &key, sizeof(jsval_t), wm_entry); 11811 + if (wm_entry) { 11812 + wm_entry->value = value; 11813 + continue; 11814 + } 11815 + 11816 + wm_entry = (weakmap_entry_t *)ANT_GC_MALLOC(sizeof(weakmap_entry_t)); 11817 + if (!wm_entry) return js_mkerr(js, "out of memory"); 11818 + wm_entry->key_obj = key; 11819 + wm_entry->value = value; 11820 + HASH_ADD(hh, *wm_head, key_obj, sizeof(jsval_t), wm_entry); 11821 + } 11822 + 11653 11823 return wm_obj; 11654 11824 } 11655 11825 ··· 11657 11827 jsval_t ws_obj = mkobj(js, 0); 11658 11828 11659 11829 jsval_t ws_proto = get_ctor_proto(js, "WeakSet", 7); 11660 - if (vtype(ws_proto) == T_OBJ) { 11661 - set_proto(js, ws_obj, ws_proto); 11662 - } 11830 + if (vtype(ws_proto) == T_OBJ) set_proto(js, ws_obj, ws_proto); 11663 11831 11664 11832 weakset_entry_t **ws_head = (weakset_entry_t **)ANT_GC_MALLOC(sizeof(weakset_entry_t *)); 11665 11833 if (!ws_head) return js_mkerr(js, "out of memory"); ··· 11668 11836 jsval_t ws_ptr = mkval(T_NUM, (size_t)ws_head); 11669 11837 jsval_t ws_key = js_mkstr(js, "__weakset", 9); 11670 11838 setprop(js, ws_obj, ws_key, ws_ptr); 11839 + 11840 + if (nargs == 0 || vtype(args[0]) != T_ARR) return ws_obj; 11841 + 11842 + jsval_t iterable = args[0]; 11843 + jsoff_t length = arr_length(js, iterable); 11844 + 11845 + for (jsoff_t i = 0; i < length; i++) { 11846 + jsval_t value = arr_get(js, iterable, i); 11847 + 11848 + if (vtype(value) != T_OBJ) return js_mkerr(js, "WeakSet value must be an object"); 11849 + 11850 + weakset_entry_t *entry; 11851 + HASH_FIND(hh, *ws_head, &value, sizeof(jsval_t), entry); 11852 + if (entry) continue; 11853 + 11854 + entry = (weakset_entry_t *)ANT_GC_MALLOC(sizeof(weakset_entry_t)); 11855 + if (!entry) return js_mkerr(js, "out of memory"); 11856 + entry->value_obj = value; 11857 + HASH_ADD(hh, *ws_head, value_obj, sizeof(jsval_t), entry); 11858 + } 11671 11859 11672 11860 return ws_obj; 11673 11861 } ··· 11917 12105 setprop(js, map_proto, js_mkstr(js, "delete", 6), js_mkfun(map_delete)); 11918 12106 setprop(js, map_proto, js_mkstr(js, "clear", 5), js_mkfun(map_clear)); 11919 12107 setprop(js, map_proto, js_mkstr(js, "size", 4), js_mkfun(map_size)); 12108 + setprop(js, map_proto, js_mkstr(js, "@@toStringTag", 13), js_mkstr(js, "Map", 3)); 11920 12109 11921 12110 jsval_t set_proto_obj = js_mkobj(js); 11922 12111 set_proto(js, set_proto_obj, object_proto); ··· 11925 12114 setprop(js, set_proto_obj, js_mkstr(js, "delete", 6), js_mkfun(set_delete)); 11926 12115 setprop(js, set_proto_obj, js_mkstr(js, "clear", 5), js_mkfun(set_clear)); 11927 12116 setprop(js, set_proto_obj, js_mkstr(js, "size", 4), js_mkfun(set_size)); 12117 + setprop(js, set_proto_obj, js_mkstr(js, "@@toStringTag", 13), js_mkstr(js, "Set", 3)); 11928 12118 11929 12119 jsval_t weakmap_proto = js_mkobj(js); 11930 12120 set_proto(js, weakmap_proto, object_proto);
+68 -1
src/modules/fs.c
··· 9 9 #include <unistd.h> 10 10 #include <errno.h> 11 11 #include "modules/fs.h" 12 + #include "ant.h" 12 13 #include "runtime.h" 13 14 14 15 typedef enum { ··· 18 19 FS_OP_MKDIR, 19 20 FS_OP_RMDIR, 20 21 FS_OP_STAT, 21 - FS_OP_READ_BYTES 22 + FS_OP_READ_BYTES, 23 + FS_OP_EXISTS 22 24 } fs_op_type_t; 23 25 24 26 typedef struct fs_request_s { ··· 270 272 271 273 req->completed = 1; 272 274 js_resolve_promise(req->js, req->promise, stat_obj); 275 + remove_pending_request(req); 276 + free_fs_request(req); 277 + } 278 + 279 + static void on_exists_complete(uv_fs_t *uv_req) { 280 + fs_request_t *req = (fs_request_t *)uv_req->data; 281 + jsval_t result = (uv_req->result >= 0) ? js_mktrue() : js_mkfalse(); 282 + 283 + req->completed = 1; 284 + js_resolve_promise(req->js, req->promise, result); 273 285 remove_pending_request(req); 274 286 free_fs_request(req); 275 287 } ··· 800 812 return req->promise; 801 813 } 802 814 815 + static jsval_t builtin_fs_existsSync(struct js *js, jsval_t *args, int nargs) { 816 + if (nargs < 1) return js_mkerr(js, "existsSync() requires a path argument"); 817 + 818 + if (js_type(args[0]) != JS_STR) return js_mkerr(js, "existsSync() path must be a string"); 819 + 820 + size_t path_len; 821 + char *path = js_getstr(js, args[0], &path_len); 822 + if (!path) return js_mkerr(js, "Failed to get path string"); 823 + 824 + char *path_cstr = strndup(path, path_len); 825 + if (!path_cstr) return js_mkerr(js, "Out of memory"); 826 + 827 + struct stat st; 828 + int result = stat(path_cstr, &st); 829 + free(path_cstr); 830 + 831 + return (result == 0) ? js_mktrue() : js_mkfalse(); 832 + } 833 + 834 + static jsval_t builtin_fs_exists(struct js *js, jsval_t *args, int nargs) { 835 + if (nargs < 1) return js_mkerr(js, "exists() requires a path argument"); 836 + 837 + if (js_type(args[0]) != JS_STR) return js_mkerr(js, "exists() path must be a string"); 838 + 839 + size_t path_len; 840 + char *path = js_getstr(js, args[0], &path_len); 841 + if (!path) return js_mkerr(js, "Failed to get path string"); 842 + 843 + ensure_fs_loop(); 844 + 845 + fs_request_t *req = calloc(1, sizeof(fs_request_t)); 846 + if (!req) return js_mkerr(js, "Out of memory"); 847 + 848 + req->js = js; 849 + req->op_type = FS_OP_EXISTS; 850 + req->promise = js_mkpromise(js); 851 + req->path = strndup(path, path_len); 852 + req->uv_req.data = req; 853 + 854 + utarray_push_back(pending_requests, &req); 855 + 856 + int result = uv_fs_stat(fs_loop, &req->uv_req, req->path, on_exists_complete); 857 + 858 + if (result < 0) { 859 + req->completed = 1; 860 + js_resolve_promise(req->js, req->promise, js_mkfalse()); 861 + remove_pending_request(req); 862 + free_fs_request(req); 863 + } 864 + 865 + return req->promise; 866 + } 867 + 803 868 jsval_t fs_library(struct js *js) { 804 869 jsval_t lib = js_mkobj(js); 805 870 ··· 817 882 js_set(js, lib, "rmdirSync", js_mkfun(builtin_fs_rmdirSync)); 818 883 js_set(js, lib, "stat", js_mkfun(builtin_fs_stat)); 819 884 js_set(js, lib, "statSync", js_mkfun(builtin_fs_statSync)); 885 + js_set(js, lib, "exists", js_mkfun(builtin_fs_exists)); 886 + js_set(js, lib, "existsSync", js_mkfun(builtin_fs_existsSync)); 820 887 js_set(js, lib, "@@toStringTag", js_mkstr(js, "fs", 2)); 821 888 822 889 return lib;