@scriptc/cli-darwin-x64 0.2.2 → 0.2.3

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 (153) hide show
  1. package/dist/THIRD_PARTY_NOTICES +1 -1
  2. package/dist/bin/scriptc +0 -0
  3. package/dist/bin/scriptc.json +1 -1
  4. package/dist/lib/declarations/scriptc-node-fallback.d.ts +99 -27
  5. package/dist/lib/llvm/bin/scriptc-llvm-codegen +0 -0
  6. package/dist/lib/llvm/package.json +1 -1
  7. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_array.o +0 -0
  8. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_assert.o +0 -0
  9. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_async.o +0 -0
  10. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_bytes_io.o +0 -0
  11. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_events.o +0 -0
  12. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_exception.o +0 -0
  13. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_file_handle.o +0 -0
  14. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_json.o +0 -0
  15. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_lib.o +0 -0
  16. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_map.o +0 -0
  17. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_node_builtin.o +0 -0
  18. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_regex.o +0 -0
  19. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_symbol.o +0 -0
  20. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_url.o +0 -0
  21. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_url_params.o +0 -0
  22. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_util_compare.o +0 -0
  23. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_util_style.o +0 -0
  24. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_array.o +0 -0
  25. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_assert.o +0 -0
  26. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_async.o +0 -0
  27. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_bytes_io.o +0 -0
  28. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_events.o +0 -0
  29. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_exception.o +0 -0
  30. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_file_handle.o +0 -0
  31. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_island.o +0 -0
  32. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_json.o +0 -0
  33. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_lib.o +0 -0
  34. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_map.o +0 -0
  35. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_node_builtin.o +0 -0
  36. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_regex.o +0 -0
  37. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_symbol.o +0 -0
  38. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_url.o +0 -0
  39. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_url_params.o +0 -0
  40. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_util_compare.o +0 -0
  41. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_util_style.o +0 -0
  42. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_web.o +0 -0
  43. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_array.o +0 -0
  44. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_assert.o +0 -0
  45. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_bytes_io.o +0 -0
  46. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_exception.o +0 -0
  47. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_json.o +0 -0
  48. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_lib.o +0 -0
  49. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_map.o +0 -0
  50. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_node_builtin.o +0 -0
  51. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_regex.o +0 -0
  52. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_symbol.o +0 -0
  53. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_url.o +0 -0
  54. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_url_params.o +0 -0
  55. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_array.o +0 -0
  56. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_assert.o +0 -0
  57. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_bytes_io.o +0 -0
  58. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_exception.o +0 -0
  59. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_json.o +0 -0
  60. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_lib.o +0 -0
  61. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_map.o +0 -0
  62. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_node_builtin.o +0 -0
  63. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_regex.o +0 -0
  64. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_symbol.o +0 -0
  65. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_url.o +0 -0
  66. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_url_params.o +0 -0
  67. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_array.o +0 -0
  68. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_assert.o +0 -0
  69. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_bytes_io.o +0 -0
  70. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_exception.o +0 -0
  71. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_json.o +0 -0
  72. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_lib.o +0 -0
  73. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_map.o +0 -0
  74. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_node_builtin.o +0 -0
  75. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_regex.o +0 -0
  76. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_symbol.o +0 -0
  77. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_url.o +0 -0
  78. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_url_params.o +0 -0
  79. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_array.o +0 -0
  80. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_assert.o +0 -0
  81. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_bytes_io.o +0 -0
  82. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_exception.o +0 -0
  83. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_json.o +0 -0
  84. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_lib.o +0 -0
  85. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_map.o +0 -0
  86. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_node_builtin.o +0 -0
  87. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_regex.o +0 -0
  88. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_symbol.o +0 -0
  89. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_url.o +0 -0
  90. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_url_params.o +0 -0
  91. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_array.o +0 -0
  92. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_assert.o +0 -0
  93. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_async.o +0 -0
  94. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_bytes_io.o +0 -0
  95. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_events.o +0 -0
  96. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_exception.o +0 -0
  97. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_file_handle.o +0 -0
  98. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_json.o +0 -0
  99. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_lib.o +0 -0
  100. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_map.o +0 -0
  101. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_node_builtin.o +0 -0
  102. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_regex.o +0 -0
  103. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_symbol.o +0 -0
  104. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_url.o +0 -0
  105. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_url_params.o +0 -0
  106. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_util_compare.o +0 -0
  107. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_util_style.o +0 -0
  108. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_array.o +0 -0
  109. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_assert.o +0 -0
  110. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_async.o +0 -0
  111. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_bytes_io.o +0 -0
  112. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_events.o +0 -0
  113. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_exception.o +0 -0
  114. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_file_handle.o +0 -0
  115. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_island.o +0 -0
  116. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_json.o +0 -0
  117. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_lib.o +0 -0
  118. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_map.o +0 -0
  119. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_node_builtin.o +0 -0
  120. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_regex.o +0 -0
  121. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_symbol.o +0 -0
  122. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_url.o +0 -0
  123. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_url_params.o +0 -0
  124. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_util_compare.o +0 -0
  125. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_util_style.o +0 -0
  126. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_web.o +0 -0
  127. package/dist/lib/runtime-darwin-x64/package.json +1 -1
  128. package/dist/lib/runtime-darwin-x64/runtime-pack.json +351 -225
  129. package/dist/lib/runtime-sources/package.json +1 -1
  130. package/dist/lib/runtime-sources/src/scr_array.c +5 -0
  131. package/dist/lib/runtime-sources/src/scr_assert.c +26 -0
  132. package/dist/lib/runtime-sources/src/scr_async.c +44 -0
  133. package/dist/lib/runtime-sources/src/scr_bytes_io.c +305 -48
  134. package/dist/lib/runtime-sources/src/scr_events.c +9 -0
  135. package/dist/lib/runtime-sources/src/scr_exception.c +1 -0
  136. package/dist/lib/runtime-sources/src/scr_file_handle.c +11 -0
  137. package/dist/lib/runtime-sources/src/scr_island.c +1001 -417
  138. package/dist/lib/runtime-sources/src/scr_island_manifest.h +11 -7
  139. package/dist/lib/runtime-sources/src/scr_json.c +135 -2
  140. package/dist/lib/runtime-sources/src/scr_lib.c +379 -12
  141. package/dist/lib/runtime-sources/src/scr_map.c +8 -0
  142. package/dist/lib/runtime-sources/src/scr_node_builtin.c +78 -0
  143. package/dist/lib/runtime-sources/src/scr_regex.c +65 -0
  144. package/dist/lib/runtime-sources/src/scr_runtime.h +69 -1
  145. package/dist/lib/runtime-sources/src/scr_symbol.c +5 -0
  146. package/dist/lib/runtime-sources/src/scr_system_errors.h +573 -0
  147. package/dist/lib/runtime-sources/src/scr_url.c +515 -59
  148. package/dist/lib/runtime-sources/src/scr_url_internal.h +6 -0
  149. package/dist/lib/runtime-sources/src/scr_url_params.c +10 -0
  150. package/dist/lib/runtime-sources/src/scr_util_compare.c +222 -0
  151. package/dist/lib/runtime-sources/src/scr_util_style.c +337 -0
  152. package/dist/lib/runtime-sources/src/scr_web.c +720 -238
  153. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scriptc/runtime",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "scriptc native runtime sources and vendored dependencies",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://scriptc.dev",
@@ -796,6 +796,11 @@ static bool scr_arr_read_value(const ScrArr *a, double i, uint64_t *slot) {
796
796
  return state == SCR_ARR_VALUE;
797
797
  }
798
798
 
799
+ void *scr_arr_peek_ref(const ScrArr *a, double i) {
800
+ uint64_t slot;
801
+ return scr_arr_read_value(a, i, &slot) ? scr_slot_to_ptr(slot) : NULL;
802
+ }
803
+
799
804
  double scr_arr_get_number(const ScrArr *a, double i) {
800
805
  /* Keep this hot numeric-only entry self-contained: routing it through
801
806
  * scr_arr_read_value adds an out-of-line call at -O2 on native targets. */
@@ -1481,6 +1481,32 @@ void scr_assert_unwanted_rejection(ScrError *err, ScrStr *msg, bool has_msg) {
1481
1481
  scr_assert_fail_msg(ab_take(&b));
1482
1482
  }
1483
1483
 
1484
+ void scr_assert_unwanted_error(ScrStr *actual_message, bool rejection, ScrStr *msg, bool has_msg) {
1485
+ ScrAssertBuf b = {0};
1486
+ ab_cstr(&b, rejection ? "Got unwanted rejection" : "Got unwanted exception");
1487
+ if (has_msg && msg->len) {
1488
+ ab_cstr(&b, ": ");
1489
+ ab_str(&b, msg);
1490
+ } else ab_char(&b, '.');
1491
+ ab_cstr(&b, "\nActual message: \"");
1492
+ ab_str(&b, actual_message);
1493
+ ab_char(&b, '"');
1494
+ scr_assert_fail_msg(ab_take(&b));
1495
+ }
1496
+
1497
+ /* Node calls validation functions with a fresh object receiver and accepts
1498
+ * only the boolean true. A callback exception remains the pending exception. */
1499
+ bool scr_assert_no_error_predicate(ScrDyn *predicate, ScrDyn *actual) {
1500
+ ScrDyn *receiver = scr_dyn_new_obj();
1501
+ scr_dyn_this_push_dyn(receiver);
1502
+ ScrDyn *result = scr_dyn_call(predicate, &actual, 1, "expected");
1503
+ scr_dyn_this_pop();
1504
+ scr_dyn_release(receiver);
1505
+ bool matched = result && result->kind == SCR_DYN_BOOL && result->v.b;
1506
+ scr_dyn_release(result);
1507
+ return matched;
1508
+ }
1509
+
1484
1510
  /* ── assert.ifError ───────────────────────────────────────────────────
1485
1511
  * Node throws for ANY value but null/undefined (falsy included); the
1486
1512
  * frontend routes null/undefined to a no-op and everything else here.
@@ -1758,6 +1758,50 @@ ScrPromise *scr_fsp_chmod(ScrStr *path, double mode) {
1758
1758
  return scr_promise_settled_void();
1759
1759
  }
1760
1760
 
1761
+ ScrPromise *scr_fsp_utimes(const ScrDyn *path, const ScrDyn *atime, const ScrDyn *mtime) {
1762
+ scr_fs_utimes_checked(path, atime, mtime);
1763
+ return scr_promise_settled_void();
1764
+ }
1765
+
1766
+ ScrPromise *scr_fsp_link(const ScrDyn *existing, const ScrDyn *destination) {
1767
+ scr_fs_link_checked(existing, destination);
1768
+ return scr_promise_settled_void();
1769
+ }
1770
+
1771
+ ScrPromise *scr_fsp_symlink(const ScrDyn *target, const ScrDyn *destination, const ScrDyn *type) {
1772
+ scr_fs_symlink_promise_checked(target, destination, type);
1773
+ return scr_promise_settled_void();
1774
+ }
1775
+
1776
+ ScrPromise *scr_fsp_readlink_dyn(const ScrDyn *path, const ScrDyn *options) {
1777
+ ScrDyn *result = scr_fs_readlink_checked(path, options, true);
1778
+ return scr_promise_settled_ref(result, scr_dyn_retain_v, scr_dyn_release_v, scr_dyn_trace_v);
1779
+ }
1780
+
1781
+ ScrPromise *scr_fsp_statfs(const ScrDyn *path, const ScrDyn *options) {
1782
+ ScrDyn *result = scr_fs_statfs_checked(path, options);
1783
+ return scr_promise_settled_ref(result, scr_dyn_retain_v, scr_dyn_release_v, scr_dyn_trace_v);
1784
+ }
1785
+
1786
+ ScrPromise *scr_fsp_readlink_str(const ScrDyn *path, const ScrDyn *options) {
1787
+ ScrDyn *result = scr_fs_readlink_checked(path, options, true);
1788
+ ScrStr *text = result && result->kind == SCR_DYN_STR ? scr_str_retain(result->v.str) : NULL;
1789
+ scr_dyn_release(result);
1790
+ return scr_promise_settled_str(text);
1791
+ }
1792
+
1793
+ ScrPromise *scr_fsp_readlink_buffer(const ScrDyn *path, const ScrDyn *options) {
1794
+ ScrDyn *result = scr_fs_readlink_checked(path, options, true);
1795
+ ScrBytes *bytes = result ? scr_dyn_bytes_unbox(result) : NULL;
1796
+ scr_dyn_release(result);
1797
+ return scr_promise_settled_ref(bytes, scr_bytes_retain_v, scr_bytes_release_v, NULL);
1798
+ }
1799
+
1800
+ ScrPromise *scr_fsp_lutimes(const ScrDyn *path, const ScrDyn *atime, const ScrDyn *mtime) {
1801
+ scr_fs_lutimes_checked(path, atime, mtime);
1802
+ return scr_promise_settled_void();
1803
+ }
1804
+
1761
1805
  ScrPromise *scr_fsp_readdir(ScrStr *path) {
1762
1806
  ScrArr *names = scr_fs_readdir(path);
1763
1807
  return scr_promise_settled_ref(names, &scr_arr_retain_v, &scr_arr_release_v, NULL);
@@ -279,13 +279,204 @@ static ScrStr *scr_fs_cb_path(const ScrDyn *value, const char *name) {
279
279
  ScrStr *path = value->kind == SCR_DYN_STR ? scr_str_retain(value->v.str)
280
280
  : scr_str_new((const char *)value->v.bytes->data, value->v.bytes->len);
281
281
  if (memchr(path->data, 0, path->len)) {
282
- scr_dyn_arg_value_fail(name, "must be a string, Uint8Array, or URL without null bytes", value);
282
+ scr_fs_invalid_named_path(value, path, name);
283
283
  scr_str_release(path);
284
284
  return NULL;
285
285
  }
286
286
  return path;
287
287
  }
288
288
 
289
+ static ScrStr *scr_fs_checked_path(const ScrDyn *value, const char *name) {
290
+ if (!scr_dyn_native_url_is(value)) return scr_fs_cb_path(value, name);
291
+ ScrStr *path = scr_url_checked_to_path(value);
292
+ if (!path) return NULL;
293
+ ScrDyn input = { .kind = SCR_DYN_STR, .v.str = path };
294
+ ScrStr *checked = scr_fs_cb_path(&input, name);
295
+ scr_str_release(path);
296
+ return checked;
297
+ }
298
+
299
+ static ScrStr *scr_fs_timestamp_path(const ScrDyn *value) {
300
+ return scr_fs_checked_path(value, "path");
301
+ }
302
+
303
+ /* statfs reads options.bigint without validating/coercing the options object.
304
+ * Undefined selects the default; null throws, and only boolean true opts in. */
305
+ static bool scr_fs_statfs_bigint(const ScrDyn *options) {
306
+ if (options->kind == SCR_DYN_UNDEF) return false;
307
+ if (options->kind == SCR_DYN_NULL) {
308
+ static const char message[] = "Cannot read properties of null (reading 'bigint')";
309
+ scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1);
310
+ return false;
311
+ }
312
+ ScrDyn *value = scr_fs_cb_option(options, "bigint");
313
+ bool bigint = value && value->kind == SCR_DYN_BOOL && value->v.b;
314
+ scr_dyn_release(value);
315
+ return bigint;
316
+ }
317
+
318
+ ScrDyn *scr_fs_statfs_checked(const ScrDyn *input, const ScrDyn *options) {
319
+ ScrStr *path = scr_fs_checked_path(input, "path");
320
+ if (!path) return NULL;
321
+ bool bigint = scr_fs_statfs_bigint(options);
322
+ ScrDyn *result = scr_exc_pending() ? NULL : scr_fs_statfs(path, bigint);
323
+ scr_str_release(path);
324
+ return result;
325
+ }
326
+
327
+ /* validateOneOf runs before either path, even on POSIX where the type has
328
+ * no syscall effect. -1 means infer the target's kind on Windows. */
329
+ static int scr_fs_symlink_type(const ScrDyn *type) {
330
+ if (scr_fs_dyn_absent(type)) return -1;
331
+ if (type->kind == SCR_DYN_STR) {
332
+ const ScrStr *s = type->v.str;
333
+ if (s->len == 4 && !memcmp(s->data, "file", 4)) return 0;
334
+ if (s->len == 3 && !memcmp(s->data, "dir", 3)) return 1;
335
+ if (s->len == 8 && !memcmp(s->data, "junction", 8)) return 2;
336
+ }
337
+ scr_dyn_arg_value_fail("type", "must be one of: 'dir', 'file', 'junction', null, undefined", type);
338
+ return -1;
339
+ }
340
+
341
+ void scr_fs_link_checked(const ScrDyn *existing, const ScrDyn *destination) {
342
+ ScrStr *from = scr_fs_checked_path(existing, "existingPath");
343
+ if (!from) return;
344
+ ScrStr *to = scr_fs_checked_path(destination, "newPath");
345
+ if (to) scr_fs_link(from, to);
346
+ scr_str_release(from); scr_str_release(to);
347
+ }
348
+
349
+ static void scr_fs_symlink_checked_core(const ScrDyn *target, const ScrDyn *destination, const ScrDyn *type, bool promise) {
350
+ int kind = scr_fs_symlink_type(type);
351
+ if (scr_exc_pending()) return;
352
+ #ifdef _WIN32
353
+ if (kind == -1) {
354
+ // Sync/promise inference stringifies the raw destination and target before
355
+ // getValidatedPath. Sync stat exposes null-byte errors in the resolved path;
356
+ // the promise form catches inference failures and continues as a file link.
357
+ ScrStr *link = scr_dyn_string_coerce_js(destination);
358
+ ScrStr *source = !scr_exc_pending() ? scr_dyn_string_coerce_js(target) : NULL;
359
+ ScrStr *absolute = NULL;
360
+ if (link && source && !scr_exc_pending()) {
361
+ ScrArr *parts = scr_arr_new_ref(scr_str_retain_v, scr_str_release_v, NULL, 3);
362
+ scr_arr_push_ref(parts, scr_str_retain(link));
363
+ scr_arr_push_ref(parts, scr_str_new("..", 2));
364
+ scr_arr_push_ref(parts, scr_str_retain(source));
365
+ absolute = scr_path_win32_resolve(parts);
366
+ scr_arr_release(parts);
367
+ }
368
+ kind = 0;
369
+ if (absolute && !scr_exc_pending()) {
370
+ ScrDyn input = { .kind = SCR_DYN_STR, .v.str = absolute };
371
+ ScrStr *checked = scr_fs_cb_path(&input, "path");
372
+ if (checked) kind = scr_fs_symlink_infer(checked);
373
+ scr_str_release(checked);
374
+ }
375
+ scr_str_release(link); scr_str_release(source); scr_str_release(absolute);
376
+ if (scr_exc_pending()) {
377
+ if (!promise) return;
378
+ scr_caught_release(scr_exc_take());
379
+ }
380
+ }
381
+ #else
382
+ (void)promise;
383
+ #endif
384
+ ScrStr *from = scr_fs_checked_path(target, "target");
385
+ if (!from) return;
386
+ ScrStr *to = scr_fs_checked_path(destination, "path");
387
+ #ifdef _WIN32
388
+ if (to && kind == 2 && scr_dyn_bytes_is(destination, SCR_BYTES_U8)) {
389
+ scr_dyn_arg_type_fail("paths[0]", "of type string", destination);
390
+ scr_str_release(to); to = NULL;
391
+ }
392
+ #endif
393
+ if (to) scr_fs_symlink(from, to, kind);
394
+ scr_str_release(from); scr_str_release(to);
395
+ }
396
+
397
+ void scr_fs_symlink_checked(const ScrDyn *target, const ScrDyn *destination, const ScrDyn *type) {
398
+ scr_fs_symlink_checked_core(target, destination, type, false);
399
+ }
400
+
401
+ void scr_fs_symlink_promise_checked(const ScrDyn *target, const ScrDyn *destination, const ScrDyn *type) {
402
+ scr_fs_symlink_checked_core(target, destination, type, true);
403
+ }
404
+
405
+ /* getOptions accepts functions as defaults and validates signals without
406
+ * cancelling readlink. Unlike readFile, an aborted signal is ignored. */
407
+ static ScrDyn *scr_fs_readlink_encoding(const ScrDyn *options) {
408
+ if (scr_fs_dyn_absent(options) || options->kind == SCR_DYN_FUNC) return scr_dyn_retain(scr_dyn_undefined());
409
+ if (options->kind != SCR_DYN_STR && options->kind != SCR_DYN_OBJ &&
410
+ options->kind != SCR_DYN_ARR && options->kind != SCR_DYN_TYPED_REF &&
411
+ options->kind != SCR_DYN_HANDLE && options->kind != SCR_DYN_BYTES) {
412
+ scr_dyn_arg_type_fail("options", "one of type string or object", options);
413
+ return NULL;
414
+ }
415
+ ScrDyn *encoding = options->kind == SCR_DYN_STR ? scr_dyn_retain((ScrDyn *)options) : scr_fs_cb_option(options, "encoding");
416
+ if (!encoding) return NULL;
417
+ bool buffer = encoding->kind == SCR_DYN_STR && encoding->v.str->len == 6 && !memcmp(encoding->v.str->data, "buffer", 6);
418
+ if (!buffer && scr_dyn_truthy(encoding) &&
419
+ (encoding->kind != SCR_DYN_STR || !scr_bytes_is_encoding(encoding->v.str))) {
420
+ scr_dyn_arg_value_fail("encoding", "is invalid encoding", encoding);
421
+ scr_dyn_release(encoding);
422
+ return NULL;
423
+ }
424
+ if (options->kind != SCR_DYN_STR) {
425
+ ScrDyn *signal = scr_fs_cb_option(options, "signal");
426
+ if (!signal) { scr_dyn_release(encoding); return NULL; }
427
+ bool valid = signal->kind == SCR_DYN_UNDEF || (signal->kind == SCR_DYN_HANDLE && signal->v.handle.tag == SCR_DYNH_ABORT_SIGNAL);
428
+ if (!valid) scr_dyn_prop_type_fail("options.signal", "an instance of AbortSignal", signal);
429
+ scr_dyn_release(signal);
430
+ if (!valid) { scr_dyn_release(encoding); return NULL; }
431
+ }
432
+ return encoding;
433
+ }
434
+
435
+ static ScrDyn *scr_fs_readlink_result(ScrStr *path, const ScrDyn *encoding) {
436
+ ScrBytes *bytes = scr_fs_readlink_bytes(path);
437
+ if (!bytes) return NULL;
438
+ ScrDyn *result;
439
+ if (encoding->kind == SCR_DYN_STR && encoding->v.str->len == 6 && !memcmp(encoding->v.str->data, "buffer", 6)) {
440
+ result = scr_dyn_new_buffer(bytes);
441
+ }
442
+ else {
443
+ ScrStr *codec = encoding->kind == SCR_DYN_STR && encoding->v.str->len ? scr_str_retain(encoding->v.str) : scr_str_new("utf8", 4);
444
+ ScrStr *text = scr_bytes_to_str(bytes, codec);
445
+ scr_str_release(codec);
446
+ result = text ? scr_dyn_new_str(text) : NULL;
447
+ scr_str_release(text);
448
+ }
449
+ scr_bytes_release(bytes);
450
+ return result;
451
+ }
452
+
453
+ ScrDyn *scr_fs_readlink_checked(const ScrDyn *input, const ScrDyn *options, bool promise) {
454
+ ScrDyn *encoding = scr_fs_readlink_encoding(options);
455
+ if (!encoding) return NULL;
456
+ ScrStr *path = scr_fs_checked_path(input, promise ? "oldPath" : "path");
457
+ ScrDyn *result = path ? scr_fs_readlink_result(path, encoding) : NULL;
458
+ scr_str_release(path); scr_dyn_release(encoding);
459
+ return result;
460
+ }
461
+
462
+ ScrDyn *scr_fs_readlink_dyn(const ScrDyn *path, const ScrDyn *options) {
463
+ return scr_fs_readlink_checked(path, options, false);
464
+ }
465
+
466
+ ScrStr *scr_fs_readlink_str(const ScrDyn *path, const ScrDyn *options) {
467
+ ScrDyn *result = scr_fs_readlink_dyn(path, options);
468
+ ScrStr *text = result && result->kind == SCR_DYN_STR ? scr_str_retain(result->v.str) : NULL;
469
+ scr_dyn_release(result);
470
+ return text;
471
+ }
472
+
473
+ ScrBytes *scr_fs_readlink_buffer(const ScrDyn *path, const ScrDyn *options) {
474
+ ScrDyn *result = scr_fs_readlink_dyn(path, options);
475
+ ScrBytes *bytes = result ? scr_dyn_bytes_unbox(result) : NULL;
476
+ scr_dyn_release(result);
477
+ return bytes;
478
+ }
479
+
289
480
  static ScrDyn *scr_fs_cb_string(ScrStr *text) {
290
481
  if (!text) return NULL;
291
482
  ScrDyn *value = scr_dyn_new_str(text);
@@ -472,6 +663,13 @@ static bool scr_fs_encoding_chk(const ScrDyn *);
472
663
  static bool scr_fs_int_range_chk(const ScrDyn *, const char *, double, double, const char *);
473
664
  static bool scr_fs_mode_chk(const ScrDyn *, const char *);
474
665
 
666
+ static bool scr_fs_callback_times(const ScrDyn *atime, const ScrDyn *mtime, bool descriptor, double *access, double *modified) {
667
+ *access = scr_fs_timestamp_value(atime, descriptor ? "atime" : "time");
668
+ if (scr_exc_pending()) return false;
669
+ *modified = scr_fs_timestamp_value(mtime, descriptor ? "mtime" : "time");
670
+ return !scr_exc_pending();
671
+ }
672
+
475
673
  static ScrArr *scr_fs_cb_vectors(const ScrDyn *buffers) {
476
674
  ScrDyn *snapshot = buffers->kind == SCR_DYN_TYPED_REF && scr_dyn_isl_is_array(buffers)
477
675
  ? scr_dyn_typed_ref_materialize(buffers) : NULL;
@@ -499,21 +697,86 @@ static ScrArr *scr_fs_cb_vectors(const ScrDyn *buffers) {
499
697
 
500
698
  static ScrDyn *scr_fs_cb_invoke(ScrStr *member, ScrDyn *const *args, size_t argc) {
501
699
  const char *op = member->data;
700
+ if (!strcmp(op, "statfs")) {
701
+ const ScrDyn *options = scr_fs_cb_arg(args, argc, 1);
702
+ const ScrDyn *callback = options->kind == SCR_DYN_FUNC ? options : scr_fs_cb_arg(args, argc, 2);
703
+ if (!scr_fs_cb_chk(callback, "cb")) return NULL;
704
+ ScrStr *path = scr_fs_checked_path(scr_fs_cb_arg(args, argc, 0), "path");
705
+ if (!path) return NULL;
706
+ if (options->kind == SCR_DYN_FUNC) options = scr_dyn_undefined();
707
+ bool bigint = scr_fs_statfs_bigint(options);
708
+ /* FSReqCallback captures the first read; binding.statfs reads it again. */
709
+ if (!scr_exc_pending()) (void)scr_fs_statfs_bigint(options);
710
+ if (scr_exc_pending()) { scr_str_release(path); return NULL; }
711
+ ScrDyn *result = scr_fs_statfs(path, bigint);
712
+ scr_str_release(path);
713
+ scr_fs_cb_schedule(callback, result, NULL);
714
+ return scr_dyn_retain(scr_dyn_undefined());
715
+ }
716
+ if (!strcmp(op, "link") || !strcmp(op, "symlink") || !strcmp(op, "readlink")) {
717
+ const ScrDyn *first = scr_fs_cb_arg(args, argc, 0);
718
+ const ScrDyn *second = scr_fs_cb_arg(args, argc, 1);
719
+ bool symbolic = !strcmp(op, "symlink"), reading = !strcmp(op, "readlink");
720
+ const ScrDyn *third = scr_fs_cb_arg(args, argc, 2);
721
+ const ScrDyn *fourth = scr_fs_cb_arg(args, argc, 3);
722
+ const ScrDyn *callback = symbolic && fourth->kind != SCR_DYN_UNDEF ? fourth
723
+ : reading && second->kind == SCR_DYN_FUNC ? second : third;
724
+ int kind = -1;
725
+ if (symbolic && fourth->kind != SCR_DYN_UNDEF) {
726
+ kind = scr_fs_symlink_type(third);
727
+ if (scr_exc_pending()) return NULL;
728
+ } else if (!scr_fs_cb_chk(callback, "cb")) return NULL;
729
+ ScrDyn *encoding = reading ? scr_fs_readlink_encoding(second) : NULL;
730
+ if (reading && !encoding) return NULL;
731
+ ScrStr *from = scr_fs_checked_path(first, symbolic ? "target" : reading ? "path" : "existingPath");
732
+ if (!from) { scr_dyn_release(encoding); return NULL; }
733
+ ScrStr *to = reading ? NULL : scr_fs_checked_path(second, symbolic ? "path" : "newPath");
734
+ if ((!reading && !to) || (symbolic && fourth->kind != SCR_DYN_UNDEF && !scr_fs_cb_chk(callback, "cb"))) {
735
+ scr_str_release(from); scr_str_release(to); scr_dyn_release(encoding); return NULL;
736
+ }
737
+ #ifdef _WIN32
738
+ if (symbolic) {
739
+ if (kind == 2 && scr_dyn_bytes_is(second, SCR_BYTES_U8)) {
740
+ scr_dyn_arg_type_fail("paths[0]", "of type string", second);
741
+ scr_str_release(from); scr_str_release(to); return NULL;
742
+ }
743
+ // Callback inference passes validated paths directly to path.resolve;
744
+ // Buffer paths fail that probe and fall back to a file link.
745
+ if (kind == -1 && (scr_dyn_bytes_is(first, SCR_BYTES_U8) || scr_dyn_bytes_is(second, SCR_BYTES_U8))) kind = 0;
746
+ }
747
+ #endif
748
+ ScrDyn *result = NULL;
749
+ if (reading) result = scr_fs_readlink_result(from, encoding);
750
+ else if (symbolic) scr_fs_symlink(from, to, kind);
751
+ else scr_fs_link(from, to);
752
+ scr_str_release(from); scr_str_release(to); scr_dyn_release(encoding);
753
+ scr_fs_cb_schedule(callback, result, NULL);
754
+ return scr_dyn_retain(scr_dyn_undefined());
755
+ }
502
756
  bool vector = !strcmp(op, "readv") || !strcmp(op, "writev");
757
+ bool timestamp = !strcmp(op, "utimes") || !strcmp(op, "lutimes") || !strcmp(op, "futimes");
758
+ bool timestamp_fd = !strcmp(op, "futimes");
759
+ double access = 0, modified = 0;
760
+ // futimes converts times before checking the callback; path variants do
761
+ // callback and path validation first. Validation failures throw immediately.
762
+ if (timestamp_fd && !scr_fs_callback_times(scr_fs_cb_arg(args, argc, 1), scr_fs_cb_arg(args, argc, 2), true, &access, &modified)) return NULL;
503
763
  bool newControl = !strcmp(op, "fdatasync") || !strcmp(op, "fchmod") || !strcmp(op, "ftruncate");
504
764
  if ((vector || newControl) && !scr_fs_int_range_chk(scr_fs_cb_arg(args, argc, 0), "fd", 0, 2147483647.0, ">= 0 && <= 2147483647")) return NULL;
505
765
  ScrArr *vectors = vector ? scr_fs_cb_vectors(scr_fs_cb_arg(args, argc, 1)) : NULL;
506
766
  if (vector && !vectors) return NULL;
507
767
  bool fileCallback = !strcmp(op, "readFile") || !strcmp(op, "mkdtemp");
508
- const ScrDyn *callback = argc >= (fileCallback ? 2u : 1u) ? args[argc - 1] : scr_dyn_undefined();
509
- if (!scr_fs_cb_chk(callback, fileCallback || vector ? "cb" : "callback")) { scr_arr_release(vectors); return NULL; }
510
- size_t count = argc - 1;
768
+ const ScrDyn *callback = timestamp ? scr_fs_cb_arg(args, argc, 3)
769
+ : argc >= (fileCallback ? 2u : 1u) ? args[argc - 1] : scr_dyn_undefined();
770
+ if (!scr_fs_cb_chk(callback, fileCallback || vector || timestamp ? "cb" : "callback")) { scr_arr_release(vectors); return NULL; }
771
+ size_t count = timestamp ? argc : argc - 1;
511
772
  #define ARG(index) scr_fs_cb_arg(args, count, index)
512
773
  if (!strcmp(op, "readFile") && !scr_fs_encoding_chk(ARG(1))) return NULL;
513
774
  if (!strcmp(op, "read") && count >= 4 && !scr_fs_read_validate(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4))) return NULL;
514
- bool descriptor = vector || newControl || !strcmp(op, "close") || !strcmp(op, "fstat") || !strcmp(op, "fsync") || !strcmp(op, "read") || !strcmp(op, "write");
515
- ScrStr *path = descriptor ? NULL : scr_fs_cb_path(ARG(0), !strcmp(op, "mkdtemp") ? "prefix" : "path");
775
+ bool descriptor = timestamp_fd || vector || newControl || !strcmp(op, "close") || !strcmp(op, "fstat") || !strcmp(op, "fsync") || !strcmp(op, "read") || !strcmp(op, "write");
776
+ ScrStr *path = descriptor ? NULL : timestamp ? scr_fs_timestamp_path(ARG(0)) : scr_fs_cb_path(ARG(0), !strcmp(op, "mkdtemp") ? "prefix" : "path");
516
777
  if (!descriptor && !path) return NULL;
778
+ if (timestamp && !timestamp_fd && !scr_fs_callback_times(ARG(1), ARG(2), false, &access, &modified)) { scr_str_release(path); return NULL; }
779
+ if (timestamp_fd && !scr_fs_int_range_chk(ARG(0), "fd", 0, 2147483647.0, ">= 0 && <= 2147483647")) return NULL;
517
780
  double fd = descriptor ? scr_fs_cb_number(ARG(0), "fd", -1) : -1;
518
781
  if (scr_exc_pending()) { scr_str_release(path); scr_arr_release(vectors); return NULL; }
519
782
  if (newControl && strcmp(op, "fdatasync")) {
@@ -646,11 +909,10 @@ static ScrDyn *scr_fs_cb_invoke(ScrStr *member, ScrDyn *const *args, size_t argc
646
909
  scr_dyn_release(ownedBuffer); scr_str_release(path);
647
910
  return scr_dyn_retain(scr_dyn_undefined());
648
911
  }
649
- } else if (!strcmp(op, "copyFile") || !strcmp(op, "rename") || !strcmp(op, "link") || !strcmp(op, "symlink")) {
912
+ } else if (!strcmp(op, "copyFile") || !strcmp(op, "rename")) {
650
913
  ScrStr *destination = scr_fs_cb_path(ARG(1), "dest");
651
914
  if (destination) {
652
915
  if (!strcmp(op, "rename")) scr_fs_rename(path, destination);
653
- else if (!strcmp(op, "link")) scr_fs_link(path, destination);
654
916
  else if (!strcmp(op, "copyFile")) {
655
917
  double flags = scr_fs_cb_number(ARG(2), "mode", 0);
656
918
  if (flags == 1 && scr_fs_exists(destination)) scr_fs_throw(EEXIST, "copyfile", destination);
@@ -659,13 +921,6 @@ static ScrDyn *scr_fs_cb_invoke(ScrStr *member, ScrDyn *const *args, size_t argc
659
921
  scr_throw_error_msg_code(SCR_ERR_ERROR, message, sizeof message - 1, "SC2020");
660
922
  } else if (!scr_exc_pending()) scr_fs_copyfile(path, destination);
661
923
  }
662
- else {
663
- #if !defined(_WIN32)
664
- if (symlink(path->data, destination->data) < 0) scr_fs_throw(errno, "symlink", path);
665
- #else
666
- scr_fs_throw(ENOSYS, "symlink", path);
667
- #endif
668
- }
669
924
  scr_str_release(destination);
670
925
  }
671
926
  } else if (!strcmp(op, "truncate") || !strcmp(op, "ftruncate")) {
@@ -674,35 +929,9 @@ static ScrDyn *scr_fs_cb_invoke(ScrStr *member, ScrDyn *const *args, size_t argc
674
929
  if (descriptor) scr_fs_ftruncate(fd, length);
675
930
  else if (truncate(path->data, (off_t)length) < 0) scr_fs_throw(errno, "truncate", path);
676
931
  }
677
- } else if (!strcmp(op, "readlink")) {
678
- #ifndef _WIN32
679
- size_t capacity = 256;
680
- char *text = NULL;
681
- for (;;) {
682
- char *grown = realloc(text, capacity);
683
- if (!grown) scr_bytes_io_oom();
684
- text = grown;
685
- ssize_t size = readlink(path->data, text, capacity);
686
- if (size < 0) { scr_fs_throw(errno, "readlink", path); break; }
687
- if ((size_t)size < capacity) { result = scr_fs_cb_string(scr_str_new(text, (size_t)size)); break; }
688
- capacity *= 2;
689
- }
690
- free(text);
691
- #else
692
- scr_fs_throw(ENOSYS, "readlink", path);
693
- #endif
694
- } else if (!strcmp(op, "utimes")) {
695
- double access = scr_fs_to_unix_timestamp(ARG(1)), modified = scr_fs_to_unix_timestamp(ARG(2));
696
- #if !defined(_WIN32)
697
- struct timespec times[2] = {
698
- { (time_t)floor(access), (long)((access - floor(access)) * 1000000000) },
699
- { (time_t)floor(modified), (long)((modified - floor(modified)) * 1000000000) },
700
- };
701
- if (!scr_exc_pending() && utimensat(AT_FDCWD, path->data, times, 0) < 0) scr_fs_throw(errno, "utime", path);
702
- #else
703
- struct utimbuf times = { (time_t)access, (time_t)modified };
704
- if (!scr_exc_pending() && utime(path->data, &times) < 0) scr_fs_throw(errno, "utime", path);
705
- #endif
932
+ } else if (timestamp) {
933
+ if (timestamp_fd) scr_fs_futimes(fd, access, modified);
934
+ else scr_fs_utimes(path, access, modified, !strcmp(op, "lutimes"));
706
935
  } else if (!strcmp(op, "readdir")) {
707
936
  if (scr_fs_cb_refuse_option(ARG(1), "encoding") || scr_fs_cb_option_bool(ARG(1), "withFileTypes") || scr_fs_cb_option_bool(ARG(1), "recursive")) {
708
937
  if (!scr_exc_pending()) { const char *message = "Recursive readdir and Dirent results have no native lowering"; scr_throw_error_msg_code(SCR_ERR_ERROR, message, strlen(message), "SC2020"); }
@@ -929,10 +1158,11 @@ ScrBytes *scr_buffer_new_string_fail(const ScrDyn *got) {
929
1158
  * its time arguments (fs.js's toUnixTimestamp, underscore-exported):
930
1159
  * numeric STRINGS pass ToNumber's loose-equality gate (+time == time —
931
1160
  * whitespace-only strings answer 0), finite numbers pass (negatives
932
- * answer now/1000, Node's "past times" shape), everything else throws
1161
+ * answer now/1000, Node's "past times" shape), Dates supply milliseconds,
1162
+ * and everything else throws
933
1163
  * Node's exact ERR_INVALID_ARG_TYPE. Borrowed; the throw is pending on
934
1164
  * the dummy 0 return. */
935
- double scr_fs_to_unix_timestamp(const ScrDyn *t) {
1165
+ double scr_fs_timestamp_value(const ScrDyn *t, const char *name) {
936
1166
  if (t->kind == SCR_DYN_STR) {
937
1167
  double n = scr_string_to_number(t->v.str);
938
1168
  /* +time == time: NaN fails; every parsed number loosely equals its
@@ -943,14 +1173,41 @@ double scr_fs_to_unix_timestamp(const ScrDyn *t) {
943
1173
  if (t->v.num < 0) {
944
1174
  struct timespec ts;
945
1175
  clock_gettime(CLOCK_REALTIME, &ts);
946
- return ((double)ts.tv_sec * 1000.0 + (double)ts.tv_nsec / 1e6) / 1000.0;
1176
+ return ((double)ts.tv_sec * 1000.0 + (double)(ts.tv_nsec / 1000000)) / 1000.0;
947
1177
  }
948
1178
  return t->v.num;
949
1179
  }
950
- scr_dyn_arg_type_fail("time", "an instance of Date or an Time in seconds", t);
1180
+ if (scr_dyn_native_date_is(t)) return scr_dyn_native_date_value(t) / 1000.0;
1181
+ scr_dyn_arg_type_fail(name, "an instance of Date or an Time in seconds", t);
951
1182
  return 0;
952
1183
  }
953
1184
 
1185
+ double scr_fs_to_unix_timestamp(const ScrDyn *t) {
1186
+ return scr_fs_timestamp_value(t, "time");
1187
+ }
1188
+
1189
+ static void scr_fs_path_times_checked(const ScrDyn *input, const ScrDyn *atime, const ScrDyn *mtime, bool nofollow) {
1190
+ ScrStr *path = scr_fs_timestamp_path(input);
1191
+ if (!path) return;
1192
+ double access, modified;
1193
+ if (scr_fs_callback_times(atime, mtime, false, &access, &modified)) scr_fs_utimes(path, access, modified, nofollow);
1194
+ scr_str_release(path);
1195
+ }
1196
+
1197
+ void scr_fs_utimes_checked(const ScrDyn *path, const ScrDyn *atime, const ScrDyn *mtime) {
1198
+ scr_fs_path_times_checked(path, atime, mtime, false);
1199
+ }
1200
+
1201
+ void scr_fs_lutimes_checked(const ScrDyn *path, const ScrDyn *atime, const ScrDyn *mtime) {
1202
+ scr_fs_path_times_checked(path, atime, mtime, true);
1203
+ }
1204
+
1205
+ void scr_fs_futimes_checked(const ScrDyn *fd, const ScrDyn *atime, const ScrDyn *mtime) {
1206
+ double access, modified;
1207
+ if (!scr_fs_callback_times(atime, mtime, true, &access, &modified)) return;
1208
+ if (scr_fs_int_range_chk(fd, "fd", 0, 2147483647.0, ">= 0 && <= 2147483647")) scr_fs_futimes(fd->v.num, access, modified);
1209
+ }
1210
+
954
1211
  /* ── the fs argument-validation ladders (checked-dynamic lane) ─────────
955
1212
  * Each fs.*Chk libCall replicates its API's Node-order validation over
956
1213
  * dyn values and throws Node's exact typed errors; when every validation
@@ -1062,6 +1062,15 @@ ScrDyn *scr_process_stdio(double fd) {
1062
1062
  return scr_dyn_new_handle(&scr_stdio_streams[fd == 0 ? 0 : fd == 2 ? 2 : 1], SCR_DYNH_STDIO);
1063
1063
  }
1064
1064
 
1065
+ bool scr_dyn_process_stdio_is(const ScrDyn *value) {
1066
+ return value->kind == SCR_DYN_HANDLE && value->v.handle.tag == SCR_DYNH_STDIO;
1067
+ }
1068
+
1069
+ double scr_dyn_process_stdio_fd(const ScrDyn *value, const ScrDynPath *path) {
1070
+ ScrStdio *stream = scr_dyn_handle_unbox(value, SCR_DYNH_STDIO, path, "ProcessStream");
1071
+ return stream ? stream->fd : 0;
1072
+ }
1073
+
1065
1074
  /* ── the process 'exit' event ─────────────────────────────────────────
1066
1075
  * Listeners run SYNCHRONOUSLY when the process exits: at the end of a
1067
1076
  * normal run (the unit's atexit, registered by scr_events_install after
@@ -273,6 +273,7 @@ ScrStr *scr_caught_to_string(const ScrCaught *c) {
273
273
  /* fall through */
274
274
  case SCR_EXC_REF:
275
275
  case SCR_EXC_PRIMITIVE_REF:
276
+ if (c->retain_fn == scr_dyn_retain_v) return scr_dyn_string_constructor(c->payload);
276
277
  /* Object.prototype.toString — exact for thrown records and class
277
278
  * instances without a toString override; thrown arrays/closures/union
278
279
  * boxes print this too where Node would vary (SEMANTICS.md). */
@@ -497,6 +497,17 @@ ScrPromise *scr_file_handle_chmod_promise(ScrFileHandle *h, double mode) {
497
497
  return scr_promise_settled_void();
498
498
  }
499
499
 
500
+ ScrPromise *scr_file_handle_utimes_promise(ScrFileHandle *h, const ScrDyn *atime, const ScrDyn *mtime) {
501
+ if (scr_file_handle_require_open(h)) {
502
+ double access = scr_fs_timestamp_value(atime, "atime");
503
+ if (!scr_exc_pending()) {
504
+ double modified = scr_fs_timestamp_value(mtime, "mtime");
505
+ if (!scr_exc_pending()) scr_fs_futimes((double)h->fd, access, modified);
506
+ }
507
+ }
508
+ return scr_promise_settled_void();
509
+ }
510
+
500
511
  double scr_file_handle_readv(ScrFileHandle *h, ScrArr *buffers, double position) {
501
512
  if (!scr_file_handle_require_open(h)) return 0;
502
513
  return scr_fs_readv_sync((double)h->fd, buffers, position);