eyeling 1.22.14 → 1.22.15

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.
@@ -541,6 +541,19 @@
541
541
  // (Ackermann(4,2) is ~65k bits, so well below this.)
542
542
  const MAX_BIGINT_POW_RESULT_BITS = 2_000_000n;
543
543
 
544
+ function __emptySubst() {
545
+ return Object.create(null);
546
+ }
547
+
548
+ function __cloneSubst(subst) {
549
+ if (!subst) return __emptySubst();
550
+ const out = Object.create(null);
551
+ for (const k in subst) {
552
+ if (Object.prototype.hasOwnProperty.call(subst, k)) out[k] = subst[k];
553
+ }
554
+ return out;
555
+ }
556
+
544
557
  function __useNumericCacheKey(key) {
545
558
  return typeof key === 'string' && key.length <= MAX_NUMERIC_CACHE_KEY_LEN;
546
559
  }
@@ -1746,12 +1759,12 @@
1746
1759
  function evalNumericComparisonBuiltin(g, subst, op) {
1747
1760
  const aInfo = parseNumericForCompareTerm(g.s);
1748
1761
  const bInfo = parseNumericForCompareTerm(g.o);
1749
- if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, op)) return [{ ...subst }];
1762
+ if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, op)) return [__cloneSubst(subst)];
1750
1763
 
1751
1764
  if (g.s instanceof ListTerm && g.s.elems.length === 2) {
1752
1765
  const a2 = parseNumericForCompareTerm(g.s.elems[0]);
1753
1766
  const b2 = parseNumericForCompareTerm(g.s.elems[1]);
1754
- if (a2 && b2 && cmpNumericInfo(a2, b2, op)) return [{ ...subst }];
1767
+ if (a2 && b2 && cmpNumericInfo(a2, b2, op)) return [__cloneSubst(subst)];
1755
1768
  }
1756
1769
  return [];
1757
1770
  }
@@ -1967,12 +1980,12 @@
1967
1980
  if (outDt === XSD_INTEGER_DT && !Number.isInteger(outVal)) outDt = XSD_DECIMAL_DT;
1968
1981
 
1969
1982
  if (g.o instanceof Var) {
1970
- const s2 = { ...subst };
1983
+ const s2 = __cloneSubst(subst);
1971
1984
  s2[g.o.name] = makeNumericOutputLiteral(outVal, outDt);
1972
1985
  return [s2];
1973
1986
  }
1974
- if (g.o instanceof Blank) return [{ ...subst }];
1975
- if (numEqualTerm(g.o, outVal)) return [{ ...subst }];
1987
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
1988
+ if (numEqualTerm(g.o, outVal)) return [__cloneSubst(subst)];
1976
1989
  return [];
1977
1990
  }
1978
1991
 
@@ -1985,12 +1998,12 @@
1985
1998
  if (inDt === XSD_INTEGER_DT && !Number.isInteger(inVal)) inDt = XSD_DECIMAL_DT;
1986
1999
 
1987
2000
  if (g.s instanceof Var) {
1988
- const s2 = { ...subst };
2001
+ const s2 = __cloneSubst(subst);
1989
2002
  s2[g.s.name] = makeNumericOutputLiteral(inVal, inDt);
1990
2003
  return [s2];
1991
2004
  }
1992
- if (g.s instanceof Blank) return [{ ...subst }];
1993
- if (numEqualTerm(g.s, inVal)) return [{ ...subst }];
2005
+ if (g.s instanceof Blank) return [__cloneSubst(subst)];
2006
+ if (numEqualTerm(g.s, inVal)) return [__cloneSubst(subst)];
1994
2007
  return [];
1995
2008
  }
1996
2009
 
@@ -2010,7 +2023,7 @@
2010
2023
 
2011
2024
  function listAppendSplit(parts, resElems, subst) {
2012
2025
  if (!parts.length) {
2013
- if (!resElems.length) return [{ ...subst }];
2026
+ if (!resElems.length) return [__cloneSubst(subst)];
2014
2027
  return [];
2015
2028
  }
2016
2029
  const out = [];
@@ -2338,7 +2351,7 @@
2338
2351
  const lit = hashLiteralTerm(g.s, algo);
2339
2352
  if (!lit) return [];
2340
2353
  if (g.o instanceof Var) {
2341
- const s2 = { ...subst };
2354
+ const s2 = __cloneSubst(subst);
2342
2355
  s2[g.o.name] = lit;
2343
2356
  return [s2];
2344
2357
  }
@@ -2479,7 +2492,7 @@
2479
2492
  const lex = time.utcIsoDateTimeStringFromEpochSeconds(outSecs);
2480
2493
  const lit = internLiteral(`"${lex}"^^<${XSD_NS}dateTime>`);
2481
2494
  if (g.o instanceof Var) {
2482
- const s2 = { ...subst };
2495
+ const s2 = __cloneSubst(subst);
2483
2496
  s2[g.o.name] = lit;
2484
2497
  return [s2];
2485
2498
  }
@@ -2495,7 +2508,7 @@
2495
2508
  const lex = time.utcIsoDateTimeStringFromEpochSeconds(outSecs);
2496
2509
  const lit = internLiteral(`"${lex}"^^<${XSD_NS}dateTime>`);
2497
2510
  if (g.o instanceof Var) {
2498
- const s2 = { ...subst };
2511
+ const s2 = __cloneSubst(subst);
2499
2512
  s2[g.o.name] = lit;
2500
2513
  return [s2];
2501
2514
  }
@@ -2517,17 +2530,17 @@
2517
2530
  }
2518
2531
 
2519
2532
  if (g.o instanceof Var) {
2520
- const s2 = { ...subst };
2533
+ const s2 = __cloneSubst(subst);
2521
2534
  s2[g.o.name] = makeNumericOutputLiteral(total, XSD_INTEGER_DT);
2522
2535
  return [s2];
2523
2536
  }
2524
- if (g.o instanceof Blank) return [{ ...subst }];
2537
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
2525
2538
 
2526
2539
  const oi = parseIntLiteral(g.o);
2527
- if (oi !== null && oi === total) return [{ ...subst }];
2540
+ if (oi !== null && oi === total) return [__cloneSubst(subst)];
2528
2541
 
2529
2542
  // Fallback numeric compare
2530
- if (numEqualTerm(g.o, Number(total))) return [{ ...subst }];
2543
+ if (numEqualTerm(g.o, Number(total))) return [__cloneSubst(subst)];
2531
2544
  return [];
2532
2545
  }
2533
2546
 
@@ -2544,12 +2557,12 @@
2544
2557
  const lit = makeNumericOutputLiteral(total, dtOut);
2545
2558
 
2546
2559
  if (g.o instanceof Var) {
2547
- const s2 = { ...subst };
2560
+ const s2 = __cloneSubst(subst);
2548
2561
  s2[g.o.name] = lit;
2549
2562
  return [s2];
2550
2563
  }
2551
- if (g.o instanceof Blank) return [{ ...subst }];
2552
- if (numEqualTerm(g.o, total)) return [{ ...subst }];
2564
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
2565
+ if (numEqualTerm(g.o, total)) return [__cloneSubst(subst)];
2553
2566
  return [];
2554
2567
  }
2555
2568
 
@@ -2572,15 +2585,15 @@
2572
2585
  }
2573
2586
 
2574
2587
  if (g.o instanceof Var) {
2575
- const s2 = { ...subst };
2588
+ const s2 = __cloneSubst(subst);
2576
2589
  s2[g.o.name] = makeNumericOutputLiteral(prod, XSD_INTEGER_DT);
2577
2590
  return [s2];
2578
2591
  }
2579
- if (g.o instanceof Blank) return [{ ...subst }];
2592
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
2580
2593
 
2581
2594
  const oi = parseIntLiteral(g.o);
2582
- if (oi !== null && oi === prod) return [{ ...subst }];
2583
- if (numEqualTerm(g.o, Number(prod))) return [{ ...subst }];
2595
+ if (oi !== null && oi === prod) return [__cloneSubst(subst)];
2596
+ if (numEqualTerm(g.o, Number(prod))) return [__cloneSubst(subst)];
2584
2597
  return [];
2585
2598
  }
2586
2599
 
@@ -2597,12 +2610,12 @@
2597
2610
  const lit = makeNumericOutputLiteral(prod, dtOut);
2598
2611
 
2599
2612
  if (g.o instanceof Var) {
2600
- const s2 = { ...subst };
2613
+ const s2 = __cloneSubst(subst);
2601
2614
  s2[g.o.name] = lit;
2602
2615
  return [s2];
2603
2616
  }
2604
- if (g.o instanceof Blank) return [{ ...subst }];
2605
- if (numEqualTerm(g.o, prod)) return [{ ...subst }];
2617
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
2618
+ if (numEqualTerm(g.o, prod)) return [__cloneSubst(subst)];
2606
2619
  return [];
2607
2620
  }
2608
2621
 
@@ -2626,7 +2639,7 @@
2626
2639
  const lit = formatDurationLiteralFromSeconds(diffSecs);
2627
2640
 
2628
2641
  if (g.o instanceof Var) {
2629
- const s2 = { ...subst };
2642
+ const s2 = __cloneSubst(subst);
2630
2643
  s2[g.o.name] = lit;
2631
2644
  return [s2];
2632
2645
  }
@@ -2642,7 +2655,7 @@
2642
2655
  const lex = time.utcIsoDateTimeStringFromEpochSeconds(outSecs);
2643
2656
  const lit = internLiteral(`"${lex}"^^<${XSD_NS}dateTime>`);
2644
2657
  if (g.o instanceof Var) {
2645
- const s2 = { ...subst };
2658
+ const s2 = __cloneSubst(subst);
2646
2659
  s2[g.o.name] = lit;
2647
2660
  return [s2];
2648
2661
  }
@@ -2658,7 +2671,7 @@
2658
2671
  const ci = ai - bi;
2659
2672
  const lit = internLiteral(ci.toString());
2660
2673
  if (g.o instanceof Var) {
2661
- const s2 = { ...subst };
2674
+ const s2 = __cloneSubst(subst);
2662
2675
  s2[g.o.name] = lit;
2663
2676
  return [s2];
2664
2677
  }
@@ -2681,19 +2694,19 @@
2681
2694
  const lit = makeNumericOutputLiteral(c, dtOut);
2682
2695
 
2683
2696
  if (g.o instanceof Var) {
2684
- const s2 = { ...subst };
2697
+ const s2 = __cloneSubst(subst);
2685
2698
  s2[g.o.name] = lit;
2686
2699
  return [s2];
2687
2700
  }
2688
- if (g.o instanceof Blank) return [{ ...subst }];
2689
- if (numEqualTerm(g.o, c)) return [{ ...subst }];
2701
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
2702
+ if (numEqualTerm(g.o, c)) return [__cloneSubst(subst)];
2690
2703
  return [];
2691
2704
  }
2692
2705
 
2693
2706
  // Fallback (if you *don’t* have those helpers yet):
2694
2707
  const lit = internLiteral(formatNum(c));
2695
2708
  if (g.o instanceof Var) {
2696
- const s2 = { ...subst };
2709
+ const s2 = __cloneSubst(subst);
2697
2710
  s2[g.o.name] = lit;
2698
2711
  return [s2];
2699
2712
  }
@@ -2720,12 +2733,12 @@
2720
2733
  const lit = makeNumericOutputLiteral(c, dtOut);
2721
2734
 
2722
2735
  if (g.o instanceof Var) {
2723
- const s2 = { ...subst };
2736
+ const s2 = __cloneSubst(subst);
2724
2737
  s2[g.o.name] = lit;
2725
2738
  return [s2];
2726
2739
  }
2727
- if (g.o instanceof Blank) return [{ ...subst }];
2728
- if (numEqualTerm(g.o, c)) return [{ ...subst }];
2740
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
2741
+ if (numEqualTerm(g.o, c)) return [__cloneSubst(subst)];
2729
2742
  return [];
2730
2743
  }
2731
2744
 
@@ -2744,19 +2757,19 @@
2744
2757
  const q = ai / bi; // BigInt division truncates toward zero
2745
2758
  const lit = internLiteral(q.toString());
2746
2759
  if (g.o instanceof Var) {
2747
- const s2 = { ...subst };
2760
+ const s2 = __cloneSubst(subst);
2748
2761
  s2[g.o.name] = lit;
2749
2762
  return [s2];
2750
2763
  }
2751
- if (g.o instanceof Blank) return [{ ...subst }];
2764
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
2752
2765
 
2753
2766
  const oi = parseIntLiteral(g.o);
2754
- if (oi !== null && oi === q) return [{ ...subst }];
2767
+ if (oi !== null && oi === q) return [__cloneSubst(subst)];
2755
2768
 
2756
2769
  // Only do numeric compare when safe enough to convert
2757
2770
  const qNum = Number(q);
2758
2771
  if (Number.isFinite(qNum) && Math.abs(qNum) <= Number.MAX_SAFE_INTEGER) {
2759
- if (numEqualTerm(g.o, qNum)) return [{ ...subst }];
2772
+ if (numEqualTerm(g.o, qNum)) return [__cloneSubst(subst)];
2760
2773
  }
2761
2774
 
2762
2775
  const s2 = unifyTerm(g.o, lit, subst);
@@ -2773,13 +2786,13 @@
2773
2786
  const q = Math.trunc(a / b);
2774
2787
  const lit = internLiteral(String(q));
2775
2788
  if (g.o instanceof Var) {
2776
- const s2 = { ...subst };
2789
+ const s2 = __cloneSubst(subst);
2777
2790
  s2[g.o.name] = lit;
2778
2791
  return [s2];
2779
2792
  }
2780
- if (g.o instanceof Blank) return [{ ...subst }];
2793
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
2781
2794
 
2782
- if (numEqualTerm(g.o, q)) return [{ ...subst }];
2795
+ if (numEqualTerm(g.o, q)) return [__cloneSubst(subst)];
2783
2796
 
2784
2797
  const s2 = unifyTerm(g.o, lit, subst);
2785
2798
  return s2 !== null ? [s2] : [];
@@ -2812,14 +2825,14 @@
2812
2825
  const lit = makeNumericOutputLiteral(out, XSD_INTEGER_DT);
2813
2826
 
2814
2827
  if (g.o instanceof Var) {
2815
- const s2 = { ...subst };
2828
+ const s2 = __cloneSubst(subst);
2816
2829
  s2[g.o.name] = lit;
2817
2830
  return [s2];
2818
2831
  }
2819
- if (g.o instanceof Blank) return [{ ...subst }];
2832
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
2820
2833
 
2821
2834
  const oi = parseIntLiteral(g.o);
2822
- if (oi !== null && oi === out) return [{ ...subst }];
2835
+ if (oi !== null && oi === out) return [__cloneSubst(subst)];
2823
2836
 
2824
2837
  const s2 = unifyTerm(g.o, lit, subst);
2825
2838
  return s2 !== null ? [s2] : [];
@@ -2839,12 +2852,12 @@
2839
2852
  const lit = makeNumericOutputLiteral(cVal, dtOut);
2840
2853
 
2841
2854
  if (g.o instanceof Var) {
2842
- const s2 = { ...subst };
2855
+ const s2 = __cloneSubst(subst);
2843
2856
  s2[g.o.name] = lit;
2844
2857
  return [s2];
2845
2858
  }
2846
- if (g.o instanceof Blank) return [{ ...subst }];
2847
- if (numEqualTerm(g.o, cVal)) return [{ ...subst }];
2859
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
2860
+ if (numEqualTerm(g.o, cVal)) return [__cloneSubst(subst)];
2848
2861
  }
2849
2862
 
2850
2863
  // Inverse: solve exponent using logs (Number mode only)
@@ -2857,7 +2870,7 @@
2857
2870
  let dtB = commonNumericDatatype([baseTerm, g.o], expTerm);
2858
2871
  if (dtB === XSD_INTEGER_DT && !Number.isInteger(bVal)) dtB = XSD_DECIMAL_DT;
2859
2872
 
2860
- const s2 = { ...subst };
2873
+ const s2 = __cloneSubst(subst);
2861
2874
  s2[expTerm.name] = makeNumericOutputLiteral(bVal, dtB);
2862
2875
  return [s2];
2863
2876
  }
@@ -2873,15 +2886,15 @@
2873
2886
  const lit = makeNumericOutputLiteral(outVal, XSD_INTEGER_DT);
2874
2887
 
2875
2888
  if (g.o instanceof Var) {
2876
- const s2 = { ...subst };
2889
+ const s2 = __cloneSubst(subst);
2877
2890
  s2[g.o.name] = lit;
2878
2891
  return [s2];
2879
2892
  }
2880
- if (g.o instanceof Blank) return [{ ...subst }];
2893
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
2881
2894
 
2882
2895
  const oi = parseIntLiteral(g.o);
2883
- if (oi !== null && oi === outVal) return [{ ...subst }];
2884
- if (numEqualTerm(g.o, Number(outVal))) return [{ ...subst }];
2896
+ if (oi !== null && oi === outVal) return [__cloneSubst(subst)];
2897
+ if (numEqualTerm(g.o, Number(outVal))) return [__cloneSubst(subst)];
2885
2898
  return [];
2886
2899
  }
2887
2900
 
@@ -2895,12 +2908,12 @@
2895
2908
  if (dtOut === XSD_INTEGER_DT && !Number.isInteger(outVal)) dtOut = XSD_DECIMAL_DT;
2896
2909
 
2897
2910
  if (g.o instanceof Var) {
2898
- const s2 = { ...subst };
2911
+ const s2 = __cloneSubst(subst);
2899
2912
  s2[g.o.name] = makeNumericOutputLiteral(outVal, dtOut);
2900
2913
  return [s2];
2901
2914
  }
2902
- if (g.o instanceof Blank) return [{ ...subst }];
2903
- if (numEqualTerm(g.o, outVal)) return [{ ...subst }];
2915
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
2916
+ if (numEqualTerm(g.o, outVal)) return [__cloneSubst(subst)];
2904
2917
  return [];
2905
2918
  }
2906
2919
 
@@ -2962,14 +2975,14 @@
2962
2975
  const outVal = -si;
2963
2976
  const lit = makeNumericOutputLiteral(outVal, XSD_INTEGER_DT);
2964
2977
  if (g.o instanceof Var) {
2965
- const s2 = { ...subst };
2978
+ const s2 = __cloneSubst(subst);
2966
2979
  s2[g.o.name] = lit;
2967
2980
  return [s2];
2968
2981
  }
2969
- if (g.o instanceof Blank) return [{ ...subst }];
2982
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
2970
2983
  const oi = parseIntLiteral(g.o);
2971
- if (oi !== null && oi === outVal) return [{ ...subst }];
2972
- if (numEqualTerm(g.o, Number(outVal))) return [{ ...subst }];
2984
+ if (oi !== null && oi === outVal) return [__cloneSubst(subst)];
2985
+ if (numEqualTerm(g.o, Number(outVal))) return [__cloneSubst(subst)];
2973
2986
  return [];
2974
2987
  }
2975
2988
 
@@ -2978,14 +2991,14 @@
2978
2991
  const inVal = -oi;
2979
2992
  const lit = makeNumericOutputLiteral(inVal, XSD_INTEGER_DT);
2980
2993
  if (g.s instanceof Var) {
2981
- const s2 = { ...subst };
2994
+ const s2 = __cloneSubst(subst);
2982
2995
  s2[g.s.name] = lit;
2983
2996
  return [s2];
2984
2997
  }
2985
- if (g.s instanceof Blank) return [{ ...subst }];
2998
+ if (g.s instanceof Blank) return [__cloneSubst(subst)];
2986
2999
  const si2 = parseIntLiteral(g.s);
2987
- if (si2 !== null && si2 === inVal) return [{ ...subst }];
2988
- if (numEqualTerm(g.s, Number(inVal))) return [{ ...subst }];
3000
+ if (si2 !== null && si2 === inVal) return [__cloneSubst(subst)];
3001
+ if (numEqualTerm(g.s, Number(inVal))) return [__cloneSubst(subst)];
2989
3002
  return [];
2990
3003
  }
2991
3004
 
@@ -3009,15 +3022,15 @@
3009
3022
  const lit = makeNumericOutputLiteral(r, XSD_INTEGER_DT);
3010
3023
 
3011
3024
  if (g.o instanceof Var) {
3012
- const s2 = { ...subst };
3025
+ const s2 = __cloneSubst(subst);
3013
3026
  s2[g.o.name] = lit;
3014
3027
  return [s2];
3015
3028
  }
3016
- if (g.o instanceof Blank) return [{ ...subst }];
3029
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3017
3030
 
3018
3031
  const oi = parseIntLiteral(g.o);
3019
- if (oi !== null && oi === r) return [{ ...subst }];
3020
- if (numEqualTerm(g.o, Number(r))) return [{ ...subst }];
3032
+ if (oi !== null && oi === r) return [__cloneSubst(subst)];
3033
+ if (numEqualTerm(g.o, Number(r))) return [__cloneSubst(subst)];
3021
3034
  return [];
3022
3035
  }
3023
3036
 
@@ -3032,12 +3045,12 @@
3032
3045
  const lit = makeNumericOutputLiteral(rVal, XSD_INTEGER_DT);
3033
3046
 
3034
3047
  if (g.o instanceof Var) {
3035
- const s2 = { ...subst };
3048
+ const s2 = __cloneSubst(subst);
3036
3049
  s2[g.o.name] = lit;
3037
3050
  return [s2];
3038
3051
  }
3039
- if (g.o instanceof Blank) return [{ ...subst }];
3040
- if (numEqualTerm(g.o, rVal)) return [{ ...subst }];
3052
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3053
+ if (numEqualTerm(g.o, rVal)) return [__cloneSubst(subst)];
3041
3054
  return [];
3042
3055
  }
3043
3056
 
@@ -3052,15 +3065,15 @@
3052
3065
  const lit = makeNumericOutputLiteral(ai, XSD_INTEGER_DT);
3053
3066
 
3054
3067
  if (g.o instanceof Var) {
3055
- const s2 = { ...subst };
3068
+ const s2 = __cloneSubst(subst);
3056
3069
  s2[g.o.name] = lit;
3057
3070
  return [s2];
3058
3071
  }
3059
- if (g.o instanceof Blank) return [{ ...subst }];
3072
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3060
3073
 
3061
3074
  const oi = parseIntLiteral(g.o);
3062
- if (oi !== null && oi === ai) return [{ ...subst }];
3063
- if (numEqualTerm(g.o, Number(ai))) return [{ ...subst }];
3075
+ if (oi !== null && oi === ai) return [__cloneSubst(subst)];
3076
+ if (numEqualTerm(g.o, Number(ai))) return [__cloneSubst(subst)];
3064
3077
 
3065
3078
  const s2 = unifyTerm(g.o, lit, subst);
3066
3079
  return s2 !== null ? [s2] : [];
@@ -3074,14 +3087,14 @@
3074
3087
  const lit = internLiteral(String(rVal)); // integer token
3075
3088
 
3076
3089
  if (g.o instanceof Var) {
3077
- const s2 = { ...subst };
3090
+ const s2 = __cloneSubst(subst);
3078
3091
  s2[g.o.name] = lit;
3079
3092
  return [s2];
3080
3093
  }
3081
- if (g.o instanceof Blank) return [{ ...subst }];
3094
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3082
3095
 
3083
3096
  // Accept typed numeric literals too (e.g., "3"^^xsd:float) if numerically equal.
3084
- if (numEqualTerm(g.o, rVal)) return [{ ...subst }];
3097
+ if (numEqualTerm(g.o, rVal)) return [__cloneSubst(subst)];
3085
3098
 
3086
3099
  // Fallback to strict unification
3087
3100
  const s2 = unifyTerm(g.o, lit, subst);
@@ -3101,16 +3114,16 @@
3101
3114
  const out = internLiteral(String(parts.day));
3102
3115
 
3103
3116
  if (g.o instanceof Var) {
3104
- const s2 = { ...subst };
3117
+ const s2 = __cloneSubst(subst);
3105
3118
  s2[g.o.name] = out;
3106
3119
  return [s2];
3107
3120
  }
3108
- if (g.o instanceof Blank) return [{ ...subst }];
3121
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3109
3122
 
3110
3123
  const oi = parseIntLiteral(g.o);
3111
3124
  if (oi !== null) {
3112
3125
  try {
3113
- if (oi === BigInt(parts.day)) return [{ ...subst }];
3126
+ if (oi === BigInt(parts.day)) return [__cloneSubst(subst)];
3114
3127
  } catch {}
3115
3128
  }
3116
3129
 
@@ -3127,16 +3140,16 @@
3127
3140
  const out = internLiteral(String(parts.hour));
3128
3141
 
3129
3142
  if (g.o instanceof Var) {
3130
- const s2 = { ...subst };
3143
+ const s2 = __cloneSubst(subst);
3131
3144
  s2[g.o.name] = out;
3132
3145
  return [s2];
3133
3146
  }
3134
- if (g.o instanceof Blank) return [{ ...subst }];
3147
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3135
3148
 
3136
3149
  const oi = parseIntLiteral(g.o);
3137
3150
  if (oi !== null) {
3138
3151
  try {
3139
- if (oi === BigInt(parts.hour)) return [{ ...subst }];
3152
+ if (oi === BigInt(parts.hour)) return [__cloneSubst(subst)];
3140
3153
  } catch {}
3141
3154
  }
3142
3155
 
@@ -3153,16 +3166,16 @@
3153
3166
  const out = internLiteral(String(parts.minute));
3154
3167
 
3155
3168
  if (g.o instanceof Var) {
3156
- const s2 = { ...subst };
3169
+ const s2 = __cloneSubst(subst);
3157
3170
  s2[g.o.name] = out;
3158
3171
  return [s2];
3159
3172
  }
3160
- if (g.o instanceof Blank) return [{ ...subst }];
3173
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3161
3174
 
3162
3175
  const oi = parseIntLiteral(g.o);
3163
3176
  if (oi !== null) {
3164
3177
  try {
3165
- if (oi === BigInt(parts.minute)) return [{ ...subst }];
3178
+ if (oi === BigInt(parts.minute)) return [__cloneSubst(subst)];
3166
3179
  } catch {}
3167
3180
  }
3168
3181
 
@@ -3179,16 +3192,16 @@
3179
3192
  const out = internLiteral(String(parts.month));
3180
3193
 
3181
3194
  if (g.o instanceof Var) {
3182
- const s2 = { ...subst };
3195
+ const s2 = __cloneSubst(subst);
3183
3196
  s2[g.o.name] = out;
3184
3197
  return [s2];
3185
3198
  }
3186
- if (g.o instanceof Blank) return [{ ...subst }];
3199
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3187
3200
 
3188
3201
  const oi = parseIntLiteral(g.o);
3189
3202
  if (oi !== null) {
3190
3203
  try {
3191
- if (oi === BigInt(parts.month)) return [{ ...subst }];
3204
+ if (oi === BigInt(parts.month)) return [__cloneSubst(subst)];
3192
3205
  } catch {}
3193
3206
  }
3194
3207
 
@@ -3205,16 +3218,16 @@
3205
3218
  const out = internLiteral(String(parts.second));
3206
3219
 
3207
3220
  if (g.o instanceof Var) {
3208
- const s2 = { ...subst };
3221
+ const s2 = __cloneSubst(subst);
3209
3222
  s2[g.o.name] = out;
3210
3223
  return [s2];
3211
3224
  }
3212
- if (g.o instanceof Blank) return [{ ...subst }];
3225
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3213
3226
 
3214
3227
  const oi = parseIntLiteral(g.o);
3215
3228
  if (oi !== null) {
3216
3229
  try {
3217
- if (oi === BigInt(parts.second)) return [{ ...subst }];
3230
+ if (oi === BigInt(parts.second)) return [__cloneSubst(subst)];
3218
3231
  } catch {}
3219
3232
  }
3220
3233
 
@@ -3232,18 +3245,18 @@
3232
3245
  const out = internLiteral(`"${parts.tz}"`);
3233
3246
 
3234
3247
  if (g.o instanceof Var) {
3235
- const s2 = { ...subst };
3248
+ const s2 = __cloneSubst(subst);
3236
3249
  s2[g.o.name] = out;
3237
3250
  return [s2];
3238
3251
  }
3239
- if (g.o instanceof Blank) return [{ ...subst }];
3252
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3240
3253
 
3241
- if (termsEqual(g.o, out)) return [{ ...subst }];
3254
+ if (termsEqual(g.o, out)) return [__cloneSubst(subst)];
3242
3255
 
3243
3256
  // Also accept explicitly typed xsd:string literals.
3244
3257
  if (g.o instanceof Literal) {
3245
3258
  const [lexO, dtO] = literalParts(g.o.value);
3246
- if (dtO === XSD_NS + 'string' && stripQuotes(lexO) === parts.tz) return [{ ...subst }];
3259
+ if (dtO === XSD_NS + 'string' && stripQuotes(lexO) === parts.tz) return [__cloneSubst(subst)];
3247
3260
  }
3248
3261
  return [];
3249
3262
  }
@@ -3257,16 +3270,16 @@
3257
3270
  const out = internLiteral(String(parts.yearStr));
3258
3271
 
3259
3272
  if (g.o instanceof Var) {
3260
- const s2 = { ...subst };
3273
+ const s2 = __cloneSubst(subst);
3261
3274
  s2[g.o.name] = out;
3262
3275
  return [s2];
3263
3276
  }
3264
- if (g.o instanceof Blank) return [{ ...subst }];
3277
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3265
3278
 
3266
3279
  const oi = parseIntLiteral(g.o);
3267
3280
  if (oi !== null) {
3268
3281
  try {
3269
- if (oi === BigInt(parts.yearStr)) return [{ ...subst }];
3282
+ if (oi === BigInt(parts.yearStr)) return [__cloneSubst(subst)];
3270
3283
  } catch {}
3271
3284
  }
3272
3285
 
@@ -3280,13 +3293,13 @@
3280
3293
  const now = time.getNowLex();
3281
3294
 
3282
3295
  if (g.o instanceof Var) {
3283
- const s2 = { ...subst };
3296
+ const s2 = __cloneSubst(subst);
3284
3297
  s2[g.o.name] = internLiteral(`"${now}"^^<${XSD_NS}dateTime>`);
3285
3298
  return [s2];
3286
3299
  }
3287
3300
  if (g.o instanceof Literal) {
3288
3301
  const [lexO] = literalParts(g.o.value);
3289
- if (stripQuotes(lexO) === now) return [{ ...subst }];
3302
+ if (stripQuotes(lexO) === now) return [__cloneSubst(subst)];
3290
3303
  }
3291
3304
  return [];
3292
3305
  }
@@ -3311,11 +3324,11 @@
3311
3324
  }
3312
3325
  const result = new ListTerm(outElems);
3313
3326
  if (g.o instanceof Var) {
3314
- const s2 = { ...subst };
3327
+ const s2 = __cloneSubst(subst);
3315
3328
  s2[g.o.name] = result;
3316
3329
  return [s2];
3317
3330
  }
3318
- if (termsEqual(g.o, result)) return [{ ...subst }];
3331
+ if (termsEqual(g.o, result)) return [__cloneSubst(subst)];
3319
3332
  return [];
3320
3333
  }
3321
3334
 
@@ -3419,7 +3432,7 @@
3419
3432
  const idxPat2 = applySubstTerm(indexTerm, subst);
3420
3433
  if (isGroundTerm(idxPat2)) {
3421
3434
  if (!termsEqualNoIntDecimal(idxPat2, idxLit)) continue;
3422
- s1 = { ...subst };
3435
+ s1 = __cloneSubst(subst);
3423
3436
  } else {
3424
3437
  s1 = unifyTerm(indexTerm, idxLit, subst);
3425
3438
  if (s1 === null) continue;
@@ -3494,7 +3507,7 @@
3494
3507
 
3495
3508
  const o2 = applySubstTerm(g.o, subst);
3496
3509
  if (isGroundTerm(o2)) {
3497
- return termsEqualNoIntDecimal(o2, nTerm) ? [{ ...subst }] : [];
3510
+ return termsEqualNoIntDecimal(o2, nTerm) ? [__cloneSubst(subst)] : [];
3498
3511
  }
3499
3512
 
3500
3513
  const s2 = unifyTerm(g.o, nTerm, subst);
@@ -3508,7 +3521,7 @@
3508
3521
  for (const el of xs) {
3509
3522
  if (unifyTerm(g.o, el, subst) !== null) return [];
3510
3523
  }
3511
- return [{ ...subst }];
3524
+ return [__cloneSubst(subst)];
3512
3525
  }
3513
3526
 
3514
3527
  // list:reverse
@@ -3694,7 +3707,7 @@
3694
3707
  if (pv === LOG_NS + 'notEqualTo') {
3695
3708
  const s2 = unifyTerm(goal.s, goal.o, subst);
3696
3709
  if (s2 !== null) return [];
3697
- return [{ ...subst }];
3710
+ return [__cloneSubst(subst)];
3698
3711
  }
3699
3712
 
3700
3713
  // log:conjunction
@@ -3743,7 +3756,7 @@
3743
3756
  const outFormula = new GraphTerm(merged);
3744
3757
 
3745
3758
  // Allow blank nodes as a don't-care output (common in builtin schemas).
3746
- if (g.o instanceof Blank) return [{ ...subst }];
3759
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3747
3760
 
3748
3761
  const s2 = unifyTerm(g.o, outFormula, subst);
3749
3762
  return s2 !== null ? [s2] : [];
@@ -3763,11 +3776,11 @@
3763
3776
  if (!(conclusion instanceof GraphTerm)) return [];
3764
3777
 
3765
3778
  if (g.o instanceof Var) {
3766
- const s2 = { ...subst };
3779
+ const s2 = __cloneSubst(subst);
3767
3780
  s2[g.o.name] = conclusion;
3768
3781
  return [s2];
3769
3782
  }
3770
- if (g.o instanceof Blank) return [{ ...subst }];
3783
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3771
3784
 
3772
3785
  const s2 = unifyTerm(g.o, conclusion, subst);
3773
3786
  return s2 !== null ? [s2] : [];
@@ -3787,11 +3800,11 @@
3787
3800
  const lit = internLiteral(`${JSON.stringify(text)}^^<${XSD_NS}string>`);
3788
3801
 
3789
3802
  if (g.o instanceof Var) {
3790
- const s2 = { ...subst };
3803
+ const s2 = __cloneSubst(subst);
3791
3804
  s2[g.o.name] = lit;
3792
3805
  return [s2];
3793
3806
  }
3794
- if (g.o instanceof Blank) return [{ ...subst }];
3807
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3795
3808
 
3796
3809
  const s2 = unifyTerm(g.o, lit, subst);
3797
3810
  return s2 !== null ? [s2] : [];
@@ -3811,7 +3824,7 @@
3811
3824
  // Avoid variable capture between the returned quoted formula and the
3812
3825
  // surrounding proof environment.
3813
3826
  const formulaStd = standardizeTermApart(formula, varGen);
3814
- if (g.o instanceof Blank) return [{ ...subst }];
3827
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3815
3828
 
3816
3829
  const s2 = unifyTerm(g.o, formulaStd, subst);
3817
3830
  return s2 !== null ? [s2] : [];
@@ -3831,7 +3844,7 @@
3831
3844
  // surrounding proof environment.
3832
3845
  if (term instanceof GraphTerm) term = standardizeTermApart(term, varGen);
3833
3846
 
3834
- if (g.o instanceof Blank) return [{ ...subst }];
3847
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3835
3848
 
3836
3849
  const s2 = unifyTerm(g.o, term, subst);
3837
3850
  return s2 !== null ? [s2] : [];
@@ -3857,7 +3870,7 @@
3857
3870
  // surrounding proof environment.
3858
3871
  formula = standardizeTermApart(formula, varGen);
3859
3872
 
3860
- if (g.o instanceof Blank) return [{ ...subst }];
3873
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3861
3874
 
3862
3875
  const s2 = unifyTerm(g.o, formula, subst);
3863
3876
  return s2 !== null ? [s2] : [];
@@ -3876,11 +3889,11 @@
3876
3889
  else ty = internIri(LOG_NS + 'Other');
3877
3890
 
3878
3891
  if (g.o instanceof Var) {
3879
- const s2 = { ...subst };
3892
+ const s2 = __cloneSubst(subst);
3880
3893
  s2[g.o.name] = ty;
3881
3894
  return [s2];
3882
3895
  }
3883
- if (g.o instanceof Blank) return [{ ...subst }];
3896
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3884
3897
 
3885
3898
  const s2 = unifyTerm(g.o, ty, subst);
3886
3899
  return s2 !== null ? [s2] : [];
@@ -3892,7 +3905,7 @@
3892
3905
  if (pv === LOG_NS + 'dtlit') {
3893
3906
  // Fully unbound (both arguments '?'-mode): treat as satisfiable, succeed once.
3894
3907
  // Required by notation3tests "success-fullUnbound-*".
3895
- if (g.s instanceof Var && g.o instanceof Var) return [{ ...subst }];
3908
+ if (g.s instanceof Var && g.o instanceof Var) return [__cloneSubst(subst)];
3896
3909
 
3897
3910
  const results = [];
3898
3911
 
@@ -3945,7 +3958,7 @@
3945
3958
  // true iff $o is a language-tagged literal with string value $s.1 and language tag $s.2
3946
3959
  if (pv === LOG_NS + 'langlit') {
3947
3960
  // Fully unbound (both arguments '?'-mode): treat as satisfiable, succeed once.
3948
- if (g.s instanceof Var && g.o instanceof Var) return [{ ...subst }];
3961
+ if (g.s instanceof Var && g.o instanceof Var) return [__cloneSubst(subst)];
3949
3962
  const results = [];
3950
3963
  const LANG_RE = /^[A-Za-z]+(?:-[A-Za-z0-9]+)*$/; // (same notion as literalParts/literalHasLangTag)
3951
3964
 
@@ -4135,7 +4148,7 @@
4135
4148
  }
4136
4149
 
4137
4150
  // Empty formula is always included (but may be priority-gated above).
4138
- if (g.o instanceof Literal && g.o.value === 'true') return [{ ...subst }];
4151
+ if (g.o instanceof Literal && g.o.value === 'true') return [__cloneSubst(subst)];
4139
4152
  if (!(g.o instanceof GraphTerm)) return [];
4140
4153
 
4141
4154
  const visited2 = [];
@@ -4148,7 +4161,7 @@
4148
4161
  for (const variant of goalVariants) {
4149
4162
  const sols = proveGoals(
4150
4163
  variant.goals,
4151
- { ...subst },
4164
+ __cloneSubst(subst),
4152
4165
  scopeFacts,
4153
4166
  scopeBackRules,
4154
4167
  depth + 1,
@@ -4226,7 +4239,7 @@
4226
4239
  const visited2 = [];
4227
4240
  const sols = proveGoals(
4228
4241
  __prepareQuotedPatternTriples(goal.o, subst, varGen),
4229
- { ...subst },
4242
+ __cloneSubst(subst),
4230
4243
  scopeFacts,
4231
4244
  scopeBackRules,
4232
4245
  depth + 1,
@@ -4234,7 +4247,7 @@
4234
4247
  varGen,
4235
4248
  1,
4236
4249
  );
4237
- return sols.length ? [] : [{ ...subst }];
4250
+ return sols.length ? [] : [__cloneSubst(subst)];
4238
4251
  }
4239
4252
 
4240
4253
  // log:trace
@@ -4248,7 +4261,7 @@
4248
4261
  const yStr = termToN3(g.o, pref);
4249
4262
 
4250
4263
  trace.writeTraceLine(`${xStr} TRACE ${yStr}`);
4251
- return [{ ...subst }];
4264
+ return [__cloneSubst(subst)];
4252
4265
  }
4253
4266
 
4254
4267
  // log:outputString
@@ -4262,7 +4275,7 @@
4262
4275
  if (g.o instanceof Var) return [];
4263
4276
  const s = termToJsString(g.o);
4264
4277
  if (s === null) return [];
4265
- return [{ ...subst }];
4278
+ return [__cloneSubst(subst)];
4266
4279
  }
4267
4280
 
4268
4281
  // log:collectAllIn (scoped)
@@ -4277,7 +4290,7 @@
4277
4290
  // - object = Var: treat as priority 1 (do not bind)
4278
4291
  // - any other object: backward-compatible default priority 1
4279
4292
 
4280
- const outSubst = { ...subst };
4293
+ const outSubst = __cloneSubst(subst);
4281
4294
  let scopeFacts = null;
4282
4295
  let scopeBackRules = backRules;
4283
4296
 
@@ -4324,7 +4337,7 @@
4324
4337
  rawClauseTerm instanceof GraphTerm
4325
4338
  ? __prepareQuotedPatternTriples(rawClauseTerm, subst, varGen)
4326
4339
  : __prepareQuotedPatternTriples(clauseTerm, subst, varGen);
4327
- const sols = proveGoals(clauseGoals, {}, scopeFacts, scopeBackRules, depth + 1, visited2, varGen);
4340
+ const sols = proveGoals(clauseGoals, __emptySubst(), scopeFacts, scopeBackRules, depth + 1, visited2, varGen);
4328
4341
 
4329
4342
  const collected = sols.map((sBody) => applySubstTerm(valueTempl, sBody));
4330
4343
  const collectedList = new ListTerm(collected);
@@ -4341,7 +4354,7 @@
4341
4354
 
4342
4355
  // See log:collectAllIn above for the priority / closure semantics.
4343
4356
 
4344
- const outSubst = { ...subst };
4357
+ const outSubst = __cloneSubst(subst);
4345
4358
  let scopeFacts = null;
4346
4359
  let scopeBackRules = backRules;
4347
4360
 
@@ -4389,7 +4402,7 @@
4389
4402
  : __prepareQuotedPatternTriples(thenClause, subst, varGen);
4390
4403
 
4391
4404
  const visited1 = [];
4392
- const sols1 = proveGoals(whereGoals, {}, scopeFacts, scopeBackRules, depth + 1, visited1, varGen);
4405
+ const sols1 = proveGoals(whereGoals, __emptySubst(), scopeFacts, scopeBackRules, depth + 1, visited1, varGen);
4393
4406
 
4394
4407
  for (const s1 of sols1) {
4395
4408
  const visited2 = [];
@@ -4441,7 +4454,7 @@
4441
4454
  const sOk = g.s instanceof Var || g.s instanceof Blank || g.s instanceof Iri;
4442
4455
  const oOk = g.o instanceof Var || g.o instanceof Blank || g.o instanceof Literal;
4443
4456
  if (!sOk || !oOk) return [];
4444
- return [{ ...subst }];
4457
+ return [__cloneSubst(subst)];
4445
4458
  }
4446
4459
 
4447
4460
  // -----------------------------------------------------------------
@@ -4460,7 +4473,7 @@
4460
4473
  const lit = makeStringLiteral(parts.join(''));
4461
4474
 
4462
4475
  if (g.o instanceof Var) {
4463
- const s2 = { ...subst };
4476
+ const s2 = __cloneSubst(subst);
4464
4477
  s2[g.o.name] = lit;
4465
4478
  return [s2];
4466
4479
  }
@@ -4473,7 +4486,7 @@
4473
4486
  const sStr = termToJsString(g.s);
4474
4487
  const oStr = termToJsString(g.o);
4475
4488
  if (sStr === null || oStr === null) return [];
4476
- return sStr.includes(oStr) ? [{ ...subst }] : [];
4489
+ return sStr.includes(oStr) ? [__cloneSubst(subst)] : [];
4477
4490
  }
4478
4491
 
4479
4492
  // string:containsIgnoringCase
@@ -4481,7 +4494,7 @@
4481
4494
  const sStr = termToJsString(g.s);
4482
4495
  const oStr = termToJsString(g.o);
4483
4496
  if (sStr === null || oStr === null) return [];
4484
- return sStr.toLowerCase().includes(oStr.toLowerCase()) ? [{ ...subst }] : [];
4497
+ return sStr.toLowerCase().includes(oStr.toLowerCase()) ? [__cloneSubst(subst)] : [];
4485
4498
  }
4486
4499
 
4487
4500
  // string:endsWith
@@ -4489,7 +4502,7 @@
4489
4502
  const sStr = termToJsString(g.s);
4490
4503
  const oStr = termToJsString(g.o);
4491
4504
  if (sStr === null || oStr === null) return [];
4492
- return sStr.endsWith(oStr) ? [{ ...subst }] : [];
4505
+ return sStr.endsWith(oStr) ? [__cloneSubst(subst)] : [];
4493
4506
  }
4494
4507
 
4495
4508
  // string:equalIgnoringCase
@@ -4497,7 +4510,7 @@
4497
4510
  const sStr = termToJsString(g.s);
4498
4511
  const oStr = termToJsString(g.o);
4499
4512
  if (sStr === null || oStr === null) return [];
4500
- return sStr.toLowerCase() === oStr.toLowerCase() ? [{ ...subst }] : [];
4513
+ return sStr.toLowerCase() === oStr.toLowerCase() ? [__cloneSubst(subst)] : [];
4501
4514
  }
4502
4515
 
4503
4516
  // string:format
@@ -4517,7 +4530,7 @@
4517
4530
 
4518
4531
  const lit = makeStringLiteral(formatted);
4519
4532
  if (g.o instanceof Var) {
4520
- const s2 = { ...subst };
4533
+ const s2 = __cloneSubst(subst);
4521
4534
  s2[g.o.name] = lit;
4522
4535
  return [s2];
4523
4536
  }
@@ -4530,7 +4543,7 @@
4530
4543
  const sStr = termToJsString(g.s);
4531
4544
  const oStr = termToJsString(g.o);
4532
4545
  if (sStr === null || oStr === null) return [];
4533
- return sStr > oStr ? [{ ...subst }] : [];
4546
+ return sStr > oStr ? [__cloneSubst(subst)] : [];
4534
4547
  }
4535
4548
 
4536
4549
  // string:lessThan
@@ -4538,7 +4551,7 @@
4538
4551
  const sStr = termToJsString(g.s);
4539
4552
  const oStr = termToJsString(g.o);
4540
4553
  if (sStr === null || oStr === null) return [];
4541
- return sStr < oStr ? [{ ...subst }] : [];
4554
+ return sStr < oStr ? [__cloneSubst(subst)] : [];
4542
4555
  }
4543
4556
 
4544
4557
  // string:matches
@@ -4548,7 +4561,7 @@
4548
4561
  if (sStr === null || pattern === null) return [];
4549
4562
  const re = compileSwapRegex(pattern, '');
4550
4563
  if (!re) return [];
4551
- return re.test(sStr) ? [{ ...subst }] : [];
4564
+ return re.test(sStr) ? [__cloneSubst(subst)] : [];
4552
4565
  }
4553
4566
 
4554
4567
  // string:notEqualIgnoringCase
@@ -4556,7 +4569,7 @@
4556
4569
  const sStr = termToJsString(g.s);
4557
4570
  const oStr = termToJsString(g.o);
4558
4571
  if (sStr === null || oStr === null) return [];
4559
- return sStr.toLowerCase() !== oStr.toLowerCase() ? [{ ...subst }] : [];
4572
+ return sStr.toLowerCase() !== oStr.toLowerCase() ? [__cloneSubst(subst)] : [];
4560
4573
  }
4561
4574
 
4562
4575
  // string:notGreaterThan (≤ in Unicode code order)
@@ -4564,7 +4577,7 @@
4564
4577
  const sStr = termToJsString(g.s);
4565
4578
  const oStr = termToJsString(g.o);
4566
4579
  if (sStr === null || oStr === null) return [];
4567
- return sStr <= oStr ? [{ ...subst }] : [];
4580
+ return sStr <= oStr ? [__cloneSubst(subst)] : [];
4568
4581
  }
4569
4582
 
4570
4583
  // string:notLessThan (≥ in Unicode code order)
@@ -4572,7 +4585,7 @@
4572
4585
  const sStr = termToJsString(g.s);
4573
4586
  const oStr = termToJsString(g.o);
4574
4587
  if (sStr === null || oStr === null) return [];
4575
- return sStr >= oStr ? [{ ...subst }] : [];
4588
+ return sStr >= oStr ? [__cloneSubst(subst)] : [];
4576
4589
  }
4577
4590
 
4578
4591
  // string:notMatches
@@ -4582,7 +4595,7 @@
4582
4595
  if (sStr === null || pattern === null) return [];
4583
4596
  const re = compileSwapRegex(pattern, '');
4584
4597
  if (!re) return [];
4585
- return re.test(sStr) ? [] : [{ ...subst }];
4598
+ return re.test(sStr) ? [] : [__cloneSubst(subst)];
4586
4599
  }
4587
4600
 
4588
4601
  // string:replace
@@ -4600,7 +4613,7 @@
4600
4613
  const lit = makeStringLiteral(outStr);
4601
4614
 
4602
4615
  if (g.o instanceof Var) {
4603
- const s2 = { ...subst };
4616
+ const s2 = __cloneSubst(subst);
4604
4617
  s2[g.o.name] = lit;
4605
4618
  return [s2];
4606
4619
  }
@@ -4625,7 +4638,7 @@
4625
4638
  const lit = makeStringLiteral(group);
4626
4639
 
4627
4640
  if (g.o instanceof Var) {
4628
- const s2 = { ...subst };
4641
+ const s2 = __cloneSubst(subst);
4629
4642
  s2[g.o.name] = lit;
4630
4643
  return [s2];
4631
4644
  }
@@ -4638,7 +4651,7 @@
4638
4651
  const sStr = termToJsString(g.s);
4639
4652
  const oStr = termToJsString(g.o);
4640
4653
  if (sStr === null || oStr === null) return [];
4641
- return sStr.startsWith(oStr) ? [{ ...subst }] : [];
4654
+ return sStr.startsWith(oStr) ? [__cloneSubst(subst)] : [];
4642
4655
  }
4643
4656
 
4644
4657
  // -----------------------------------------------------------------
@@ -4653,7 +4666,7 @@
4653
4666
  if (sStr === null) return [];
4654
4667
  const lit = internLiteral(String(sStr.length));
4655
4668
  if (g.o instanceof Var) {
4656
- const s2 = { ...subst };
4669
+ const s2 = __cloneSubst(subst);
4657
4670
  s2[g.o.name] = lit;
4658
4671
  return [s2];
4659
4672
  }
@@ -4673,7 +4686,7 @@
4673
4686
  const ch = idx < 0 || idx >= sStr.length ? '' : sStr.charAt(idx);
4674
4687
  const lit = makeStringLiteral(ch);
4675
4688
  if (g.o instanceof Var) {
4676
- const s2 = { ...subst };
4689
+ const s2 = __cloneSubst(subst);
4677
4690
  s2[g.o.name] = lit;
4678
4691
  return [s2];
4679
4692
  }
@@ -4696,7 +4709,7 @@
4696
4709
  if (idx >= 0 && idx < sStr.length) out = sStr.slice(0, idx) + rep + sStr.slice(idx + 1);
4697
4710
  const lit = makeStringLiteral(out);
4698
4711
  if (g.o instanceof Var) {
4699
- const s2 = { ...subst };
4712
+ const s2 = __cloneSubst(subst);
4700
4713
  s2[g.o.name] = lit;
4701
4714
  return [s2];
4702
4715
  }
@@ -5904,6 +5917,19 @@
5904
5917
 
5905
5918
  const hasOwn = Object.prototype.hasOwnProperty;
5906
5919
 
5920
+ function __emptySubst() {
5921
+ return Object.create(null);
5922
+ }
5923
+
5924
+ function __cloneSubst(subst) {
5925
+ if (!subst) return __emptySubst();
5926
+ const out = Object.create(null);
5927
+ for (const k in subst) {
5928
+ if (hasOwn.call(subst, k)) out[k] = subst[k];
5929
+ }
5930
+ return out;
5931
+ }
5932
+
5907
5933
  let version = 'dev';
5908
5934
  try {
5909
5935
  // Node: keep package.json version if available
@@ -7117,7 +7143,7 @@
7117
7143
 
7118
7144
  function makeDerivedRecord(fact, rule, premises, subst, captureExplanations) {
7119
7145
  if (captureExplanations === false) return { fact };
7120
- return new DerivedFact(fact, rule, premises.slice(), { ...subst });
7146
+ return new DerivedFact(fact, rule, premises.slice(), __cloneSubst(subst));
7121
7147
  }
7122
7148
 
7123
7149
  function ensureBackRuleIndexes(backRules) {
@@ -7709,7 +7735,7 @@
7709
7735
  const t = b;
7710
7736
  if (t instanceof Var && t.name === v) return subst;
7711
7737
  if (containsVarTerm(t, v)) return null;
7712
- const s2 = { ...subst };
7738
+ const s2 = __cloneSubst(subst);
7713
7739
  s2[v] = t;
7714
7740
  return s2;
7715
7741
  }
@@ -7899,7 +7925,7 @@
7899
7925
  }
7900
7926
  }
7901
7927
 
7902
- const out = {};
7928
+ const out = __emptySubst();
7903
7929
  for (const k of Object.keys(subst)) {
7904
7930
  if (keep.has(k)) out[k] = subst[k];
7905
7931
  }
@@ -7962,7 +7988,7 @@
7962
7988
  const allowDeferredBuiltins = !!(opts && opts.deferBuiltins);
7963
7989
 
7964
7990
  const initialGoals = Array.isArray(goals) ? goals.slice() : [];
7965
- const substMut = subst ? { ...subst } : {};
7991
+ const substMut = subst ? __cloneSubst(subst) : {};
7966
7992
  const initialVisited = visited ? visited.slice() : [];
7967
7993
 
7968
7994
  // Variables from the original goal list (needed by the caller to instantiate conclusions)
@@ -8424,7 +8450,7 @@
8424
8450
  const builtinMax = Number.isFinite(remaining) && !restGoals.length ? remaining : undefined;
8425
8451
 
8426
8452
  const builtinGoalForEval = goalPredicateIri === LOG_NS + 'rawType' ? rawGoal : goal0;
8427
- const builtinSubstForEval = goalPredicateIri === LOG_NS + 'rawType' ? substMut : {};
8453
+ const builtinSubstForEval = goalPredicateIri === LOG_NS + 'rawType' ? substMut : __emptySubst();
8428
8454
  let deltas = evalBuiltin(
8429
8455
  builtinGoalForEval,
8430
8456
  builtinSubstForEval,
@@ -8467,7 +8493,7 @@
8467
8493
  subjectAndObjectAreFullyUnbound &&
8468
8494
  (!restGoals.length || dc >= goalsNow.length)
8469
8495
  ) {
8470
- deltas = [{}];
8496
+ deltas = [__emptySubst()];
8471
8497
  }
8472
8498
 
8473
8499
  if (deltas.length) {
@@ -8902,7 +8928,7 @@ ${triples.map((tr) => ` ${tripleToN3(tr, prefixes)}`).join('\n')}
8902
8928
  const r = entry.rule;
8903
8929
  if (__skipForwardRuleNow(r)) continue;
8904
8930
 
8905
- const s = unifyTriple(entry.goal, fact, {});
8931
+ const s = unifyTriple(entry.goal, fact, __emptySubst());
8906
8932
  if (s === null) continue;
8907
8933
 
8908
8934
  const outcome = __emitForwardRuleSolution(r, entry.ruleIndex, s);