@scriptc/runtime 0.0.9 → 0.0.11

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.
package/src/scr_lib.c CHANGED
@@ -1347,7 +1347,27 @@ static double scr_net_autosel_timeout_ms = 250;
1347
1347
 
1348
1348
  double scr_net_get_autosel_timeout(void) { return scr_net_autosel_timeout_ms; }
1349
1349
 
1350
- void scr_net_set_autosel_timeout(double ms) { scr_net_autosel_timeout_ms = ms; }
1350
+ /* Node's setDefaultAutoSelectFamilyAttemptTimeout: validateInt32(value,
1351
+ * 'value', 1), then the sub-10ms floor (Node clamps small budgets to
1352
+ * 10ms). Throws ERR_OUT_OF_RANGE catchably. */
1353
+ void scr_net_set_autosel_timeout(double ms) {
1354
+ char recv[48], msg[160];
1355
+ if (!(isfinite(ms) && trunc(ms) == ms)) {
1356
+ scr_num_received(ms, recv);
1357
+ int len = snprintf(msg, sizeof msg,
1358
+ "The value of \"value\" is out of range. It must be an integer. Received %s", recv);
1359
+ scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)len, "ERR_OUT_OF_RANGE");
1360
+ return;
1361
+ }
1362
+ if (ms < 1 || ms > 2147483647.0) {
1363
+ scr_num_received(ms, recv);
1364
+ int len = snprintf(msg, sizeof msg,
1365
+ "The value of \"value\" is out of range. It must be >= 1 && <= 2147483647. Received %s", recv);
1366
+ scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)len, "ERR_OUT_OF_RANGE");
1367
+ return;
1368
+ }
1369
+ scr_net_autosel_timeout_ms = ms < 10 ? 10 : ms;
1370
+ }
1351
1371
 
1352
1372
  /* ── fs error formatting ─────────────────────────────────────────────
1353
1373
  * Node's fs errors read "<ERRNO>: <text>, <syscall> '<path>'"
package/src/scr_library.c CHANGED
@@ -251,6 +251,21 @@ ScrBytes *scr_library_bytes_in(const uint8_t *p, size_t len, const char *trap_ms
251
251
  return b;
252
252
  }
253
253
 
254
+ double scr_library_i64_in(int64_t v, const char *trap_msg) {
255
+ /* The inbound declared-integer edge (ask 4): only |v| <= 2^53-1 rides
256
+ * f64 exactly; past it, (double)v silently rounds — a coercion the
257
+ * author never wrote, so the wrapper funnels the host-contract
258
+ * violation instead (same story as an impossible bytes length; the
259
+ * message is the compiler-assembled structured trap-teaching form). */
260
+ if (v > 9007199254740991LL || v < -9007199254740991LL) scr_trap(trap_msg);
261
+ return (double)v;
262
+ }
263
+
264
+ double scr_library_u64_in(uint64_t v, const char *trap_msg) {
265
+ if (v > 9007199254740991ULL) scr_trap(trap_msg);
266
+ return (double)v;
267
+ }
268
+
254
269
  void scr_library_str_out(ScrStr *s, const uint8_t **out, size_t *out_len) {
255
270
  scr_library_arena_keep(s, true);
256
271
  *out = (const uint8_t *)s->data; /* NUL-terminated after len (ScrStr layout) */
package/src/scr_net.c CHANGED
@@ -92,6 +92,7 @@
92
92
  #include "scr_runtime.h"
93
93
 
94
94
  #include <errno.h>
95
+ #include <math.h>
95
96
  #include <fcntl.h>
96
97
  #include <stdio.h>
97
98
  #include <stdlib.h>
@@ -1601,6 +1602,117 @@ ScrNetSocket *scr_net_connect(double port, ScrStr *host /*borrowed, nullable*/,
1601
1602
  return s;
1602
1603
  }
1603
1604
 
1605
+ /* ── the connect option-bag validation ladders (checked-dynamic lane) ──
1606
+ * Node-order validation over DOM option values with Node's exact typed
1607
+ * errors; the honest tail (connect for the validated forms, the
1608
+ * compiler-rendered fence for bags with unmodeled keys) runs only after
1609
+ * every validation passes. */
1610
+
1611
+ static bool scr_net_attempt_timeout_chk(const ScrDyn *t, const char *name) {
1612
+ if (t->kind != SCR_DYN_NUM) {
1613
+ scr_dyn_prop_type_fail(name, "of type number", t);
1614
+ return false;
1615
+ }
1616
+ char recv[48], msg[192];
1617
+ if (!(isfinite(t->v.num) && trunc(t->v.num) == t->v.num)) {
1618
+ scr_num_received(t->v.num, recv);
1619
+ int len = snprintf(msg, sizeof msg,
1620
+ "The value of \"%s\" is out of range. It must be an integer. Received %s",
1621
+ name, recv);
1622
+ scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)len, "ERR_OUT_OF_RANGE");
1623
+ return false;
1624
+ }
1625
+ if (t->v.num < 1 || t->v.num > 2147483647.0) {
1626
+ scr_num_received(t->v.num, recv);
1627
+ int len = snprintf(msg, sizeof msg,
1628
+ "The value of \"%s\" is out of range. It must be >= 1 && <= 2147483647. Received %s",
1629
+ name, recv);
1630
+ scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)len, "ERR_OUT_OF_RANGE");
1631
+ return false;
1632
+ }
1633
+ return true;
1634
+ }
1635
+
1636
+ /* connect({ ..., autoSelectFamilyAttemptTimeout }): the budget validates
1637
+ * (validateInt32 from 1, Node's exact texts) and is then inert — this
1638
+ * slice's single dial has nothing to time, the same simplification the
1639
+ * autoSelectFamily flag already takes. NULL with the throw pending. */
1640
+ ScrNetSocket *scr_net_connect_attempt(double port, ScrStr *host, const ScrDyn *t) {
1641
+ if (!scr_net_attempt_timeout_chk(t, "options.autoSelectFamilyAttemptTimeout")) return NULL;
1642
+ return scr_net_connect(port, host, NULL);
1643
+ }
1644
+
1645
+ static bool scr_net_dyn_truthy(const ScrDyn *v) {
1646
+ switch (v->kind) {
1647
+ case SCR_DYN_UNDEF:
1648
+ case SCR_DYN_NULL: return false;
1649
+ case SCR_DYN_BOOL: return v->v.b;
1650
+ case SCR_DYN_NUM: return v->v.num == v->v.num && v->v.num != 0;
1651
+ case SCR_DYN_STR: return v->v.str->len > 0;
1652
+ default: return true;
1653
+ }
1654
+ }
1655
+
1656
+ /* net.connect/createConnection over a RUNTIME option bag (computed keys
1657
+ * — the invalid-input probes): Node's Socket-constructor order — the
1658
+ * objectMode trio throws ERR_INVALID_ARG_VALUE first, then the port
1659
+ * (validatePort), the host's string contract, autoSelectFamily's boolean
1660
+ * contract, and the attempt budget. A bag that survives everything meets
1661
+ * the compiler-rendered fence: an unmodeled key must refuse loudly, never
1662
+ * silently drop. Always leaves an exception pending. */
1663
+ void scr_net_connect_opts_chk(const ScrDyn *opts, const ScrStr *fence) {
1664
+ if (opts == NULL || opts->kind != SCR_DYN_OBJ) {
1665
+ scr_dyn_arg_type_fail("options", "of type object",
1666
+ opts ? opts : scr_dyn_undefined());
1667
+ return;
1668
+ }
1669
+ static const char *const om[] = { "objectMode", "readableObjectMode", "writableObjectMode" };
1670
+ for (size_t i = 0; i < 3; i++) {
1671
+ const ScrDyn *v = scr_dyn_obj_get(opts, om[i], strlen(om[i]));
1672
+ if (v != NULL && scr_net_dyn_truthy(v)) {
1673
+ char name[48];
1674
+ snprintf(name, sizeof name, "options.%s", om[i]);
1675
+ scr_dyn_arg_value_fail(name, "is not supported", v);
1676
+ return;
1677
+ }
1678
+ }
1679
+ const ScrDyn *port = scr_dyn_obj_get(opts, "port", 4);
1680
+ if (port != NULL && port->kind != SCR_DYN_UNDEF) {
1681
+ bool ok = port->kind == SCR_DYN_NUM && trunc(port->v.num) == port->v.num &&
1682
+ port->v.num >= 0 && port->v.num < 65536;
1683
+ if (!ok && port->kind == SCR_DYN_STR) {
1684
+ ScrStr *ps = scr_str_retain(port->v.str);
1685
+ double n = scr_string_to_number(ps);
1686
+ scr_str_release(ps);
1687
+ ok = n == n && trunc(n) == n && n >= 0 && n < 65536 && port->v.str->len > 0;
1688
+ }
1689
+ if (!ok) {
1690
+ char detail[64], msg[160];
1691
+ const char *d = scr_dyn_specific_type(port, detail, sizeof detail);
1692
+ int len = snprintf(msg, sizeof msg,
1693
+ "options.port should be >= 0 and < 65536. Received %s", d);
1694
+ scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)len, "ERR_SOCKET_BAD_PORT");
1695
+ return;
1696
+ }
1697
+ }
1698
+ const ScrDyn *host = scr_dyn_obj_get(opts, "host", 4);
1699
+ if (host != NULL && host->kind != SCR_DYN_UNDEF && host->kind != SCR_DYN_STR) {
1700
+ scr_dyn_prop_type_fail("options.host", "of type string", host);
1701
+ return;
1702
+ }
1703
+ const ScrDyn *asf = scr_dyn_obj_get(opts, "autoSelectFamily", 16);
1704
+ if (asf != NULL && asf->kind != SCR_DYN_UNDEF && asf->kind != SCR_DYN_BOOL) {
1705
+ scr_dyn_prop_type_fail("options.autoSelectFamily", "of type boolean", asf);
1706
+ return;
1707
+ }
1708
+ const ScrDyn *att = scr_dyn_obj_get(opts, "autoSelectFamilyAttemptTimeout", 30);
1709
+ if (att != NULL && att->kind != SCR_DYN_UNDEF &&
1710
+ !scr_net_attempt_timeout_chk(att, "options.autoSelectFamilyAttemptTimeout")) {
1711
+ return;
1712
+ }
1713
+ scr_throw_lowering_fence(fence);
1714
+ }
1715
+
1604
1716
  /* ── the caller-lookup dial (net.connect with a lookup option) ─────────
1605
1717
  *
1606
1718
  * portless's createLoopbackConnection: connect({ host, port,
package/src/scr_regex.c CHANGED
@@ -484,13 +484,20 @@ ScrArr *scr_regex_match_all_into(ScrStr *s, ScrRegex *re, ScrArr *indices) {
484
484
  * replacements are frontend-fenced): $$, $&, $` and $', $1..$99 (two digits
485
485
  * win when in range — the refined ES2019 rule quickjs implements), with
486
486
  * out-of-range references left literal and unmatched groups substituting
487
- * empty. $<name> stays literal: named capture groups are frontend-fenced,
488
- * so namedCaptures is always undefined here (the spec's literal case).
489
- * The template is scanned byte-wise: every '$' directive is ASCII, and
490
- * UTF-8 continuation bytes can never alias it. */
487
+ * empty. $<name> resolves against the pattern's named capture groups
488
+ * (lre_get_groupnames one NUL-terminated name per capture, "" for
489
+ * unnamed): the first PARTICIPATING capture with that name substitutes
490
+ * (ES2025 duplicates live in distinct alternatives, so at most one
491
+ * participates); a nonexistent or nonparticipating name substitutes
492
+ * empty (the spec's Get→undefined→"" path, Node-exact). When the pattern
493
+ * has NO named groups, namedCaptures is undefined and '$<' stays literal
494
+ * — also Node's unterminated-'$<name' answer either way. The template is
495
+ * scanned byte-wise: every '$' directive is ASCII, and UTF-8 continuation
496
+ * bytes can never alias it. */
491
497
  static void scr_put_substitution(ScrJsonBuf *b, const uint16_t *u, int len,
492
498
  int start, int end, uint8_t **capture,
493
- int capture_count, const ScrStr *rep) {
499
+ int capture_count, const char *groupnames,
500
+ const ScrStr *rep) {
494
501
  const uint8_t *ubase = (const uint8_t *)u;
495
502
  size_t i = 0;
496
503
  while (i < rep->len) {
@@ -534,6 +541,31 @@ static void scr_put_substitution(ScrJsonBuf *b, const uint16_t *u, int len,
534
541
  scr_jb_putc(b, '$');
535
542
  i++;
536
543
  }
544
+ } else if (c1 == '<' && groupnames != NULL) {
545
+ /* $<name>: scan for '>'; absent, the '$' is literal (the rest
546
+ * re-scans verbatim — GetSubstitution's not-found answer). */
547
+ size_t gt = i + 2;
548
+ while (gt < rep->len && rep->data[gt] != '>') gt++;
549
+ if (gt >= rep->len) {
550
+ scr_jb_putc(b, '$');
551
+ i++;
552
+ continue;
553
+ }
554
+ const char *name = rep->data + i + 2;
555
+ size_t name_len = gt - (i + 2);
556
+ const char *p = groupnames; /* capture_count-1 entries: name NUL scope */
557
+ for (int k = 1; k < capture_count; k++) {
558
+ size_t glen = strlen(p);
559
+ if (glen == name_len && memcmp(p, name, name_len) == 0) {
560
+ const uint8_t *cs = capture[2 * k], *ce = capture[2 * k + 1];
561
+ if (cs && ce) {
562
+ scr_jb_put_utf16(b, u, (int)((cs - ubase) >> 1), (int)((ce - ubase) >> 1));
563
+ break; /* at most one duplicate participates */
564
+ }
565
+ }
566
+ p += glen + LRE_GROUP_NAME_TRAILER_LEN;
567
+ }
568
+ i = gt + 1;
537
569
  } else {
538
570
  scr_jb_putc(b, '$');
539
571
  i++;
@@ -550,6 +582,7 @@ static ScrStr *scr_replace_impl(ScrStr *s, ScrRegex *re, ScrStr *rep) {
550
582
  bool global = (re_flags & LRE_FLAG_GLOBAL) != 0;
551
583
  bool unicode = (re_flags & (LRE_FLAG_UNICODE | LRE_FLAG_UNICODE_SETS)) != 0;
552
584
  int capture_count = lre_get_capture_count(bc);
585
+ const char *groupnames = lre_get_groupnames(bc);
553
586
  int len;
554
587
  uint16_t *u = scr_to_utf16(s, &len);
555
588
  uint8_t **capture = scr_capture_alloc(bc);
@@ -562,7 +595,7 @@ static ScrStr *scr_replace_impl(ScrStr *s, ScrRegex *re, ScrStr *rep) {
562
595
  int start = (int)((capture[0] - ubase) >> 1);
563
596
  int end = (int)((capture[1] - ubase) >> 1);
564
597
  scr_jb_put_utf16(&b, u, next, start);
565
- scr_put_substitution(&b, u, len, start, end, capture, capture_count, rep);
598
+ scr_put_substitution(&b, u, len, start, end, capture, capture_count, groupnames, rep);
566
599
  next = end;
567
600
  if (!global) break;
568
601
  pos = end == start ? scr_advance(u, len, end, unicode) : end;