@fictjs/runtime 0.2.1 → 0.2.3

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/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  createContext,
3
3
  hasContext,
4
4
  useContext
5
- } from "./chunk-VVNMIER7.js";
5
+ } from "./chunk-3WD7QD5G.js";
6
6
  import {
7
7
  Fragment,
8
8
  batch2 as batch,
@@ -34,7 +34,7 @@ import {
34
34
  untrack2 as untrack,
35
35
  useDeferredValue,
36
36
  useTransition
37
- } from "./chunk-FOLRR3NZ.js";
37
+ } from "./chunk-YVDWXY44.js";
38
38
 
39
39
  // src/ref.ts
40
40
  function createRef() {
package/dist/internal.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
- var _chunk7EAEROZ5cjs = require('./chunk-7EAEROZ5.cjs');
3
+ var _chunk5YTFFAVUcjs = require('./chunk-5YTFFAVU.cjs');
4
4
 
5
5
 
6
6
 
@@ -66,7 +66,7 @@ var _chunk7EAEROZ5cjs = require('./chunk-7EAEROZ5.cjs');
66
66
 
67
67
 
68
68
 
69
- var _chunkMWI3USXBcjs = require('./chunk-MWI3USXB.cjs');
69
+ var _chunk2U6M3LKScjs = require('./chunk-2U6M3LKS.cjs');
70
70
 
71
71
  // src/store.ts
72
72
  var PROXY = Symbol("fict:store-proxy");
@@ -76,7 +76,7 @@ function createStore(initialValue) {
76
76
  const unwrapped = unwrap2(initialValue);
77
77
  const wrapped = wrap(unwrapped);
78
78
  function setStore(fn) {
79
- _chunkMWI3USXBcjs.batch.call(void 0, () => {
79
+ _chunk2U6M3LKScjs.batch.call(void 0, () => {
80
80
  const result = fn(wrapped);
81
81
  if (result !== void 0) {
82
82
  reconcile(wrapped, result);
@@ -176,7 +176,7 @@ function track(target, prop2) {
176
176
  let s = signals.get(prop2);
177
177
  if (!s) {
178
178
  const initial = prop2 === ITERATE_KEY ? Reflect.ownKeys(target).length : getLastValue(target, prop2);
179
- s = _chunkMWI3USXBcjs.signal.call(void 0, initial);
179
+ s = _chunk2U6M3LKScjs.signal.call(void 0, initial);
180
180
  signals.set(prop2, s);
181
181
  }
182
182
  s();
@@ -351,7 +351,7 @@ var MAX_SAFE_VERSION = 9007199254740991;
351
351
  function createVersionedSignalAccessor(initialValue) {
352
352
  let current = initialValue;
353
353
  let version = 0;
354
- const track2 = _chunkMWI3USXBcjs.signal.call(void 0, version);
354
+ const track2 = _chunk2U6M3LKScjs.signal.call(void 0, version);
355
355
  function accessor(value) {
356
356
  if (arguments.length === 0) {
357
357
  track2();
@@ -368,7 +368,7 @@ function createKeyedListContainer() {
368
368
  const endMarker = document.createComment("fict:list:end");
369
369
  const dispose = () => {
370
370
  for (const block of container.blocks.values()) {
371
- _chunkMWI3USXBcjs.destroyRoot.call(void 0, block.root);
371
+ _chunk2U6M3LKScjs.destroyRoot.call(void 0, block.root);
372
372
  }
373
373
  container.blocks.clear();
374
374
  container.nextBlocks.clear();
@@ -407,32 +407,32 @@ function createKeyedListContainer() {
407
407
  }
408
408
  function createKeyedBlock(key, item, index, render, needsIndex = true, hostRoot) {
409
409
  const itemSig = createVersionedSignalAccessor(item);
410
- const indexSig = needsIndex ? _chunkMWI3USXBcjs.signal.call(void 0, index) : ((next) => {
410
+ const indexSig = needsIndex ? _chunk2U6M3LKScjs.signal.call(void 0, index) : ((next) => {
411
411
  if (arguments.length === 0) return index;
412
412
  index = next;
413
413
  return index;
414
414
  });
415
- const root = _chunkMWI3USXBcjs.createRootContext.call(void 0, hostRoot);
416
- const prevRoot = _chunkMWI3USXBcjs.pushRoot.call(void 0, root);
415
+ const root = _chunk2U6M3LKScjs.createRootContext.call(void 0, hostRoot);
416
+ const prevRoot = _chunk2U6M3LKScjs.pushRoot.call(void 0, root);
417
417
  let nodes = [];
418
418
  let scopeDispose;
419
- const prevSub = _chunkMWI3USXBcjs.setActiveSub.call(void 0, void 0);
419
+ const prevSub = _chunk2U6M3LKScjs.setActiveSub.call(void 0, void 0);
420
420
  try {
421
- scopeDispose = _chunkMWI3USXBcjs.effectScope.call(void 0, () => {
421
+ scopeDispose = _chunk2U6M3LKScjs.effectScope.call(void 0, () => {
422
422
  const rendered = render(itemSig, indexSig, key);
423
423
  if (rendered instanceof Node || Array.isArray(rendered) && rendered.every((n) => n instanceof Node)) {
424
- nodes = _chunkMWI3USXBcjs.toNodeArray.call(void 0, rendered);
424
+ nodes = _chunk2U6M3LKScjs.toNodeArray.call(void 0, rendered);
425
425
  } else {
426
- const element = _chunkMWI3USXBcjs.createElement.call(void 0, rendered);
427
- nodes = _chunkMWI3USXBcjs.toNodeArray.call(void 0, element);
426
+ const element = _chunk2U6M3LKScjs.createElement.call(void 0, rendered);
427
+ nodes = _chunk2U6M3LKScjs.toNodeArray.call(void 0, element);
428
428
  }
429
429
  });
430
430
  if (scopeDispose) {
431
431
  root.cleanups.push(scopeDispose);
432
432
  }
433
433
  } finally {
434
- _chunkMWI3USXBcjs.setActiveSub.call(void 0, prevSub);
435
- _chunkMWI3USXBcjs.popRoot.call(void 0, prevRoot);
434
+ _chunk2U6M3LKScjs.setActiveSub.call(void 0, prevSub);
435
+ _chunk2U6M3LKScjs.popRoot.call(void 0, prevRoot);
436
436
  }
437
437
  return {
438
438
  key,
@@ -538,7 +538,7 @@ function createKeyedList(getItems, keyFn, renderItem, needsIndex) {
538
538
  }
539
539
  function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
540
540
  const container = createKeyedListContainer();
541
- const hostRoot = _chunkMWI3USXBcjs.getCurrentRoot.call(void 0, );
541
+ const hostRoot = _chunk2U6M3LKScjs.getCurrentRoot.call(void 0, );
542
542
  const fragment = document.createDocumentFragment();
543
543
  fragment.append(container.startMarker, container.endMarker);
544
544
  let disposed = false;
@@ -560,7 +560,7 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
560
560
  if (disposed) return;
561
561
  const parent = getConnectedParent();
562
562
  if (!parent) return;
563
- _chunkMWI3USXBcjs.batch2.call(void 0, () => {
563
+ _chunk2U6M3LKScjs.batch2.call(void 0, () => {
564
564
  const oldBlocks = container.blocks;
565
565
  const newBlocks = container.nextBlocks;
566
566
  const prevOrderedBlocks = container.orderedBlocks;
@@ -570,7 +570,7 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
570
570
  if (newItems.length === 0) {
571
571
  if (oldBlocks.size > 0) {
572
572
  for (const block of oldBlocks.values()) {
573
- _chunkMWI3USXBcjs.destroyRoot.call(void 0, block.root);
573
+ _chunk2U6M3LKScjs.destroyRoot.call(void 0, block.root);
574
574
  }
575
575
  const range = document.createRange();
576
576
  range.setStartAfter(container.startMarker);
@@ -651,8 +651,8 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
651
651
  `[fict] Duplicate key "${String(key)}" detected in list rendering. Each item should have a unique key. The previous item with this key will be replaced.`
652
652
  );
653
653
  }
654
- _chunkMWI3USXBcjs.destroyRoot.call(void 0, existingBlock.root);
655
- _chunkMWI3USXBcjs.removeNodes.call(void 0, existingBlock.nodes);
654
+ _chunk2U6M3LKScjs.destroyRoot.call(void 0, existingBlock.root);
655
+ _chunk2U6M3LKScjs.removeNodes.call(void 0, existingBlock.nodes);
656
656
  }
657
657
  block = createKeyedBlock(key, item, index, renderItem, needsIndex, hostRoot);
658
658
  createdBlocks.push(block);
@@ -665,8 +665,8 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
665
665
  hasDuplicateKey = true;
666
666
  const prior = nextOrderedBlocks[position];
667
667
  if (prior && prior !== resolvedBlock) {
668
- _chunkMWI3USXBcjs.destroyRoot.call(void 0, prior.root);
669
- _chunkMWI3USXBcjs.removeNodes.call(void 0, prior.nodes);
668
+ _chunk2U6M3LKScjs.destroyRoot.call(void 0, prior.root);
669
+ _chunk2U6M3LKScjs.removeNodes.call(void 0, prior.nodes);
670
670
  }
671
671
  nextOrderedBlocks[position] = resolvedBlock;
672
672
  } else {
@@ -704,7 +704,7 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
704
704
  }
705
705
  }
706
706
  if (appendedNodes.length > 0) {
707
- _chunkMWI3USXBcjs.insertNodesBefore.call(void 0, parent, appendedNodes, container.endMarker);
707
+ _chunk2U6M3LKScjs.insertNodesBefore.call(void 0, parent, appendedNodes, container.endMarker);
708
708
  const currentNodes = container.currentNodes;
709
709
  currentNodes.pop();
710
710
  for (let i = 0; i < appendedNodes.length; i++) {
@@ -718,15 +718,15 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
718
718
  container.nextOrderedBlocks = prevOrderedBlocks;
719
719
  for (const block of createdBlocks) {
720
720
  if (newBlocks.get(block.key) === block) {
721
- _chunkMWI3USXBcjs.flushOnMount.call(void 0, block.root);
721
+ _chunk2U6M3LKScjs.flushOnMount.call(void 0, block.root);
722
722
  }
723
723
  }
724
724
  return;
725
725
  }
726
726
  if (oldBlocks.size > 0) {
727
727
  for (const block of oldBlocks.values()) {
728
- _chunkMWI3USXBcjs.destroyRoot.call(void 0, block.root);
729
- _chunkMWI3USXBcjs.removeNodes.call(void 0, block.nodes);
728
+ _chunk2U6M3LKScjs.destroyRoot.call(void 0, block.root);
729
+ _chunk2U6M3LKScjs.removeNodes.call(void 0, block.nodes);
730
730
  }
731
731
  oldBlocks.clear();
732
732
  }
@@ -785,7 +785,7 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
785
785
  container.nextOrderedBlocks = prevOrderedBlocks;
786
786
  for (const block of createdBlocks) {
787
787
  if (newBlocks.get(block.key) === block) {
788
- _chunkMWI3USXBcjs.flushOnMount.call(void 0, block.root);
788
+ _chunk2U6M3LKScjs.flushOnMount.call(void 0, block.root);
789
789
  }
790
790
  }
791
791
  });
@@ -799,15 +799,15 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
799
799
  const parent = getConnectedParent();
800
800
  if (!parent) return false;
801
801
  const start = () => {
802
- effectDispose = _chunkMWI3USXBcjs.createRenderEffect.call(void 0, performDiff);
802
+ effectDispose = _chunk2U6M3LKScjs.createRenderEffect.call(void 0, performDiff);
803
803
  effectStarted = true;
804
804
  };
805
805
  if (hostRoot) {
806
- const prev = _chunkMWI3USXBcjs.pushRoot.call(void 0, hostRoot);
806
+ const prev = _chunk2U6M3LKScjs.pushRoot.call(void 0, hostRoot);
807
807
  try {
808
808
  start();
809
809
  } finally {
810
- _chunkMWI3USXBcjs.popRoot.call(void 0, prev);
810
+ _chunk2U6M3LKScjs.popRoot.call(void 0, prev);
811
811
  }
812
812
  } else {
813
813
  start();
@@ -821,7 +821,7 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
821
821
  if (getConnectedParent()) {
822
822
  disconnectObserver();
823
823
  if (ensureEffectStarted()) {
824
- _chunkMWI3USXBcjs.flush.call(void 0, );
824
+ _chunk2U6M3LKScjs.flush.call(void 0, );
825
825
  }
826
826
  }
827
827
  });
@@ -855,7 +855,7 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
855
855
  if (disposed) return;
856
856
  scheduleStart();
857
857
  if (ensureEffectStarted()) {
858
- _chunkMWI3USXBcjs.flush.call(void 0, );
858
+ _chunk2U6M3LKScjs.flush.call(void 0, );
859
859
  } else {
860
860
  waitForConnection();
861
861
  }
@@ -928,5 +928,5 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
928
928
 
929
929
 
930
930
 
931
- exports.Aliases = _chunkMWI3USXBcjs.Aliases; exports.BooleanAttributes = _chunkMWI3USXBcjs.BooleanAttributes; exports.ChildProperties = _chunkMWI3USXBcjs.ChildProperties; exports.DelegatedEvents = _chunkMWI3USXBcjs.DelegatedEvents; exports.Fragment = _chunkMWI3USXBcjs.Fragment; exports.Properties = _chunkMWI3USXBcjs.Properties; exports.SVGElements = _chunkMWI3USXBcjs.SVGElements; exports.SVGNamespace = _chunkMWI3USXBcjs.SVGNamespace; exports.UnitlessStyles = _chunkMWI3USXBcjs.UnitlessStyles; exports.__fictPopContext = _chunkMWI3USXBcjs.__fictPopContext; exports.__fictProp = _chunkMWI3USXBcjs.__fictProp; exports.__fictPropsRest = _chunkMWI3USXBcjs.__fictPropsRest; exports.__fictPushContext = _chunkMWI3USXBcjs.__fictPushContext; exports.__fictRender = _chunkMWI3USXBcjs.__fictRender; exports.__fictResetContext = _chunkMWI3USXBcjs.__fictResetContext; exports.__fictUseContext = _chunkMWI3USXBcjs.__fictUseContext; exports.__fictUseEffect = _chunkMWI3USXBcjs.__fictUseEffect; exports.__fictUseMemo = _chunkMWI3USXBcjs.__fictUseMemo; exports.__fictUseSignal = _chunkMWI3USXBcjs.__fictUseSignal; exports.addEventListener = _chunkMWI3USXBcjs.addEventListener; exports.assign = _chunkMWI3USXBcjs.assign; exports.bindAttribute = _chunkMWI3USXBcjs.bindAttribute; exports.bindClass = _chunkMWI3USXBcjs.bindClass; exports.bindEvent = _chunkMWI3USXBcjs.bindEvent; exports.bindProperty = _chunkMWI3USXBcjs.bindProperty; exports.bindRef = _chunkMWI3USXBcjs.bindRef; exports.bindStyle = _chunkMWI3USXBcjs.bindStyle; exports.bindText = _chunkMWI3USXBcjs.bindText; exports.callEventHandler = _chunkMWI3USXBcjs.callEventHandler; exports.classList = _chunkMWI3USXBcjs.classList; exports.clearDelegatedEvents = _chunkMWI3USXBcjs.clearDelegatedEvents; exports.createConditional = _chunkMWI3USXBcjs.createConditional; exports.createEffect = _chunkMWI3USXBcjs.createEffect; exports.createElement = _chunkMWI3USXBcjs.createElement; exports.createKeyedList = createKeyedList; exports.createMemo = _chunkMWI3USXBcjs.createMemo; exports.createPortal = _chunkMWI3USXBcjs.createPortal; exports.createPropsProxy = _chunkMWI3USXBcjs.createPropsProxy; exports.createRenderEffect = _chunkMWI3USXBcjs.createRenderEffect; exports.createSelector = _chunkMWI3USXBcjs.createSelector; exports.createSignal = _chunkMWI3USXBcjs.signal; exports.createStore = createStore; exports.delegateEvents = _chunkMWI3USXBcjs.delegateEvents; exports.getPropAlias = _chunkMWI3USXBcjs.getPropAlias; exports.insert = _chunkMWI3USXBcjs.insert; exports.insertNodesBefore = _chunkMWI3USXBcjs.insertNodesBefore; exports.isNodeBetweenMarkers = isNodeBetweenMarkers; exports.isReactive = _chunkMWI3USXBcjs.isReactive; exports.mergeProps = _chunkMWI3USXBcjs.mergeProps; exports.moveNodesBefore = moveNodesBefore; exports.onDestroy = _chunkMWI3USXBcjs.onDestroy; exports.prop = _chunkMWI3USXBcjs.prop; exports.reconcileArrays = reconcileArrays; exports.removeNodes = _chunkMWI3USXBcjs.removeNodes; exports.runInScope = _chunk7EAEROZ5cjs.runInScope; exports.spread = _chunkMWI3USXBcjs.spread; exports.template = _chunkMWI3USXBcjs.template; exports.toNodeArray = _chunkMWI3USXBcjs.toNodeArray; exports.unwrap = _chunkMWI3USXBcjs.unwrap;
931
+ exports.Aliases = _chunk2U6M3LKScjs.Aliases; exports.BooleanAttributes = _chunk2U6M3LKScjs.BooleanAttributes; exports.ChildProperties = _chunk2U6M3LKScjs.ChildProperties; exports.DelegatedEvents = _chunk2U6M3LKScjs.DelegatedEvents; exports.Fragment = _chunk2U6M3LKScjs.Fragment; exports.Properties = _chunk2U6M3LKScjs.Properties; exports.SVGElements = _chunk2U6M3LKScjs.SVGElements; exports.SVGNamespace = _chunk2U6M3LKScjs.SVGNamespace; exports.UnitlessStyles = _chunk2U6M3LKScjs.UnitlessStyles; exports.__fictPopContext = _chunk2U6M3LKScjs.__fictPopContext; exports.__fictProp = _chunk2U6M3LKScjs.__fictProp; exports.__fictPropsRest = _chunk2U6M3LKScjs.__fictPropsRest; exports.__fictPushContext = _chunk2U6M3LKScjs.__fictPushContext; exports.__fictRender = _chunk2U6M3LKScjs.__fictRender; exports.__fictResetContext = _chunk2U6M3LKScjs.__fictResetContext; exports.__fictUseContext = _chunk2U6M3LKScjs.__fictUseContext; exports.__fictUseEffect = _chunk2U6M3LKScjs.__fictUseEffect; exports.__fictUseMemo = _chunk2U6M3LKScjs.__fictUseMemo; exports.__fictUseSignal = _chunk2U6M3LKScjs.__fictUseSignal; exports.addEventListener = _chunk2U6M3LKScjs.addEventListener; exports.assign = _chunk2U6M3LKScjs.assign; exports.bindAttribute = _chunk2U6M3LKScjs.bindAttribute; exports.bindClass = _chunk2U6M3LKScjs.bindClass; exports.bindEvent = _chunk2U6M3LKScjs.bindEvent; exports.bindProperty = _chunk2U6M3LKScjs.bindProperty; exports.bindRef = _chunk2U6M3LKScjs.bindRef; exports.bindStyle = _chunk2U6M3LKScjs.bindStyle; exports.bindText = _chunk2U6M3LKScjs.bindText; exports.callEventHandler = _chunk2U6M3LKScjs.callEventHandler; exports.classList = _chunk2U6M3LKScjs.classList; exports.clearDelegatedEvents = _chunk2U6M3LKScjs.clearDelegatedEvents; exports.createConditional = _chunk2U6M3LKScjs.createConditional; exports.createEffect = _chunk2U6M3LKScjs.createEffect; exports.createElement = _chunk2U6M3LKScjs.createElement; exports.createKeyedList = createKeyedList; exports.createMemo = _chunk2U6M3LKScjs.createMemo; exports.createPortal = _chunk2U6M3LKScjs.createPortal; exports.createPropsProxy = _chunk2U6M3LKScjs.createPropsProxy; exports.createRenderEffect = _chunk2U6M3LKScjs.createRenderEffect; exports.createSelector = _chunk2U6M3LKScjs.createSelector; exports.createSignal = _chunk2U6M3LKScjs.signal; exports.createStore = createStore; exports.delegateEvents = _chunk2U6M3LKScjs.delegateEvents; exports.getPropAlias = _chunk2U6M3LKScjs.getPropAlias; exports.insert = _chunk2U6M3LKScjs.insert; exports.insertNodesBefore = _chunk2U6M3LKScjs.insertNodesBefore; exports.isNodeBetweenMarkers = isNodeBetweenMarkers; exports.isReactive = _chunk2U6M3LKScjs.isReactive; exports.mergeProps = _chunk2U6M3LKScjs.mergeProps; exports.moveNodesBefore = moveNodesBefore; exports.onDestroy = _chunk2U6M3LKScjs.onDestroy; exports.prop = _chunk2U6M3LKScjs.prop; exports.reconcileArrays = reconcileArrays; exports.removeNodes = _chunk2U6M3LKScjs.removeNodes; exports.runInScope = _chunk5YTFFAVUcjs.runInScope; exports.spread = _chunk2U6M3LKScjs.spread; exports.template = _chunk2U6M3LKScjs.template; exports.toNodeArray = _chunk2U6M3LKScjs.toNodeArray; exports.unwrap = _chunk2U6M3LKScjs.unwrap;
932
932
  //# sourceMappingURL=internal.cjs.map
package/dist/internal.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runInScope
3
- } from "./chunk-7TPCESQS.js";
3
+ } from "./chunk-UHXUEGQH.js";
4
4
  import {
5
5
  Aliases,
6
6
  BooleanAttributes,
@@ -66,7 +66,7 @@ import {
66
66
  template,
67
67
  toNodeArray,
68
68
  unwrap
69
- } from "./chunk-FOLRR3NZ.js";
69
+ } from "./chunk-YVDWXY44.js";
70
70
 
71
71
  // src/store.ts
72
72
  var PROXY = Symbol("fict:store-proxy");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fictjs/runtime",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Fict reactive runtime",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -6,6 +6,8 @@ const isDev =
6
6
  : typeof process === 'undefined' || process.env?.NODE_ENV !== 'production'
7
7
 
8
8
  export interface CycleProtectionOptions {
9
+ /** Enable cycle protection guards (defaults to dev-only) */
10
+ enabled?: boolean
9
11
  maxFlushCyclesPerMicrotask?: number
10
12
  maxEffectRunsPerFlush?: number
11
13
  windowSize?: number
@@ -28,128 +30,135 @@ let endFlushGuard: () => void = () => {}
28
30
  let enterRootGuard: (root: object) => boolean = () => true
29
31
  let exitRootGuard: (root: object) => void = () => {}
30
32
 
31
- if (isDev) {
32
- const defaultOptions = {
33
- maxFlushCyclesPerMicrotask: 10_000,
34
- maxEffectRunsPerFlush: 20_000,
35
- windowSize: 5,
36
- highUsageRatio: 0.8,
37
- maxRootReentrantDepth: 10,
38
- enableWindowWarning: true,
39
- devMode: false,
40
- }
41
-
42
- let options: Required<CycleProtectionOptions> = {
43
- ...defaultOptions,
44
- } as Required<CycleProtectionOptions>
45
-
46
- let effectRunsThisFlush = 0
47
- let windowUsage: CycleWindowEntry[] = []
48
- let rootDepth = new WeakMap<object, number>()
49
- let flushWarned = false
50
- let rootWarned = false
51
- let windowWarned = false
33
+ const defaultOptions = {
34
+ enabled: isDev,
35
+ maxFlushCyclesPerMicrotask: 10_000,
36
+ maxEffectRunsPerFlush: 20_000,
37
+ windowSize: 5,
38
+ highUsageRatio: 0.8,
39
+ maxRootReentrantDepth: 10,
40
+ enableWindowWarning: true,
41
+ devMode: false,
42
+ }
52
43
 
53
- setCycleProtectionOptions = opts => {
54
- options = { ...options, ...opts }
44
+ let enabled = defaultOptions.enabled
45
+ let options: Required<CycleProtectionOptions> = {
46
+ ...defaultOptions,
47
+ } as Required<CycleProtectionOptions>
48
+
49
+ let effectRunsThisFlush = 0
50
+ let windowUsage: CycleWindowEntry[] = []
51
+ let rootDepth = new WeakMap<object, number>()
52
+ let flushWarned = false
53
+ let rootWarned = false
54
+ let windowWarned = false
55
+
56
+ setCycleProtectionOptions = opts => {
57
+ if (typeof opts.enabled === 'boolean') {
58
+ enabled = opts.enabled
55
59
  }
60
+ options = { ...options, ...opts }
61
+ }
56
62
 
57
- resetCycleProtectionStateForTests = () => {
58
- options = { ...defaultOptions } as Required<CycleProtectionOptions>
59
- effectRunsThisFlush = 0
60
- windowUsage = []
61
- rootDepth = new WeakMap<object, number>()
62
- flushWarned = false
63
- rootWarned = false
64
- windowWarned = false
65
- }
63
+ resetCycleProtectionStateForTests = () => {
64
+ options = { ...defaultOptions } as Required<CycleProtectionOptions>
65
+ enabled = defaultOptions.enabled
66
+ effectRunsThisFlush = 0
67
+ windowUsage = []
68
+ rootDepth = new WeakMap<object, number>()
69
+ flushWarned = false
70
+ rootWarned = false
71
+ windowWarned = false
72
+ }
66
73
 
67
- beginFlushGuard = () => {
68
- effectRunsThisFlush = 0
69
- flushWarned = false
70
- windowWarned = false
71
- }
74
+ beginFlushGuard = () => {
75
+ if (!enabled) return
76
+ effectRunsThisFlush = 0
77
+ flushWarned = false
78
+ windowWarned = false
79
+ }
72
80
 
73
- beforeEffectRunGuard = () => {
74
- const next = ++effectRunsThisFlush
75
- if (next > options.maxFlushCyclesPerMicrotask || next > options.maxEffectRunsPerFlush) {
76
- const message = `[fict] cycle protection triggered: flush-budget-exceeded`
77
- if (options.devMode) {
78
- throw new Error(message)
79
- }
80
- if (!flushWarned) {
81
- flushWarned = true
82
- console.warn(message, { effectRuns: next })
83
- }
84
- return false
81
+ beforeEffectRunGuard = () => {
82
+ if (!enabled) return true
83
+ const next = ++effectRunsThisFlush
84
+ if (next > options.maxFlushCyclesPerMicrotask || next > options.maxEffectRunsPerFlush) {
85
+ const message = `[fict] cycle protection triggered: flush-budget-exceeded`
86
+ if (options.devMode) {
87
+ throw new Error(message)
85
88
  }
86
- return true
89
+ if (!flushWarned) {
90
+ flushWarned = true
91
+ console.warn(message, { effectRuns: next })
92
+ }
93
+ return false
87
94
  }
95
+ return true
96
+ }
88
97
 
89
- endFlushGuard = () => {
90
- recordWindowUsage(effectRunsThisFlush, options.maxFlushCyclesPerMicrotask)
91
- effectRunsThisFlush = 0
92
- }
98
+ endFlushGuard = () => {
99
+ if (!enabled) return
100
+ recordWindowUsage(effectRunsThisFlush, options.maxFlushCyclesPerMicrotask)
101
+ effectRunsThisFlush = 0
102
+ }
93
103
 
94
- enterRootGuard = root => {
95
- const depth = (rootDepth.get(root) ?? 0) + 1
96
- if (depth > options.maxRootReentrantDepth) {
97
- const message = `[fict] cycle protection triggered: root-reentry`
98
- if (options.devMode) {
99
- throw new Error(message)
100
- }
101
- if (!rootWarned) {
102
- rootWarned = true
103
- console.warn(message, { depth })
104
- }
105
- return false
104
+ enterRootGuard = root => {
105
+ if (!enabled) return true
106
+ const depth = (rootDepth.get(root) ?? 0) + 1
107
+ if (depth > options.maxRootReentrantDepth) {
108
+ const message = `[fict] cycle protection triggered: root-reentry`
109
+ if (options.devMode) {
110
+ throw new Error(message)
106
111
  }
107
- rootDepth.set(root, depth)
108
- return true
109
- }
110
-
111
- exitRootGuard = root => {
112
- const depth = rootDepth.get(root)
113
- if (depth === undefined) return
114
- if (depth <= 1) {
115
- rootDepth.delete(root)
116
- } else {
117
- rootDepth.set(root, depth - 1)
112
+ if (!rootWarned) {
113
+ rootWarned = true
114
+ console.warn(message, { depth })
118
115
  }
116
+ return false
119
117
  }
118
+ rootDepth.set(root, depth)
119
+ return true
120
+ }
120
121
 
121
- const recordWindowUsage = (used: number, budget: number): void => {
122
- if (!options.enableWindowWarning) return
123
- const entry = { used, budget }
124
- windowUsage.push(entry)
125
- if (windowUsage.length > options.windowSize) {
126
- windowUsage.shift()
127
- }
128
- if (windowWarned) return
129
- if (
130
- windowUsage.length >= options.windowSize &&
131
- windowUsage.every(
132
- item => item.budget > 0 && item.used / item.budget >= options.highUsageRatio,
133
- )
134
- ) {
135
- windowWarned = true
136
- reportCycle('high-usage-window', {
137
- windowSize: options.windowSize,
138
- ratio: options.highUsageRatio,
139
- })
140
- }
122
+ exitRootGuard = root => {
123
+ if (!enabled) return
124
+ const depth = rootDepth.get(root)
125
+ if (depth === undefined) return
126
+ if (depth <= 1) {
127
+ rootDepth.delete(root)
128
+ } else {
129
+ rootDepth.set(root, depth - 1)
141
130
  }
131
+ }
142
132
 
143
- const reportCycle = (
144
- reason: string,
145
- detail: Record<string, unknown> | undefined = undefined,
146
- ): void => {
147
- const hook = getDevtoolsHook()
148
- hook?.cycleDetected?.(detail ? { reason, detail } : { reason })
149
- console.warn(`[fict] cycle protection triggered: ${reason}`, detail ?? '')
133
+ const recordWindowUsage = (used: number, budget: number): void => {
134
+ if (!options.enableWindowWarning) return
135
+ const entry = { used, budget }
136
+ windowUsage.push(entry)
137
+ if (windowUsage.length > options.windowSize) {
138
+ windowUsage.shift()
139
+ }
140
+ if (windowWarned) return
141
+ if (
142
+ windowUsage.length >= options.windowSize &&
143
+ windowUsage.every(item => item.budget > 0 && item.used / item.budget >= options.highUsageRatio)
144
+ ) {
145
+ windowWarned = true
146
+ reportCycle('high-usage-window', {
147
+ windowSize: options.windowSize,
148
+ ratio: options.highUsageRatio,
149
+ })
150
150
  }
151
151
  }
152
152
 
153
+ const reportCycle = (
154
+ reason: string,
155
+ detail: Record<string, unknown> | undefined = undefined,
156
+ ): void => {
157
+ const hook = getDevtoolsHook()
158
+ hook?.cycleDetected?.(detail ? { reason, detail } : { reason })
159
+ console.warn(`[fict] cycle protection triggered: ${reason}`, detail ?? '')
160
+ }
161
+
153
162
  export {
154
163
  setCycleProtectionOptions,
155
164
  resetCycleProtectionStateForTests,
package/src/dom.ts CHANGED
@@ -687,7 +687,10 @@ const setProperty: AttributeSetter = (el: Element, key: string, value: unknown):
687
687
  * Set innerHTML on an element (used for dangerouslySetInnerHTML)
688
688
  */
689
689
  const setInnerHTML: AttributeSetter = (el: Element, _key: string, value: unknown): void => {
690
- ;(el as HTMLElement).innerHTML = value == null ? '' : String(value)
690
+ const next = value == null ? '' : String(value)
691
+ const node = el as HTMLElement
692
+ if (node.innerHTML === next) return
693
+ node.innerHTML = next
691
694
  }
692
695
 
693
696
  /**