@scriptc/cli-darwin-arm64 0.2.2 → 0.2.4

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 (180) 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-arm64/artifacts/dev/runtime/default/scr_array.o +0 -0
  8. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_assert.o +0 -0
  9. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_async.o +0 -0
  10. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_bigint.o +0 -0
  11. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_bytes_io.o +0 -0
  12. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_cycle.o +0 -0
  13. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_events.o +0 -0
  14. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_exception.o +0 -0
  15. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_file_handle.o +0 -0
  16. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_json.o +0 -0
  17. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_lib.o +0 -0
  18. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_map.o +0 -0
  19. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_node_builtin.o +0 -0
  20. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_regex.o +0 -0
  21. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_string.o +0 -0
  22. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_symbol.o +0 -0
  23. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_url.o +0 -0
  24. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_url_params.o +0 -0
  25. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_util_compare.o +0 -0
  26. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_util_style.o +0 -0
  27. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_array.o +0 -0
  28. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_assert.o +0 -0
  29. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_async.o +0 -0
  30. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_bigint.o +0 -0
  31. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_bytes_io.o +0 -0
  32. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_cycle.o +0 -0
  33. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_events.o +0 -0
  34. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_exception.o +0 -0
  35. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_file_handle.o +0 -0
  36. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_island.o +0 -0
  37. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_json.o +0 -0
  38. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_lib.o +0 -0
  39. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_map.o +0 -0
  40. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_node_builtin.o +0 -0
  41. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_regex.o +0 -0
  42. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_string.o +0 -0
  43. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_symbol.o +0 -0
  44. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_url.o +0 -0
  45. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_url_params.o +0 -0
  46. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_util_compare.o +0 -0
  47. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_util_style.o +0 -0
  48. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_web.o +0 -0
  49. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_array.o +0 -0
  50. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_assert.o +0 -0
  51. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_bigint.o +0 -0
  52. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_bytes_io.o +0 -0
  53. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_cycle.o +0 -0
  54. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_exception.o +0 -0
  55. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_json.o +0 -0
  56. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_lib.o +0 -0
  57. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_map.o +0 -0
  58. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_node_builtin.o +0 -0
  59. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_regex.o +0 -0
  60. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_string.o +0 -0
  61. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_symbol.o +0 -0
  62. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_url.o +0 -0
  63. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_url_params.o +0 -0
  64. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_array.o +0 -0
  65. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_assert.o +0 -0
  66. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_bigint.o +0 -0
  67. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_bytes_io.o +0 -0
  68. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_cycle.o +0 -0
  69. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_exception.o +0 -0
  70. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_json.o +0 -0
  71. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_lib.o +0 -0
  72. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_map.o +0 -0
  73. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_node_builtin.o +0 -0
  74. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_regex.o +0 -0
  75. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_string.o +0 -0
  76. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_symbol.o +0 -0
  77. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_url.o +0 -0
  78. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_url_params.o +0 -0
  79. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_array.o +0 -0
  80. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_assert.o +0 -0
  81. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_bigint.o +0 -0
  82. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_bytes_io.o +0 -0
  83. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_cycle.o +0 -0
  84. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_exception.o +0 -0
  85. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_json.o +0 -0
  86. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_lib.o +0 -0
  87. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_map.o +0 -0
  88. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_node_builtin.o +0 -0
  89. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_regex.o +0 -0
  90. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_string.o +0 -0
  91. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_symbol.o +0 -0
  92. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_url.o +0 -0
  93. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_url_params.o +0 -0
  94. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_array.o +0 -0
  95. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_assert.o +0 -0
  96. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_bigint.o +0 -0
  97. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_bytes_io.o +0 -0
  98. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_cycle.o +0 -0
  99. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_exception.o +0 -0
  100. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_json.o +0 -0
  101. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_lib.o +0 -0
  102. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_map.o +0 -0
  103. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_node_builtin.o +0 -0
  104. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_regex.o +0 -0
  105. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_string.o +0 -0
  106. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_symbol.o +0 -0
  107. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_url.o +0 -0
  108. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_url_params.o +0 -0
  109. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_array.o +0 -0
  110. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_assert.o +0 -0
  111. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_async.o +0 -0
  112. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_bigint.o +0 -0
  113. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_bytes_io.o +0 -0
  114. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_cycle.o +0 -0
  115. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_events.o +0 -0
  116. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_exception.o +0 -0
  117. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_file_handle.o +0 -0
  118. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_json.o +0 -0
  119. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_lib.o +0 -0
  120. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_map.o +0 -0
  121. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_node_builtin.o +0 -0
  122. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_regex.o +0 -0
  123. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_string.o +0 -0
  124. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_symbol.o +0 -0
  125. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_url.o +0 -0
  126. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_url_params.o +0 -0
  127. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_util_compare.o +0 -0
  128. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_util_style.o +0 -0
  129. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_array.o +0 -0
  130. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_assert.o +0 -0
  131. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_async.o +0 -0
  132. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_bigint.o +0 -0
  133. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_bytes_io.o +0 -0
  134. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_cycle.o +0 -0
  135. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_events.o +0 -0
  136. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_exception.o +0 -0
  137. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_file_handle.o +0 -0
  138. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_island.o +0 -0
  139. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_json.o +0 -0
  140. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_lib.o +0 -0
  141. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_map.o +0 -0
  142. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_node_builtin.o +0 -0
  143. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_regex.o +0 -0
  144. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_string.o +0 -0
  145. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_symbol.o +0 -0
  146. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_url.o +0 -0
  147. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_url_params.o +0 -0
  148. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_util_compare.o +0 -0
  149. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_util_style.o +0 -0
  150. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_web.o +0 -0
  151. package/dist/lib/runtime-darwin-arm64/package.json +1 -1
  152. package/dist/lib/runtime-darwin-arm64/runtime-pack.json +399 -273
  153. package/dist/lib/runtime-sources/package.json +1 -1
  154. package/dist/lib/runtime-sources/src/scr_array.c +55 -34
  155. package/dist/lib/runtime-sources/src/scr_assert.c +26 -0
  156. package/dist/lib/runtime-sources/src/scr_async.c +44 -0
  157. package/dist/lib/runtime-sources/src/scr_bigint.c +15 -0
  158. package/dist/lib/runtime-sources/src/scr_bytes_io.c +305 -48
  159. package/dist/lib/runtime-sources/src/scr_cycle.c +84 -60
  160. package/dist/lib/runtime-sources/src/scr_events.c +9 -0
  161. package/dist/lib/runtime-sources/src/scr_exception.c +1 -0
  162. package/dist/lib/runtime-sources/src/scr_file_handle.c +11 -0
  163. package/dist/lib/runtime-sources/src/scr_island.c +1001 -417
  164. package/dist/lib/runtime-sources/src/scr_island_manifest.h +11 -7
  165. package/dist/lib/runtime-sources/src/scr_json.c +150 -13
  166. package/dist/lib/runtime-sources/src/scr_lib.c +424 -28
  167. package/dist/lib/runtime-sources/src/scr_map.c +236 -40
  168. package/dist/lib/runtime-sources/src/scr_node_builtin.c +78 -0
  169. package/dist/lib/runtime-sources/src/scr_regex.c +112 -8
  170. package/dist/lib/runtime-sources/src/scr_runtime.h +108 -14
  171. package/dist/lib/runtime-sources/src/scr_string.c +29 -0
  172. package/dist/lib/runtime-sources/src/scr_symbol.c +5 -0
  173. package/dist/lib/runtime-sources/src/scr_system_errors.h +573 -0
  174. package/dist/lib/runtime-sources/src/scr_url.c +515 -59
  175. package/dist/lib/runtime-sources/src/scr_url_internal.h +6 -0
  176. package/dist/lib/runtime-sources/src/scr_url_params.c +10 -0
  177. package/dist/lib/runtime-sources/src/scr_util_compare.c +222 -0
  178. package/dist/lib/runtime-sources/src/scr_util_style.c +337 -0
  179. package/dist/lib/runtime-sources/src/scr_web.c +720 -238
  180. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- /* The WHATWG URL slice (scr_runtime.h has the API contract): an immutable,
1
+ /* The WHATWG URL slice (scr_runtime.h has the API contract): a mutable,
2
2
  * refcounted URL value parsed once at construction, plus the file-URL
3
3
  * bridge pair (fileURLToPath / pathToFileURL).
4
4
  *
@@ -197,7 +197,7 @@ static void ub_push_encoded(UrlBuf *b, unsigned char c, bool (*needs)(unsigned c
197
197
  /* ── parsing ─────────────────────────────────────────────────────────── */
198
198
 
199
199
  static void scr_url_throw_invalid(void) {
200
- scr_throw_error_msg(SCR_ERR_TYPE, "Invalid URL", 11);
200
+ scr_throw_error_msg_code(SCR_ERR_TYPE, "Invalid URL", 11, "ERR_INVALID_URL");
201
201
  }
202
202
 
203
203
  static bool is_special_scheme(const char *s, size_t len) {
@@ -322,6 +322,47 @@ static ScrStr *parse_rooted_path(const char *raw, size_t len, bool special) {
322
322
  return parse_rooted_path_mode(raw, len, special, false);
323
323
  }
324
324
 
325
+ /* Shared constructor/setter host parsing keeps the existing ASCII-host
326
+ * boundary and canonical IPv6 serialization in both execution tiers. */
327
+ static bool parse_host(const char *hp, size_t host_len, bool special, bool is_file,
328
+ ScrStr **host) {
329
+ bool ipv6 = host_len > 0 && hp[0] == '[';
330
+ size_t ipv6_end = host_len ? host_len - 1 : 0;
331
+ if (ipv6 && (host_len < 3 || hp[ipv6_end] != ']')) return false;
332
+ /* Validate + lowercase (special) the host. Divergence: non-ASCII and
333
+ * %-escapes (IDNA territory) are rejected outright. */
334
+ UrlBuf hb;
335
+ ub_init(&hb);
336
+ if (ipv6) {
337
+ if (!ub_append_ipv6(&hb, hp + 1, ipv6_end - 1)) {
338
+ free(hb.data);
339
+ return false;
340
+ }
341
+ } else {
342
+ for (size_t i = 0; i < host_len; i++) {
343
+ unsigned char c = (unsigned char)hp[i];
344
+ if (c >= 0x80 || c == '%' || c == ':' || c == '[' || c == ']' ||
345
+ c <= 0x20 || c == '#' || c == '/' || c == '<' || c == '>' ||
346
+ c == '?' || c == '@' || c == '\\' || c == '^' || c == '|') {
347
+ free(hb.data);
348
+ return false;
349
+ }
350
+ if (special && c >= 'A' && c <= 'Z') {
351
+ c = (unsigned char)(c - 'A' + 'a');
352
+ }
353
+ ub_push(&hb, (char)c);
354
+ }
355
+ }
356
+ /* file: "localhost" normalizes to "" at parse time (Node). */
357
+ if (is_file && hb.len == 9 && memcmp(hb.data, "localhost", 9) == 0) hb.len = 0;
358
+ if (hb.len == 0 && special && !is_file) {
359
+ free(hb.data);
360
+ return false; /* http:// — special non-file needs a host */
361
+ }
362
+ *host = ub_take(&hb);
363
+ return true;
364
+ }
365
+
325
366
  /* Parses `authority` (between the slashes and the path/query/fragment):
326
367
  * [userinfo@]host[:port]. Returns false (throws) on invalid input. */
327
368
  static bool parse_authority(const char *raw, size_t len, bool special, bool is_file,
@@ -376,37 +417,7 @@ static bool parse_authority(const char *raw, size_t len, bool special, bool is_f
376
417
  }
377
418
  host_len = colon >= 0 ? (size_t)colon : hp_len;
378
419
  }
379
- /* Validate + lowercase (special) the host. Divergence: non-ASCII and
380
- * %-escapes (IDNA territory) are rejected outright. */
381
- UrlBuf hb;
382
- ub_init(&hb);
383
- if (ipv6) {
384
- if (!ub_append_ipv6(&hb, hp + 1, ipv6_end - 1)) {
385
- free(hb.data);
386
- return false;
387
- }
388
- } else {
389
- for (size_t i = 0; i < host_len; i++) {
390
- unsigned char c = (unsigned char)hp[i];
391
- if (c >= 0x80 || c == '%' || c == ':' || c == '[' || c == ']' ||
392
- c <= 0x20 || c == '#' || c == '/' || c == '<' || c == '>' ||
393
- c == '?' || c == '@' || c == '\\' || c == '^' || c == '|') {
394
- free(hb.data);
395
- return false;
396
- }
397
- if (special && c >= 'A' && c <= 'Z') {
398
- c = (unsigned char)(c - 'A' + 'a');
399
- }
400
- ub_push(&hb, (char)c);
401
- }
402
- }
403
- /* file: "localhost" normalizes to "" at parse time (Node). */
404
- if (is_file && hb.len == 9 && memcmp(hb.data, "localhost", 9) == 0) hb.len = 0;
405
- if (hb.len == 0 && special && !is_file) {
406
- free(hb.data);
407
- return false; /* http:// — special non-file needs a host */
408
- }
409
- *host = ub_take(&hb);
420
+ if (!parse_host(hp, host_len, special, is_file, host)) return false;
410
421
  /* Port: digits only, leading zeros stripped, defaults dropped. */
411
422
  UrlBuf pb;
412
423
  ub_init(&pb);
@@ -645,7 +656,9 @@ ScrUrl *scr_url_new(ScrStr *input) {
645
656
  u->query = query;
646
657
  u->fragment = fragment;
647
658
  u->has_authority = has_authority;
659
+ u->host_path_escape = false;
648
660
  u->sp_cache = NULL;
661
+ u->sp_reload = NULL;
649
662
  return u;
650
663
  }
651
664
 
@@ -797,12 +810,15 @@ ScrStr *scr_url_pathname(ScrUrl *u) { return scr_str_retain(u->path); }
797
810
  * normalized form findRoute-style authority compares want (lowercased
798
811
  * host, no default :443/:80). */
799
812
  ScrStr *scr_url_host(ScrUrl *u) {
800
- if (u->port->len == 0) return scr_str_retain(u->host);
813
+ if (u->port->len == 0 && !u->host_path_escape) return scr_str_retain(u->host);
801
814
  UrlBuf b;
802
815
  ub_init(&b);
803
816
  ub_append(&b, u->host->data, u->host->len);
804
- ub_push(&b, ':');
805
- ub_append(&b, u->port->data, u->port->len);
817
+ if (u->port->len) {
818
+ ub_push(&b, ':');
819
+ ub_append(&b, u->port->data, u->port->len);
820
+ }
821
+ if (u->host_path_escape) ub_append(&b, "/.", 2);
806
822
  return ub_take(&b);
807
823
  }
808
824
 
@@ -878,12 +894,289 @@ ScrStr *scr_url_href(ScrUrl *u) {
878
894
  ub_append(&b, u->port->data, u->port->len);
879
895
  }
880
896
  }
897
+ /* A host-less hierarchical path starting with // must not serialize
898
+ * as an authority (the URL Standard's /. escape). */
899
+ if (u->host_path_escape || (!u->has_authority && u->path->len >= 2 && !memcmp(u->path->data, "//", 2))) ub_append(&b, "/.", 2);
881
900
  ub_append(&b, u->path->data, u->path->len);
882
901
  ub_append(&b, u->query->data, u->query->len);
883
902
  ub_append(&b, u->fragment->data, u->fragment->len);
884
903
  return ub_take(&b);
885
904
  }
886
905
 
906
+ static bool url_opaque(ScrUrl *u) {
907
+ return !u->has_authority && (u->path->len == 0 || u->path->data[0] != '/');
908
+ }
909
+
910
+ static void url_strip_opaque_spaces(ScrUrl *u) {
911
+ if (!url_opaque(u) || u->query->len || u->fragment->len) return;
912
+ size_t end = u->path->len;
913
+ while (end && u->path->data[end - 1] == ' ') end--;
914
+ if (end != u->path->len) {
915
+ ScrStr *path = scr_str_new(u->path->data, end);
916
+ scr_str_release(u->path);
917
+ u->path = path;
918
+ }
919
+ }
920
+
921
+ /* Setter state overrides preserve the other components. Unlike the full
922
+ * parser they strip TAB/LF/CR without trimming surrounding spaces. */
923
+ static void url_copy_fields(ScrUrl *u, ScrUrl *next) {
924
+ scr_str_release(u->scheme); u->scheme = scr_str_retain(next->scheme);
925
+ scr_str_release(u->userinfo); u->userinfo = scr_str_retain(next->userinfo);
926
+ scr_str_release(u->host); u->host = scr_str_retain(next->host);
927
+ scr_str_release(u->port); u->port = scr_str_retain(next->port);
928
+ scr_str_release(u->path); u->path = scr_str_retain(next->path);
929
+ scr_str_release(u->query); u->query = scr_str_retain(next->query);
930
+ scr_str_release(u->fragment); u->fragment = scr_str_retain(next->fragment);
931
+ u->has_authority = next->has_authority;
932
+ u->host_path_escape = next->host_path_escape;
933
+ }
934
+
935
+ static bool url_file(ScrUrl *u) {
936
+ return u->scheme->len == 4 && !memcmp(u->scheme->data, "file", 4);
937
+ }
938
+
939
+ static bool url_cannot_have_credentials_or_port(ScrUrl *u) {
940
+ return !u->host->len || url_file(u) || url_opaque(u);
941
+ }
942
+
943
+ static bool url_set_port(ScrUrl *u, const char *raw, size_t len, bool empty_input) {
944
+ if (url_cannot_have_credentials_or_port(u)) return false;
945
+ if (empty_input) {
946
+ scr_str_release(u->port);
947
+ u->port = scr_str_new("", 0);
948
+ return true;
949
+ }
950
+ if (!len) return true; /* TAB/LF/CR-only input leaves the port unchanged. */
951
+ if (raw[0] < '0' || raw[0] > '9') return false;
952
+ unsigned int number = 0;
953
+ for (size_t i = 0; i < len && raw[i] >= '0' && raw[i] <= '9'; i++) {
954
+ number = number * 10 + (unsigned int)(raw[i] - '0');
955
+ if (number > 65535) return false;
956
+ }
957
+ char digits[6];
958
+ size_t size = (size_t)snprintf(digits, sizeof digits, "%u", number);
959
+ const char *dflt = default_port(u->scheme->data, u->scheme->len);
960
+ scr_str_release(u->port);
961
+ u->port = scr_str_new(digits, dflt && strlen(dflt) == size && !memcmp(dflt, digits, size) ? 0 : size);
962
+ return true;
963
+ }
964
+
965
+ static bool url_set_protocol(ScrUrl *u, const char *raw, size_t len) {
966
+ if (!len) return true;
967
+ if (!((raw[0] >= 'a' && raw[0] <= 'z') || (raw[0] >= 'A' && raw[0] <= 'Z'))) return false;
968
+ size_t end = 1;
969
+ while (end < len && ((raw[end] >= 'a' && raw[end] <= 'z') ||
970
+ (raw[end] >= 'A' && raw[end] <= 'Z') || (raw[end] >= '0' && raw[end] <= '9') ||
971
+ raw[end] == '+' || raw[end] == '-' || raw[end] == '.')) end++;
972
+ if (end < len && raw[end] != ':') return false;
973
+ UrlBuf normalized;
974
+ ub_init(&normalized);
975
+ for (size_t i = 0; i < end; i++) {
976
+ char c = raw[i];
977
+ ub_push(&normalized, c >= 'A' && c <= 'Z' ? (char)(c + 'a' - 'A') : c);
978
+ }
979
+ ScrStr *scheme = ub_take(&normalized);
980
+ bool special = is_special_scheme(scheme->data, scheme->len);
981
+ bool file = scheme->len == 4 && !memcmp(scheme->data, "file", 4);
982
+ if (is_special_scheme(u->scheme->data, u->scheme->len) != special ||
983
+ (file && (u->userinfo->len || u->port->len)) || (url_file(u) && !u->host->len)) {
984
+ scr_str_release(scheme);
985
+ /* Ada reports a successful unchanged URL for non-special targets,
986
+ * so these writes still replace the pre-conversion snapshot in Node. */
987
+ return !special;
988
+ }
989
+ scr_str_release(u->scheme);
990
+ u->scheme = scheme;
991
+ const char *dflt = default_port(scheme->data, scheme->len);
992
+ if (dflt && u->port->len == strlen(dflt) && !memcmp(u->port->data, dflt, u->port->len)) {
993
+ scr_str_release(u->port);
994
+ u->port = scr_str_new("", 0);
995
+ }
996
+ return true;
997
+ }
998
+
999
+ static bool url_set_credentials(ScrUrl *u, ScrStr *value, bool username) {
1000
+ if (url_cannot_have_credentials_or_port(u)) return false;
1001
+ ScrStr *other = username ? scr_url_password(u) : scr_url_username(u);
1002
+ UrlBuf encoded;
1003
+ ub_init(&encoded);
1004
+ for (size_t i = 0; i < value->len; i++) ub_push_encoded(&encoded, (unsigned char)value->data[i], enc_userinfo);
1005
+ ScrStr *part = ub_take(&encoded);
1006
+ ScrStr *user = username ? part : other;
1007
+ ScrStr *pass = username ? other : part;
1008
+ UrlBuf combined;
1009
+ ub_init(&combined);
1010
+ ub_append(&combined, user->data, user->len);
1011
+ if (pass->len) {
1012
+ ub_push(&combined, ':');
1013
+ ub_append(&combined, pass->data, pass->len);
1014
+ }
1015
+ scr_str_release(u->userinfo);
1016
+ u->userinfo = ub_take(&combined);
1017
+ scr_str_release(other);
1018
+ scr_str_release(part);
1019
+ return true;
1020
+ }
1021
+
1022
+ static bool url_set_host(ScrUrl *u, const char *raw, size_t len, bool hostname) {
1023
+ if (url_opaque(u)) return false;
1024
+ bool file = url_file(u);
1025
+ bool special = is_special_scheme(u->scheme->data, u->scheme->len);
1026
+ size_t end = 0;
1027
+ bool bracket = false, colon = false;
1028
+ for (; end < len; end++) {
1029
+ char c = raw[end];
1030
+ if (c == '/' || c == '?' || c == '#' || (special && c == '\\')) break;
1031
+ if (!file && c == ':' && !bracket) { colon = true; break; }
1032
+ if (c == '[') bracket = true;
1033
+ if (c == ']') bracket = false;
1034
+ }
1035
+ if (colon && (hostname || !end)) return false;
1036
+ if (!end && !file && (special || u->userinfo->len || u->port->len)) return false;
1037
+ ScrStr *host = NULL;
1038
+ if (!parse_host(raw, end, special, file, &host)) return false;
1039
+ bool escaped_path = !u->has_authority && u->path->len >= 2 && !memcmp(u->path->data, "//", 2);
1040
+ scr_str_release(u->host);
1041
+ u->host = host;
1042
+ if (host->len || escaped_path) u->has_authority = true;
1043
+ if (colon) {
1044
+ u->host_path_escape = escaped_path;
1045
+ if (end + 1 < len) url_set_port(u, raw + end + 1, len - end - 1, false);
1046
+ }
1047
+ return true;
1048
+ }
1049
+
1050
+ /* The result distinguishes successful state overrides from ignored writes:
1051
+ * only successful writes replace Node's pre-conversion URL snapshot. */
1052
+ static bool url_apply_component(ScrUrl *u, ScrStr *field, ScrStr *value) {
1053
+ if (field->len == 4 && !memcmp(field->data, "href", 4)) {
1054
+ ScrUrl *next = scr_url_new(value);
1055
+ if (!next) return false;
1056
+ url_copy_fields(u, next);
1057
+ scr_url_release(next);
1058
+ if (u->sp_cache) u->sp_reload(u->sp_cache, u->query);
1059
+ return true;
1060
+ }
1061
+ bool pathname = field->len == 8 && !memcmp(field->data, "pathname", 8);
1062
+ bool search = field->len == 6 && !memcmp(field->data, "search", 6);
1063
+ bool hash = field->len == 4 && !memcmp(field->data, "hash", 4);
1064
+ bool protocol = field->len == 8 && !memcmp(field->data, "protocol", 8);
1065
+ bool username = field->len == 8 && !memcmp(field->data, "username", 8);
1066
+ bool password = field->len == 8 && !memcmp(field->data, "password", 8);
1067
+ bool host = field->len == 4 && !memcmp(field->data, "host", 4);
1068
+ bool hostname = field->len == 8 && !memcmp(field->data, "hostname", 8);
1069
+ bool port = field->len == 4 && !memcmp(field->data, "port", 4);
1070
+ if (!pathname && !search && !hash && !protocol && !username && !password && !host && !hostname && !port) {
1071
+ static const char message[] = "Native URL setter has no lowering";
1072
+ scr_throw_error_msg_code(SCR_ERR_ERROR, message, sizeof message - 1, "SC2020");
1073
+ return false;
1074
+ }
1075
+ if (username || password) return url_set_credentials(u, value, username);
1076
+ if (pathname && url_opaque(u)) return false;
1077
+ UrlBuf raw;
1078
+ ub_init(&raw);
1079
+ size_t start = (search || hash) && value->len && value->data[0] == (search ? '?' : '#') ? 1 : 0;
1080
+ for (size_t i = start; i < value->len; i++) {
1081
+ char c = value->data[i];
1082
+ if ((host || hostname) && c == '#') break;
1083
+ if (c != '\t' && c != '\n' && c != '\r') ub_push(&raw, c);
1084
+ }
1085
+ bool special = is_special_scheme(u->scheme->data, u->scheme->len);
1086
+ bool applied = true;
1087
+ if (protocol) applied = url_set_protocol(u, raw.data, raw.len);
1088
+ else if (port) applied = url_set_port(u, raw.data, raw.len, !value->len);
1089
+ else if (host || hostname) applied = url_set_host(u, raw.data, raw.len, hostname);
1090
+ else if (pathname) {
1091
+ bool file = u->scheme->len == 4 && !memcmp(u->scheme->data, "file", 4);
1092
+ ScrStr *path = parse_rooted_path_mode(raw.data, raw.len, special, file);
1093
+ if (!path->len && (special || !u->has_authority)) {
1094
+ scr_str_release(path);
1095
+ path = scr_str_new("/", 1);
1096
+ }
1097
+ scr_str_release(u->path);
1098
+ u->path = path;
1099
+ } else {
1100
+ UrlBuf encoded;
1101
+ ub_init(&encoded);
1102
+ if (value->len) {
1103
+ char prefix = search ? '?' : '#';
1104
+ ub_push(&encoded, prefix);
1105
+ for (size_t i = 0; i < raw.len; i++) {
1106
+ unsigned char c = (unsigned char)raw.data[i];
1107
+ bool needs = search ? enc_query(c, special) : enc_fragment(c);
1108
+ if (needs) {
1109
+ char hex[4];
1110
+ snprintf(hex, sizeof hex, "%%%02X", c);
1111
+ ub_append(&encoded, hex, 3);
1112
+ } else ub_push(&encoded, (char)c);
1113
+ }
1114
+ }
1115
+ ScrStr **slot = search ? &u->query : &u->fragment;
1116
+ scr_str_release(*slot);
1117
+ *slot = ub_take(&encoded);
1118
+ url_strip_opaque_spaces(u);
1119
+ if (search && u->sp_cache) u->sp_reload(u->sp_cache, u->query);
1120
+ }
1121
+ free(raw.data);
1122
+ return applied;
1123
+ }
1124
+
1125
+ bool scr_url_set_component(ScrUrl *u, ScrStr *field, ScrStr *value) {
1126
+ if (field->len == 4 && !memcmp(field->data, "href", 4)) return url_apply_component(u, field, value);
1127
+ /* Node's binding reparses the captured href before applying an override.
1128
+ * This matters after protocol writes to file://localhost and for the
1129
+ * host-to-port serialization escape. Ignored overrides retain the live URL. */
1130
+ ScrStr *href = scr_url_href(u);
1131
+ ScrUrl *next = scr_url_new(href);
1132
+ scr_str_release(href);
1133
+ if (!next) return false;
1134
+ bool applied = url_apply_component(next, field, value);
1135
+ if (applied) {
1136
+ url_copy_fields(u, next);
1137
+ if (field->len == 6 && !memcmp(field->data, "search", 6) && u->sp_cache) u->sp_reload(u->sp_cache, u->query);
1138
+ }
1139
+ scr_url_release(next);
1140
+ return applied;
1141
+ }
1142
+
1143
+ void scr_url_set(ScrUrl *u, ScrStr *field, ScrStr *value) {
1144
+ scr_url_set_component(u, field, value);
1145
+ }
1146
+
1147
+ /* Node snapshots a component setter's URL before invoking user string
1148
+ * conversion. href converts first, and opaque pathname writes are ignored. */
1149
+ void scr_url_set_checked(ScrUrl *u, ScrStr *field, const ScrDyn *value) {
1150
+ bool href = field->len == 4 && !memcmp(field->data, "href", 4);
1151
+ bool ignored = field->len == 8 && !memcmp(field->data, "pathname", 8) && url_opaque(u);
1152
+ ScrUrl *saved = NULL;
1153
+ if (!href && !ignored) {
1154
+ saved = malloc(sizeof *saved);
1155
+ if (!saved) abort();
1156
+ *saved = *u;
1157
+ saved->rc = 1;
1158
+ saved->sp_cache = NULL;
1159
+ saved->sp_reload = NULL;
1160
+ saved->scheme = scr_str_retain(u->scheme);
1161
+ saved->userinfo = scr_str_retain(u->userinfo);
1162
+ saved->host = scr_str_retain(u->host);
1163
+ saved->port = scr_str_retain(u->port);
1164
+ saved->path = scr_str_retain(u->path);
1165
+ saved->query = scr_str_retain(u->query);
1166
+ saved->fragment = scr_str_retain(u->fragment);
1167
+ }
1168
+ ScrStr *text = scr_dyn_string_coerce_js(value);
1169
+ if (text && !ignored) {
1170
+ bool applied = scr_url_set_component(saved ? saved : u, field, text);
1171
+ if (saved && applied && !scr_exc_pending()) {
1172
+ url_copy_fields(u, saved);
1173
+ if (field->len == 6 && !memcmp(field->data, "search", 6) && u->sp_cache) u->sp_reload(u->sp_cache, u->query);
1174
+ }
1175
+ }
1176
+ scr_str_release(text);
1177
+ scr_url_release(saved);
1178
+ }
1179
+
887
1180
  /* ── the file-URL bridge ─────────────────────────────────────────────── */
888
1181
 
889
1182
  static int hex_val(char c) {
@@ -893,15 +1186,36 @@ static int hex_val(char c) {
893
1186
  return -1;
894
1187
  }
895
1188
 
1189
+ static bool url_file_scheme(ScrUrl *u) {
1190
+ if (u->scheme->len == 4 && memcmp(u->scheme->data, "file", 4) == 0) return true;
1191
+ scr_throw_error_msg_code(SCR_ERR_TYPE, "The URL must be of scheme file", 30, "ERR_INVALID_URL_SCHEME");
1192
+ return false;
1193
+ }
1194
+
1195
+ static bool url_file_posix_host(ScrUrl *u) {
1196
+ if (u->host->len == 0) return true;
1197
+ const char *platform =
1198
+ #if defined(__APPLE__)
1199
+ "darwin";
1200
+ #elif defined(__linux__)
1201
+ "linux";
1202
+ #elif defined(_WIN32)
1203
+ "win32";
1204
+ #else
1205
+ "posix";
1206
+ #endif
1207
+ char message[96];
1208
+ int length = snprintf(message, sizeof message, "File URL host must be \"localhost\" or empty on %s", platform);
1209
+ scr_throw_error_msg_code(SCR_ERR_TYPE, message, (size_t)length, "ERR_INVALID_FILE_URL_HOST");
1210
+ return false;
1211
+ }
1212
+
896
1213
  /* fileURLToPath over a parsed URL: Node's checks, Node's messages. The
897
1214
  * posix arm rejects hosts and keeps forward slashes; the win32 arm maps
898
1215
  * hosts to UNC \\host prefixes, flips separators, and demands a drive
899
1216
  * letter otherwise. The public wrappers below select by TARGET. */
900
1217
  static ScrStr *scr_url_to_path_impl(ScrUrl *u, bool win32) {
901
- if (!(u->scheme->len == 4 && memcmp(u->scheme->data, "file", 4) == 0)) {
902
- scr_throw_error_msg_code(SCR_ERR_TYPE, "The URL must be of scheme file", 30, "ERR_INVALID_URL_SCHEME");
903
- return NULL;
904
- }
1218
+ if (!url_file_scheme(u)) return NULL;
905
1219
  const char *p = u->path->data;
906
1220
  size_t len = u->path->len;
907
1221
  if (win32) {
@@ -951,23 +1265,7 @@ static ScrStr *scr_url_to_path_impl(ScrUrl *u, bool win32) {
951
1265
  free(out.data);
952
1266
  return s;
953
1267
  }
954
- if (u->host->len > 0) {
955
- /* Parse-time normalization already emptied "localhost". */
956
- const char *plat =
957
- #if defined(__APPLE__)
958
- "darwin";
959
- #elif defined(__linux__)
960
- "linux";
961
- #elif defined(_WIN32)
962
- "win32";
963
- #else
964
- "posix";
965
- #endif
966
- char msg[96];
967
- int mlen = snprintf(msg, sizeof msg, "File URL host must be \"localhost\" or empty on %s", plat);
968
- scr_throw_error_msg_code(SCR_ERR_TYPE, msg, (size_t)mlen, "ERR_INVALID_FILE_URL_HOST");
969
- return NULL;
970
- }
1268
+ if (!url_file_posix_host(u)) return NULL;
971
1269
  for (size_t i = 0; i < len; i++) {
972
1270
  if (p[i] == '%' && i + 2 < len) {
973
1271
  int hi = hex_val(p[i + 1]);
@@ -1021,7 +1319,9 @@ static ScrUrl *scr_url_new_file(ScrStr *host, ScrStr *encoded_path) {
1021
1319
  u->query = scr_str_new("", 0);
1022
1320
  u->fragment = scr_str_new("", 0);
1023
1321
  u->has_authority = true;
1322
+ u->host_path_escape = false;
1024
1323
  u->sp_cache = NULL;
1324
+ u->sp_reload = NULL;
1025
1325
  return u;
1026
1326
  }
1027
1327
 
@@ -1044,7 +1344,7 @@ static void scr_url_throw_arg_value(const char *reason, ScrStr *value) {
1044
1344
  ub_append(&msg, reason, strlen(reason));
1045
1345
  ub_append(&msg, ". Received ", 11);
1046
1346
  ub_push_inspected(&msg, value->data, value->len);
1047
- scr_throw_error_msg(SCR_ERR_TYPE, msg.data, msg.len);
1347
+ scr_throw_error_msg_code(SCR_ERR_TYPE, msg.data, msg.len, "ERR_INVALID_ARG_VALUE");
1048
1348
  free(msg.data);
1049
1349
  }
1050
1350
 
@@ -1233,10 +1533,21 @@ static ScrDyn *scr_native_url_invoke(void *ptr, ScrDyn *self, const char *method
1233
1533
  return NULL;
1234
1534
  }
1235
1535
 
1536
+ static bool scr_native_url_set(void *ptr, const char *key, size_t len, const ScrDyn *value) {
1537
+ bool modeled = (len == 4 && (!memcmp(key, "href", 4) || !memcmp(key, "hash", 4) || !memcmp(key, "host", 4) || !memcmp(key, "port", 4))) ||
1538
+ (len == 8 && (!memcmp(key, "pathname", 8) || !memcmp(key, "protocol", 8) || !memcmp(key, "username", 8) || !memcmp(key, "password", 8) || !memcmp(key, "hostname", 8))) ||
1539
+ (len == 6 && !memcmp(key, "search", 6));
1540
+ if (!modeled) return false;
1541
+ ScrStr *field = scr_str_new(key, len);
1542
+ scr_url_set_checked(ptr, field, value);
1543
+ scr_str_release(field);
1544
+ return true;
1545
+ }
1546
+
1236
1547
  ScrDyn *scr_dyn_native_url(ScrUrl *value) {
1237
1548
  static const ScrDynHandleOps ops = {
1238
1549
  "URL", &scr_url_retain_v, &scr_url_release_v, &scr_native_url_invoke,
1239
- &scr_native_url_get, NULL, NULL, NULL,
1550
+ &scr_native_url_get, &scr_native_url_set, NULL, NULL,
1240
1551
  };
1241
1552
  scr_dyn_handle_install(SCR_DYNH_URL, &ops);
1242
1553
  return scr_dyn_new_handle(value, SCR_DYNH_URL);
@@ -1425,3 +1736,148 @@ ScrStr *scr_url_checked_to_path(const ScrDyn *value) {
1425
1736
  scr_dyn_arg_type_fail("path", "of type string or an instance of URL", value);
1426
1737
  return NULL;
1427
1738
  }
1739
+
1740
+ /* Property reads keep option accessors/proxies live, including typed
1741
+ * record getter closures and objects held by the dynamic engine. */
1742
+ static ScrDyn *url_property_read(const ScrDyn *object, const char *name) {
1743
+ if (object->kind == SCR_DYN_TYPED_REF) {
1744
+ ScrDyn *view = scr_dyn_typed_ref_materialize(object);
1745
+ if (!view) return NULL;
1746
+ char getter_name[64];
1747
+ snprintf(getter_name, sizeof getter_name, "%%get:%s", name);
1748
+ const ScrDyn *getter = scr_dyn_obj_get(view, getter_name, strlen(getter_name));
1749
+ ScrDyn *value = getter ? scr_dyn_call(getter, NULL, 0, "getter") : url_property_read(view, name);
1750
+ scr_dyn_release(view);
1751
+ return value;
1752
+ }
1753
+ if (object->kind == SCR_DYN_JSVAL || object->kind == SCR_DYN_HANDLE) {
1754
+ ScrStr *key = scr_str_new(name, strlen(name));
1755
+ ScrDyn *value = object->kind == SCR_DYN_JSVAL ? scr_dyn_isl_key_get(object, key) : scr_dyn_handle_key_get(object, key);
1756
+ scr_str_release(key);
1757
+ return value;
1758
+ }
1759
+ return scr_dyn_obj_read(object, name, strlen(name));
1760
+ }
1761
+
1762
+ /* options?.windows is a nullish default followed by JS truthiness. */
1763
+ static bool url_options_windows(const ScrDyn *options, bool *windows) {
1764
+ #ifdef _WIN32
1765
+ *windows = true;
1766
+ #else
1767
+ *windows = false;
1768
+ #endif
1769
+ if (scr_dyn_is_nullish(options)) return true;
1770
+ ScrDyn *value = url_property_read(options, "windows");
1771
+ if (!value || scr_exc_pending()) { scr_dyn_release(value); return false; }
1772
+ if (!scr_dyn_is_nullish(value)) *windows = scr_dyn_truthy(value);
1773
+ scr_dyn_release(value);
1774
+ return true;
1775
+ }
1776
+
1777
+ static ScrUrl *url_checked_file_input(const ScrDyn *value) {
1778
+ if (scr_dyn_native_url_is(value)) return scr_url_retain(value->v.handle.ptr);
1779
+ if (value->kind == SCR_DYN_STR) return scr_url_new(value->v.str);
1780
+ /* Node's isURL checks href/protocol/auth/path structurally. Keep the
1781
+ * short circuit order and getter exceptions before its scheme check. */
1782
+ bool is_url = !scr_dyn_is_nullish(value);
1783
+ const char *keys[] = {"href", "protocol", "auth", "path"};
1784
+ for (size_t i = 0; is_url && i < 4; i++) {
1785
+ ScrDyn *property = url_property_read(value, keys[i]);
1786
+ if (!property || scr_exc_pending()) { scr_dyn_release(property); return NULL; }
1787
+ is_url = i < 2 ? scr_dyn_truthy(property) : property->kind == SCR_DYN_UNDEF;
1788
+ scr_dyn_release(property);
1789
+ }
1790
+ if (!is_url) {
1791
+ scr_dyn_arg_type_fail("path", "of type string or an instance of URL", value);
1792
+ return NULL;
1793
+ }
1794
+ ScrDyn *protocol = url_property_read(value, "protocol");
1795
+ if (!protocol || scr_exc_pending()) { scr_dyn_release(protocol); return NULL; }
1796
+ bool file = protocol->kind == SCR_DYN_STR && protocol->v.str->len == 5 && memcmp(protocol->v.str->data, "file:", 5) == 0;
1797
+ scr_dyn_release(protocol);
1798
+ if (!file) {
1799
+ scr_throw_error_msg_code(SCR_ERR_TYPE, "The URL must be of scheme file", 30, "ERR_INVALID_URL_SCHEME");
1800
+ return NULL;
1801
+ }
1802
+ ScrDyn *host = url_property_read(value, "hostname");
1803
+ if (!host || scr_exc_pending()) { scr_dyn_release(host); return NULL; }
1804
+ ScrDyn *path = url_property_read(value, "pathname");
1805
+ if (!path || scr_exc_pending()) { scr_dyn_release(host); scr_dyn_release(path); return NULL; }
1806
+ if (host->kind != SCR_DYN_STR || path->kind != SCR_DYN_STR) {
1807
+ static const char message[] = "file URL-like objects require string hostname and pathname properties in scriptc";
1808
+ scr_throw_error_msg(SCR_ERR_TYPE, message, sizeof message - 1);
1809
+ scr_dyn_release(host); scr_dyn_release(path);
1810
+ return NULL;
1811
+ }
1812
+ ScrUrl *url = scr_url_new_file(scr_str_retain(host->v.str), scr_str_retain(path->v.str));
1813
+ scr_dyn_release(host); scr_dyn_release(path);
1814
+ return url;
1815
+ }
1816
+
1817
+ ScrStr *scr_url_checked_to_path_options(const ScrDyn *value, const ScrDyn *options) {
1818
+ bool windows;
1819
+ if (!url_options_windows(options, &windows)) return NULL;
1820
+ ScrUrl *url = url_checked_file_input(value);
1821
+ if (!url) return NULL;
1822
+ ScrStr *path = scr_url_to_path_impl(url, windows);
1823
+ scr_url_release(url);
1824
+ return path;
1825
+ }
1826
+
1827
+ ScrUrl *scr_url_checked_from_path(const ScrDyn *value, const ScrDyn *options) {
1828
+ if (value->kind != SCR_DYN_STR) {
1829
+ scr_dyn_arg_type_fail("path", "of type string", value);
1830
+ return NULL;
1831
+ }
1832
+ bool windows;
1833
+ if (!url_options_windows(options, &windows)) return NULL;
1834
+ return scr_url_from_path_impl(value->v.str, windows);
1835
+ }
1836
+
1837
+ ScrBytes *scr_url_checked_to_path_buffer(const ScrDyn *value, const ScrDyn *options) {
1838
+ bool windows;
1839
+ if (!url_options_windows(options, &windows)) return NULL;
1840
+ ScrUrl *url = url_checked_file_input(value);
1841
+ if (!url) return NULL;
1842
+ if (!url_file_scheme(url) || (!windows && !url_file_posix_host(url))) {
1843
+ scr_url_release(url);
1844
+ return NULL;
1845
+ }
1846
+ UrlBuf decoded;
1847
+ ub_init(&decoded);
1848
+ if (windows && url->host->len) {
1849
+ ub_append(&decoded, "\\\\", 2);
1850
+ ub_append(&decoded, url->host->data, url->host->len);
1851
+ }
1852
+ const char *path = url->path->data;
1853
+ for (size_t i = 0; i < url->path->len; i++) {
1854
+ if (path[i] == '%' && i + 2 < url->path->len) {
1855
+ int high = hex_val(path[i + 1]), low = hex_val(path[i + 2]);
1856
+ if (high >= 0 && low >= 0) {
1857
+ ub_push(&decoded, (char)((high << 4) | low));
1858
+ i += 2;
1859
+ continue;
1860
+ }
1861
+ }
1862
+ /* Convert literal separators before decoding: encoded %2F stays '/'.
1863
+ * Invalid escapes and invalid UTF-8 are preserved as raw bytes. */
1864
+ ub_push(&decoded, windows && path[i] == '/' ? '\\' : path[i]);
1865
+ }
1866
+ size_t start = 0;
1867
+ if (windows && url->host->len == 0) {
1868
+ int letter = (decoded.len > 1 ? (unsigned char)decoded.data[1] : 0) | 0x20;
1869
+ if (letter < 'a' || letter > 'z' || decoded.len < 3 || decoded.data[2] != ':') {
1870
+ free(decoded.data);
1871
+ scr_url_release(url);
1872
+ scr_throw_error_msg_code(SCR_ERR_TYPE, "File URL path must be absolute", 30, "ERR_INVALID_FILE_URL_PATH");
1873
+ return NULL;
1874
+ }
1875
+ start = 1;
1876
+ }
1877
+ ScrBytes *result = scr_bytes_new(SCR_BYTES_U8, (double)(decoded.len - start));
1878
+ result->is_buffer = true;
1879
+ if (decoded.len > start) memcpy(result->data, decoded.data + start, decoded.len - start);
1880
+ free(decoded.data);
1881
+ scr_url_release(url);
1882
+ return result;
1883
+ }