eyeling 1.22.13 → 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.
package/eyeling.js CHANGED
@@ -537,6 +537,19 @@ const MAX_NUMERIC_CACHE_KEY_LEN = 1024;
537
537
  // (Ackermann(4,2) is ~65k bits, so well below this.)
538
538
  const MAX_BIGINT_POW_RESULT_BITS = 2_000_000n;
539
539
 
540
+ function __emptySubst() {
541
+ return Object.create(null);
542
+ }
543
+
544
+ function __cloneSubst(subst) {
545
+ if (!subst) return __emptySubst();
546
+ const out = Object.create(null);
547
+ for (const k in subst) {
548
+ if (Object.prototype.hasOwnProperty.call(subst, k)) out[k] = subst[k];
549
+ }
550
+ return out;
551
+ }
552
+
540
553
  function __useNumericCacheKey(key) {
541
554
  return typeof key === 'string' && key.length <= MAX_NUMERIC_CACHE_KEY_LEN;
542
555
  }
@@ -1740,12 +1753,12 @@ function cmpNumericInfo(aInfo, bInfo, op) {
1740
1753
  function evalNumericComparisonBuiltin(g, subst, op) {
1741
1754
  const aInfo = parseNumericForCompareTerm(g.s);
1742
1755
  const bInfo = parseNumericForCompareTerm(g.o);
1743
- if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, op)) return [{ ...subst }];
1756
+ if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, op)) return [__cloneSubst(subst)];
1744
1757
 
1745
1758
  if (g.s instanceof ListTerm && g.s.elems.length === 2) {
1746
1759
  const a2 = parseNumericForCompareTerm(g.s.elems[0]);
1747
1760
  const b2 = parseNumericForCompareTerm(g.s.elems[1]);
1748
- if (a2 && b2 && cmpNumericInfo(a2, b2, op)) return [{ ...subst }];
1761
+ if (a2 && b2 && cmpNumericInfo(a2, b2, op)) return [__cloneSubst(subst)];
1749
1762
  }
1750
1763
  return [];
1751
1764
  }
@@ -1961,12 +1974,12 @@ function evalUnaryMathRel(g, subst, forwardFn, inverseFn /* may be null */) {
1961
1974
  if (outDt === XSD_INTEGER_DT && !Number.isInteger(outVal)) outDt = XSD_DECIMAL_DT;
1962
1975
 
1963
1976
  if (g.o instanceof Var) {
1964
- const s2 = { ...subst };
1977
+ const s2 = __cloneSubst(subst);
1965
1978
  s2[g.o.name] = makeNumericOutputLiteral(outVal, outDt);
1966
1979
  return [s2];
1967
1980
  }
1968
- if (g.o instanceof Blank) return [{ ...subst }];
1969
- if (numEqualTerm(g.o, outVal)) return [{ ...subst }];
1981
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
1982
+ if (numEqualTerm(g.o, outVal)) return [__cloneSubst(subst)];
1970
1983
  return [];
1971
1984
  }
1972
1985
 
@@ -1979,12 +1992,12 @@ function evalUnaryMathRel(g, subst, forwardFn, inverseFn /* may be null */) {
1979
1992
  if (inDt === XSD_INTEGER_DT && !Number.isInteger(inVal)) inDt = XSD_DECIMAL_DT;
1980
1993
 
1981
1994
  if (g.s instanceof Var) {
1982
- const s2 = { ...subst };
1995
+ const s2 = __cloneSubst(subst);
1983
1996
  s2[g.s.name] = makeNumericOutputLiteral(inVal, inDt);
1984
1997
  return [s2];
1985
1998
  }
1986
- if (g.s instanceof Blank) return [{ ...subst }];
1987
- if (numEqualTerm(g.s, inVal)) return [{ ...subst }];
1999
+ if (g.s instanceof Blank) return [__cloneSubst(subst)];
2000
+ if (numEqualTerm(g.s, inVal)) return [__cloneSubst(subst)];
1988
2001
  return [];
1989
2002
  }
1990
2003
 
@@ -2004,7 +2017,7 @@ function evalUnaryMathRel(g, subst, forwardFn, inverseFn /* may be null */) {
2004
2017
 
2005
2018
  function listAppendSplit(parts, resElems, subst) {
2006
2019
  if (!parts.length) {
2007
- if (!resElems.length) return [{ ...subst }];
2020
+ if (!resElems.length) return [__cloneSubst(subst)];
2008
2021
  return [];
2009
2022
  }
2010
2023
  const out = [];
@@ -2332,7 +2345,7 @@ function evalCryptoHashBuiltin(g, subst, algo) {
2332
2345
  const lit = hashLiteralTerm(g.s, algo);
2333
2346
  if (!lit) return [];
2334
2347
  if (g.o instanceof Var) {
2335
- const s2 = { ...subst };
2348
+ const s2 = __cloneSubst(subst);
2336
2349
  s2[g.o.name] = lit;
2337
2350
  return [s2];
2338
2351
  }
@@ -2464,7 +2477,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
2464
2477
  const lex = time.utcIsoDateTimeStringFromEpochSeconds(outSecs);
2465
2478
  const lit = internLiteral(`"${lex}"^^<${XSD_NS}dateTime>`);
2466
2479
  if (g.o instanceof Var) {
2467
- const s2 = { ...subst };
2480
+ const s2 = __cloneSubst(subst);
2468
2481
  s2[g.o.name] = lit;
2469
2482
  return [s2];
2470
2483
  }
@@ -2480,7 +2493,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
2480
2493
  const lex = time.utcIsoDateTimeStringFromEpochSeconds(outSecs);
2481
2494
  const lit = internLiteral(`"${lex}"^^<${XSD_NS}dateTime>`);
2482
2495
  if (g.o instanceof Var) {
2483
- const s2 = { ...subst };
2496
+ const s2 = __cloneSubst(subst);
2484
2497
  s2[g.o.name] = lit;
2485
2498
  return [s2];
2486
2499
  }
@@ -2502,17 +2515,17 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
2502
2515
  }
2503
2516
 
2504
2517
  if (g.o instanceof Var) {
2505
- const s2 = { ...subst };
2518
+ const s2 = __cloneSubst(subst);
2506
2519
  s2[g.o.name] = makeNumericOutputLiteral(total, XSD_INTEGER_DT);
2507
2520
  return [s2];
2508
2521
  }
2509
- if (g.o instanceof Blank) return [{ ...subst }];
2522
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
2510
2523
 
2511
2524
  const oi = parseIntLiteral(g.o);
2512
- if (oi !== null && oi === total) return [{ ...subst }];
2525
+ if (oi !== null && oi === total) return [__cloneSubst(subst)];
2513
2526
 
2514
2527
  // Fallback numeric compare
2515
- if (numEqualTerm(g.o, Number(total))) return [{ ...subst }];
2528
+ if (numEqualTerm(g.o, Number(total))) return [__cloneSubst(subst)];
2516
2529
  return [];
2517
2530
  }
2518
2531
 
@@ -2529,12 +2542,12 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
2529
2542
  const lit = makeNumericOutputLiteral(total, dtOut);
2530
2543
 
2531
2544
  if (g.o instanceof Var) {
2532
- const s2 = { ...subst };
2545
+ const s2 = __cloneSubst(subst);
2533
2546
  s2[g.o.name] = lit;
2534
2547
  return [s2];
2535
2548
  }
2536
- if (g.o instanceof Blank) return [{ ...subst }];
2537
- if (numEqualTerm(g.o, total)) return [{ ...subst }];
2549
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
2550
+ if (numEqualTerm(g.o, total)) return [__cloneSubst(subst)];
2538
2551
  return [];
2539
2552
  }
2540
2553
 
@@ -2557,15 +2570,15 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
2557
2570
  }
2558
2571
 
2559
2572
  if (g.o instanceof Var) {
2560
- const s2 = { ...subst };
2573
+ const s2 = __cloneSubst(subst);
2561
2574
  s2[g.o.name] = makeNumericOutputLiteral(prod, XSD_INTEGER_DT);
2562
2575
  return [s2];
2563
2576
  }
2564
- if (g.o instanceof Blank) return [{ ...subst }];
2577
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
2565
2578
 
2566
2579
  const oi = parseIntLiteral(g.o);
2567
- if (oi !== null && oi === prod) return [{ ...subst }];
2568
- if (numEqualTerm(g.o, Number(prod))) return [{ ...subst }];
2580
+ if (oi !== null && oi === prod) return [__cloneSubst(subst)];
2581
+ if (numEqualTerm(g.o, Number(prod))) return [__cloneSubst(subst)];
2569
2582
  return [];
2570
2583
  }
2571
2584
 
@@ -2582,12 +2595,12 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
2582
2595
  const lit = makeNumericOutputLiteral(prod, dtOut);
2583
2596
 
2584
2597
  if (g.o instanceof Var) {
2585
- const s2 = { ...subst };
2598
+ const s2 = __cloneSubst(subst);
2586
2599
  s2[g.o.name] = lit;
2587
2600
  return [s2];
2588
2601
  }
2589
- if (g.o instanceof Blank) return [{ ...subst }];
2590
- if (numEqualTerm(g.o, prod)) return [{ ...subst }];
2602
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
2603
+ if (numEqualTerm(g.o, prod)) return [__cloneSubst(subst)];
2591
2604
  return [];
2592
2605
  }
2593
2606
 
@@ -2611,7 +2624,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
2611
2624
  const lit = formatDurationLiteralFromSeconds(diffSecs);
2612
2625
 
2613
2626
  if (g.o instanceof Var) {
2614
- const s2 = { ...subst };
2627
+ const s2 = __cloneSubst(subst);
2615
2628
  s2[g.o.name] = lit;
2616
2629
  return [s2];
2617
2630
  }
@@ -2627,7 +2640,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
2627
2640
  const lex = time.utcIsoDateTimeStringFromEpochSeconds(outSecs);
2628
2641
  const lit = internLiteral(`"${lex}"^^<${XSD_NS}dateTime>`);
2629
2642
  if (g.o instanceof Var) {
2630
- const s2 = { ...subst };
2643
+ const s2 = __cloneSubst(subst);
2631
2644
  s2[g.o.name] = lit;
2632
2645
  return [s2];
2633
2646
  }
@@ -2643,7 +2656,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
2643
2656
  const ci = ai - bi;
2644
2657
  const lit = internLiteral(ci.toString());
2645
2658
  if (g.o instanceof Var) {
2646
- const s2 = { ...subst };
2659
+ const s2 = __cloneSubst(subst);
2647
2660
  s2[g.o.name] = lit;
2648
2661
  return [s2];
2649
2662
  }
@@ -2666,19 +2679,19 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
2666
2679
  const lit = makeNumericOutputLiteral(c, dtOut);
2667
2680
 
2668
2681
  if (g.o instanceof Var) {
2669
- const s2 = { ...subst };
2682
+ const s2 = __cloneSubst(subst);
2670
2683
  s2[g.o.name] = lit;
2671
2684
  return [s2];
2672
2685
  }
2673
- if (g.o instanceof Blank) return [{ ...subst }];
2674
- if (numEqualTerm(g.o, c)) return [{ ...subst }];
2686
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
2687
+ if (numEqualTerm(g.o, c)) return [__cloneSubst(subst)];
2675
2688
  return [];
2676
2689
  }
2677
2690
 
2678
2691
  // Fallback (if you *don’t* have those helpers yet):
2679
2692
  const lit = internLiteral(formatNum(c));
2680
2693
  if (g.o instanceof Var) {
2681
- const s2 = { ...subst };
2694
+ const s2 = __cloneSubst(subst);
2682
2695
  s2[g.o.name] = lit;
2683
2696
  return [s2];
2684
2697
  }
@@ -2705,12 +2718,12 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
2705
2718
  const lit = makeNumericOutputLiteral(c, dtOut);
2706
2719
 
2707
2720
  if (g.o instanceof Var) {
2708
- const s2 = { ...subst };
2721
+ const s2 = __cloneSubst(subst);
2709
2722
  s2[g.o.name] = lit;
2710
2723
  return [s2];
2711
2724
  }
2712
- if (g.o instanceof Blank) return [{ ...subst }];
2713
- if (numEqualTerm(g.o, c)) return [{ ...subst }];
2725
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
2726
+ if (numEqualTerm(g.o, c)) return [__cloneSubst(subst)];
2714
2727
  return [];
2715
2728
  }
2716
2729
 
@@ -2729,19 +2742,19 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
2729
2742
  const q = ai / bi; // BigInt division truncates toward zero
2730
2743
  const lit = internLiteral(q.toString());
2731
2744
  if (g.o instanceof Var) {
2732
- const s2 = { ...subst };
2745
+ const s2 = __cloneSubst(subst);
2733
2746
  s2[g.o.name] = lit;
2734
2747
  return [s2];
2735
2748
  }
2736
- if (g.o instanceof Blank) return [{ ...subst }];
2749
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
2737
2750
 
2738
2751
  const oi = parseIntLiteral(g.o);
2739
- if (oi !== null && oi === q) return [{ ...subst }];
2752
+ if (oi !== null && oi === q) return [__cloneSubst(subst)];
2740
2753
 
2741
2754
  // Only do numeric compare when safe enough to convert
2742
2755
  const qNum = Number(q);
2743
2756
  if (Number.isFinite(qNum) && Math.abs(qNum) <= Number.MAX_SAFE_INTEGER) {
2744
- if (numEqualTerm(g.o, qNum)) return [{ ...subst }];
2757
+ if (numEqualTerm(g.o, qNum)) return [__cloneSubst(subst)];
2745
2758
  }
2746
2759
 
2747
2760
  const s2 = unifyTerm(g.o, lit, subst);
@@ -2758,13 +2771,13 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
2758
2771
  const q = Math.trunc(a / b);
2759
2772
  const lit = internLiteral(String(q));
2760
2773
  if (g.o instanceof Var) {
2761
- const s2 = { ...subst };
2774
+ const s2 = __cloneSubst(subst);
2762
2775
  s2[g.o.name] = lit;
2763
2776
  return [s2];
2764
2777
  }
2765
- if (g.o instanceof Blank) return [{ ...subst }];
2778
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
2766
2779
 
2767
- if (numEqualTerm(g.o, q)) return [{ ...subst }];
2780
+ if (numEqualTerm(g.o, q)) return [__cloneSubst(subst)];
2768
2781
 
2769
2782
  const s2 = unifyTerm(g.o, lit, subst);
2770
2783
  return s2 !== null ? [s2] : [];
@@ -2797,14 +2810,14 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
2797
2810
  const lit = makeNumericOutputLiteral(out, XSD_INTEGER_DT);
2798
2811
 
2799
2812
  if (g.o instanceof Var) {
2800
- const s2 = { ...subst };
2813
+ const s2 = __cloneSubst(subst);
2801
2814
  s2[g.o.name] = lit;
2802
2815
  return [s2];
2803
2816
  }
2804
- if (g.o instanceof Blank) return [{ ...subst }];
2817
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
2805
2818
 
2806
2819
  const oi = parseIntLiteral(g.o);
2807
- if (oi !== null && oi === out) return [{ ...subst }];
2820
+ if (oi !== null && oi === out) return [__cloneSubst(subst)];
2808
2821
 
2809
2822
  const s2 = unifyTerm(g.o, lit, subst);
2810
2823
  return s2 !== null ? [s2] : [];
@@ -2824,12 +2837,12 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
2824
2837
  const lit = makeNumericOutputLiteral(cVal, dtOut);
2825
2838
 
2826
2839
  if (g.o instanceof Var) {
2827
- const s2 = { ...subst };
2840
+ const s2 = __cloneSubst(subst);
2828
2841
  s2[g.o.name] = lit;
2829
2842
  return [s2];
2830
2843
  }
2831
- if (g.o instanceof Blank) return [{ ...subst }];
2832
- if (numEqualTerm(g.o, cVal)) return [{ ...subst }];
2844
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
2845
+ if (numEqualTerm(g.o, cVal)) return [__cloneSubst(subst)];
2833
2846
  }
2834
2847
 
2835
2848
  // Inverse: solve exponent using logs (Number mode only)
@@ -2842,7 +2855,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
2842
2855
  let dtB = commonNumericDatatype([baseTerm, g.o], expTerm);
2843
2856
  if (dtB === XSD_INTEGER_DT && !Number.isInteger(bVal)) dtB = XSD_DECIMAL_DT;
2844
2857
 
2845
- const s2 = { ...subst };
2858
+ const s2 = __cloneSubst(subst);
2846
2859
  s2[expTerm.name] = makeNumericOutputLiteral(bVal, dtB);
2847
2860
  return [s2];
2848
2861
  }
@@ -2858,15 +2871,15 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
2858
2871
  const lit = makeNumericOutputLiteral(outVal, XSD_INTEGER_DT);
2859
2872
 
2860
2873
  if (g.o instanceof Var) {
2861
- const s2 = { ...subst };
2874
+ const s2 = __cloneSubst(subst);
2862
2875
  s2[g.o.name] = lit;
2863
2876
  return [s2];
2864
2877
  }
2865
- if (g.o instanceof Blank) return [{ ...subst }];
2878
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
2866
2879
 
2867
2880
  const oi = parseIntLiteral(g.o);
2868
- if (oi !== null && oi === outVal) return [{ ...subst }];
2869
- if (numEqualTerm(g.o, Number(outVal))) return [{ ...subst }];
2881
+ if (oi !== null && oi === outVal) return [__cloneSubst(subst)];
2882
+ if (numEqualTerm(g.o, Number(outVal))) return [__cloneSubst(subst)];
2870
2883
  return [];
2871
2884
  }
2872
2885
 
@@ -2880,12 +2893,12 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
2880
2893
  if (dtOut === XSD_INTEGER_DT && !Number.isInteger(outVal)) dtOut = XSD_DECIMAL_DT;
2881
2894
 
2882
2895
  if (g.o instanceof Var) {
2883
- const s2 = { ...subst };
2896
+ const s2 = __cloneSubst(subst);
2884
2897
  s2[g.o.name] = makeNumericOutputLiteral(outVal, dtOut);
2885
2898
  return [s2];
2886
2899
  }
2887
- if (g.o instanceof Blank) return [{ ...subst }];
2888
- if (numEqualTerm(g.o, outVal)) return [{ ...subst }];
2900
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
2901
+ if (numEqualTerm(g.o, outVal)) return [__cloneSubst(subst)];
2889
2902
  return [];
2890
2903
  }
2891
2904
 
@@ -2947,14 +2960,14 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
2947
2960
  const outVal = -si;
2948
2961
  const lit = makeNumericOutputLiteral(outVal, XSD_INTEGER_DT);
2949
2962
  if (g.o instanceof Var) {
2950
- const s2 = { ...subst };
2963
+ const s2 = __cloneSubst(subst);
2951
2964
  s2[g.o.name] = lit;
2952
2965
  return [s2];
2953
2966
  }
2954
- if (g.o instanceof Blank) return [{ ...subst }];
2967
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
2955
2968
  const oi = parseIntLiteral(g.o);
2956
- if (oi !== null && oi === outVal) return [{ ...subst }];
2957
- if (numEqualTerm(g.o, Number(outVal))) return [{ ...subst }];
2969
+ if (oi !== null && oi === outVal) return [__cloneSubst(subst)];
2970
+ if (numEqualTerm(g.o, Number(outVal))) return [__cloneSubst(subst)];
2958
2971
  return [];
2959
2972
  }
2960
2973
 
@@ -2963,14 +2976,14 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
2963
2976
  const inVal = -oi;
2964
2977
  const lit = makeNumericOutputLiteral(inVal, XSD_INTEGER_DT);
2965
2978
  if (g.s instanceof Var) {
2966
- const s2 = { ...subst };
2979
+ const s2 = __cloneSubst(subst);
2967
2980
  s2[g.s.name] = lit;
2968
2981
  return [s2];
2969
2982
  }
2970
- if (g.s instanceof Blank) return [{ ...subst }];
2983
+ if (g.s instanceof Blank) return [__cloneSubst(subst)];
2971
2984
  const si2 = parseIntLiteral(g.s);
2972
- if (si2 !== null && si2 === inVal) return [{ ...subst }];
2973
- if (numEqualTerm(g.s, Number(inVal))) return [{ ...subst }];
2985
+ if (si2 !== null && si2 === inVal) return [__cloneSubst(subst)];
2986
+ if (numEqualTerm(g.s, Number(inVal))) return [__cloneSubst(subst)];
2974
2987
  return [];
2975
2988
  }
2976
2989
 
@@ -2994,15 +3007,15 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
2994
3007
  const lit = makeNumericOutputLiteral(r, XSD_INTEGER_DT);
2995
3008
 
2996
3009
  if (g.o instanceof Var) {
2997
- const s2 = { ...subst };
3010
+ const s2 = __cloneSubst(subst);
2998
3011
  s2[g.o.name] = lit;
2999
3012
  return [s2];
3000
3013
  }
3001
- if (g.o instanceof Blank) return [{ ...subst }];
3014
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3002
3015
 
3003
3016
  const oi = parseIntLiteral(g.o);
3004
- if (oi !== null && oi === r) return [{ ...subst }];
3005
- if (numEqualTerm(g.o, Number(r))) return [{ ...subst }];
3017
+ if (oi !== null && oi === r) return [__cloneSubst(subst)];
3018
+ if (numEqualTerm(g.o, Number(r))) return [__cloneSubst(subst)];
3006
3019
  return [];
3007
3020
  }
3008
3021
 
@@ -3017,12 +3030,12 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3017
3030
  const lit = makeNumericOutputLiteral(rVal, XSD_INTEGER_DT);
3018
3031
 
3019
3032
  if (g.o instanceof Var) {
3020
- const s2 = { ...subst };
3033
+ const s2 = __cloneSubst(subst);
3021
3034
  s2[g.o.name] = lit;
3022
3035
  return [s2];
3023
3036
  }
3024
- if (g.o instanceof Blank) return [{ ...subst }];
3025
- if (numEqualTerm(g.o, rVal)) return [{ ...subst }];
3037
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3038
+ if (numEqualTerm(g.o, rVal)) return [__cloneSubst(subst)];
3026
3039
  return [];
3027
3040
  }
3028
3041
 
@@ -3037,15 +3050,15 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3037
3050
  const lit = makeNumericOutputLiteral(ai, XSD_INTEGER_DT);
3038
3051
 
3039
3052
  if (g.o instanceof Var) {
3040
- const s2 = { ...subst };
3053
+ const s2 = __cloneSubst(subst);
3041
3054
  s2[g.o.name] = lit;
3042
3055
  return [s2];
3043
3056
  }
3044
- if (g.o instanceof Blank) return [{ ...subst }];
3057
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3045
3058
 
3046
3059
  const oi = parseIntLiteral(g.o);
3047
- if (oi !== null && oi === ai) return [{ ...subst }];
3048
- if (numEqualTerm(g.o, Number(ai))) return [{ ...subst }];
3060
+ if (oi !== null && oi === ai) return [__cloneSubst(subst)];
3061
+ if (numEqualTerm(g.o, Number(ai))) return [__cloneSubst(subst)];
3049
3062
 
3050
3063
  const s2 = unifyTerm(g.o, lit, subst);
3051
3064
  return s2 !== null ? [s2] : [];
@@ -3059,14 +3072,14 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3059
3072
  const lit = internLiteral(String(rVal)); // integer token
3060
3073
 
3061
3074
  if (g.o instanceof Var) {
3062
- const s2 = { ...subst };
3075
+ const s2 = __cloneSubst(subst);
3063
3076
  s2[g.o.name] = lit;
3064
3077
  return [s2];
3065
3078
  }
3066
- if (g.o instanceof Blank) return [{ ...subst }];
3079
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3067
3080
 
3068
3081
  // Accept typed numeric literals too (e.g., "3"^^xsd:float) if numerically equal.
3069
- if (numEqualTerm(g.o, rVal)) return [{ ...subst }];
3082
+ if (numEqualTerm(g.o, rVal)) return [__cloneSubst(subst)];
3070
3083
 
3071
3084
  // Fallback to strict unification
3072
3085
  const s2 = unifyTerm(g.o, lit, subst);
@@ -3086,16 +3099,16 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3086
3099
  const out = internLiteral(String(parts.day));
3087
3100
 
3088
3101
  if (g.o instanceof Var) {
3089
- const s2 = { ...subst };
3102
+ const s2 = __cloneSubst(subst);
3090
3103
  s2[g.o.name] = out;
3091
3104
  return [s2];
3092
3105
  }
3093
- if (g.o instanceof Blank) return [{ ...subst }];
3106
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3094
3107
 
3095
3108
  const oi = parseIntLiteral(g.o);
3096
3109
  if (oi !== null) {
3097
3110
  try {
3098
- if (oi === BigInt(parts.day)) return [{ ...subst }];
3111
+ if (oi === BigInt(parts.day)) return [__cloneSubst(subst)];
3099
3112
  } catch {}
3100
3113
  }
3101
3114
 
@@ -3112,16 +3125,16 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3112
3125
  const out = internLiteral(String(parts.hour));
3113
3126
 
3114
3127
  if (g.o instanceof Var) {
3115
- const s2 = { ...subst };
3128
+ const s2 = __cloneSubst(subst);
3116
3129
  s2[g.o.name] = out;
3117
3130
  return [s2];
3118
3131
  }
3119
- if (g.o instanceof Blank) return [{ ...subst }];
3132
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3120
3133
 
3121
3134
  const oi = parseIntLiteral(g.o);
3122
3135
  if (oi !== null) {
3123
3136
  try {
3124
- if (oi === BigInt(parts.hour)) return [{ ...subst }];
3137
+ if (oi === BigInt(parts.hour)) return [__cloneSubst(subst)];
3125
3138
  } catch {}
3126
3139
  }
3127
3140
 
@@ -3138,16 +3151,16 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3138
3151
  const out = internLiteral(String(parts.minute));
3139
3152
 
3140
3153
  if (g.o instanceof Var) {
3141
- const s2 = { ...subst };
3154
+ const s2 = __cloneSubst(subst);
3142
3155
  s2[g.o.name] = out;
3143
3156
  return [s2];
3144
3157
  }
3145
- if (g.o instanceof Blank) return [{ ...subst }];
3158
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3146
3159
 
3147
3160
  const oi = parseIntLiteral(g.o);
3148
3161
  if (oi !== null) {
3149
3162
  try {
3150
- if (oi === BigInt(parts.minute)) return [{ ...subst }];
3163
+ if (oi === BigInt(parts.minute)) return [__cloneSubst(subst)];
3151
3164
  } catch {}
3152
3165
  }
3153
3166
 
@@ -3164,16 +3177,16 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3164
3177
  const out = internLiteral(String(parts.month));
3165
3178
 
3166
3179
  if (g.o instanceof Var) {
3167
- const s2 = { ...subst };
3180
+ const s2 = __cloneSubst(subst);
3168
3181
  s2[g.o.name] = out;
3169
3182
  return [s2];
3170
3183
  }
3171
- if (g.o instanceof Blank) return [{ ...subst }];
3184
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3172
3185
 
3173
3186
  const oi = parseIntLiteral(g.o);
3174
3187
  if (oi !== null) {
3175
3188
  try {
3176
- if (oi === BigInt(parts.month)) return [{ ...subst }];
3189
+ if (oi === BigInt(parts.month)) return [__cloneSubst(subst)];
3177
3190
  } catch {}
3178
3191
  }
3179
3192
 
@@ -3190,16 +3203,16 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3190
3203
  const out = internLiteral(String(parts.second));
3191
3204
 
3192
3205
  if (g.o instanceof Var) {
3193
- const s2 = { ...subst };
3206
+ const s2 = __cloneSubst(subst);
3194
3207
  s2[g.o.name] = out;
3195
3208
  return [s2];
3196
3209
  }
3197
- if (g.o instanceof Blank) return [{ ...subst }];
3210
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3198
3211
 
3199
3212
  const oi = parseIntLiteral(g.o);
3200
3213
  if (oi !== null) {
3201
3214
  try {
3202
- if (oi === BigInt(parts.second)) return [{ ...subst }];
3215
+ if (oi === BigInt(parts.second)) return [__cloneSubst(subst)];
3203
3216
  } catch {}
3204
3217
  }
3205
3218
 
@@ -3217,18 +3230,18 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3217
3230
  const out = internLiteral(`"${parts.tz}"`);
3218
3231
 
3219
3232
  if (g.o instanceof Var) {
3220
- const s2 = { ...subst };
3233
+ const s2 = __cloneSubst(subst);
3221
3234
  s2[g.o.name] = out;
3222
3235
  return [s2];
3223
3236
  }
3224
- if (g.o instanceof Blank) return [{ ...subst }];
3237
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3225
3238
 
3226
- if (termsEqual(g.o, out)) return [{ ...subst }];
3239
+ if (termsEqual(g.o, out)) return [__cloneSubst(subst)];
3227
3240
 
3228
3241
  // Also accept explicitly typed xsd:string literals.
3229
3242
  if (g.o instanceof Literal) {
3230
3243
  const [lexO, dtO] = literalParts(g.o.value);
3231
- if (dtO === XSD_NS + 'string' && stripQuotes(lexO) === parts.tz) return [{ ...subst }];
3244
+ if (dtO === XSD_NS + 'string' && stripQuotes(lexO) === parts.tz) return [__cloneSubst(subst)];
3232
3245
  }
3233
3246
  return [];
3234
3247
  }
@@ -3242,16 +3255,16 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3242
3255
  const out = internLiteral(String(parts.yearStr));
3243
3256
 
3244
3257
  if (g.o instanceof Var) {
3245
- const s2 = { ...subst };
3258
+ const s2 = __cloneSubst(subst);
3246
3259
  s2[g.o.name] = out;
3247
3260
  return [s2];
3248
3261
  }
3249
- if (g.o instanceof Blank) return [{ ...subst }];
3262
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3250
3263
 
3251
3264
  const oi = parseIntLiteral(g.o);
3252
3265
  if (oi !== null) {
3253
3266
  try {
3254
- if (oi === BigInt(parts.yearStr)) return [{ ...subst }];
3267
+ if (oi === BigInt(parts.yearStr)) return [__cloneSubst(subst)];
3255
3268
  } catch {}
3256
3269
  }
3257
3270
 
@@ -3265,13 +3278,13 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3265
3278
  const now = time.getNowLex();
3266
3279
 
3267
3280
  if (g.o instanceof Var) {
3268
- const s2 = { ...subst };
3281
+ const s2 = __cloneSubst(subst);
3269
3282
  s2[g.o.name] = internLiteral(`"${now}"^^<${XSD_NS}dateTime>`);
3270
3283
  return [s2];
3271
3284
  }
3272
3285
  if (g.o instanceof Literal) {
3273
3286
  const [lexO] = literalParts(g.o.value);
3274
- if (stripQuotes(lexO) === now) return [{ ...subst }];
3287
+ if (stripQuotes(lexO) === now) return [__cloneSubst(subst)];
3275
3288
  }
3276
3289
  return [];
3277
3290
  }
@@ -3296,11 +3309,11 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3296
3309
  }
3297
3310
  const result = new ListTerm(outElems);
3298
3311
  if (g.o instanceof Var) {
3299
- const s2 = { ...subst };
3312
+ const s2 = __cloneSubst(subst);
3300
3313
  s2[g.o.name] = result;
3301
3314
  return [s2];
3302
3315
  }
3303
- if (termsEqual(g.o, result)) return [{ ...subst }];
3316
+ if (termsEqual(g.o, result)) return [__cloneSubst(subst)];
3304
3317
  return [];
3305
3318
  }
3306
3319
 
@@ -3404,7 +3417,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3404
3417
  const idxPat2 = applySubstTerm(indexTerm, subst);
3405
3418
  if (isGroundTerm(idxPat2)) {
3406
3419
  if (!termsEqualNoIntDecimal(idxPat2, idxLit)) continue;
3407
- s1 = { ...subst };
3420
+ s1 = __cloneSubst(subst);
3408
3421
  } else {
3409
3422
  s1 = unifyTerm(indexTerm, idxLit, subst);
3410
3423
  if (s1 === null) continue;
@@ -3479,7 +3492,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3479
3492
 
3480
3493
  const o2 = applySubstTerm(g.o, subst);
3481
3494
  if (isGroundTerm(o2)) {
3482
- return termsEqualNoIntDecimal(o2, nTerm) ? [{ ...subst }] : [];
3495
+ return termsEqualNoIntDecimal(o2, nTerm) ? [__cloneSubst(subst)] : [];
3483
3496
  }
3484
3497
 
3485
3498
  const s2 = unifyTerm(g.o, nTerm, subst);
@@ -3493,7 +3506,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3493
3506
  for (const el of xs) {
3494
3507
  if (unifyTerm(g.o, el, subst) !== null) return [];
3495
3508
  }
3496
- return [{ ...subst }];
3509
+ return [__cloneSubst(subst)];
3497
3510
  }
3498
3511
 
3499
3512
  // list:reverse
@@ -3679,7 +3692,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3679
3692
  if (pv === LOG_NS + 'notEqualTo') {
3680
3693
  const s2 = unifyTerm(goal.s, goal.o, subst);
3681
3694
  if (s2 !== null) return [];
3682
- return [{ ...subst }];
3695
+ return [__cloneSubst(subst)];
3683
3696
  }
3684
3697
 
3685
3698
  // log:conjunction
@@ -3728,7 +3741,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3728
3741
  const outFormula = new GraphTerm(merged);
3729
3742
 
3730
3743
  // Allow blank nodes as a don't-care output (common in builtin schemas).
3731
- if (g.o instanceof Blank) return [{ ...subst }];
3744
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3732
3745
 
3733
3746
  const s2 = unifyTerm(g.o, outFormula, subst);
3734
3747
  return s2 !== null ? [s2] : [];
@@ -3748,11 +3761,11 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3748
3761
  if (!(conclusion instanceof GraphTerm)) return [];
3749
3762
 
3750
3763
  if (g.o instanceof Var) {
3751
- const s2 = { ...subst };
3764
+ const s2 = __cloneSubst(subst);
3752
3765
  s2[g.o.name] = conclusion;
3753
3766
  return [s2];
3754
3767
  }
3755
- if (g.o instanceof Blank) return [{ ...subst }];
3768
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3756
3769
 
3757
3770
  const s2 = unifyTerm(g.o, conclusion, subst);
3758
3771
  return s2 !== null ? [s2] : [];
@@ -3772,11 +3785,11 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3772
3785
  const lit = internLiteral(`${JSON.stringify(text)}^^<${XSD_NS}string>`);
3773
3786
 
3774
3787
  if (g.o instanceof Var) {
3775
- const s2 = { ...subst };
3788
+ const s2 = __cloneSubst(subst);
3776
3789
  s2[g.o.name] = lit;
3777
3790
  return [s2];
3778
3791
  }
3779
- if (g.o instanceof Blank) return [{ ...subst }];
3792
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3780
3793
 
3781
3794
  const s2 = unifyTerm(g.o, lit, subst);
3782
3795
  return s2 !== null ? [s2] : [];
@@ -3796,7 +3809,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3796
3809
  // Avoid variable capture between the returned quoted formula and the
3797
3810
  // surrounding proof environment.
3798
3811
  const formulaStd = standardizeTermApart(formula, varGen);
3799
- if (g.o instanceof Blank) return [{ ...subst }];
3812
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3800
3813
 
3801
3814
  const s2 = unifyTerm(g.o, formulaStd, subst);
3802
3815
  return s2 !== null ? [s2] : [];
@@ -3816,7 +3829,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3816
3829
  // surrounding proof environment.
3817
3830
  if (term instanceof GraphTerm) term = standardizeTermApart(term, varGen);
3818
3831
 
3819
- if (g.o instanceof Blank) return [{ ...subst }];
3832
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3820
3833
 
3821
3834
  const s2 = unifyTerm(g.o, term, subst);
3822
3835
  return s2 !== null ? [s2] : [];
@@ -3842,7 +3855,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3842
3855
  // surrounding proof environment.
3843
3856
  formula = standardizeTermApart(formula, varGen);
3844
3857
 
3845
- if (g.o instanceof Blank) return [{ ...subst }];
3858
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3846
3859
 
3847
3860
  const s2 = unifyTerm(g.o, formula, subst);
3848
3861
  return s2 !== null ? [s2] : [];
@@ -3861,11 +3874,11 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3861
3874
  else ty = internIri(LOG_NS + 'Other');
3862
3875
 
3863
3876
  if (g.o instanceof Var) {
3864
- const s2 = { ...subst };
3877
+ const s2 = __cloneSubst(subst);
3865
3878
  s2[g.o.name] = ty;
3866
3879
  return [s2];
3867
3880
  }
3868
- if (g.o instanceof Blank) return [{ ...subst }];
3881
+ if (g.o instanceof Blank) return [__cloneSubst(subst)];
3869
3882
 
3870
3883
  const s2 = unifyTerm(g.o, ty, subst);
3871
3884
  return s2 !== null ? [s2] : [];
@@ -3877,7 +3890,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3877
3890
  if (pv === LOG_NS + 'dtlit') {
3878
3891
  // Fully unbound (both arguments '?'-mode): treat as satisfiable, succeed once.
3879
3892
  // Required by notation3tests "success-fullUnbound-*".
3880
- if (g.s instanceof Var && g.o instanceof Var) return [{ ...subst }];
3893
+ if (g.s instanceof Var && g.o instanceof Var) return [__cloneSubst(subst)];
3881
3894
 
3882
3895
  const results = [];
3883
3896
 
@@ -3930,7 +3943,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
3930
3943
  // true iff $o is a language-tagged literal with string value $s.1 and language tag $s.2
3931
3944
  if (pv === LOG_NS + 'langlit') {
3932
3945
  // Fully unbound (both arguments '?'-mode): treat as satisfiable, succeed once.
3933
- if (g.s instanceof Var && g.o instanceof Var) return [{ ...subst }];
3946
+ if (g.s instanceof Var && g.o instanceof Var) return [__cloneSubst(subst)];
3934
3947
  const results = [];
3935
3948
  const LANG_RE = /^[A-Za-z]+(?:-[A-Za-z0-9]+)*$/; // (same notion as literalParts/literalHasLangTag)
3936
3949
 
@@ -4120,7 +4133,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4120
4133
  }
4121
4134
 
4122
4135
  // Empty formula is always included (but may be priority-gated above).
4123
- if (g.o instanceof Literal && g.o.value === 'true') return [{ ...subst }];
4136
+ if (g.o instanceof Literal && g.o.value === 'true') return [__cloneSubst(subst)];
4124
4137
  if (!(g.o instanceof GraphTerm)) return [];
4125
4138
 
4126
4139
  const visited2 = [];
@@ -4133,7 +4146,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4133
4146
  for (const variant of goalVariants) {
4134
4147
  const sols = proveGoals(
4135
4148
  variant.goals,
4136
- { ...subst },
4149
+ __cloneSubst(subst),
4137
4150
  scopeFacts,
4138
4151
  scopeBackRules,
4139
4152
  depth + 1,
@@ -4211,7 +4224,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4211
4224
  const visited2 = [];
4212
4225
  const sols = proveGoals(
4213
4226
  __prepareQuotedPatternTriples(goal.o, subst, varGen),
4214
- { ...subst },
4227
+ __cloneSubst(subst),
4215
4228
  scopeFacts,
4216
4229
  scopeBackRules,
4217
4230
  depth + 1,
@@ -4219,7 +4232,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4219
4232
  varGen,
4220
4233
  1,
4221
4234
  );
4222
- return sols.length ? [] : [{ ...subst }];
4235
+ return sols.length ? [] : [__cloneSubst(subst)];
4223
4236
  }
4224
4237
 
4225
4238
  // log:trace
@@ -4233,7 +4246,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4233
4246
  const yStr = termToN3(g.o, pref);
4234
4247
 
4235
4248
  trace.writeTraceLine(`${xStr} TRACE ${yStr}`);
4236
- return [{ ...subst }];
4249
+ return [__cloneSubst(subst)];
4237
4250
  }
4238
4251
 
4239
4252
  // log:outputString
@@ -4247,7 +4260,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4247
4260
  if (g.o instanceof Var) return [];
4248
4261
  const s = termToJsString(g.o);
4249
4262
  if (s === null) return [];
4250
- return [{ ...subst }];
4263
+ return [__cloneSubst(subst)];
4251
4264
  }
4252
4265
 
4253
4266
  // log:collectAllIn (scoped)
@@ -4262,7 +4275,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4262
4275
  // - object = Var: treat as priority 1 (do not bind)
4263
4276
  // - any other object: backward-compatible default priority 1
4264
4277
 
4265
- const outSubst = { ...subst };
4278
+ const outSubst = __cloneSubst(subst);
4266
4279
  let scopeFacts = null;
4267
4280
  let scopeBackRules = backRules;
4268
4281
 
@@ -4309,7 +4322,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4309
4322
  rawClauseTerm instanceof GraphTerm
4310
4323
  ? __prepareQuotedPatternTriples(rawClauseTerm, subst, varGen)
4311
4324
  : __prepareQuotedPatternTriples(clauseTerm, subst, varGen);
4312
- const sols = proveGoals(clauseGoals, {}, scopeFacts, scopeBackRules, depth + 1, visited2, varGen);
4325
+ const sols = proveGoals(clauseGoals, __emptySubst(), scopeFacts, scopeBackRules, depth + 1, visited2, varGen);
4313
4326
 
4314
4327
  const collected = sols.map((sBody) => applySubstTerm(valueTempl, sBody));
4315
4328
  const collectedList = new ListTerm(collected);
@@ -4326,7 +4339,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4326
4339
 
4327
4340
  // See log:collectAllIn above for the priority / closure semantics.
4328
4341
 
4329
- const outSubst = { ...subst };
4342
+ const outSubst = __cloneSubst(subst);
4330
4343
  let scopeFacts = null;
4331
4344
  let scopeBackRules = backRules;
4332
4345
 
@@ -4374,7 +4387,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4374
4387
  : __prepareQuotedPatternTriples(thenClause, subst, varGen);
4375
4388
 
4376
4389
  const visited1 = [];
4377
- const sols1 = proveGoals(whereGoals, {}, scopeFacts, scopeBackRules, depth + 1, visited1, varGen);
4390
+ const sols1 = proveGoals(whereGoals, __emptySubst(), scopeFacts, scopeBackRules, depth + 1, visited1, varGen);
4378
4391
 
4379
4392
  for (const s1 of sols1) {
4380
4393
  const visited2 = [];
@@ -4426,7 +4439,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4426
4439
  const sOk = g.s instanceof Var || g.s instanceof Blank || g.s instanceof Iri;
4427
4440
  const oOk = g.o instanceof Var || g.o instanceof Blank || g.o instanceof Literal;
4428
4441
  if (!sOk || !oOk) return [];
4429
- return [{ ...subst }];
4442
+ return [__cloneSubst(subst)];
4430
4443
  }
4431
4444
 
4432
4445
  // -----------------------------------------------------------------
@@ -4445,7 +4458,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4445
4458
  const lit = makeStringLiteral(parts.join(''));
4446
4459
 
4447
4460
  if (g.o instanceof Var) {
4448
- const s2 = { ...subst };
4461
+ const s2 = __cloneSubst(subst);
4449
4462
  s2[g.o.name] = lit;
4450
4463
  return [s2];
4451
4464
  }
@@ -4458,7 +4471,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4458
4471
  const sStr = termToJsString(g.s);
4459
4472
  const oStr = termToJsString(g.o);
4460
4473
  if (sStr === null || oStr === null) return [];
4461
- return sStr.includes(oStr) ? [{ ...subst }] : [];
4474
+ return sStr.includes(oStr) ? [__cloneSubst(subst)] : [];
4462
4475
  }
4463
4476
 
4464
4477
  // string:containsIgnoringCase
@@ -4466,7 +4479,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4466
4479
  const sStr = termToJsString(g.s);
4467
4480
  const oStr = termToJsString(g.o);
4468
4481
  if (sStr === null || oStr === null) return [];
4469
- return sStr.toLowerCase().includes(oStr.toLowerCase()) ? [{ ...subst }] : [];
4482
+ return sStr.toLowerCase().includes(oStr.toLowerCase()) ? [__cloneSubst(subst)] : [];
4470
4483
  }
4471
4484
 
4472
4485
  // string:endsWith
@@ -4474,7 +4487,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4474
4487
  const sStr = termToJsString(g.s);
4475
4488
  const oStr = termToJsString(g.o);
4476
4489
  if (sStr === null || oStr === null) return [];
4477
- return sStr.endsWith(oStr) ? [{ ...subst }] : [];
4490
+ return sStr.endsWith(oStr) ? [__cloneSubst(subst)] : [];
4478
4491
  }
4479
4492
 
4480
4493
  // string:equalIgnoringCase
@@ -4482,7 +4495,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4482
4495
  const sStr = termToJsString(g.s);
4483
4496
  const oStr = termToJsString(g.o);
4484
4497
  if (sStr === null || oStr === null) return [];
4485
- return sStr.toLowerCase() === oStr.toLowerCase() ? [{ ...subst }] : [];
4498
+ return sStr.toLowerCase() === oStr.toLowerCase() ? [__cloneSubst(subst)] : [];
4486
4499
  }
4487
4500
 
4488
4501
  // string:format
@@ -4502,7 +4515,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4502
4515
 
4503
4516
  const lit = makeStringLiteral(formatted);
4504
4517
  if (g.o instanceof Var) {
4505
- const s2 = { ...subst };
4518
+ const s2 = __cloneSubst(subst);
4506
4519
  s2[g.o.name] = lit;
4507
4520
  return [s2];
4508
4521
  }
@@ -4515,7 +4528,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4515
4528
  const sStr = termToJsString(g.s);
4516
4529
  const oStr = termToJsString(g.o);
4517
4530
  if (sStr === null || oStr === null) return [];
4518
- return sStr > oStr ? [{ ...subst }] : [];
4531
+ return sStr > oStr ? [__cloneSubst(subst)] : [];
4519
4532
  }
4520
4533
 
4521
4534
  // string:lessThan
@@ -4523,7 +4536,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4523
4536
  const sStr = termToJsString(g.s);
4524
4537
  const oStr = termToJsString(g.o);
4525
4538
  if (sStr === null || oStr === null) return [];
4526
- return sStr < oStr ? [{ ...subst }] : [];
4539
+ return sStr < oStr ? [__cloneSubst(subst)] : [];
4527
4540
  }
4528
4541
 
4529
4542
  // string:matches
@@ -4533,7 +4546,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4533
4546
  if (sStr === null || pattern === null) return [];
4534
4547
  const re = compileSwapRegex(pattern, '');
4535
4548
  if (!re) return [];
4536
- return re.test(sStr) ? [{ ...subst }] : [];
4549
+ return re.test(sStr) ? [__cloneSubst(subst)] : [];
4537
4550
  }
4538
4551
 
4539
4552
  // string:notEqualIgnoringCase
@@ -4541,7 +4554,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4541
4554
  const sStr = termToJsString(g.s);
4542
4555
  const oStr = termToJsString(g.o);
4543
4556
  if (sStr === null || oStr === null) return [];
4544
- return sStr.toLowerCase() !== oStr.toLowerCase() ? [{ ...subst }] : [];
4557
+ return sStr.toLowerCase() !== oStr.toLowerCase() ? [__cloneSubst(subst)] : [];
4545
4558
  }
4546
4559
 
4547
4560
  // string:notGreaterThan (≤ in Unicode code order)
@@ -4549,7 +4562,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4549
4562
  const sStr = termToJsString(g.s);
4550
4563
  const oStr = termToJsString(g.o);
4551
4564
  if (sStr === null || oStr === null) return [];
4552
- return sStr <= oStr ? [{ ...subst }] : [];
4565
+ return sStr <= oStr ? [__cloneSubst(subst)] : [];
4553
4566
  }
4554
4567
 
4555
4568
  // string:notLessThan (≥ in Unicode code order)
@@ -4557,7 +4570,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4557
4570
  const sStr = termToJsString(g.s);
4558
4571
  const oStr = termToJsString(g.o);
4559
4572
  if (sStr === null || oStr === null) return [];
4560
- return sStr >= oStr ? [{ ...subst }] : [];
4573
+ return sStr >= oStr ? [__cloneSubst(subst)] : [];
4561
4574
  }
4562
4575
 
4563
4576
  // string:notMatches
@@ -4567,7 +4580,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4567
4580
  if (sStr === null || pattern === null) return [];
4568
4581
  const re = compileSwapRegex(pattern, '');
4569
4582
  if (!re) return [];
4570
- return re.test(sStr) ? [] : [{ ...subst }];
4583
+ return re.test(sStr) ? [] : [__cloneSubst(subst)];
4571
4584
  }
4572
4585
 
4573
4586
  // string:replace
@@ -4585,7 +4598,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4585
4598
  const lit = makeStringLiteral(outStr);
4586
4599
 
4587
4600
  if (g.o instanceof Var) {
4588
- const s2 = { ...subst };
4601
+ const s2 = __cloneSubst(subst);
4589
4602
  s2[g.o.name] = lit;
4590
4603
  return [s2];
4591
4604
  }
@@ -4610,7 +4623,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4610
4623
  const lit = makeStringLiteral(group);
4611
4624
 
4612
4625
  if (g.o instanceof Var) {
4613
- const s2 = { ...subst };
4626
+ const s2 = __cloneSubst(subst);
4614
4627
  s2[g.o.name] = lit;
4615
4628
  return [s2];
4616
4629
  }
@@ -4623,7 +4636,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4623
4636
  const sStr = termToJsString(g.s);
4624
4637
  const oStr = termToJsString(g.o);
4625
4638
  if (sStr === null || oStr === null) return [];
4626
- return sStr.startsWith(oStr) ? [{ ...subst }] : [];
4639
+ return sStr.startsWith(oStr) ? [__cloneSubst(subst)] : [];
4627
4640
  }
4628
4641
 
4629
4642
  // -----------------------------------------------------------------
@@ -4638,7 +4651,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4638
4651
  if (sStr === null) return [];
4639
4652
  const lit = internLiteral(String(sStr.length));
4640
4653
  if (g.o instanceof Var) {
4641
- const s2 = { ...subst };
4654
+ const s2 = __cloneSubst(subst);
4642
4655
  s2[g.o.name] = lit;
4643
4656
  return [s2];
4644
4657
  }
@@ -4658,7 +4671,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4658
4671
  const ch = idx < 0 || idx >= sStr.length ? '' : sStr.charAt(idx);
4659
4672
  const lit = makeStringLiteral(ch);
4660
4673
  if (g.o instanceof Var) {
4661
- const s2 = { ...subst };
4674
+ const s2 = __cloneSubst(subst);
4662
4675
  s2[g.o.name] = lit;
4663
4676
  return [s2];
4664
4677
  }
@@ -4681,7 +4694,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4681
4694
  if (idx >= 0 && idx < sStr.length) out = sStr.slice(0, idx) + rep + sStr.slice(idx + 1);
4682
4695
  const lit = makeStringLiteral(out);
4683
4696
  if (g.o instanceof Var) {
4684
- const s2 = { ...subst };
4697
+ const s2 = __cloneSubst(subst);
4685
4698
  s2[g.o.name] = lit;
4686
4699
  return [s2];
4687
4700
  }
@@ -5886,6 +5899,19 @@ const deref = require('./deref');
5886
5899
 
5887
5900
  const hasOwn = Object.prototype.hasOwnProperty;
5888
5901
 
5902
+ function __emptySubst() {
5903
+ return Object.create(null);
5904
+ }
5905
+
5906
+ function __cloneSubst(subst) {
5907
+ if (!subst) return __emptySubst();
5908
+ const out = Object.create(null);
5909
+ for (const k in subst) {
5910
+ if (hasOwn.call(subst, k)) out[k] = subst[k];
5911
+ }
5912
+ return out;
5913
+ }
5914
+
5889
5915
  let version = 'dev';
5890
5916
  try {
5891
5917
  // Node: keep package.json version if available
@@ -7097,7 +7123,7 @@ function pushFactIndexed(facts, tr) {
7097
7123
 
7098
7124
  function makeDerivedRecord(fact, rule, premises, subst, captureExplanations) {
7099
7125
  if (captureExplanations === false) return { fact };
7100
- return new DerivedFact(fact, rule, premises.slice(), { ...subst });
7126
+ return new DerivedFact(fact, rule, premises.slice(), __cloneSubst(subst));
7101
7127
  }
7102
7128
 
7103
7129
  function ensureBackRuleIndexes(backRules) {
@@ -7605,6 +7631,21 @@ function unifyOpenWithList(prefix, tailv, ys, subst) {
7605
7631
  return s2;
7606
7632
  }
7607
7633
 
7634
+ function graphTriplesContainVars(triples) {
7635
+ function termHasVar(t) {
7636
+ if (t instanceof Var) return true;
7637
+ if (t instanceof ListTerm) return t.elems.some(termHasVar);
7638
+ if (t instanceof OpenListTerm) return t.prefix.some(termHasVar) || true;
7639
+ if (t instanceof GraphTerm) return t.triples.some((tr) => termHasVar(tr.s) || termHasVar(tr.p) || termHasVar(tr.o));
7640
+ return false;
7641
+ }
7642
+
7643
+ for (const tr of triples) {
7644
+ if (termHasVar(tr.s) || termHasVar(tr.p) || termHasVar(tr.o)) return true;
7645
+ }
7646
+ return false;
7647
+ }
7648
+
7608
7649
  function unifyGraphTriples(xs, ys, subst) {
7609
7650
  if (xs.length !== ys.length) return null;
7610
7651
 
@@ -7673,7 +7714,7 @@ function unifyTermWithOptions(a, b, subst, opts) {
7673
7714
  const t = b;
7674
7715
  if (t instanceof Var && t.name === v) return subst;
7675
7716
  if (containsVarTerm(t, v)) return null;
7676
- const s2 = { ...subst };
7717
+ const s2 = __cloneSubst(subst);
7677
7718
  s2[v] = t;
7678
7719
  return s2;
7679
7720
  }
@@ -7766,17 +7807,22 @@ function unifyTermWithOptions(a, b, subst, opts) {
7766
7807
 
7767
7808
  // Graphs
7768
7809
  if (a instanceof GraphTerm && b instanceof GraphTerm) {
7769
- const protectedNamesA = collectProtectedNamesForTerm(aRaw, subst);
7770
- const protectedNamesB = collectProtectedNamesForTerm(bRaw, subst);
7771
- if (
7772
- alphaEqGraphTriples(a.triples, b.triples, {
7773
- protectedVarsA: protectedNamesA.protectedVars,
7774
- protectedVarsB: protectedNamesB.protectedVars,
7775
- protectedBlanksA: protectedNamesA.protectedBlanks,
7776
- protectedBlanksB: protectedNamesB.protectedBlanks,
7777
- })
7778
- ) {
7779
- return subst;
7810
+ // Only use alpha-equivalence as a binding-free fast path when both quoted
7811
+ // formulas are variable-free after substitution. If unbound variables remain,
7812
+ // they may be shared with the outer rule and must unify/bind structurally.
7813
+ if (!graphTriplesContainVars(a.triples) && !graphTriplesContainVars(b.triples)) {
7814
+ const protectedNamesA = collectProtectedNamesForTerm(aRaw, subst);
7815
+ const protectedNamesB = collectProtectedNamesForTerm(bRaw, subst);
7816
+ if (
7817
+ alphaEqGraphTriples(a.triples, b.triples, {
7818
+ protectedVarsA: protectedNamesA.protectedVars,
7819
+ protectedVarsB: protectedNamesB.protectedVars,
7820
+ protectedBlanksA: protectedNamesA.protectedBlanks,
7821
+ protectedBlanksB: protectedNamesB.protectedBlanks,
7822
+ })
7823
+ ) {
7824
+ return subst;
7825
+ }
7780
7826
  }
7781
7827
  return unifyGraphTriples(a.triples, b.triples, subst);
7782
7828
  }
@@ -7858,7 +7904,7 @@ function gcCompactForGoals(subst, goals, answerVars) {
7858
7904
  }
7859
7905
  }
7860
7906
 
7861
- const out = {};
7907
+ const out = __emptySubst();
7862
7908
  for (const k of Object.keys(subst)) {
7863
7909
  if (keep.has(k)) out[k] = subst[k];
7864
7910
  }
@@ -7921,7 +7967,7 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen, maxR
7921
7967
  const allowDeferredBuiltins = !!(opts && opts.deferBuiltins);
7922
7968
 
7923
7969
  const initialGoals = Array.isArray(goals) ? goals.slice() : [];
7924
- const substMut = subst ? { ...subst } : {};
7970
+ const substMut = subst ? __cloneSubst(subst) : {};
7925
7971
  const initialVisited = visited ? visited.slice() : [];
7926
7972
 
7927
7973
  // Variables from the original goal list (needed by the caller to instantiate conclusions)
@@ -8157,17 +8203,22 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen, maxR
8157
8203
 
8158
8204
  // Graphs
8159
8205
  if (a instanceof GraphTerm && b instanceof GraphTerm) {
8160
- const protectedNamesA = collectProtectedNamesForTerm(aRaw, substMut);
8161
- const protectedNamesB = collectProtectedNamesForTerm(bRaw, substMut);
8162
- if (
8163
- alphaEqGraphTriples(a.triples, b.triples, {
8164
- protectedVarsA: protectedNamesA.protectedVars,
8165
- protectedVarsB: protectedNamesB.protectedVars,
8166
- protectedBlanksA: protectedNamesA.protectedBlanks,
8167
- protectedBlanksB: protectedNamesB.protectedBlanks,
8168
- })
8169
- ) {
8170
- return true;
8206
+ // Only use alpha-equivalence as a binding-free fast path when both quoted
8207
+ // formulas are variable-free after substitution. If unbound variables remain,
8208
+ // they may be shared with the outer rule and must unify/bind structurally.
8209
+ if (!graphTriplesContainVars(a.triples) && !graphTriplesContainVars(b.triples)) {
8210
+ const protectedNamesA = collectProtectedNamesForTerm(aRaw, substMut);
8211
+ const protectedNamesB = collectProtectedNamesForTerm(bRaw, substMut);
8212
+ if (
8213
+ alphaEqGraphTriples(a.triples, b.triples, {
8214
+ protectedVarsA: protectedNamesA.protectedVars,
8215
+ protectedVarsB: protectedNamesB.protectedVars,
8216
+ protectedBlanksA: protectedNamesA.protectedBlanks,
8217
+ protectedBlanksB: protectedNamesB.protectedBlanks,
8218
+ })
8219
+ ) {
8220
+ return true;
8221
+ }
8171
8222
  }
8172
8223
  const merged = unifyGraphTriples(a.triples, b.triples, substMut);
8173
8224
  if (merged === null) return false;
@@ -8378,7 +8429,7 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen, maxR
8378
8429
  const builtinMax = Number.isFinite(remaining) && !restGoals.length ? remaining : undefined;
8379
8430
 
8380
8431
  const builtinGoalForEval = goalPredicateIri === LOG_NS + 'rawType' ? rawGoal : goal0;
8381
- const builtinSubstForEval = goalPredicateIri === LOG_NS + 'rawType' ? substMut : {};
8432
+ const builtinSubstForEval = goalPredicateIri === LOG_NS + 'rawType' ? substMut : __emptySubst();
8382
8433
  let deltas = evalBuiltin(
8383
8434
  builtinGoalForEval,
8384
8435
  builtinSubstForEval,
@@ -8420,7 +8471,7 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen, maxR
8420
8471
  subjectAndObjectAreFullyUnbound &&
8421
8472
  (!restGoals.length || dc >= goalsNow.length)
8422
8473
  ) {
8423
- deltas = [{}];
8474
+ deltas = [__emptySubst()];
8424
8475
  }
8425
8476
 
8426
8477
  if (deltas.length) {
@@ -8853,7 +8904,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
8853
8904
  const r = entry.rule;
8854
8905
  if (__skipForwardRuleNow(r)) continue;
8855
8906
 
8856
- const s = unifyTriple(entry.goal, fact, {});
8907
+ const s = unifyTriple(entry.goal, fact, __emptySubst());
8857
8908
  if (s === null) continue;
8858
8909
 
8859
8910
  const outcome = __emitForwardRuleSolution(r, entry.ruleIndex, s);
@@ -13004,7 +13055,8 @@ function liftBlankRuleVars(premise, conclusion) {
13004
13055
  if (t instanceof OpenListTerm) return new OpenListTerm(t.prefix.map(convertQuotedPatternTerm), t.tailVar);
13005
13056
  if (t instanceof GraphTerm) {
13006
13057
  const triples = t.triples.map(
13007
- (tr) => new Triple(convertQuotedPatternTerm(tr.s), convertQuotedPatternTerm(tr.p), convertQuotedPatternTerm(tr.o)),
13058
+ (tr) =>
13059
+ new Triple(convertQuotedPatternTerm(tr.s), convertQuotedPatternTerm(tr.p), convertQuotedPatternTerm(tr.o)),
13008
13060
  );
13009
13061
  return copyQuotedGraphMetadata(t, new GraphTerm(triples));
13010
13062
  }
@@ -13014,13 +13066,17 @@ function liftBlankRuleVars(premise, conclusion) {
13014
13066
  function convertTerm(t, allowDirectQuotedPattern = false) {
13015
13067
  if (t instanceof Blank) return blankToVar(t.label);
13016
13068
  if (t instanceof ListTerm) return new ListTerm(t.elems.map((e) => convertTerm(e, false)));
13017
- if (t instanceof OpenListTerm) return new OpenListTerm(t.prefix.map((e) => convertTerm(e, false)), t.tailVar);
13069
+ if (t instanceof OpenListTerm)
13070
+ return new OpenListTerm(
13071
+ t.prefix.map((e) => convertTerm(e, false)),
13072
+ t.tailVar,
13073
+ );
13018
13074
  if (t instanceof GraphTerm) return allowDirectQuotedPattern ? convertQuotedPatternTerm(t) : copyQuotedTerm(t);
13019
13075
  return t;
13020
13076
  }
13021
13077
 
13022
- const newPremise = premise.map((tr) =>
13023
- new Triple(convertTerm(tr.s, true), convertTerm(tr.p, true), convertTerm(tr.o, true)),
13078
+ const newPremise = premise.map(
13079
+ (tr) => new Triple(convertTerm(tr.s, true), convertTerm(tr.p, true), convertTerm(tr.o, true)),
13024
13080
  );
13025
13081
  return [newPremise, conclusion];
13026
13082
  }