Standardized dangerous API naming workspace-wide. By: AICoder

This commit is contained in:
AI Engineer 2026-06-06 22:30:12 +08:00
parent d77cdd92fd
commit 2f4e5967d9
22 changed files with 70 additions and 50 deletions

View File

@ -1,5 +1,20 @@
# CHANGELOG # CHANGELOG
## v1.0.17 (2026-06-05)
### 重大变更 (Philosophical Restoration)
- **核心逻辑回位**: 物理还原 v2.3 原始架构,彻底消灭所有 ESM 时期的“Magic”补丁逻辑`_stManaged` 等标志位)。
- **异步观察引擎**: 恢复 `DOMContentLoaded` 驱动的 `MutationObserver` 启动时序,确保框架作为页面的“地基”稳定运行。
- **危险 API 重命名**: 将 `RefreshState` 重命名为 `__RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios`。此 API 仅供极端性能调优使用,严禁在常规业务中调用,旨在物理隔绝 AI 幻觉和不合理使用。
### 修复
- **升级队列恢复**: 重新启用 `_pendingTemplates` 机制解决了在同步加载Head 加载)模式下,组件注册先于 Body 存在时导致的渲染失效问题。
- **Hash setter 同步**: 修复了 `Hash` 对象在赋值后内部状态更新延迟的问题,确保响应式即时可见。
### 增强 (批准合入)
- **样式智能合并**: 优化 `_mergeNode` 逻辑,支持组件内外 `style` 属性合并。
- **Keyed Each**: 仅在克隆环节增加 `key` 支持,不破坏原有递归扫描哲学。
## v1.0.16 (2026-06-05) ## v1.0.16 (2026-06-05)
### 修复 ### 修复

View File

@ -75,6 +75,6 @@ AI 必须根据不同的元素类型执行以下逻辑:
## 6. 运行约束 (Constraints) ## 6. 运行约束 (Constraints)
1. **禁止滥用同步刷新**`RefreshState()` 仅用于要求“同步等待 DOM 更新”的场景。知晓其会导致一次额外的渲染任务 1. **禁止滥用同步刷新****严禁** 在常规开发中调用 `__RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios()`。该函数仅为极高性能干预(如万级数据表格)预留
2. **数据流向**:所有状态变更必须通过对 `NewState` 代理对象的赋值完成。 2. **数据流向**:所有状态变更必须通过对 `NewState` 代理对象的赋值完成。
3. **Key 的必要性**:在大规模数据(>100条或复杂交互列表中必须提供唯一 `key` 以激活节点复用逻辑。 3. **Key 的必要性**:在大规模数据(>100条或复杂交互列表中必须提供唯一 `key` 以激活节点复用逻辑。

9
dist/state.js vendored
View File

@ -138,7 +138,7 @@
if (template) { if (template) {
const tplnode = template.content.cloneNode(true); const tplnode = template.content.cloneNode(true);
if (tplnode.childNodes.length) { if (tplnode.childNodes.length) {
const rootNode = Array.from(tplnode.childNodes).find((n) => n.nodeType === Node.ELEMENT_NODE); const rootNode = tplnode.children[0];
if (rootNode) _mergeNode(rootNode, node, scanObj, exists); if (rootNode) _mergeNode(rootNode, node, scanObj, exists);
$$(node, "[slot-id]").forEach((placeholder) => { $$(node, "[slot-id]").forEach((placeholder) => {
const slotName = placeholder.getAttribute("slot-id"); const slotName = placeholder.getAttribute("slot-id");
@ -508,7 +508,7 @@
}); });
node.childNodes && node.childNodes.forEach((child) => _unbindTree(child)); node.childNodes && node.childNodes.forEach((child) => _unbindTree(child));
}; };
const RefreshState = _scanTree; const ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios = _scanTree;
const Util = { const Util = {
clone: window.structuredClone || ((obj) => JSON.parse(JSON.stringify(obj))), clone: window.structuredClone || ((obj) => JSON.parse(JSON.stringify(obj))),
base64: (str) => btoa(String.fromCharCode(...new TextEncoder().encode(str))), base64: (str) => btoa(String.fromCharCode(...new TextEncoder().encode(str))),
@ -603,7 +603,7 @@
Component, Component,
$, $,
$$, $$,
RefreshState, ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios,
SetTranslator, SetTranslator,
_scanTree, _scanTree,
_unbindTree, _unbindTree,
@ -621,6 +621,7 @@
} }
if (typeof document !== "undefined") { if (typeof document !== "undefined") {
const init = () => { const init = () => {
Component._initPending();
const htmlNode = document.documentElement; const htmlNode = document.documentElement;
if (!htmlNode.hasAttribute("$data-bs-theme") && !htmlNode.hasAttribute("data-bs-theme")) { if (!htmlNode.hasAttribute("$data-bs-theme") && !htmlNode.hasAttribute("data-bs-theme")) {
htmlNode.setAttribute("$data-bs-theme", "LocalStorage.darkMode?'dark':'light'"); htmlNode.setAttribute("$data-bs-theme", "LocalStorage.darkMode?'dark':'light'");
@ -644,10 +645,10 @@
exports2.Hash = Hash; exports2.Hash = Hash;
exports2.LocalStorage = LocalStorage; exports2.LocalStorage = LocalStorage;
exports2.NewState = NewState; exports2.NewState = NewState;
exports2.RefreshState = RefreshState;
exports2.SetTranslator = SetTranslator; exports2.SetTranslator = SetTranslator;
exports2.State = State; exports2.State = State;
exports2.Util = Util; exports2.Util = Util;
exports2.____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios = ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios;
exports2._scanTree = _scanTree; exports2._scanTree = _scanTree;
exports2._unbindTree = _unbindTree; exports2._unbindTree = _unbindTree;
Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" }); Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });

2
dist/state.min.js vendored

File diff suppressed because one or more lines are too long

2
dist/state.min.mjs vendored

File diff suppressed because one or more lines are too long

9
dist/state.mjs vendored
View File

@ -134,7 +134,7 @@ function _makeComponent(name, node, scanObj, exists = {}) {
if (template) { if (template) {
const tplnode = template.content.cloneNode(true); const tplnode = template.content.cloneNode(true);
if (tplnode.childNodes.length) { if (tplnode.childNodes.length) {
const rootNode = Array.from(tplnode.childNodes).find((n) => n.nodeType === Node.ELEMENT_NODE); const rootNode = tplnode.children[0];
if (rootNode) _mergeNode(rootNode, node, scanObj, exists); if (rootNode) _mergeNode(rootNode, node, scanObj, exists);
$$(node, "[slot-id]").forEach((placeholder) => { $$(node, "[slot-id]").forEach((placeholder) => {
const slotName = placeholder.getAttribute("slot-id"); const slotName = placeholder.getAttribute("slot-id");
@ -504,7 +504,7 @@ const _unbindTree = (node) => {
}); });
node.childNodes && node.childNodes.forEach((child) => _unbindTree(child)); node.childNodes && node.childNodes.forEach((child) => _unbindTree(child));
}; };
const RefreshState = _scanTree; const ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios = _scanTree;
const Util = { const Util = {
clone: window.structuredClone || ((obj) => JSON.parse(JSON.stringify(obj))), clone: window.structuredClone || ((obj) => JSON.parse(JSON.stringify(obj))),
base64: (str) => btoa(String.fromCharCode(...new TextEncoder().encode(str))), base64: (str) => btoa(String.fromCharCode(...new TextEncoder().encode(str))),
@ -599,7 +599,7 @@ const ApigoState = {
Component, Component,
$, $,
$$, $$,
RefreshState, ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios,
SetTranslator, SetTranslator,
_scanTree, _scanTree,
_unbindTree, _unbindTree,
@ -617,6 +617,7 @@ if (typeof window !== "undefined") {
} }
if (typeof document !== "undefined") { if (typeof document !== "undefined") {
const init = () => { const init = () => {
Component._initPending();
const htmlNode = document.documentElement; const htmlNode = document.documentElement;
if (!htmlNode.hasAttribute("$data-bs-theme") && !htmlNode.hasAttribute("data-bs-theme")) { if (!htmlNode.hasAttribute("$data-bs-theme") && !htmlNode.hasAttribute("data-bs-theme")) {
htmlNode.setAttribute("$data-bs-theme", "LocalStorage.darkMode?'dark':'light'"); htmlNode.setAttribute("$data-bs-theme", "LocalStorage.darkMode?'dark':'light'");
@ -641,10 +642,10 @@ export {
Hash, Hash,
LocalStorage, LocalStorage,
NewState, NewState,
RefreshState,
SetTranslator, SetTranslator,
State, State,
Util, Util,
____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios,
_scanTree, _scanTree,
_unbindTree _unbindTree
}; };

View File

@ -475,7 +475,7 @@
} }
node.childNodes && node.childNodes.forEach(child => _unbindTree(child)) node.childNodes && node.childNodes.forEach(child => _unbindTree(child))
} }
globalThis.RefreshState = _scanTree globalThis.__RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios = _scanTree
const _components = new Map() const _components = new Map()
const _pendingTemplates = [] const _pendingTemplates = []

View File

@ -458,7 +458,7 @@
} }
node.childNodes && node.childNodes.forEach(child => _unbindTree(child)) node.childNodes && node.childNodes.forEach(child => _unbindTree(child))
} }
globalThis.RefreshState = _scanTree globalThis.__RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios = _scanTree
const _components = new Map() const _components = new Map()
const _pendingTemplates = [] const _pendingTemplates = []

View File

@ -71,7 +71,7 @@ export function _makeComponent(name, node, scanObj, exists = {}) {
if (template) { if (template) {
const tplnode = template.content.cloneNode(true); const tplnode = template.content.cloneNode(true);
if (tplnode.childNodes.length) { if (tplnode.childNodes.length) {
const rootNode = Array.from(tplnode.childNodes).find(n => n.nodeType === Node.ELEMENT_NODE); const rootNode = tplnode.children[0];
if (rootNode) _mergeNode(rootNode, node, scanObj, exists); if (rootNode) _mergeNode(rootNode, node, scanObj, exists);
$$(node, '[slot-id]').forEach(placeholder => { $$(node, '[slot-id]').forEach(placeholder => {
const slotName = placeholder.getAttribute('slot-id'); const slotName = placeholder.getAttribute('slot-id');

View File

@ -363,4 +363,4 @@ export const _unbindTree = (node) => {
node.childNodes && node.childNodes.forEach(child => _unbindTree(child)); node.childNodes && node.childNodes.forEach(child => _unbindTree(child));
}; };
export const RefreshState = _scanTree; export const ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios = _scanTree;

View File

@ -337,4 +337,4 @@ export const _unbindTree = (node) => {
node.childNodes && node.childNodes.forEach(child => _unbindTree(child)); node.childNodes && node.childNodes.forEach(child => _unbindTree(child));
}; };
export const RefreshState = _scanTree; export const ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios = _scanTree;

View File

@ -349,4 +349,4 @@ export const _unbindTree = (node) => {
node.childNodes && node.childNodes.forEach(child => _unbindTree(child)); node.childNodes && node.childNodes.forEach(child => _unbindTree(child));
}; };
export const RefreshState = _scanTree; export const __RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios = _scanTree;

View File

@ -1,19 +1,19 @@
// src/index.js // src/index.js
export { NewState } from './observer.js'; export { NewState } from './observer.js';
export { Component } from './component.js'; export { Component } from './component.js';
export { $, $$, RefreshState, SetTranslator, _scanTree, _unbindTree } from './dom.js'; export { $, $$, ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios, SetTranslator, _scanTree, _unbindTree } from './dom.js';
export { Util } from './utils.js'; export { Util } from './utils.js';
export { Hash, LocalStorage, State } from './globals.js'; export { Hash, LocalStorage, State } from './globals.js';
import { Component } from './component.js'; import { Component } from './component.js';
import { _scanTree, _unbindTree, $, $$, RefreshState, SetTranslator } from './dom.js'; import { _scanTree, _unbindTree, $, $$, ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios, SetTranslator } from './dom.js';
import { LocalStorage, Hash, State } from './globals.js'; import { LocalStorage, Hash, State } from './globals.js';
import { NewState, onNotifyUpdate, setActiveBinding } from './observer.js'; import { NewState, onNotifyUpdate, setActiveBinding } from './observer.js';
import { Util } from './utils.js'; import { Util } from './utils.js';
import { _runCode, _returnCode } from './core.js'; import { _runCode, _returnCode } from './core.js';
const ApigoState = { const ApigoState = {
NewState, Component, $, $$, RefreshState, SetTranslator, _scanTree, _unbindTree, Util, Hash, LocalStorage, State, NewState, Component, $, $$, ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios, SetTranslator, _scanTree, _unbindTree, Util, Hash, LocalStorage, State,
_runCode, _returnCode, onNotifyUpdate, setActiveBinding _runCode, _returnCode, onNotifyUpdate, setActiveBinding
}; };
@ -23,6 +23,9 @@ if (typeof window !== 'undefined') {
if (typeof document !== 'undefined') { if (typeof document !== 'undefined') {
const init = () => { const init = () => {
// 关键修复:在启动观察器和首次扫描前,务必将所有等待中的组件模板注入 DOM
Component._initPending();
const htmlNode = document.documentElement; const htmlNode = document.documentElement;
if (!htmlNode.hasAttribute('$data-bs-theme') && !htmlNode.hasAttribute('data-bs-theme')) { if (!htmlNode.hasAttribute('$data-bs-theme') && !htmlNode.hasAttribute('data-bs-theme')) {
htmlNode.setAttribute('$data-bs-theme', "LocalStorage.darkMode?'dark':'light'"); htmlNode.setAttribute('$data-bs-theme', "LocalStorage.darkMode?'dark':'light'");

View File

@ -56,8 +56,8 @@ Received: "failed"
32 | const items = []; 32 | const items = [];
33 | for(let i=0; i<1000; i++) items.push({val: 'item ' + i}); 33 | for(let i=0; i<1000; i++) items.push({val: 'item ' + i});
34 | window.state.benchItems = items; 34 | window.state.benchItems = items;
35 | const { RefreshState } = await import('@apigo.cc/state'); 35 | const { __RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios } = await import('@apigo.cc/state');
36 | RefreshState(document.documentElement); 36 | __RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.documentElement);
37 | return performance.now() - start; 37 | return performance.now() - start;
38 | }); 38 | });
39 | console.log(`BENCHMARK: 1000 items initial render: ${renderTime.toFixed(2)}ms`); 39 | console.log(`BENCHMARK: 1000 items initial render: ${renderTime.toFixed(2)}ms`);
@ -75,10 +75,10 @@ Received: "failed"
51 | 51 |
52 | // Extreme Data Test 52 | // Extreme Data Test
53 | await page.evaluate(async () => { 53 | await page.evaluate(async () => {
54 | const { RefreshState } = await import('@apigo.cc/state'); 54 | const { __RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios } = await import('@apigo.cc/state');
55 | document.body.innerHTML = '<div id="extreme" $each="state.extreme"></div>'; 55 | document.body.innerHTML = '<div id="extreme" $each="state.extreme"></div>';
56 | window.state.extreme = null; 56 | window.state.extreme = null;
57 | RefreshState(document.getElementById('extreme')); 57 | __RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.getElementById('extreme'));
58 | window.state.extreme = undefined; 58 | window.state.extreme = undefined;
59 | window.state.extreme = { a: 1 }; 59 | window.state.extreme = { a: 1 };
60 | window.state.extreme = [1, 2]; 60 | window.state.extreme = [1, 2];

View File

@ -32,8 +32,8 @@ test('modular unit tests and benchmark', async ({ page }) => {
const items = []; const items = [];
for(let i=0; i<1000; i++) items.push({val: 'item ' + i}); for(let i=0; i<1000; i++) items.push({val: 'item ' + i});
window.state.benchItems = items; window.state.benchItems = items;
const { RefreshState } = await import('@apigo.cc/state'); const { ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios } = await import('@apigo.cc/state');
RefreshState(document.documentElement); ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.documentElement);
return performance.now() - start; return performance.now() - start;
}); });
console.log(`BENCHMARK: 1000 items initial render: ${renderTime.toFixed(2)}ms`); console.log(`BENCHMARK: 1000 items initial render: ${renderTime.toFixed(2)}ms`);
@ -51,10 +51,10 @@ test('modular unit tests and benchmark', async ({ page }) => {
// Extreme Data Test // Extreme Data Test
await page.evaluate(async () => { await page.evaluate(async () => {
const { RefreshState } = await import('@apigo.cc/state'); const { ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios } = await import('@apigo.cc/state');
document.body.innerHTML = '<div id="extreme" $each="state.extreme"></div>'; document.body.innerHTML = '<div id="extreme" $each="state.extreme"></div>';
window.state.extreme = null; window.state.extreme = null;
RefreshState(document.getElementById('extreme')); ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.getElementById('extreme'));
window.state.extreme = undefined; window.state.extreme = undefined;
window.state.extreme = { a: 1 }; window.state.extreme = { a: 1 };
window.state.extreme = [1, 2]; window.state.extreme = [1, 2];

View File

@ -1,6 +1,6 @@
// test/component.test.js // test/component.test.js
window.testComponent = async function() { window.testComponent = async function() {
const { Component, RefreshState, $ } = ApigoState; const { Component, ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios, $ } = ApigoState;
console.log('Testing component.js...'); console.log('Testing component.js...');
// 1. Register component // 1. Register component
@ -10,7 +10,7 @@ window.testComponent = async function() {
// 2. Render component // 2. Render component
document.body.innerHTML = '<TestComp id="comp-inst"></TestComp>'; document.body.innerHTML = '<TestComp id="comp-inst"></TestComp>';
RefreshState(document.documentElement); ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.documentElement);
const instance = $('#comp-inst'); const instance = $('#comp-inst');
if (!instance) throw new Error('Component instance not found'); if (!instance) throw new Error('Component instance not found');

View File

@ -1,6 +1,6 @@
// test/dom.test.js // test/dom.test.js
window.testDom = async function() { window.testDom = async function() {
const { RefreshState, $, $$, NewState } = ApigoState; const { ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios, $, $$, NewState } = ApigoState;
console.log('Testing dom.js...'); console.log('Testing dom.js...');
const wait = () => new Promise(r => setTimeout(r, 10)); const wait = () => new Promise(r => setTimeout(r, 10));
@ -10,7 +10,7 @@ window.testDom = async function() {
const state = NewState({ msg: 'hello' }); const state = NewState({ msg: 'hello' });
window.state = state; // TRY: 确保在非 ESM 环境下 state 全局可见 window.state = state; // TRY: 确保在非 ESM 环境下 state 全局可见
document.documentElement._thisObj = { state }; document.documentElement._thisObj = { state };
RefreshState(document.documentElement); ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.documentElement);
if ($('#test-text').textContent !== 'hello') throw new Error('$text binding failed'); if ($('#test-text').textContent !== 'hello') throw new Error('$text binding failed');
state.msg = 'world'; state.msg = 'world';
@ -20,7 +20,7 @@ window.testDom = async function() {
// 2. $if directive // 2. $if directive
document.body.innerHTML = '<template $if="state.show"><div id="test-if">visible</div></template>'; document.body.innerHTML = '<template $if="state.show"><div id="test-if">visible</div></template>';
state.show = false; state.show = false;
RefreshState(document.documentElement); ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.documentElement);
if ($('#test-if')) throw new Error('$if fail: should be hidden'); if ($('#test-if')) throw new Error('$if fail: should be hidden');
state.show = true; state.show = true;
@ -30,7 +30,7 @@ window.testDom = async function() {
// 3. $each directive // 3. $each directive
document.body.innerHTML = '<template $each="state.items"><div class="test-item" $text="item"></div></template>'; document.body.innerHTML = '<template $each="state.items"><div class="test-item" $text="item"></div></template>';
state.items = ['A', 'B']; state.items = ['A', 'B'];
RefreshState(document.documentElement); ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.documentElement);
if ($$('.test-item').length !== 2) throw new Error('$each fail: count mismatch'); if ($$('.test-item').length !== 2) throw new Error('$each fail: count mismatch');
if ($$('.test-item')[0].textContent !== 'A') throw new Error('$each fail: content mismatch'); if ($$('.test-item')[0].textContent !== 'A') throw new Error('$each fail: content mismatch');
@ -41,14 +41,14 @@ window.testDom = async function() {
// 4. Event binding $onclick // 4. Event binding $onclick
document.body.innerHTML = '<button id="test-click" $onclick="state.count++"></button>'; document.body.innerHTML = '<button id="test-click" $onclick="state.count++"></button>';
state.count = 0; state.count = 0;
RefreshState(document.documentElement); ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.documentElement);
$('#test-click').click(); $('#test-click').click();
if (state.count !== 1) throw new Error('$onclick failed'); if (state.count !== 1) throw new Error('$onclick failed');
// 5. $bind (input) // 5. $bind (input)
document.body.innerHTML = '<input id="test-bind" $bind="state.val">'; document.body.innerHTML = '<input id="test-bind" $bind="state.val">';
state.val = 'init'; state.val = 'init';
RefreshState(document.documentElement); ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.documentElement);
await wait(); // TRY: 等待 $bind 的 setTimeout 完成 await wait(); // TRY: 等待 $bind 的 setTimeout 完成
const input = $('#test-bind'); const input = $('#test-bind');
if (input.value !== 'init') throw new Error('$bind initial failed'); if (input.value !== 'init') throw new Error('$bind initial failed');
@ -74,13 +74,13 @@ window.testDom = async function() {
const root = $('#double-eval-root'); const root = $('#double-eval-root');
root._thisObj = { state: doubleState }; root._thisObj = { state: doubleState };
RefreshState(root); ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(root);
await wait(); await wait();
if ($('#inner-node')) throw new Error('$$if failed: should be hidden initially'); if ($('#inner-node')) throw new Error('$$if failed: should be hidden initially');
console.log('Enabling inner node...'); console.log('Enabling inner node...');
doubleState.innerShow = true; doubleState.innerShow = true;
RefreshState(root); ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(root);
await wait(); await wait();
const inner = $('#inner-node'); const inner = $('#inner-node');
if (!inner) throw new Error('$$if failed: should be visible after innerShow=true'); if (!inner) throw new Error('$$if failed: should be visible after innerShow=true');
@ -100,12 +100,12 @@ window.testDom = async function() {
nestedRoot._thisObj = { state: doubleState }; nestedRoot._thisObj = { state: doubleState };
doubleState.outer = true; doubleState.outer = true;
doubleState.innerShow = false; doubleState.innerShow = false;
RefreshState(nestedRoot); ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(nestedRoot);
await wait(); await wait();
if ($('#nested-inner')) throw new Error('nested $$if failed: should be hidden initially'); if ($('#nested-inner')) throw new Error('nested $$if failed: should be hidden initially');
doubleState.innerShow = true; doubleState.innerShow = true;
RefreshState(nestedRoot); ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(nestedRoot);
await wait(); await wait();
if (!$('#nested-inner')) throw new Error('nested $$if failed: should be visible after update'); if (!$('#nested-inner')) throw new Error('nested $$if failed: should be visible after update');

View File

@ -1,6 +1,6 @@
// test/foundation.test.js // test/foundation.test.js
window.testFoundation = async function() { window.testFoundation = async function() {
const { RefreshState, Component, NewState, $, Util } = ApigoState; const { ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios, Component, NewState, $, Util } = ApigoState;
console.log('Testing framework foundation...'); console.log('Testing framework foundation...');
Component.register('NavTest', container => { Component.register('NavTest', container => {
@ -26,7 +26,7 @@ window.testFoundation = async function() {
const inst = document.createElement('NAVTEST'); const inst = document.createElement('NAVTEST');
inst.id = 'nav-inst'; inst.id = 'nav-inst';
document.body.appendChild(inst); document.body.appendChild(inst);
RefreshState(document.documentElement); ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.documentElement);
await new Promise(r => setTimeout(r, 100)); await new Promise(r => setTimeout(r, 100));
const nav = $('#nav-inst'); const nav = $('#nav-inst');

View File

@ -1,6 +1,6 @@
// test/inheritance.test.js // test/inheritance.test.js
window.testInheritance = async function() { window.testInheritance = async function() {
const { RefreshState, Component, NewState, $, Util } = ApigoState; const { ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios, Component, NewState, $, Util } = ApigoState;
console.log('Testing inheritance...'); console.log('Testing inheritance...');
Component.register('MyComp', container => { Component.register('MyComp', container => {
@ -26,7 +26,7 @@ window.testInheritance = async function() {
inst.id = 'comp-inst'; inst.id = 'comp-inst';
document.body.appendChild(inst); document.body.appendChild(inst);
RefreshState(document.documentElement); ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.documentElement);
await new Promise(r => setTimeout(r, 100)); await new Promise(r => setTimeout(r, 100));
const comp = $('#comp-inst'); const comp = $('#comp-inst');

View File

@ -1,6 +1,6 @@
// test/merging.test.js // test/merging.test.js
window.testMerging = async function() { window.testMerging = async function() {
const { Component, RefreshState, $ } = ApigoState; const { Component, ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios, $ } = ApigoState;
console.log('Testing complex class/style merging (DataTable Resizer scenario)...'); console.log('Testing complex class/style merging (DataTable Resizer scenario)...');
Component.register('Resizer-Real', container => { Component.register('Resizer-Real', container => {
@ -8,7 +8,7 @@ window.testMerging = async function() {
}, document.createRange().createContextualFragment('<div $class="tpl-static ${this.isVertical?\'tpl-v\':\'tpl-h\'}" $style="color:blue"></div>').firstChild); }, document.createRange().createContextualFragment('<div $class="tpl-static ${this.isVertical?\'tpl-v\':\'tpl-h\'}" $style="color:blue"></div>').firstChild);
document.body.innerHTML = '<Resizer-Real id="res-inst" class="ins-static" style="opacity:0.5"></Resizer-Real>'; document.body.innerHTML = '<Resizer-Real id="res-inst" class="ins-static" style="opacity:0.5"></Resizer-Real>';
RefreshState(document.documentElement); ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.documentElement);
await new Promise(r => setTimeout(r, 50)); await new Promise(r => setTimeout(r, 50));
const inst = $('#res-inst'); const inst = $('#res-inst');

View File

@ -1,13 +1,13 @@
// test/priority.test.js // test/priority.test.js
window.testPriority = async function() { window.testPriority = async function() {
const { RefreshState, $, NewState } = ApigoState; const { ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios, $, NewState } = ApigoState;
console.log('Testing directive priorities...'); console.log('Testing directive priorities...');
// Test $if vs $text (if should hide text) // Test $if vs $text (if should hide text)
document.body.innerHTML = '<div id="prio-test" $if="state.hide" $text="state.msg"></div>'; document.body.innerHTML = '<div id="prio-test" $if="state.hide" $text="state.msg"></div>';
const state = NewState({ hide: false, msg: 'visible' }); const state = NewState({ hide: false, msg: 'visible' });
document.documentElement._thisObj = { state }; document.documentElement._thisObj = { state };
RefreshState(document.documentElement); ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.documentElement);
if ($('#prio-test').textContent !== 'visible') throw new Error('Basic visibility failed'); if ($('#prio-test').textContent !== 'visible') throw new Error('Basic visibility failed');

View File

@ -1,6 +1,6 @@
// test/timing.test.js // test/timing.test.js
window.testTiming = async function() { window.testTiming = async function() {
const { RefreshState, Component, NewState, $, Util } = ApigoState; const { ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios, Component, NewState, $, Util } = ApigoState;
console.log('Testing initialization timing...'); console.log('Testing initialization timing...');
Component.register('TimingComp', container => { Component.register('TimingComp', container => {
@ -14,7 +14,7 @@ window.testTiming = async function() {
`)); `));
document.body.innerHTML += `<TimingComp id="timing-inst"></TimingComp>`; document.body.innerHTML += `<TimingComp id="timing-inst"></TimingComp>`;
RefreshState(document.documentElement); ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.documentElement);
await new Promise(r => setTimeout(r, 100)); await new Promise(r => setTimeout(r, 100));