@velarscript/web 0.12.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +2 -2
  2. package/dist/analyzer.d.ts +164 -23
  3. package/dist/analyzer.d.ts.map +1 -1
  4. package/dist/analyzer.js +1254 -133
  5. package/dist/analyzer.js.map +1 -1
  6. package/dist/ast.d.ts +11 -0
  7. package/dist/ast.d.ts.map +1 -1
  8. package/dist/ast.js +22 -0
  9. package/dist/ast.js.map +1 -1
  10. package/dist/compiler.d.ts.map +1 -1
  11. package/dist/compiler.js +69 -44
  12. package/dist/compiler.js.map +1 -1
  13. package/dist/css-string.d.ts +32 -0
  14. package/dist/css-string.d.ts.map +1 -0
  15. package/dist/css-string.js +66 -0
  16. package/dist/css-string.js.map +1 -0
  17. package/dist/css-tokens.d.ts +16 -0
  18. package/dist/css-tokens.d.ts.map +1 -1
  19. package/dist/css-tokens.js +84 -0
  20. package/dist/css-tokens.js.map +1 -1
  21. package/dist/elements.d.ts +55 -0
  22. package/dist/elements.d.ts.map +1 -1
  23. package/dist/elements.js +152 -0
  24. package/dist/elements.js.map +1 -1
  25. package/dist/emitter.d.ts +10 -0
  26. package/dist/emitter.d.ts.map +1 -1
  27. package/dist/emitter.js +588 -159
  28. package/dist/emitter.js.map +1 -1
  29. package/dist/keyframes.d.ts +17 -0
  30. package/dist/keyframes.d.ts.map +1 -1
  31. package/dist/keyframes.js +28 -9
  32. package/dist/keyframes.js.map +1 -1
  33. package/dist/lexer.d.ts +21 -7
  34. package/dist/lexer.d.ts.map +1 -1
  35. package/dist/lexer.js +32 -12
  36. package/dist/lexer.js.map +1 -1
  37. package/dist/look-static.d.ts.map +1 -1
  38. package/dist/look-static.js +120 -20
  39. package/dist/look-static.js.map +1 -1
  40. package/dist/look.d.ts +15 -0
  41. package/dist/look.d.ts.map +1 -1
  42. package/dist/look.js +7 -2
  43. package/dist/look.js.map +1 -1
  44. package/dist/parser.d.ts.map +1 -1
  45. package/dist/parser.js +15 -20
  46. package/dist/parser.js.map +1 -1
  47. package/dist/runtime-foundation.d.ts.map +1 -1
  48. package/dist/runtime-foundation.js +321 -70
  49. package/dist/runtime-foundation.js.map +1 -1
  50. package/dist/runtime.d.ts.map +1 -1
  51. package/dist/runtime.js +320 -179
  52. package/dist/runtime.js.map +1 -1
  53. package/dist/stable-order.d.ts +16 -0
  54. package/dist/stable-order.d.ts.map +1 -0
  55. package/dist/stable-order.js +18 -0
  56. package/dist/stable-order.js.map +1 -0
  57. package/dist/types.d.ts +0 -1
  58. package/dist/types.d.ts.map +1 -1
  59. package/dist/types.js +0 -16
  60. package/dist/types.js.map +1 -1
  61. package/dist/websocket-runtime.d.ts.map +1 -1
  62. package/dist/websocket-runtime.js +9 -5
  63. package/dist/websocket-runtime.js.map +1 -1
  64. package/dist/worker-runtime.d.ts.map +1 -1
  65. package/dist/worker-runtime.js +9 -7
  66. package/dist/worker-runtime.js.map +1 -1
  67. package/package.json +2 -2
@@ -432,6 +432,8 @@ const __velarGraphNativeWeakMap = globalThis.WeakMap;
432
432
  const __velarGraphNativeProxy = globalThis.Proxy;
433
433
  const __velarGraphReflectApply = Object.getOwnPropertyDescriptor(Reflect, "apply")?.value;
434
434
  const __velarGraphArrayIsArray = Object.getOwnPropertyDescriptor(Array, "isArray")?.value;
435
+ const __velarGraphArrayPrototype = Object.getOwnPropertyDescriptor(__velarGraphNativeArray, "prototype")?.value;
436
+ const __velarGraphArraySort = __velarGraphArrayPrototype && Object.getOwnPropertyDescriptor(__velarGraphArrayPrototype, "sort")?.value;
435
437
  const __velarGraphObjectIs = Object.getOwnPropertyDescriptor(Object, "is")?.value;
436
438
  const __velarGraphObjectFreeze = Object.getOwnPropertyDescriptor(Object, "freeze")?.value;
437
439
  const __velarGraphObjectDefineProperty = Object.getOwnPropertyDescriptor(Object, "defineProperty")?.value;
@@ -509,6 +511,7 @@ function __velarGraphWeakMapRead(value, key) { return __velarGraphApply(__velarG
509
511
  function __velarGraphWeakMapWrite(value, key, item) { return __velarGraphApply(__velarGraphWeakMapSet, value, [key, item], "WeakMap.set"); }
510
512
  function __velarGraphWeakMapRemove(value, key) { return __velarGraphApply(__velarGraphWeakMapDelete, value, [key], "WeakMap.delete"); }
511
513
  function __velarGraphIsList(value) { return __velarGraphApply(__velarGraphArrayIsArray, __velarGraphNativeArray, [value], "Array.isArray"); }
514
+ function __velarGraphOrder(value, compare) { return __velarGraphApply(__velarGraphArraySort, value, [compare], "Array.sort"); }
512
515
  function __velarGraphSame(left, right) { return __velarGraphApply(__velarGraphObjectIs, __velarGraphNativeObject, [left, right], "Object.is"); }
513
516
  function __velarGraphFreeze(value) { return __velarGraphApply(__velarGraphObjectFreeze, __velarGraphNativeObject, [value], "Object.freeze"); }
514
517
  function __velarGraphDefine(value, key, descriptor) { return __velarGraphApply(__velarGraphObjectDefineProperty, __velarGraphNativeObject, [value, key, descriptor], "Object.defineProperty"); }
@@ -630,6 +633,239 @@ function __velarReportOptions(value) {
630
633
  return output;
631
634
  }
632
635
 
636
+ // D90 R21: the number stamped on each observer when it is created, and the
637
+ // order the flush runs the watch tier in -- execution order is the order the
638
+ // author wrote the watches, and "wrote" spans more than one file: watches of
639
+ // one module register as that module initializes, instances of one component
640
+ // register as they mount, and modules register in the order the import graph
641
+ // initializes them. Every one of those is registration order, so one counter
642
+ // answers all three.
643
+ //
644
+ // It has to be application-wide, not per module. Each emitted module carries
645
+ // its own copy of this runtime, so a module-scope counter would restart at zero
646
+ // in the second module and the two modules' watches would compare equal. The
647
+ // counter therefore lives on one global slot every copy reads, the same way the
648
+ // runtime registry itself does.
649
+ const __velarObserverSequenceKey = Symbol.for("velar.web.observer.sequence.v1");
650
+ const __velarObserverSequenceCell = (() => {
651
+ const descriptor = __velarGraphOwnDescriptor(globalThis, __velarObserverSequenceKey);
652
+ if (descriptor) {
653
+ if (!("value" in descriptor) || descriptor.enumerable || descriptor.configurable || descriptor.writable
654
+ || !__velarGraphIsList(descriptor.value)) {
655
+ throw new TypeError("VelarScript Web observer sequence ownership is invalid");
656
+ }
657
+ return descriptor.value;
658
+ }
659
+ const cell = [0];
660
+ __velarGraphDefine(globalThis, __velarObserverSequenceKey, {
661
+ value: cell,
662
+ enumerable: false,
663
+ configurable: false,
664
+ writable: false,
665
+ });
666
+ return cell;
667
+ })();
668
+
669
+ function __velarNextObserverSequence() {
670
+ __velarObserverSequenceCell[0] += 1;
671
+ return __velarObserverSequenceCell[0];
672
+ }
673
+
674
+ // The identity a flush stamps its run counts with, carried by an overrun into
675
+ // the flush it schedules and consumed by the first flush that starts.
676
+ let __velarOverflowToken = null;
677
+
678
+ // The three observers that have run most in the settle running now, kept as
679
+ // the run counts are stamped rather than reconstructed afterwards: the queue at
680
+ // the moment the budget runs out holds only whichever half of a cycle has not
681
+ // just run, so it cannot name the ring on its own. An observer that has run
682
+ // once is not a candidate, which is every observer of an ordinary flush.
683
+ const __velarFlushLeaders = [];
684
+
685
+ function __velarFlushLead(observer) {
686
+ let kept = 0;
687
+ for (let index = 0; index < __velarFlushLeaders.length; index += 1) {
688
+ if (__velarFlushLeaders[index] === observer) continue;
689
+ __velarFlushLeaders[kept] = __velarFlushLeaders[index];
690
+ kept += 1;
691
+ }
692
+ __velarFlushLeaders.length = kept;
693
+ let position = kept;
694
+ while (position > 0 && __velarFlushLeaders[position - 1].flushRuns < observer.flushRuns) {
695
+ __velarFlushLeaders[position] = __velarFlushLeaders[position - 1];
696
+ position -= 1;
697
+ }
698
+ __velarFlushLeaders[position] = observer;
699
+ if (__velarFlushLeaders.length > 3) __velarFlushLeaders.length = 3;
700
+ }
701
+
702
+ // D90 R21: compile time no longer refuses two watches that write one state, so
703
+ // this budget is the only gate left against a mutual-write cycle -- and a
704
+ // report that names nobody hands the author a number with no line to go to.
705
+ // The observers that ran most in this flush chain are the cycle; flushRuns
706
+ // already counts exactly that, so the highest few are named. A watch's label is
707
+ // its subject as the author spelled it, which in a write cycle is also the
708
+ // state each watch is reacting to, and the scope it was registered in supplies
709
+ // the component the report has always had a field for.
710
+ //
711
+ // Nothing here asks who wrote what. That question is gone from the language,
712
+ // and answering it would need the write frames R21 deleted.
713
+ function __velarFlushRunaway(stalled, token) {
714
+ const ranked = [];
715
+ const seen = __velarGraphCreateSet();
716
+ const consider = (observer) => {
717
+ if (observer.flushToken !== token || __velarGraphSetContains(seen, observer)) return;
718
+ __velarGraphSetInsert(seen, observer);
719
+ ranked[ranked.length] = observer;
720
+ };
721
+ // A cycle alternates, so the queue at the instant the budget ran out holds
722
+ // only the half of the ring that has not just run. The leaders carry the
723
+ // other half.
724
+ for (let index = 0; index < __velarFlushLeaders.length; index += 1) consider(__velarFlushLeaders[index]);
725
+ for (let index = 0; index < stalled.length; index += 1) consider(stalled[index]);
726
+ __velarGraphOrder(ranked, (left, right) => right.flushRuns - left.flushRuns);
727
+ const limit = ranked.length < 3 ? ranked.length : 3;
728
+ let detail = "";
729
+ let component = "";
730
+ for (let index = 0; index < limit; index += 1) {
731
+ const observer = ranked[index];
732
+ const named = observer.mode === "watch" && typeof observer.label === "string" && observer.label !== ""
733
+ ? "the watch on '" + observer.label + "'"
734
+ : "a " + observer.mode + " observer";
735
+ detail += (detail === "" ? "Ran most in this flush: " : ", ") + named
736
+ + " (" + observer.flushRuns + (observer.flushRuns === 1 ? " run)" : " runs)");
737
+ if (component === "" && typeof observer.component === "string") component = observer.component;
738
+ }
739
+ return { detail, component };
740
+ }
741
+
742
+ function __velarScheduleFlush() {
743
+ if (__velarRuntime.flushPending) return;
744
+ __velarRuntime.flushPending = true;
745
+ __velarEnqueue(__velarFlush);
746
+ }
747
+
748
+ // Only the observers that actually re-entered the queue during the overrun
749
+ // belong to the runaway cycle. An observer queued once by an unrelated write
750
+ // in the same turn is innocent, and stopping it is irreversible, so it is
751
+ // put back for the next microtask instead.
752
+ //
753
+ // Every overrun must still remove something, or the requeue turns the
754
+ // microtask chain into the freeze the budget exists to end: a cycle spread
755
+ // across more observers than the budget can run four times over re-enters
756
+ // none of them four times, so a fixed threshold of four stops nobody and the
757
+ // next flush repeats it forever. Two rules make the progress unconditional.
758
+ // The threshold falls to the highest run count actually present, so any
759
+ // queued observer this flush chain has already run is stopped; and the token
760
+ // is carried into the flush the overrun schedules, so run counts accumulate
761
+ // across the chain and the observers the chain has not reached yet are a
762
+ // pool that only shrinks.
763
+ function __velarFlushOverflow(token) {
764
+ const stalled = [];
765
+ for (const observer of __velarGraphSetItems(__velarRuntime.domQueue)) stalled[stalled.length] = observer;
766
+ for (const observer of __velarGraphSetItems(__velarRuntime.watchQueue)) stalled[stalled.length] = observer;
767
+ __velarGraphSetEmpty(__velarRuntime.domQueue);
768
+ __velarGraphSetEmpty(__velarRuntime.watchQueue);
769
+ let threshold = 0;
770
+ for (let index = 0; index < stalled.length; index += 1) {
771
+ const observer = stalled[index];
772
+ if (observer.flushToken === token && observer.flushRuns > threshold) threshold = observer.flushRuns;
773
+ }
774
+ if (threshold > 4) threshold = 4;
775
+ let requeued = false;
776
+ for (let index = 0; index < stalled.length; index += 1) {
777
+ const observer = stalled[index];
778
+ if (threshold > 0 && observer.flushToken === token && observer.flushRuns >= threshold) {
779
+ if (typeof observer.stop === "function") observer.stop();
780
+ else observer.stopped = true;
781
+ continue;
782
+ }
783
+ if (observer.stopped) continue;
784
+ __velarGraphSetInsert(observer.mode === "watch" ? __velarRuntime.watchQueue : __velarRuntime.domQueue, observer);
785
+ requeued = true;
786
+ }
787
+ const runaway = __velarFlushRunaway(stalled, token);
788
+ __velarRuntime.report(new RangeError("Reactive updates cannot run more than 100000 observers in one flush"),
789
+ { phase: "update", detail: runaway.detail, component: runaway.component, unhandled: true });
790
+ if (requeued) {
791
+ __velarOverflowToken = token;
792
+ __velarScheduleFlush();
793
+ }
794
+ }
795
+
796
+ // The one entry point: the microtask __velarScheduleFlush enqueues, and the
797
+ // synchronous drain the emitted tick() performs. It used to open and close D90
798
+ // R1-a-scope's flush-scoped writer registry around the settle; R21 deleted the
799
+ // registry with the referee that read it, so the name is all that remains and
800
+ // it remains because __velarEnqueue and tick() both hold it.
801
+ function __velarFlush() {
802
+ __velarFlushSettle();
803
+ }
804
+
805
+ // Glitch-free order: every derived value and every watch settles to a fixed
806
+ // point before a single DOM node is written, so no watch and no rendered
807
+ // position can read a half-updated world and no corrective watch can push an
808
+ // invalid value through the DOM first. That is D90 R1 and it is unchanged.
809
+ //
810
+ // D90 R21 changed the other axis. Within the settle the watches run in
811
+ // registration order -- source order in a module, mount order across instances
812
+ // of one component, module-initialization order across modules -- and a watch
813
+ // that writes state is not promoted ahead of one that only observes. Whoever is
814
+ // written first runs first, two watches writing one state both take effect in
815
+ // that order, and a later write that clobbers an earlier one is the author's.
816
+ //
817
+ // Queue insertion order is not registration order and cannot stand in for it:
818
+ // two watches on two different states, both dirtied by one action, enter the
819
+ // queue in the order the action wrote the states. So each pass takes the queue
820
+ // as it stands and runs it by sequence number. Watches queued by this pass are
821
+ // picked up by the next one, in their own order.
822
+ function __velarFlushSettle() {
823
+ __velarRuntime.flushPending = false;
824
+ const token = __velarOverflowToken === null ? {} : __velarOverflowToken;
825
+ __velarOverflowToken = null;
826
+ __velarFlushLeaders.length = 0;
827
+ let budget = 100000;
828
+ const step = (observer) => {
829
+ __velarGraphSetRemove(observer.mode === "watch" ? __velarRuntime.watchQueue : __velarRuntime.domQueue, observer);
830
+ if (observer.flushToken === token) observer.flushRuns += 1;
831
+ else { observer.flushToken = token; observer.flushRuns = 1; }
832
+ if (observer.flushRuns > 1) __velarFlushLead(observer);
833
+ observer.run();
834
+ };
835
+ while (__velarGraphSetCount(__velarRuntime.domQueue) || __velarGraphSetCount(__velarRuntime.watchQueue)) {
836
+ while (true) {
837
+ let ran = false;
838
+ for (const observer of __velarGraphSetItems(__velarRuntime.domQueue)) {
839
+ if (observer.mode !== "computed") continue;
840
+ if ((budget -= 1) < 0) { __velarFlushOverflow(token); return; }
841
+ step(observer);
842
+ ran = true;
843
+ }
844
+ if (ran) continue;
845
+ const pending = [];
846
+ for (const observer of __velarGraphSetItems(__velarRuntime.watchQueue)) pending[pending.length] = observer;
847
+ __velarGraphOrder(pending, (left, right) => left.sequence - right.sequence);
848
+ for (let index = 0; index < pending.length; index += 1) {
849
+ const observer = pending[index];
850
+ // A watch this pass already re-ran or stopped is no longer queued; the
851
+ // snapshot is a plan, and the queue is what says whether it still owes
852
+ // a run.
853
+ if (observer.stopped || !__velarGraphSetContains(__velarRuntime.watchQueue, observer)) continue;
854
+ if ((budget -= 1) < 0) { __velarFlushOverflow(token); return; }
855
+ step(observer);
856
+ ran = true;
857
+ }
858
+ if (!ran) break;
859
+ }
860
+ for (const observer of __velarGraphSetItems(__velarRuntime.domQueue)) {
861
+ if (observer.mode === "computed") break;
862
+ if ((budget -= 1) < 0) { __velarFlushOverflow(token); return; }
863
+ step(observer);
864
+ }
865
+ }
866
+ if (__velarGraphSetCount(__velarRuntime.domQueue) || __velarGraphSetCount(__velarRuntime.watchQueue)) __velarScheduleFlush();
867
+ }
868
+
633
869
  function __velarCreateRuntime() {
634
870
  const runtime = Object.create(null);
635
871
  const domQueue = Object.freeze(__velarGraphCreateSet());
@@ -756,14 +992,23 @@ function __velarCreateRuntime() {
756
992
  };
757
993
  const link = (child, parent) => {
758
994
  if (child === null || (typeof child !== "object" && typeof child !== "function")) return;
759
- linkOwner(toRaw(child), parent);
995
+ linkOwner(toRaw(child), parent, true);
760
996
  };
761
- const linkOwner = (child, parent) => {
997
+ // 'structural' separates a slot being filled from a slot merely being read.
998
+ // A read proves the child is in the parent right now; only a write adds an
999
+ // occurrence, and only a write can create the duplicate the running count
1000
+ // cannot see for itself.
1001
+ const linkOwner = (child, parent, structural = false) => {
762
1002
  parent = toRaw(parent);
763
1003
  if (parent === null || (typeof parent !== "object" && typeof parent !== "function") || child === parent) return;
764
1004
  let owners = __velarGraphWeakMapRead(parents, child);
765
1005
  if (!owners) { owners = __velarGraphCreateSet(); __velarGraphWeakMapWrite(parents, child, owners); }
1006
+ const known = __velarGraphSetContains(owners, parent);
766
1007
  __velarGraphSetInsert(owners, parent);
1008
+ const counts = __velarGraphWeakMapRead(containment, parent);
1009
+ if (!counts) return;
1010
+ if (structural && known) { __velarGraphWeakMapRemove(containment, parent); return; }
1011
+ if ((__velarGraphMapRead(counts, child) ?? 0) < 1) __velarGraphMapWrite(counts, child, 1);
767
1012
  };
768
1013
  // Removing the last owner of a value detaches everything only that value
769
1014
  // owned. Without the cascade, replacing a state root leaves every surviving
@@ -850,43 +1095,74 @@ function __velarCreateRuntime() {
850
1095
  if (value !== null && (typeof value === "object" || typeof value === "function")) visit(value);
851
1096
  }
852
1097
  };
853
- const contains = (parent, child) => {
854
- parent = toRaw(parent);
855
- child = toRaw(child);
1098
+ // Containment used to be re-derived by scanning the whole container on every
1099
+ // element write, which made one assignment into a rendered List cost O(list
1100
+ // length). The occurrence index answers the same question -- does the parent
1101
+ // still reference this child anywhere? -- in constant time: it is built once
1102
+ // from the container's real contents and then maintained by the same link and
1103
+ // release calls that change them.
1104
+ const containment = __velarGraphCreateWeakMap();
1105
+ const countOccurrence = (counts, value) => {
1106
+ value = toRaw(value);
1107
+ if (value === null || (typeof value !== "object" && typeof value !== "function")) return;
1108
+ __velarGraphMapWrite(counts, value, (__velarGraphMapRead(counts, value) ?? 0) + 1);
1109
+ };
1110
+ // Mirrors exactly what the old scan looked at, Map keys included as owned
1111
+ // values are, so the index cannot answer a question the scan answered
1112
+ // differently.
1113
+ const buildContainment = (parent) => {
1114
+ const counts = __velarGraphCreateMap();
856
1115
  if (__velarGraphIsList(parent)) {
857
- for (let index = 0; index < parent.length; index += 1) {
858
- if (toRaw(__velarGraphOwnDescriptor(parent, index)?.value) === child) return true;
859
- }
860
- return false;
1116
+ for (let index = 0; index < parent.length; index += 1) countOccurrence(counts, __velarGraphOwnDescriptor(parent, index)?.value);
1117
+ return counts;
861
1118
  }
862
1119
  const brand = collectionBrand(parent);
863
1120
  if (brand === 1) {
864
- for (const value of __velarGraphMapItems(parent)) if (toRaw(value) === child) return true;
865
- return false;
1121
+ for (const value of __velarGraphMapItems(parent)) countOccurrence(counts, value);
1122
+ return counts;
866
1123
  }
867
1124
  if (brand === 2) {
868
- for (const value of __velarGraphSetItems(parent)) if (toRaw(value) === child) return true;
869
- return false;
1125
+ for (const value of __velarGraphSetItems(parent)) countOccurrence(counts, value);
1126
+ return counts;
870
1127
  }
871
- if (!parent || typeof parent !== "object") return false;
1128
+ if (!parent || typeof parent !== "object") return counts;
872
1129
  for (const name of __velarGraphOwnNames(parent)) {
873
1130
  const descriptor = __velarGraphOwnDescriptor(parent, name);
874
- if (descriptor && "value" in descriptor && toRaw(descriptor.value) === child) return true;
1131
+ if (descriptor && "value" in descriptor) countOccurrence(counts, descriptor.value);
875
1132
  }
876
- return false;
1133
+ return counts;
877
1134
  };
878
1135
  // The one guard every detach path shares: a primitive was never linked, and
879
1136
  // an object with no owners has nothing to release, so neither may reach the
880
- // O(fields) containment scan.
1137
+ // containment bookkeeping.
881
1138
  const releaseChild = (parent, child) => {
882
1139
  if (child === null || (typeof child !== "object" && typeof child !== "function")
883
1140
  || !__velarGraphWeakMapContains(parents, child)) return;
884
- if (!contains(parent, child)) unlink(child, parent);
1141
+ parent = toRaw(parent);
1142
+ if (parent === null || (typeof parent !== "object" && typeof parent !== "function")) return;
1143
+ child = toRaw(child);
1144
+ // A freshly built index already reflects the slot that was just cleared;
1145
+ // a maintained one still counts it, so only that one takes the decrement.
1146
+ let counts = __velarGraphWeakMapRead(containment, parent);
1147
+ let remaining;
1148
+ if (counts) remaining = (__velarGraphMapRead(counts, child) ?? 0) - 1;
1149
+ else {
1150
+ counts = buildContainment(parent);
1151
+ __velarGraphWeakMapWrite(containment, parent, counts);
1152
+ remaining = __velarGraphMapRead(counts, child) ?? 0;
1153
+ }
1154
+ if (remaining > 0) { __velarGraphMapWrite(counts, child, remaining); return; }
1155
+ __velarGraphMapRemove(counts, child);
1156
+ unlink(child, parent);
885
1157
  };
886
- const reactive = (value, parent = null) => {
1158
+ // The registry operation stays exactly two parameters wide; 'structural'
1159
+ // belongs to the internal call, which knows whether a slot is being filled or
1160
+ // merely read, and every caller outside this file fills one.
1161
+ const reactive = (value, parent = null) => reactiveValue(value, parent, true);
1162
+ const reactiveValue = (value, parent, structural) => {
887
1163
  if (value === null || (typeof value !== "object" && typeof value !== "function")) return value;
888
1164
  value = toRaw(value);
889
- if (parent !== null) linkOwner(value, parent);
1165
+ if (parent !== null) linkOwner(value, parent, structural);
890
1166
  if (typeof value !== "object" || __velarGraphIsList(value) || !__velarGraphIsExtensible(value)) return value;
891
1167
  const prototype = __velarGraphPrototype(value);
892
1168
  if (prototype !== __velarGraphNativeObject.prototype && prototype !== null) return value;
@@ -895,7 +1171,7 @@ function __velarCreateRuntime() {
895
1171
  proxy = new __velarGraphNativeProxy(value, {
896
1172
  get(target, key) {
897
1173
  track(target, key);
898
- return reactive(__velarGraphGet(target, key, target), target);
1174
+ return reactiveValue(__velarGraphGet(target, key, target), target, false);
899
1175
  },
900
1176
  set(target, key, next) {
901
1177
  next = toRaw(next);
@@ -941,7 +1217,7 @@ function __velarCreateRuntime() {
941
1217
  // through them. Fresh derived Lists (filter/map/etc.) stay ephemeral and
942
1218
  // cannot become strongly retained parents of every item merely by being
943
1219
  // iterated during rendering.
944
- return reactive(child, __velarGraphWeakMapContains(parents, value) ? value : null);
1220
+ return reactiveValue(child, __velarGraphWeakMapContains(parents, value) ? value : null, false);
945
1221
  };
946
1222
  const collectionTrigger = (value, key, iterate = true, structure = false, indexFrom = null, allKeys = false) => trigger(toRaw(value), key, iterate, structure, indexFrom, allKeys);
947
1223
  const collectionUnlink = (value, child) => {
@@ -1115,52 +1391,17 @@ function __velarCreateRuntime() {
1115
1391
  try { report(error, { phase: "update", detail: "", component: "", unhandled: true }); }
1116
1392
  finally { runtime.activeObserver = previousObserver; }
1117
1393
  };
1118
- // The scheduler lives on the runtime registry itself: computed observers are
1119
- // created by whichever module stamped the registry first (velar/app under
1120
- // ESM import order, or the application prelude), and their notifications
1121
- // must schedule correctly no matter which module that was. The emitted
1122
- // application prelude keeps its own identical drain for the observers it
1123
- // creates; the shared flushPending flag keeps exactly one flush microtask
1124
- // enqueued whichever side scheduled first, and either drain runs every
1125
- // queued observer.
1126
- const scheduleFlush = () => {
1127
- if (runtime.flushPending) return;
1128
- runtime.flushPending = true;
1129
- __velarEnqueue(flush);
1130
- };
1131
- const flushOverflow = () => {
1132
- const stalled = [];
1133
- for (const observer of __velarGraphSetItems(domQueue)) stalled[stalled.length] = observer;
1134
- for (const observer of __velarGraphSetItems(watchQueue)) stalled[stalled.length] = observer;
1135
- __velarGraphSetEmpty(domQueue);
1136
- __velarGraphSetEmpty(watchQueue);
1137
- for (let index = 0; index < stalled.length; index += 1) {
1138
- const observer = stalled[index];
1139
- if (typeof observer.stop === "function") observer.stop();
1140
- else observer.stopped = true;
1141
- }
1142
- report(new RangeError("Reactive updates cannot run more than 100000 observers in one flush"), { phase: "update", detail: "", component: "", unhandled: true });
1143
- };
1144
- const flush = () => {
1145
- runtime.flushPending = false;
1146
- let budget = 100000;
1147
- for (const observer of __velarGraphSetItems(domQueue)) {
1148
- __velarGraphSetRemove(domQueue, observer);
1149
- if ((budget -= 1) < 0) { flushOverflow(); return; }
1150
- observer.run();
1151
- }
1152
- for (const observer of __velarGraphSetItems(watchQueue)) {
1153
- __velarGraphSetRemove(watchQueue, observer);
1154
- if ((budget -= 1) < 0) { flushOverflow(); return; }
1155
- observer.run();
1156
- }
1157
- if (__velarGraphSetCount(domQueue) || __velarGraphSetCount(watchQueue)) scheduleFlush();
1158
- };
1394
+ // The queue insert stays on the registry -- computed observers are created by
1395
+ // whichever module stamped it first (velar/app under ESM import order, or the
1396
+ // application prelude) and must schedule correctly whichever that was -- but
1397
+ // the drain it calls is the module-scope one below, and there is now exactly
1398
+ // one of those. A queue that outgrows its bound is the flush budget's failure
1399
+ // to own, not an exception thrown out of the assignment that happened to
1400
+ // cross the line: throwing here left the writing cell's subscriber walk half
1401
+ // finished, with the remaining observers subscribed and never notified again.
1159
1402
  const schedule = (observer) => {
1160
- const queue = observer.mode === "watch" ? watchQueue : domQueue;
1161
- if (!__velarGraphSetContains(queue, observer) && __velarGraphSetCount(queue) >= 100000) throw new RangeError("VelarScript reactive queues cannot exceed 100000 observers");
1162
- __velarGraphSetInsert(queue, observer);
1163
- scheduleFlush();
1403
+ __velarGraphSetInsert(observer.mode === "watch" ? watchQueue : domQueue, observer);
1404
+ __velarScheduleFlush();
1164
1405
  };
1165
1406
  const applyLook = (...arguments_) => {
1166
1407
  if (!lookImplementation) throw new TypeError("Link Look requires the VelarScript Web runtime");
@@ -1188,7 +1429,18 @@ function __velarCreateRuntime() {
1188
1429
  }
1189
1430
 
1190
1431
  function __velarRequireRuntime(value) {
1191
- if (!value || typeof value !== "object" || Object.getPrototypeOf(value) !== null || Object.isExtensible(value)
1432
+ if (!value || typeof value !== "object") throw new TypeError("VelarScript Web runtime ownership is invalid");
1433
+ // The schema comparison comes first, ahead of ownership and the field roster:
1434
+ // a schema bump normally changes the roster too, so leaving it last reported
1435
+ // "fields are invalid" and never named the one fact that identifies the
1436
+ // cause. The version is read through its descriptor rather than the property
1437
+ // so an unvalidated object cannot answer it with a getter.
1438
+ const __velarVersionDescriptor = Object.getOwnPropertyDescriptor(value, "version");
1439
+ const __velarVersion = __velarVersionDescriptor && "value" in __velarVersionDescriptor ? __velarVersionDescriptor.value : undefined;
1440
+ if (__velarVersion !== ${JSON.stringify(VELAR_RUNTIME_SCHEMA_VERSION)}) {
1441
+ throw new TypeError("VelarScript Web runtime schema " + (typeof __velarVersion === "string" ? __velarVersion : "(unknown)") + " does not match this module's schema ${VELAR_RUNTIME_SCHEMA_VERSION}; one build mixed two generations of @velarscript/* — run 'npm ls @velarscript/compiler' and pin one version");
1442
+ }
1443
+ if (Object.getPrototypeOf(value) !== null || Object.isExtensible(value)
1192
1444
  || Object.getOwnPropertySymbols(value).length > 0) throw new TypeError("VelarScript Web runtime ownership is invalid");
1193
1445
  const names = Object.getOwnPropertyNames(value);
1194
1446
  if (names.length !== __velarRuntimeFields.length || __velarRuntimeFields.some((name) => !names.includes(name))) {
@@ -1201,8 +1453,7 @@ function __velarRequireRuntime(value) {
1201
1453
  throw new TypeError("VelarScript Web runtime field '" + name + "' is invalid");
1202
1454
  }
1203
1455
  }
1204
- if (value.version !== ${JSON.stringify(VELAR_RUNTIME_SCHEMA_VERSION)}
1205
- || !__velarRuntimeCollection(value.domQueue, "Set") || !__velarRuntimeCollection(value.watchQueue, "Set")
1456
+ if (!__velarRuntimeCollection(value.domQueue, "Set") || !__velarRuntimeCollection(value.watchQueue, "Set")
1206
1457
  || typeof value.flushPending !== "boolean" || (value.activeObserver !== null && typeof value.activeObserver !== "object")
1207
1458
  || !__velarRuntimeCollection(value.errorHandlers, "Set") || !__velarRuntimeCollection(value.actionFailures, "WeakSet")
1208
1459
  || !__velarRuntimeCollection(value.unhandledFailures, "Set")
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-foundation.js","sourceRoot":"","sources":["../src/runtime-foundation.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iCAAiC,EACjC,0BAA0B,EAC1B,4BAA4B,GAC7B,MAAM,iCAAiC,CAAC;AAEzC,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsa7C,CAAC,SAAS,EAAE,CAAC;AAEd,MAAM,CAAC,MAAM,2BAA2B,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmGpD,CAAC,SAAS,EAAE,CAAC;AAEd,MAAM,CAAC,MAAM,2BAA2B,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;CAyBpD,CAAC,SAAS,EAAE,CAAC;AAEd,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,iCAAiC,KAAK,2BAA2B,EAAE,CAAC;AAE7G,SAAS,oBAAoB,CAAC,gBAAwB;IACpD,OAAO,MAAM,CAAC,GAAG,CAAA;EACjB,2BAA2B;EAC3B,oBAAoB;EACpB,gBAAgB;uCACqB,IAAI,CAAC,SAAS,CAAC,0BAA0B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eA0mBlE,IAAI,CAAC,SAAS,CAAC,4BAA4B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BA6BjC,IAAI,CAAC,SAAS,CAAC,4BAA4B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqCrE,CAAC,SAAS,EAAE,CAAC;AACd,CAAC;AAED,MAAM,CAAC,MAAM,sBAAsB,GAAG,oBAAoB,CAAC,sBAAsB,CAAC,CAAC;AACnF,MAAM,CAAC,MAAM,mCAAmC,GAAG,oBAAoB,CAAC,2BAA2B,CAAC,CAAC"}
1
+ {"version":3,"file":"runtime-foundation.js","sourceRoot":"","sources":["../src/runtime-foundation.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AA6eA,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CA04FvD,CAAC;AAEH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC3D;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,GAAE,qBAAqC,GAAG,MAAM,GAAG,IAAI,CAUzG"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAsgBA,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CA8/FvD,CAAC;AAEH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC3D;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,GAAE,qBAAqC,GAAG,MAAM,GAAG,IAAI,CAUzG"}