Rename after_load to on_loaded, add load_children option to load_detached_action, update npm
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1087,6 +1087,10 @@ class Spa {
|
||||
* These are merged with URL-extracted args (extra_args take precedence).
|
||||
* Pass {use_cached_data: true} to have the action load with cached data
|
||||
* without revalidation if cached data is available.
|
||||
* @param {object} options - Optional behavior options
|
||||
* @param {boolean} options.load_children - If true, renders the full component tree
|
||||
* so all children (datagrids, views, etc.) also run their on_load(). Uses
|
||||
* _load_render_only instead of _load_only. Useful for preloading/warming cache.
|
||||
* @returns {Promise<Spa_Action|null>} The fully-loaded action instance, or null if route not found
|
||||
*
|
||||
* @example
|
||||
@@ -1101,8 +1105,13 @@ class Spa {
|
||||
* @example
|
||||
* // With cached data (faster, no network request if cached)
|
||||
* const action = await Spa.load_detached_action('/contacts/123', {use_cached_data: true});
|
||||
*
|
||||
* @example
|
||||
* // Preload with children (warms cache for entire page tree)
|
||||
* const action = await Spa.load_detached_action('/contacts/123', {}, {load_children: true});
|
||||
* action.stop();
|
||||
*/
|
||||
static async load_detached_action(url, extra_args = {}) {
|
||||
static async load_detached_action(url, extra_args = {}, options = {}) {
|
||||
// Parse URL and match to route
|
||||
const parsed = Spa.parse_url(url);
|
||||
const url_without_hash = parsed.path + parsed.search;
|
||||
@@ -1116,12 +1125,14 @@ class Spa {
|
||||
const action_class = route_match.action_class;
|
||||
const action_name = action_class.name;
|
||||
|
||||
// Merge URL args with extra_args (extra_args take precedence)
|
||||
// Include _load_only to skip render/on_ready (detached, no DOM needed)
|
||||
// _load_only: action on_load() only, no children
|
||||
// _load_render_only: renders full tree, all children run on_load() too
|
||||
const lifecycle_flag = options.load_children ? '_load_render_only' : '_load_only';
|
||||
|
||||
const args = {
|
||||
...route_match.args,
|
||||
...extra_args,
|
||||
_load_only: true
|
||||
[lifecycle_flag]: true
|
||||
};
|
||||
|
||||
console_debug('Spa', `load_detached_action: Loading ${action_name} with args:`, args);
|
||||
|
||||
844
app/RSpade/man/custom_playwright_tests_spa.txt
Executable file
844
app/RSpade/man/custom_playwright_tests_spa.txt
Executable file
@@ -0,0 +1,844 @@
|
||||
PLAYWRIGHT_SPA(7) RSX Framework PLAYWRIGHT_SPA(7)
|
||||
|
||||
NAME
|
||||
custom_playwright_tests_spa - writing custom Playwright tests for
|
||||
SPA applications with jqhtml component lifecycle awareness
|
||||
|
||||
SYNOPSIS
|
||||
node bin/my-test.js [auth-token]
|
||||
|
||||
Where auth-token is generated by the PHP helper:
|
||||
|
||||
php -r "
|
||||
foreach (file('.env') as \$line) {
|
||||
if (strpos(\$line, 'APP_KEY=') === 0) {
|
||||
\$app_key = trim(substr(\$line, 8));
|
||||
break;
|
||||
}
|
||||
}
|
||||
\$payload = json_encode([
|
||||
'url' => '/initial-route',
|
||||
'user_id' => 1,
|
||||
'portal' => false
|
||||
]);
|
||||
echo hash_hmac('sha256', \$payload, \$app_key);
|
||||
"
|
||||
|
||||
DESCRIPTION
|
||||
rsx:debug is designed for single-page-load diagnostics. For tests
|
||||
that require multi-step SPA navigation (e.g., verifying cache
|
||||
behavior across page transitions, testing component state
|
||||
persistence), you need a custom Playwright script.
|
||||
|
||||
This document covers the patterns required to write reliable
|
||||
Playwright tests against an RSX SPA application, including
|
||||
authentication, SPA navigation, and observing jqhtml component
|
||||
lifecycle events like loading spinners and data rendering.
|
||||
|
||||
AUTHENTICATION
|
||||
RSX uses backdoor header-based auth in development. The same
|
||||
mechanism that powers rsx:debug works for custom scripts.
|
||||
|
||||
Required Headers
|
||||
X-Dev-Auth-User-Id User ID to authenticate as
|
||||
X-Dev-Auth-Token HMAC-SHA256 signature (see SYNOPSIS)
|
||||
X-Playwright-Test Set to '1' for plain-text error responses
|
||||
|
||||
CRITICAL: Auth Headers Only on First Document Request
|
||||
Auth headers must ONLY be sent on the initial document request,
|
||||
NOT on every request. Sending auth headers on Ajax/XHR calls
|
||||
causes RsxAuth::login() to run repeatedly, which regenerates the
|
||||
session on each request. This makes Rsx.validate_session() detect
|
||||
a session_hash mismatch and trigger a full page reload via
|
||||
location.replace(), breaking SPA navigation entirely.
|
||||
|
||||
After the initial document request sets a session cookie, all
|
||||
subsequent requests authenticate via that cookie automatically.
|
||||
|
||||
let initial_doc_sent = false;
|
||||
|
||||
await page.route('**/*', async (route, request) => {
|
||||
const url = request.url();
|
||||
if (url.startsWith(BASE_URL)) {
|
||||
const headers = { ...request.headers() };
|
||||
if (!initial_doc_sent
|
||||
&& request.resourceType() === 'document') {
|
||||
Object.assign(headers, authHeaders);
|
||||
initial_doc_sent = true;
|
||||
} else {
|
||||
headers['X-Playwright-Test'] = '1';
|
||||
}
|
||||
await route.continue({ headers });
|
||||
} else {
|
||||
await route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
The X-Playwright-Test header is safe to send on every request
|
||||
(it only affects error response formatting). The auth headers
|
||||
(X-Dev-Auth-User-Id, X-Dev-Auth-Token) must be restricted.
|
||||
|
||||
Token Scope
|
||||
The HMAC token is signed against a specific URL. For SPA tests,
|
||||
only the initial page.goto() URL matters -- all subsequent SPA
|
||||
navigations happen client-side and do not trigger new page loads.
|
||||
Generate the token for the first URL you navigate to.
|
||||
|
||||
SESSION VALIDATION
|
||||
After initial page load, RSX runs Rsx.validate_session() on
|
||||
every SPA dispatch. This Ajax call compares the server's current
|
||||
session_hash with the hash stored in window.rsxapp.session_hash.
|
||||
If they differ, RSX does location.replace() to force a full
|
||||
page reload.
|
||||
|
||||
In test environments using dev auth headers, the session
|
||||
established by the header-based login may have a different hash
|
||||
than what validate_session returns. This is a test environment
|
||||
artifact -- in production, sessions are stable.
|
||||
|
||||
Patching validate_session
|
||||
After the initial page loads and the SPA is ready, patch
|
||||
validate_session to prevent false-positive reloads:
|
||||
|
||||
// After initial page.goto() and wait_for_rsx_ready()
|
||||
await page.evaluate(() => {
|
||||
Rsx.validate_session = async () => true;
|
||||
});
|
||||
|
||||
This must happen AFTER the initial page is fully loaded (after
|
||||
_debug_ready fires) but BEFORE any SPA dispatch calls.
|
||||
|
||||
Detecting Full Page Reloads
|
||||
Track document requests after the initial load to catch
|
||||
unexpected reloads caused by validate_session or other issues:
|
||||
|
||||
let page_reload_count = 0;
|
||||
page.on('request', req => {
|
||||
if (req.resourceType() === 'document'
|
||||
&& initial_doc_sent) {
|
||||
page_reload_count++;
|
||||
console.log(`!! UNEXPECTED RELOAD: ${req.url()}`);
|
||||
}
|
||||
});
|
||||
|
||||
SPA NAVIGATION
|
||||
RSX SPA applications use client-side routing via Spa.dispatch().
|
||||
Understanding the difference between browser navigation and SPA
|
||||
navigation is critical for writing correct tests.
|
||||
|
||||
Browser Navigation (page.goto)
|
||||
Use ONLY for the initial page load:
|
||||
|
||||
await page.goto(`${BASE_URL}/contacts`, {
|
||||
waitUntil: 'commit'
|
||||
});
|
||||
|
||||
Use 'commit' rather than 'networkidle' -- the SPA will fire many
|
||||
async requests during component lifecycle. Use RSX lifecycle
|
||||
events (see LIFECYCLE EVENTS below) for precise ready detection.
|
||||
|
||||
SPA Navigation (Spa.dispatch)
|
||||
For all subsequent navigation, use Spa.dispatch() directly:
|
||||
|
||||
await page.evaluate((path) => {
|
||||
Spa.dispatch(path);
|
||||
}, '/engagements');
|
||||
|
||||
Do NOT click sidebar links with page.evaluate + element.click().
|
||||
The jQuery .click() override that prevents default and triggers
|
||||
SPA dispatch may not fire correctly from Playwright's evaluation
|
||||
context. Calling Spa.dispatch() directly is deterministic.
|
||||
|
||||
Context Destruction
|
||||
SPA navigation replaces the DOM content area. Playwright's
|
||||
execution context can be destroyed during this process. Any
|
||||
page.evaluate() call during a SPA transition may throw:
|
||||
|
||||
Error: Execution context was destroyed
|
||||
|
||||
This is expected. Wrap evaluate calls in try/catch if they run
|
||||
during or immediately after SPA navigation. The addInitScript
|
||||
approach (see below) survives these transitions.
|
||||
|
||||
LIFECYCLE EVENTS
|
||||
RSX provides two key events for determining when the application
|
||||
is ready. These replace arbitrary timeouts with precise signals.
|
||||
|
||||
_debug_ready (Initial Page Load)
|
||||
Fires once after ALL framework init phases complete, including
|
||||
SPA action dispatch. This is the definitive "page is fully
|
||||
loaded" signal for the initial page.goto():
|
||||
|
||||
async function wait_for_rsx_ready() {
|
||||
await page.evaluate(() => {
|
||||
return new Promise((resolve) => {
|
||||
if (window.Rsx && window.Rsx.on) {
|
||||
Rsx.on('_debug_ready', () => resolve());
|
||||
} else {
|
||||
// Fallback for non-RSX pages
|
||||
document.addEventListener(
|
||||
'DOMContentLoaded',
|
||||
() => setTimeout(resolve, 200)
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
The _debug_ready event fires after these phases complete:
|
||||
1. framework_init (framework bootstrap)
|
||||
2. modules_init (manifest, SPA router setup)
|
||||
3. app_init (application module init)
|
||||
4. _spa_init (Spa.dispatch() -- awaits full action lifecycle)
|
||||
5. app_ready (final ready hooks)
|
||||
|
||||
Because _spa_init awaits the SPA action's full lifecycle
|
||||
(including on_load data fetching and on_ready), _debug_ready
|
||||
guarantees the first action is fully rendered with data.
|
||||
|
||||
spa_dispatch_ready (SPA Navigation)
|
||||
Fires after each SPA dispatch completes (action's on_ready
|
||||
has fired and all children are ready):
|
||||
|
||||
async function spa_navigate(path) {
|
||||
// Set up ready listener BEFORE dispatching
|
||||
const ready = page.evaluate(() => {
|
||||
return new Promise((resolve) => {
|
||||
Rsx.on('spa_dispatch_ready', () => resolve());
|
||||
});
|
||||
});
|
||||
|
||||
// Dispatch navigation
|
||||
try {
|
||||
await page.evaluate((p) => {
|
||||
Spa.dispatch(p);
|
||||
}, path);
|
||||
} catch(e) {}
|
||||
|
||||
// Wait for action to fully load
|
||||
await ready;
|
||||
|
||||
// Optional: wait for dynamic height recalculation
|
||||
await page.waitForTimeout(500);
|
||||
}
|
||||
|
||||
IMPORTANT: Register the spa_dispatch_ready listener BEFORE
|
||||
calling Spa.dispatch(). The promise must be waiting before
|
||||
the event fires, or it will be missed.
|
||||
|
||||
Dynamic Height Settle Time
|
||||
DataGrids with $rows="dynamic" recalculate per_page from
|
||||
viewport height with a 200ms debounce after render. Add a
|
||||
500ms wait after the ready event to allow this recalculation
|
||||
to complete:
|
||||
|
||||
const DYNAMIC_HEIGHT_SETTLE = 500;
|
||||
await ready_promise;
|
||||
await page.waitForTimeout(DYNAMIC_HEIGHT_SETTLE);
|
||||
|
||||
OBSERVING STATE
|
||||
jqhtml components have async lifecycles: on_create (sync) ->
|
||||
render (sync) -> on_load (async) -> on_ready (async). A DataGrid
|
||||
may show a loading spinner for 50-500ms before data appears.
|
||||
Point-in-time checks miss these transitions.
|
||||
|
||||
The Wrong Way: Point-in-Time Checks
|
||||
This misses fast transitions:
|
||||
|
||||
// BAD: spinner may appear and disappear between these checks
|
||||
await page.waitForTimeout(500);
|
||||
const visible = await page.evaluate(() => {
|
||||
return !!document.querySelector('.spinner.is-visible');
|
||||
});
|
||||
|
||||
The Right Way: addInitScript with Polling Observer
|
||||
Inject a persistent polling loop via addInitScript. This runs on
|
||||
every page load (including the initial one), survives SPA
|
||||
navigations that destroy the execution context, and captures
|
||||
every state transition:
|
||||
|
||||
await page.addInitScript(() => {
|
||||
let _last_state = null;
|
||||
let _step = 'init';
|
||||
let _step_start = Date.now();
|
||||
|
||||
// Allow the test to label steps
|
||||
window.__test_set_step = function(step) {
|
||||
_step = step;
|
||||
_step_start = Date.now();
|
||||
_last_state = null;
|
||||
console.log(`[TEST] === ${step} === (t=0)`);
|
||||
};
|
||||
|
||||
// Poll every 10ms, log transitions via console
|
||||
setInterval(() => {
|
||||
const spinner = !!document.querySelector(
|
||||
'.datagrid-loading-overlay.is-visible'
|
||||
);
|
||||
const rows = document.querySelectorAll(
|
||||
'.DataGrid_Table tr:not(.placeholder-row)' +
|
||||
':not(.empty-row):not(.loading-row)'
|
||||
).length;
|
||||
|
||||
const key = `s=${spinner},r=${rows}`;
|
||||
if (key !== _last_state) {
|
||||
const elapsed = Date.now() - _step_start;
|
||||
const label = spinner ? 'SPINNER'
|
||||
: (rows > 0 ? `DATA(${rows})` : 'EMPTY');
|
||||
console.log(`[TEST] ${elapsed}ms: ${label}`);
|
||||
_last_state = key;
|
||||
}
|
||||
}, 10);
|
||||
});
|
||||
|
||||
Capture these logs from the Playwright side:
|
||||
|
||||
const state_log = [];
|
||||
page.on('console', msg => {
|
||||
const text = msg.text();
|
||||
if (text.startsWith('[TEST]')) {
|
||||
state_log.push(text);
|
||||
}
|
||||
});
|
||||
|
||||
Setting Steps
|
||||
Label each phase of your test so the log is readable:
|
||||
|
||||
async function set_step(label) {
|
||||
try {
|
||||
await page.evaluate((l) => {
|
||||
if (window.__test_set_step) {
|
||||
window.__test_set_step(l);
|
||||
}
|
||||
}, label);
|
||||
} catch(e) {
|
||||
// Context may be destroyed during SPA nav
|
||||
}
|
||||
}
|
||||
|
||||
Interpreting Output
|
||||
A healthy cached SPA navigation looks like:
|
||||
|
||||
[TEST] === STEP 3 === (t=0)
|
||||
[TEST] 30ms: DATA(17) <-- instant from cache
|
||||
|
||||
A broken/uncached navigation looks like:
|
||||
|
||||
[TEST] === STEP 3 === (t=0)
|
||||
[TEST] 30ms: EMPTY
|
||||
[TEST] 150ms: SPINNER
|
||||
[TEST] 800ms: DATA(10) <-- had to fetch from server
|
||||
[TEST] 1200ms: DATA(17) <-- dynamic resize reload
|
||||
|
||||
TRACKING XHR CALLS
|
||||
Monitor Ajax requests to understand how many server round-trips
|
||||
a navigation triggers:
|
||||
|
||||
let xhr_count = 0;
|
||||
page.on('request', req => {
|
||||
if (req.url().includes('datagrid_fetch')) xhr_count++;
|
||||
});
|
||||
|
||||
// Reset before each step
|
||||
xhr_count = 0;
|
||||
await spa_navigate('/contacts');
|
||||
console.log(`XHR calls: ${xhr_count}`);
|
||||
|
||||
This is essential for diagnosing redundant loads. A well-cached
|
||||
SPA return navigation should trigger 1 XHR (background
|
||||
revalidation) not 2-4.
|
||||
|
||||
VIEWPORT AND DYNAMIC SIZING
|
||||
DataGrids in dynamic row mode calculate per_page from viewport
|
||||
height. Inconsistent viewport sizes between test runs produce
|
||||
different per_page values, which produce different cache keys,
|
||||
which break cache hit expectations.
|
||||
|
||||
Always set an explicit viewport:
|
||||
|
||||
const context = await browser.newContext({
|
||||
ignoreHTTPSErrors: true,
|
||||
viewport: { width: 1920, height: 1080 }
|
||||
});
|
||||
|
||||
FULL EXAMPLE
|
||||
A complete, runnable SPA navigation test. Copy and customize
|
||||
the route paths, observer selectors, and step sequence for
|
||||
your specific test scenario.
|
||||
|
||||
See also: bin/test-datagrid-cache.js (working reference)
|
||||
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* SPA Navigation Test Template
|
||||
*
|
||||
* Tests multi-step SPA navigation with component state
|
||||
* observation. Customize ROUTE_A, ROUTE_B, and the
|
||||
* addInitScript observer for your specific scenario.
|
||||
*
|
||||
* AUTH: Headers only on first document request.
|
||||
* WAIT: RSX lifecycle events (_debug_ready,
|
||||
* spa_dispatch_ready) instead of arbitrary timeouts.
|
||||
*/
|
||||
const { chromium } = require(
|
||||
'/var/www/html/system/node_modules/playwright'
|
||||
);
|
||||
|
||||
const BASE_URL = 'https://myapp.dev.example.com';
|
||||
const USER_ID = '1';
|
||||
const TOKEN = process.argv[2] || null;
|
||||
const ROUTE_A = '/contacts'; // Primary route to test
|
||||
const ROUTE_B = '/engagements'; // Route to navigate away to
|
||||
const DYNAMIC_HEIGHT_SETTLE = 500;
|
||||
|
||||
async function run() {
|
||||
const browser = await chromium.launch({
|
||||
headless: true,
|
||||
args: ['--no-sandbox', '--disable-setuid-sandbox']
|
||||
});
|
||||
const context = await browser.newContext({
|
||||
ignoreHTTPSErrors: true,
|
||||
viewport: { width: 1920, height: 1080 }
|
||||
});
|
||||
const page = await context.newPage();
|
||||
|
||||
// -------------------------------------------------
|
||||
// AUTH: headers only on first document request
|
||||
// -------------------------------------------------
|
||||
// Sending auth headers on every request causes
|
||||
// RsxAuth::login() to regenerate the session,
|
||||
// breaking validate_session(). After the first
|
||||
// document response sets a cookie, all subsequent
|
||||
// requests authenticate via that cookie.
|
||||
const authHeaders = {
|
||||
'X-Dev-Auth-User-Id': USER_ID,
|
||||
'X-Playwright-Test': '1'
|
||||
};
|
||||
if (TOKEN) {
|
||||
authHeaders['X-Dev-Auth-Token'] = TOKEN;
|
||||
}
|
||||
|
||||
let initial_doc_sent = false;
|
||||
await page.route('**/*', async (route, request) => {
|
||||
const url = request.url();
|
||||
if (url.startsWith(BASE_URL)) {
|
||||
const headers = { ...request.headers() };
|
||||
if (!initial_doc_sent
|
||||
&& request.resourceType() === 'document'
|
||||
) {
|
||||
Object.assign(headers, authHeaders);
|
||||
initial_doc_sent = true;
|
||||
} else {
|
||||
headers['X-Playwright-Test'] = '1';
|
||||
}
|
||||
await route.continue({ headers });
|
||||
} else {
|
||||
await route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
// -------------------------------------------------
|
||||
// Console log capture
|
||||
// -------------------------------------------------
|
||||
const state_log = [];
|
||||
page.on('console', msg => {
|
||||
const text = msg.text();
|
||||
if (text.startsWith('[TEST]')) {
|
||||
state_log.push(text);
|
||||
}
|
||||
});
|
||||
|
||||
// -------------------------------------------------
|
||||
// Persistent observer via addInitScript
|
||||
// -------------------------------------------------
|
||||
// Survives SPA navigations. Polls every 10ms and
|
||||
// logs state transitions via console. Customize the
|
||||
// selectors for your component under test.
|
||||
await page.addInitScript(() => {
|
||||
let _last_state = null;
|
||||
let _step_start = Date.now();
|
||||
|
||||
window.__test_set_step = function(step) {
|
||||
_step_start = Date.now();
|
||||
_last_state = null;
|
||||
console.log(
|
||||
`[TEST] === ${step} === (t=0)`
|
||||
);
|
||||
};
|
||||
|
||||
setInterval(() => {
|
||||
// -- Customize these selectors --
|
||||
const spinner = !!document.querySelector(
|
||||
'.datagrid-loading-overlay.is-visible'
|
||||
);
|
||||
const tbody = document.querySelector(
|
||||
'.DataGrid_Table'
|
||||
);
|
||||
let data_rows = 0;
|
||||
if (tbody) {
|
||||
data_rows = tbody.querySelectorAll(
|
||||
'tr:not(.placeholder-row)' +
|
||||
':not(.empty-row)' +
|
||||
':not(.loading-row)'
|
||||
).length;
|
||||
}
|
||||
// -- End customizable selectors --
|
||||
|
||||
const key = `s=${spinner},r=${data_rows}`;
|
||||
if (key !== _last_state) {
|
||||
const ms = Date.now() - _step_start;
|
||||
const label = spinner
|
||||
? 'SPINNER'
|
||||
: (data_rows > 0
|
||||
? `DATA(${data_rows})`
|
||||
: 'EMPTY');
|
||||
console.log(
|
||||
`[TEST] ${String(ms).padStart(5)}`
|
||||
+ `ms: ${label}`
|
||||
);
|
||||
_last_state = key;
|
||||
}
|
||||
}, 10);
|
||||
});
|
||||
|
||||
// -------------------------------------------------
|
||||
// XHR tracking
|
||||
// -------------------------------------------------
|
||||
let xhr_count = 0;
|
||||
page.on('request', req => {
|
||||
if (req.url().includes('datagrid_fetch')) {
|
||||
xhr_count++;
|
||||
}
|
||||
});
|
||||
|
||||
// -------------------------------------------------
|
||||
// Full page reload detection
|
||||
// -------------------------------------------------
|
||||
// Any document request after initial load means
|
||||
// something broke SPA navigation (likely
|
||||
// validate_session or auth header issue).
|
||||
let reload_count = 0;
|
||||
page.on('request', req => {
|
||||
if (req.resourceType() === 'document'
|
||||
&& initial_doc_sent
|
||||
) {
|
||||
reload_count++;
|
||||
console.log(
|
||||
` !! UNEXPECTED RELOAD: ${req.url()}`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// -------------------------------------------------
|
||||
// Helpers
|
||||
// -------------------------------------------------
|
||||
|
||||
// Label test steps for the observer log
|
||||
async function set_step(label) {
|
||||
try {
|
||||
await page.evaluate((l) => {
|
||||
if (window.__test_set_step) {
|
||||
window.__test_set_step(l);
|
||||
}
|
||||
}, label);
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
// Print and reset the observer log
|
||||
function flush_log() {
|
||||
console.log(state_log.join('\n'));
|
||||
state_log.length = 0;
|
||||
}
|
||||
|
||||
// Wait for RSX _debug_ready (initial page load)
|
||||
async function wait_for_rsx_ready() {
|
||||
await page.evaluate(() => {
|
||||
return new Promise((resolve) => {
|
||||
if (window.Rsx && window.Rsx.on) {
|
||||
Rsx.on('_debug_ready',
|
||||
() => resolve());
|
||||
} else {
|
||||
if (document.readyState
|
||||
=== 'complete'
|
||||
|| document.readyState
|
||||
=== 'interactive'
|
||||
) {
|
||||
setTimeout(resolve, 200);
|
||||
} else {
|
||||
document.addEventListener(
|
||||
'DOMContentLoaded',
|
||||
() => setTimeout(
|
||||
resolve, 200
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// SPA navigate and wait for action ready
|
||||
async function spa_navigate(path) {
|
||||
xhr_count = 0;
|
||||
|
||||
// Listen BEFORE dispatching
|
||||
const ready = page.evaluate(() => {
|
||||
return new Promise((resolve) => {
|
||||
Rsx.on('spa_dispatch_ready',
|
||||
() => resolve());
|
||||
});
|
||||
});
|
||||
|
||||
try {
|
||||
await page.evaluate((p) => {
|
||||
Spa.dispatch(p);
|
||||
}, path);
|
||||
} catch(e) {}
|
||||
|
||||
await ready;
|
||||
await page.waitForTimeout(
|
||||
DYNAMIC_HEIGHT_SETTLE
|
||||
);
|
||||
}
|
||||
|
||||
// -------------------------------------------------
|
||||
// Test steps
|
||||
// -------------------------------------------------
|
||||
|
||||
console.log('');
|
||||
console.log('=== SPA Navigation Test ===');
|
||||
console.log(`Viewport: 1920x1080`);
|
||||
console.log('');
|
||||
|
||||
// STEP 1: Cold start — first visit to ROUTE_A
|
||||
console.log(
|
||||
`--- STEP 1: First visit to ${ROUTE_A}`
|
||||
+ ` (cold start) ---`
|
||||
);
|
||||
state_log.length = 0;
|
||||
xhr_count = 0;
|
||||
await set_step('STEP 1');
|
||||
await page.goto(`${BASE_URL}${ROUTE_A}`, {
|
||||
waitUntil: 'commit'
|
||||
});
|
||||
await wait_for_rsx_ready();
|
||||
await page.waitForTimeout(DYNAMIC_HEIGHT_SETTLE);
|
||||
await page.waitForLoadState('networkidle')
|
||||
.catch(() => {});
|
||||
|
||||
// Patch validate_session after initial load.
|
||||
// Dev auth sessions have a different hash than
|
||||
// what validate_session returns — test artifact.
|
||||
await page.evaluate(() => {
|
||||
Rsx.validate_session = async () => true;
|
||||
});
|
||||
|
||||
const s1_xhrs = xhr_count;
|
||||
flush_log();
|
||||
console.log(` XHR count: ${s1_xhrs}`);
|
||||
console.log('');
|
||||
|
||||
// STEP 2: Navigate away to ROUTE_B
|
||||
console.log(
|
||||
`--- STEP 2: SPA navigate to`
|
||||
+ ` ${ROUTE_B} ---`
|
||||
);
|
||||
await set_step('STEP 2');
|
||||
await spa_navigate(ROUTE_B);
|
||||
flush_log();
|
||||
console.log('');
|
||||
|
||||
// STEP 3: Return to ROUTE_A (2nd visit)
|
||||
console.log(
|
||||
`--- STEP 3: SPA back to ${ROUTE_A}`
|
||||
+ ` (2nd visit) ---`
|
||||
);
|
||||
xhr_count = 0;
|
||||
await set_step('STEP 3');
|
||||
await spa_navigate(ROUTE_A);
|
||||
const s3_xhrs = xhr_count;
|
||||
flush_log();
|
||||
console.log(` XHR count: ${s3_xhrs}`);
|
||||
console.log('');
|
||||
|
||||
// STEP 4: Navigate away again
|
||||
console.log(
|
||||
`--- STEP 4: SPA navigate to`
|
||||
+ ` ${ROUTE_B} ---`
|
||||
);
|
||||
await set_step('STEP 4');
|
||||
await spa_navigate(ROUTE_B);
|
||||
flush_log();
|
||||
console.log('');
|
||||
|
||||
// STEP 5: Return to ROUTE_A (3rd visit)
|
||||
console.log(
|
||||
`--- STEP 5: SPA back to ${ROUTE_A}`
|
||||
+ ` (3rd visit) ---`
|
||||
);
|
||||
xhr_count = 0;
|
||||
await set_step('STEP 5');
|
||||
await spa_navigate(ROUTE_A);
|
||||
const s5_xhrs = xhr_count;
|
||||
flush_log();
|
||||
console.log(` XHR count: ${s5_xhrs}`);
|
||||
console.log('');
|
||||
|
||||
// -------------------------------------------------
|
||||
// Summary
|
||||
// -------------------------------------------------
|
||||
console.log('=== SUMMARY ===');
|
||||
console.log(
|
||||
`Step 1 (cold): ${s1_xhrs} XHRs`
|
||||
);
|
||||
console.log(
|
||||
`Step 3 (2nd visit): ${s3_xhrs} XHRs`
|
||||
);
|
||||
console.log(
|
||||
`Step 5 (3rd visit): ${s5_xhrs} XHRs`
|
||||
);
|
||||
console.log(
|
||||
`Page reloads: ${reload_count}`
|
||||
+ ` (should be 0)`
|
||||
);
|
||||
console.log('');
|
||||
|
||||
if (reload_count > 0) {
|
||||
console.log(
|
||||
'FAIL: Full page reloads detected'
|
||||
+ ' during SPA navigation.'
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
'OK: No full page reloads'
|
||||
+ ' — true SPA navigation achieved.'
|
||||
);
|
||||
}
|
||||
console.log('');
|
||||
|
||||
await browser.close();
|
||||
}
|
||||
|
||||
run().catch(err => {
|
||||
console.error('Test failed:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
COMMON PITFALLS
|
||||
|
||||
Auth Headers on Every Request (Session Regeneration)
|
||||
The most critical pitfall. Sending X-Dev-Auth-User-Id on every
|
||||
request (not just the first document request) causes
|
||||
RsxAuth::login() to regenerate the session on each hit. This
|
||||
changes the session_hash, which Rsx.validate_session() detects
|
||||
as a mismatch, triggering location.replace() -- a full page
|
||||
reload that destroys SPA state.
|
||||
|
||||
Symptom: "Execution context was destroyed" errors, window
|
||||
markers disappearing, document request count incrementing
|
||||
after SPA dispatch calls.
|
||||
|
||||
Fix: Auth headers on first document request only. All
|
||||
subsequent requests use the session cookie set by that first
|
||||
response. See AUTHENTICATION section.
|
||||
|
||||
validate_session Causing Full Page Reloads
|
||||
Even with correct auth header scoping, dev-auth-established
|
||||
sessions may have a hash mismatch with what validate_session
|
||||
returns. This is a test environment artifact.
|
||||
|
||||
Symptom: SPA dispatch triggers a full page reload despite
|
||||
auth headers being correctly scoped.
|
||||
|
||||
Fix: Patch Rsx.validate_session after initial load. See
|
||||
SESSION VALIDATION section.
|
||||
|
||||
Playwright Require Path
|
||||
Playwright is installed in system/node_modules/, not in the
|
||||
project root. If your script is outside system/, use an absolute
|
||||
require path:
|
||||
|
||||
require('/var/www/html/system/node_modules/playwright')
|
||||
|
||||
SPA Navigation via Link Clicks
|
||||
Do not use element.click() on sidebar links from page.evaluate().
|
||||
jQuery's .click() override may not fire correctly through
|
||||
Playwright's evaluation bridge. Use Spa.dispatch() directly.
|
||||
|
||||
Checking Visibility by DOM Presence
|
||||
Many RSX components (loading overlays, spinners) are always in
|
||||
the DOM. They toggle visibility via CSS classes like 'is-visible'
|
||||
or opacity changes. Check for the class, not the element:
|
||||
|
||||
// WRONG: element always exists
|
||||
!!document.querySelector('.datagrid-loading-overlay')
|
||||
|
||||
// RIGHT: check for the visibility class
|
||||
!!document.querySelector('.datagrid-loading-overlay.is-visible')
|
||||
|
||||
Execution Context Destroyed
|
||||
Any page.evaluate() during SPA navigation can throw
|
||||
"Execution context was destroyed". This is normal -- the SPA
|
||||
replaced the DOM. Use try/catch or the addInitScript approach
|
||||
which is immune to context destruction.
|
||||
|
||||
addInitScript Reruns
|
||||
addInitScript runs on every NEW page load, not on SPA navigation.
|
||||
If you see your observer's timer reset to 0 mid-step, it means
|
||||
a full page reload occurred (not an SPA transition). This is a
|
||||
useful diagnostic signal -- SPA transitions should NOT cause
|
||||
addInitScript to re-run.
|
||||
|
||||
Dynamic Row Recalculation
|
||||
DataGrids with $rows="dynamic" measure the viewport after first
|
||||
render and may change per_page, triggering a second load. This
|
||||
is normal on first visit but should be cached on return visits.
|
||||
Expect to see two data states (e.g., DATA(10) then DATA(17))
|
||||
as the grid adjusts.
|
||||
|
||||
Arbitrary Timeouts Instead of Lifecycle Events
|
||||
Do NOT use page.waitForTimeout() or page.waitForLoadState()
|
||||
as primary wait mechanisms. Use RSX lifecycle events:
|
||||
- _debug_ready for initial page load
|
||||
- spa_dispatch_ready for SPA navigations
|
||||
|
||||
Only use waitForTimeout for the dynamic height settle period
|
||||
(500ms after ready event), where no lifecycle event exists.
|
||||
|
||||
FILES
|
||||
system/bin/route-debug.js
|
||||
Reference implementation for Playwright auth and single-page
|
||||
testing. Source for the _debug_ready wait pattern.
|
||||
|
||||
system/app/RSpade/Core/Js/Rsx.js
|
||||
Framework core. Contains validate_session() logic,
|
||||
_debug_ready trigger, and init phase orchestration.
|
||||
|
||||
system/app/RSpade/Core/SPA/Spa.js
|
||||
SPA router. Contains _on_spa_init() (awaited during init),
|
||||
Spa.dispatch(), and spa_dispatch_ready event trigger.
|
||||
|
||||
system/node_modules/@jqhtml/core/dist/jqhtml-core.esm.js
|
||||
jqhtml component lifecycle, caching (Jqhtml_Local_Storage),
|
||||
and Load_Coordinator. Understanding _load(), _reload(), and
|
||||
the cache key generation is essential for diagnosing cache
|
||||
miss issues.
|
||||
|
||||
rsx/theme/components/datagrid/datagrid_abstract.js
|
||||
DataGrid state management, dynamic row calculation, and
|
||||
load_page() orchestration. The parent component that drives
|
||||
DataGrid_Table reloads.
|
||||
|
||||
rsx/theme/components/datagrid/datagrid_table.js
|
||||
The child component whose on_load() actually fetches data.
|
||||
Its args (per_page, sort, filter, etc.) form the cache key.
|
||||
|
||||
bin/test-datagrid-cache.js
|
||||
Working reference test that validates DataGrid caching
|
||||
across SPA navigations. Demonstrates all patterns from
|
||||
this document.
|
||||
|
||||
SEE ALSO
|
||||
rsx_debug, spa, jqhtml, caching
|
||||
|
||||
RSX Framework March 2026 PLAYWRIGHT_SPA(7)
|
||||
@@ -502,7 +502,7 @@ COMMENTS IN TEMPLATES
|
||||
COMPONENT LIFECYCLE
|
||||
Five-stage deterministic lifecycle:
|
||||
|
||||
on_create → render → on_render → on_load → after_load → on_ready
|
||||
on_create → render → on_render → on_load → on_loaded → on_ready
|
||||
|
||||
1. on_create() (synchronous, runs BEFORE first render)
|
||||
- Setup default state BEFORE template executes
|
||||
@@ -535,7 +535,7 @@ COMPONENT LIFECYCLE
|
||||
- If this.data changes, triggers automatic re-render
|
||||
- Runtime enforces access restrictions with clear errors
|
||||
|
||||
5. after_load() (runs on REAL component, not detached proxy)
|
||||
5. on_loaded() (runs on REAL component, not detached proxy)
|
||||
- this.data is frozen (read-only)
|
||||
- this.$, this.state, this.args are accessible
|
||||
- Primary use case: clone this.data to this.state for widgets
|
||||
@@ -561,7 +561,7 @@ COMPONENT LIFECYCLE
|
||||
- on_load() runs in parallel for siblings (DOM unpredictable)
|
||||
- Data changes during load trigger automatic re-render
|
||||
- on_create(), on_render(), on_stop() must be synchronous
|
||||
- on_load(), after_load(), and on_ready() can be async
|
||||
- on_load(), on_loaded(), and on_ready() can be async
|
||||
|
||||
LIFECYCLE SKIP FLAGS
|
||||
Two flags truncate the component lifecycle. Both cascade to children
|
||||
@@ -573,7 +573,7 @@ LIFECYCLE SKIP FLAGS
|
||||
Lifecycle:
|
||||
on_create() → on_load() → READY
|
||||
|
||||
Skips: render, on_render, after_load, on_ready
|
||||
Skips: render, on_render, on_loaded, on_ready
|
||||
No child components are created (render never runs).
|
||||
|
||||
Use case: Loading data from a component without creating its UI.
|
||||
@@ -589,7 +589,7 @@ LIFECYCLE SKIP FLAGS
|
||||
Lifecycle:
|
||||
on_create() → render → on_load() → re-render → READY
|
||||
|
||||
Skips: on_render, after_load, on_ready
|
||||
Skips: on_render, on_loaded, on_ready
|
||||
Children ARE created (render runs), and their on_load() fires too.
|
||||
The full component tree is built with loaded data, but no DOM
|
||||
interaction (event handlers, plugin init, layout measurement).
|
||||
@@ -1353,7 +1353,7 @@ SYNCHRONOUS REQUIREMENTS
|
||||
on_render() YES NO
|
||||
on_stop() YES NO
|
||||
on_load() NO (async allowed) YES
|
||||
after_load() NO (async allowed) YES
|
||||
on_loaded() NO (async allowed) YES
|
||||
on_ready() NO (async allowed) YES
|
||||
|
||||
Framework needs predictable execution order for lifecycle coordination.
|
||||
|
||||
@@ -931,39 +931,41 @@ COMMON PATTERNS
|
||||
class Frontend_Contacts_Action extends Spa_Action { }
|
||||
|
||||
DETACHED ACTION LOADING
|
||||
Spa.load_detached_action() loads an action without affecting the live SPA state.
|
||||
The action is instantiated on a detached DOM element with the _load_only flag
|
||||
(on_create + on_load only, no render/children), and returns the component
|
||||
instance for inspection.
|
||||
Spa.load_detached_action(url, extra_args, options) loads an action without
|
||||
affecting the live SPA state. Returns a fully-loaded component instance.
|
||||
|
||||
Parameters:
|
||||
url - URL to resolve and load
|
||||
extra_args - Optional args merged with URL-extracted params
|
||||
options - Optional behavior options:
|
||||
load_children: false (default) Action on_load() only (_load_only)
|
||||
load_children: true Full tree: render children, all on_load()s
|
||||
fire (_load_render_only). Use for preloading.
|
||||
|
||||
Use cases:
|
||||
- Extracting action metadata (titles, breadcrumbs) for navigation UI
|
||||
- Pre-fetching action data before navigation
|
||||
- Inspecting action state without rendering it visibly
|
||||
- Preloading entire page tree to warm ORM/Ajax cache
|
||||
|
||||
Basic Usage:
|
||||
const action = await Spa.load_detached_action('/contacts/123');
|
||||
if (action) {
|
||||
const title = action.get_title?.() ?? action.constructor.name;
|
||||
const breadcrumbs = action.get_breadcrumbs?.();
|
||||
console.log('Page title:', title);
|
||||
|
||||
// IMPORTANT: Clean up when done to prevent memory leaks
|
||||
action.stop();
|
||||
}
|
||||
|
||||
With Cached Data:
|
||||
// Skip network request if cached data available
|
||||
const action = await Spa.load_detached_action('/contacts/123', {
|
||||
use_cached_data: true
|
||||
});
|
||||
|
||||
Extra Arguments:
|
||||
// Pass additional args merged with URL-extracted params
|
||||
const action = await Spa.load_detached_action('/contacts/123', {
|
||||
some_option: true,
|
||||
use_cached_data: true
|
||||
});
|
||||
Preload With Children (warm full page cache):
|
||||
// Renders children (datagrids, views, etc.) so their on_load()
|
||||
// fires and populates the cache. No DOM hooks execute.
|
||||
const action = await Spa.load_detached_action('/contacts/123',
|
||||
{}, { load_children: true });
|
||||
action.stop();
|
||||
|
||||
What It Does NOT Affect:
|
||||
- Spa.action() (current live action remains unchanged)
|
||||
|
||||
@@ -598,7 +598,7 @@ class Toggle_Button extends Component {
|
||||
2. **render** → Template executes (top-down: parent before children)
|
||||
3. **on_render()** → Fires after render, BEFORE children ready (top-down, sync)
|
||||
4. **on_load()** → Fetch data into `this.data` (bottom-up, parallel siblings, async)
|
||||
5. **after_load()** → Runs on real component (not proxy). `this.data` frozen, `this.$`/`this.state`/`this.args` accessible. Clone `this.data` → `this.state` for complex in-memory manipulations.
|
||||
5. **on_loaded()** → Runs on real component (not proxy). `this.data` frozen, `this.$`/`this.state`/`this.args` accessible. Clone `this.data` → `this.state` for complex in-memory manipulations.
|
||||
6. **on_ready()** → All children guaranteed ready (bottom-up, async)
|
||||
7. **on_stop()** → Teardown when destroyed (sync)
|
||||
|
||||
|
||||
24
node_modules/.package-lock.json
generated
vendored
24
node_modules/.package-lock.json
generated
vendored
@@ -2224,9 +2224,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jqhtml/core": {
|
||||
"version": "2.3.38",
|
||||
"resolved": "http://npm.internal.hanson.xyz/@jqhtml/core/-/core-2.3.38.tgz",
|
||||
"integrity": "sha512-7yjkqgYAPuyl9bjw67nm7+NwDTR4nCuem9IHmW99SO5o/iJIuJUB9txuBQBo8l2g+pNbDFFI4wvUxJbFn3fznA==",
|
||||
"version": "2.3.39",
|
||||
"resolved": "http://npm.internal.hanson.xyz/@jqhtml/core/-/core-2.3.39.tgz",
|
||||
"integrity": "sha512-qyxOBcoFCaf35etqvNOSJppqT4WQLfD9O2b8bAv5la4oSpRUmXSjVJFdv3cSMIK8qClXbupN8bm4FLbAalJqog==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
@@ -2250,9 +2250,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jqhtml/parser": {
|
||||
"version": "2.3.38",
|
||||
"resolved": "http://npm.internal.hanson.xyz/@jqhtml/parser/-/parser-2.3.38.tgz",
|
||||
"integrity": "sha512-kIb9u3p01FDTvQbq7LKmSBaGd5JZzJGZNL7oHQXzjSkvpL6/iTE2NXkOHI/0yiSfMR5s8tsMABnHQX8M8bzZJw==",
|
||||
"version": "2.3.39",
|
||||
"resolved": "http://npm.internal.hanson.xyz/@jqhtml/parser/-/parser-2.3.39.tgz",
|
||||
"integrity": "sha512-DLPwZf1X7enf2lVOaFaIWlu8vQYMgk/+Lioup2w4F07oXFx2+MnFgcJ/Ie9Pf6VUnMT1IOIZQxOd/5QugwFFDA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/jest": "^29.5.11",
|
||||
@@ -2290,9 +2290,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jqhtml/ssr": {
|
||||
"version": "2.3.38",
|
||||
"resolved": "http://npm.internal.hanson.xyz/@jqhtml/ssr/-/ssr-2.3.38.tgz",
|
||||
"integrity": "sha512-C0hFuzMVwAoKGGj2UvBkUqvNa2Q2Q95DYHuGMx+4cD0kCwxA2bpo5MnjcmtSQh2YX4UODKTaqmpHL+03MYOp7g==",
|
||||
"version": "2.3.39",
|
||||
"resolved": "http://npm.internal.hanson.xyz/@jqhtml/ssr/-/ssr-2.3.39.tgz",
|
||||
"integrity": "sha512-//MaIub8tel8w6l3AiqvoW021Aj9JR8BlVrZsezAO7svAIgsMFTeFdLKUud1+rg8I5Nxe4DE8CiGHz+f3Ts0kA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jquery": "^3.7.1",
|
||||
@@ -2386,9 +2386,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jqhtml/vscode-extension": {
|
||||
"version": "2.3.38",
|
||||
"resolved": "http://npm.internal.hanson.xyz/@jqhtml/vscode-extension/-/vscode-extension-2.3.38.tgz",
|
||||
"integrity": "sha512-/npaSwR6ibKl3z8xFlnMO1salWyzQIgs+1D7JH2QFi62o9TtIJWDhKKO/n2njc6PdUrJcDy6ElT1jYZh4eoNGg==",
|
||||
"version": "2.3.39",
|
||||
"resolved": "http://npm.internal.hanson.xyz/@jqhtml/vscode-extension/-/vscode-extension-2.3.39.tgz",
|
||||
"integrity": "sha512-Zi0iS5/t+5IhQoZP54J1/OOFB2OdoM6TM3g37SMJmPKjIDBUt883M3POszKFJwfj8+lrBV5OeJPOmPu3m9RYOQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"vscode": "^1.74.0"
|
||||
|
||||
2
node_modules/@jqhtml/core/dist/component-cache.d.ts
generated
vendored
2
node_modules/@jqhtml/core/dist/component-cache.d.ts
generated
vendored
@@ -51,6 +51,6 @@ export declare function write_html_cache_snapshot(component: any): void;
|
||||
* @param component - The component instance
|
||||
* @param data_changed - Whether this.data changed during on_load
|
||||
*/
|
||||
export declare function write_cache_after_load(component: any, data_changed: boolean): void;
|
||||
export declare function write_cache_on_loaded(component: any, data_changed: boolean): void;
|
||||
export {};
|
||||
//# sourceMappingURL=component-cache.d.ts.map
|
||||
2
node_modules/@jqhtml/core/dist/component-cache.d.ts.map
generated
vendored
2
node_modules/@jqhtml/core/dist/component-cache.d.ts.map
generated
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"component-cache.d.ts","sourceRoot":"","sources":["../src/component-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH;;GAEG;AACH,UAAU,gBAAgB;IACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,GAAG,GAAG,gBAAgB,CAanE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,GAAG,GAAG,IAAI,CA+FzD;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,CAqD7D;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,GAAG,GAAG,IAAI,CA2B9D;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,GAAG,EAAE,YAAY,EAAE,OAAO,GAAG,IAAI,CA4BlF"}
|
||||
{"version":3,"file":"component-cache.d.ts","sourceRoot":"","sources":["../src/component-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH;;GAEG;AACH,UAAU,gBAAgB;IACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,GAAG,GAAG,gBAAgB,CAanE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,GAAG,GAAG,IAAI,CA+FzD;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,CAqD7D;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,GAAG,GAAG,IAAI,CA2B9D;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,GAAG,EAAE,YAAY,EAAE,OAAO,GAAG,IAAI,CA4BjF"}
|
||||
3
node_modules/@jqhtml/core/dist/component.d.ts
generated
vendored
3
node_modules/@jqhtml/core/dist/component.d.ts
generated
vendored
@@ -52,6 +52,7 @@ export declare class Jqhtml_Component {
|
||||
private _use_cached_data_hit;
|
||||
private _load_only;
|
||||
private _load_render_only;
|
||||
_is_detached: boolean;
|
||||
private _has_rendered;
|
||||
private _load_queue;
|
||||
private __has_custom_on_load;
|
||||
@@ -310,7 +311,7 @@ export declare class Jqhtml_Component {
|
||||
on_render(): void;
|
||||
on_create(): void;
|
||||
on_load(): void | Promise<void>;
|
||||
after_load(): void | Promise<void>;
|
||||
on_loaded(): void | Promise<void>;
|
||||
on_ready(): Promise<void>;
|
||||
on_stop(): void;
|
||||
/**
|
||||
|
||||
2
node_modules/@jqhtml/core/dist/component.d.ts.map
generated
vendored
2
node_modules/@jqhtml/core/dist/component.d.ts.map
generated
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAoBH,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,YAAY,CAAC,EAAE;YACb,GAAG,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;YACjF,UAAU,EAAE,MAAM,IAAI,CAAC;SACxB,CAAC;KACH;CACF;AAED,qBAAa,gBAAgB;IAE3B,MAAM,CAAC,kBAAkB,UAAQ;IACjC,MAAM,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC;IAGtB,CAAC,EAAE,GAAG,CAAC;IACP,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAK;IAGzB,OAAO,CAAC,kBAAkB,CAAmB;IAC7C,OAAO,CAAC,aAAa,CAAiC;IACtD,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,aAAa,CAAoC;IACzD,OAAO,CAAC,iBAAiB,CAAkB;IAC3C,OAAO,CAAC,QAAQ,CAAkB;IAClC,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,mBAAmB,CAAuB;IAClD,OAAO,CAAC,oBAAoB,CAAwE;IACpG,OAAO,CAAC,iBAAiB,CAA+B;IACxD,OAAO,CAAC,iBAAiB,CAAkB;IAC3C,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,oBAAoB,CAAoC;IAChE,OAAO,CAAC,oBAAoB,CAAuB;IACnD,OAAO,CAAC,uBAAuB,CAAoC;IACnE,OAAO,CAAC,aAAa,CAAkB;IACvC,OAAO,CAAC,MAAM,CAA0C;IACxD,OAAO,CAAC,yBAAyB,CAAwB;IACzD,OAAO,CAAC,sBAAsB,CAAkB;IAGhD,OAAO,CAAC,UAAU,CAAuB;IAGzC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,iBAAiB,CAAkB;IAC3C,OAAO,CAAC,8BAA8B,CAAkB;IACxD,OAAO,CAAC,WAAW,CAAkB;IAGrC,OAAO,CAAC,mBAAmB,CAAkB;IAG7C,OAAO,CAAC,oBAAoB,CAAkB;IAG9C,OAAO,CAAC,UAAU,CAAkB;IAGpC,OAAO,CAAC,iBAAiB,CAAkB;IAI3C,YAAY,EAAE,OAAO,CAAS;IAI9B,OAAO,CAAC,aAAa,CAAkB;IAIvC,OAAO,CAAC,WAAW,CAAoC;IAKvD,OAAO,CAAC,oBAAoB,CAAkB;gBAElC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM;IA6EzD;;;;OAIG;IACH,OAAO,CAAC,0BAA0B;IAmClC;;;;;;OAMG;YACW,eAAe;IAO7B;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAO5B;;;;;OAKG;IACH,OAAO,CAAC,YAAY;IAIpB;;;OAGG;IACH;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB5B;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,GAAE,MAAM,GAAG,IAAW,EAAE,OAAO,GAAE;QAAE,cAAc,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,MAAM;IAiUrF;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,EAAE,GAAE,MAAM,GAAG,IAAW,GAAG,IAAI;IAmDtC;;;OAGG;IACH,MAAM,CAAC,EAAE,GAAE,MAAM,GAAG,IAAW,GAAG,IAAI;IAItC;;;;;;;;;;;;;;OAcG;IACG,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC;IA0D9B;;;OAGG;IACH,MAAM,IAAI,IAAI;IA6Cd;;;;;;;;;;OAUG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAiJ5B;;;;OAIG;YACW,yBAAyB;IAOvC;;;;;;;;;OASG;YACW,kBAAkB;IAqEhC;;;;OAIG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IA0B7B;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB3C;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAS9C;;;;OAIG;YACW,wBAAwB;IAqCtC;;;;;;;;;;OAUG;YACW,4BAA4B;IAqC1C;;;;;;;;OAQG;IACG,MAAM,CAAC,aAAa,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBpD;;;;;;;;OAQG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAI9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiCG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA8G9B;;;;OAIG;IACH;;;;OAIG;IACH,KAAK,IAAI,IAAI;IA+Cb;;;OAGG;IACH,IAAI,IAAI,IAAI;IAkBZ,SAAS,IAAI,IAAI;IACjB,SAAS,IAAI,IAAI;IACjB,OAAO,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAC/B,SAAS,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAC3B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAC/B,OAAO,IAAI,IAAI;IAEf;;;;;;;;;OASG;IACH,QAAQ,CAAC,IAAI,MAAM;IAEnB;;;;OAIG;IACH;;;OAGG;IACH,gBAAgB,IAAI,OAAO;IAmC3B;;OAEG;IACH,cAAc,IAAI,MAAM;IAIxB;;;OAGG;IACH,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,SAAS,EAAE,gBAAgB,EAAE,IAAI,CAAC,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI;IAIzF;;;OAGG;IACH,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI;IAI7C;;OAEG;IACH,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAI3C;;;OAGG;IACH,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAIpC;;;;;;;;;;;;;;;OAeG;IACH,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG;IAgB3B;;;;;;;;;;;;;;;OAeG;IACH,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI;IAgB9C;;;OAGG;IACH,YAAY,IAAI,gBAAgB,GAAG,IAAI;IAIvC;;OAEG;IACH,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,EAAE;IAa1C;;OAEG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI;IAoBlD;;OAEG;IACH,MAAM,CAAC,mBAAmB,IAAI,MAAM,EAAE;IA0CtC,OAAO,CAAC,aAAa;IAIrB;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAkB7B,OAAO,CAAC,kBAAkB;IA4B1B,OAAO,CAAC,yBAAyB;IAuHjC,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,gBAAgB;IAcxB;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IA+BzB,OAAO,CAAC,cAAc;IActB,OAAO,CAAC,UAAU;CAUnB"}
|
||||
59
node_modules/@jqhtml/core/dist/index.cjs
generated
vendored
59
node_modules/@jqhtml/core/dist/index.cjs
generated
vendored
@@ -39,8 +39,8 @@ class LifecycleManager {
|
||||
* Called when component is created
|
||||
*
|
||||
* Supports lifecycle truncation flags:
|
||||
* - _load_only: on_create + on_load only. No render, no children, no on_render, no after_load, no on_ready.
|
||||
* - _load_render_only: on_create + render + on_load + re-render. No on_render, no after_load, no on_ready.
|
||||
* - _load_only: on_create + on_load only. No render, no children, no on_render, no on_loaded, no on_ready.
|
||||
* - _load_render_only: on_create + render + on_load + re-render. No on_render, no on_loaded, no on_ready.
|
||||
*/
|
||||
async boot_component(component) {
|
||||
this.active_components.add(component);
|
||||
@@ -55,9 +55,10 @@ class LifecycleManager {
|
||||
// Check for lifecycle truncation flags
|
||||
const load_only = component._load_only;
|
||||
const load_render_only = component._load_render_only;
|
||||
const is_detached = component._is_detached;
|
||||
let render_id;
|
||||
if (load_only) {
|
||||
// _load_only: skip render entirely, no children created
|
||||
if (load_only || is_detached) {
|
||||
// _load_only or detached: skip initial render entirely
|
||||
render_id = 0;
|
||||
component._render_count = 0;
|
||||
}
|
||||
@@ -84,9 +85,6 @@ class LifecycleManager {
|
||||
// even if on_load() itself completes instantly (deterministic behavior)
|
||||
await Promise.resolve();
|
||||
}
|
||||
// Trigger 'loaded' event - fires after on_load completes (or would have completed)
|
||||
// This happens before the render/on_render second step
|
||||
component.trigger('loaded');
|
||||
// Check if stopped during load
|
||||
if (component._stopped)
|
||||
return;
|
||||
@@ -98,8 +96,9 @@ class LifecycleManager {
|
||||
component.trigger('ready');
|
||||
return;
|
||||
}
|
||||
// If data changed during load, re-render
|
||||
if (component._should_rerender()) {
|
||||
// Detached elements always render here (initial render was skipped)
|
||||
// Normal elements re-render only if data changed during on_load
|
||||
if (is_detached || component._should_rerender()) {
|
||||
// Disable animations during re-render to prevent visual artifacts
|
||||
// from CSS transitions/animations firing on newly rendered elements
|
||||
const $el = component.$;
|
||||
@@ -2659,7 +2658,7 @@ function write_html_cache_snapshot(component) {
|
||||
* @param component - The component instance
|
||||
* @param data_changed - Whether this.data changed during on_load
|
||||
*/
|
||||
function write_cache_after_load(component, data_changed) {
|
||||
function write_cache_on_loaded(component, data_changed) {
|
||||
if (!data_changed || !component._cache_key) {
|
||||
return;
|
||||
}
|
||||
@@ -2842,10 +2841,13 @@ class Jqhtml_Component {
|
||||
this._on_render_complete = false; // True after on_render() has been called post-on_load
|
||||
// use_cached_data feature - skip on_load() when cache hit occurs
|
||||
this._use_cached_data_hit = false; // True if use_cached_data=true AND cache was used
|
||||
// _load_only: create + load only. No render, no children, no on_render, no after_load, no on_ready.
|
||||
// _load_only: create + load only. No render, no children, no on_render, no on_loaded, no on_ready.
|
||||
this._load_only = false;
|
||||
// _load_render_only: create + render + load + re-render. No on_render, no after_load, no on_ready.
|
||||
// _load_render_only: create + render + load + re-render. No on_render, no on_loaded, no on_ready.
|
||||
this._load_render_only = false;
|
||||
// Detached optimization: true when element is not in the DOM at boot time.
|
||||
// Skips initial render and cache read — just on_load then render.
|
||||
this._is_detached = false;
|
||||
// rendered event - fires once after the synchronous render chain completes
|
||||
// (after on_load's re-render if applicable, or after first render if no on_load)
|
||||
this._has_rendered = false;
|
||||
@@ -3390,11 +3392,11 @@ class Jqhtml_Component {
|
||||
Jqhtml_Local_Storage.set(cache_key, this.data);
|
||||
}
|
||||
}
|
||||
// Call after_load() on the real component (this.data frozen, full access to this.$, this.state)
|
||||
// Call on_loaded() on the real component (this.data frozen, full access to this.$, this.state)
|
||||
// Suppressed by _load_only and _load_render_only flags (preloading mode)
|
||||
if (!this._load_only && !this._load_render_only) {
|
||||
await this._call_lifecycle('after_load');
|
||||
this.trigger('after_load');
|
||||
await this._call_lifecycle('on_loaded');
|
||||
this.trigger('on_loaded');
|
||||
}
|
||||
});
|
||||
return data_changed;
|
||||
@@ -3414,12 +3416,17 @@ class Jqhtml_Component {
|
||||
`on_create() must be synchronous code. Remove 'async' from the function declaration.`);
|
||||
// Don't await - on_create MUST be sync. The warning is enough.
|
||||
}
|
||||
// Detect detached elements — skip cache and initial render for elements not in DOM
|
||||
this._is_detached = !this.$[0].isConnected;
|
||||
// OPTIMIZATION: Skip cache operations and snapshot if no custom on_load()
|
||||
// Components without on_load() don't fetch data, so nothing to cache or restore
|
||||
if (this.__has_custom_on_load) {
|
||||
// CACHE CHECK - Read from cache based on cache mode ('data' or 'html')
|
||||
// Skip cache for detached elements — no point hydrating from cache when not in DOM
|
||||
// @see component-cache.ts for full implementation
|
||||
read_cache_in_create(this);
|
||||
if (!this._is_detached) {
|
||||
read_cache_in_create(this);
|
||||
}
|
||||
// Snapshot this.data after on_create() completes
|
||||
// This will be restored before each on_load() execution to reset state
|
||||
this.__initial_data_snapshot = JSON.parse(JSON.stringify(this.data));
|
||||
@@ -3455,8 +3462,8 @@ class Jqhtml_Component {
|
||||
this._log_lifecycle('load', 'complete (use_cached_data - skipped on_load)');
|
||||
this.trigger('load');
|
||||
if (!this._load_only && !this._load_render_only) {
|
||||
await this._call_lifecycle('after_load');
|
||||
this.trigger('after_load');
|
||||
await this._call_lifecycle('on_loaded');
|
||||
this.trigger('on_loaded');
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -3535,8 +3542,8 @@ class Jqhtml_Component {
|
||||
this._log_lifecycle('load', 'complete (follower)');
|
||||
this.trigger('load');
|
||||
if (!this._load_only && !this._load_render_only) {
|
||||
await this._call_lifecycle('after_load');
|
||||
this.trigger('after_load');
|
||||
await this._call_lifecycle('on_loaded');
|
||||
this.trigger('on_loaded');
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -3605,18 +3612,18 @@ class Jqhtml_Component {
|
||||
// Used by HTML cache mode for synchronization - static parents don't block children
|
||||
this._is_dynamic = data_changed && data_after_load !== '{}';
|
||||
// CACHE WRITE - @see component-cache.ts
|
||||
write_cache_after_load(this, this._is_dynamic);
|
||||
write_cache_on_loaded(this, this._is_dynamic);
|
||||
this._ready_state = 2;
|
||||
this._update_debug_attrs();
|
||||
this._log_lifecycle('load', 'complete');
|
||||
// Emit lifecycle event
|
||||
this.trigger('load');
|
||||
// Call after_load() - runs on the REAL component (not detached proxy)
|
||||
// Call on_loaded() - runs on the REAL component (not detached proxy)
|
||||
// this.data is frozen (read-only), but this.$, this.state, this.args are accessible
|
||||
// Suppressed by _load_only and _load_render_only flags (preloading mode)
|
||||
if (!this._load_only && !this._load_render_only) {
|
||||
await this._call_lifecycle('after_load');
|
||||
this.trigger('after_load');
|
||||
await this._call_lifecycle('on_loaded');
|
||||
this.trigger('on_loaded');
|
||||
}
|
||||
}
|
||||
finally {
|
||||
@@ -4021,7 +4028,7 @@ class Jqhtml_Component {
|
||||
on_render() { }
|
||||
on_create() { }
|
||||
on_load() { } // Override to fetch data asynchronously
|
||||
after_load() { } // Override to process loaded data (e.g., clone this.data to this.state)
|
||||
on_loaded() { } // Override to process loaded data (e.g., clone this.data to this.state)
|
||||
async on_ready() { }
|
||||
on_stop() { }
|
||||
/**
|
||||
@@ -5273,7 +5280,7 @@ function init(jQuery) {
|
||||
}
|
||||
}
|
||||
// Version - will be replaced during build with actual version from package.json
|
||||
const version = '2.3.38';
|
||||
const version = '2.3.39';
|
||||
// Default export with all functionality
|
||||
const jqhtml = {
|
||||
// Core
|
||||
|
||||
2
node_modules/@jqhtml/core/dist/index.cjs.map
generated
vendored
2
node_modules/@jqhtml/core/dist/index.cjs.map
generated
vendored
File diff suppressed because one or more lines are too long
59
node_modules/@jqhtml/core/dist/index.js
generated
vendored
59
node_modules/@jqhtml/core/dist/index.js
generated
vendored
@@ -35,8 +35,8 @@ class LifecycleManager {
|
||||
* Called when component is created
|
||||
*
|
||||
* Supports lifecycle truncation flags:
|
||||
* - _load_only: on_create + on_load only. No render, no children, no on_render, no after_load, no on_ready.
|
||||
* - _load_render_only: on_create + render + on_load + re-render. No on_render, no after_load, no on_ready.
|
||||
* - _load_only: on_create + on_load only. No render, no children, no on_render, no on_loaded, no on_ready.
|
||||
* - _load_render_only: on_create + render + on_load + re-render. No on_render, no on_loaded, no on_ready.
|
||||
*/
|
||||
async boot_component(component) {
|
||||
this.active_components.add(component);
|
||||
@@ -51,9 +51,10 @@ class LifecycleManager {
|
||||
// Check for lifecycle truncation flags
|
||||
const load_only = component._load_only;
|
||||
const load_render_only = component._load_render_only;
|
||||
const is_detached = component._is_detached;
|
||||
let render_id;
|
||||
if (load_only) {
|
||||
// _load_only: skip render entirely, no children created
|
||||
if (load_only || is_detached) {
|
||||
// _load_only or detached: skip initial render entirely
|
||||
render_id = 0;
|
||||
component._render_count = 0;
|
||||
}
|
||||
@@ -80,9 +81,6 @@ class LifecycleManager {
|
||||
// even if on_load() itself completes instantly (deterministic behavior)
|
||||
await Promise.resolve();
|
||||
}
|
||||
// Trigger 'loaded' event - fires after on_load completes (or would have completed)
|
||||
// This happens before the render/on_render second step
|
||||
component.trigger('loaded');
|
||||
// Check if stopped during load
|
||||
if (component._stopped)
|
||||
return;
|
||||
@@ -94,8 +92,9 @@ class LifecycleManager {
|
||||
component.trigger('ready');
|
||||
return;
|
||||
}
|
||||
// If data changed during load, re-render
|
||||
if (component._should_rerender()) {
|
||||
// Detached elements always render here (initial render was skipped)
|
||||
// Normal elements re-render only if data changed during on_load
|
||||
if (is_detached || component._should_rerender()) {
|
||||
// Disable animations during re-render to prevent visual artifacts
|
||||
// from CSS transitions/animations firing on newly rendered elements
|
||||
const $el = component.$;
|
||||
@@ -2655,7 +2654,7 @@ function write_html_cache_snapshot(component) {
|
||||
* @param component - The component instance
|
||||
* @param data_changed - Whether this.data changed during on_load
|
||||
*/
|
||||
function write_cache_after_load(component, data_changed) {
|
||||
function write_cache_on_loaded(component, data_changed) {
|
||||
if (!data_changed || !component._cache_key) {
|
||||
return;
|
||||
}
|
||||
@@ -2838,10 +2837,13 @@ class Jqhtml_Component {
|
||||
this._on_render_complete = false; // True after on_render() has been called post-on_load
|
||||
// use_cached_data feature - skip on_load() when cache hit occurs
|
||||
this._use_cached_data_hit = false; // True if use_cached_data=true AND cache was used
|
||||
// _load_only: create + load only. No render, no children, no on_render, no after_load, no on_ready.
|
||||
// _load_only: create + load only. No render, no children, no on_render, no on_loaded, no on_ready.
|
||||
this._load_only = false;
|
||||
// _load_render_only: create + render + load + re-render. No on_render, no after_load, no on_ready.
|
||||
// _load_render_only: create + render + load + re-render. No on_render, no on_loaded, no on_ready.
|
||||
this._load_render_only = false;
|
||||
// Detached optimization: true when element is not in the DOM at boot time.
|
||||
// Skips initial render and cache read — just on_load then render.
|
||||
this._is_detached = false;
|
||||
// rendered event - fires once after the synchronous render chain completes
|
||||
// (after on_load's re-render if applicable, or after first render if no on_load)
|
||||
this._has_rendered = false;
|
||||
@@ -3386,11 +3388,11 @@ class Jqhtml_Component {
|
||||
Jqhtml_Local_Storage.set(cache_key, this.data);
|
||||
}
|
||||
}
|
||||
// Call after_load() on the real component (this.data frozen, full access to this.$, this.state)
|
||||
// Call on_loaded() on the real component (this.data frozen, full access to this.$, this.state)
|
||||
// Suppressed by _load_only and _load_render_only flags (preloading mode)
|
||||
if (!this._load_only && !this._load_render_only) {
|
||||
await this._call_lifecycle('after_load');
|
||||
this.trigger('after_load');
|
||||
await this._call_lifecycle('on_loaded');
|
||||
this.trigger('on_loaded');
|
||||
}
|
||||
});
|
||||
return data_changed;
|
||||
@@ -3410,12 +3412,17 @@ class Jqhtml_Component {
|
||||
`on_create() must be synchronous code. Remove 'async' from the function declaration.`);
|
||||
// Don't await - on_create MUST be sync. The warning is enough.
|
||||
}
|
||||
// Detect detached elements — skip cache and initial render for elements not in DOM
|
||||
this._is_detached = !this.$[0].isConnected;
|
||||
// OPTIMIZATION: Skip cache operations and snapshot if no custom on_load()
|
||||
// Components without on_load() don't fetch data, so nothing to cache or restore
|
||||
if (this.__has_custom_on_load) {
|
||||
// CACHE CHECK - Read from cache based on cache mode ('data' or 'html')
|
||||
// Skip cache for detached elements — no point hydrating from cache when not in DOM
|
||||
// @see component-cache.ts for full implementation
|
||||
read_cache_in_create(this);
|
||||
if (!this._is_detached) {
|
||||
read_cache_in_create(this);
|
||||
}
|
||||
// Snapshot this.data after on_create() completes
|
||||
// This will be restored before each on_load() execution to reset state
|
||||
this.__initial_data_snapshot = JSON.parse(JSON.stringify(this.data));
|
||||
@@ -3451,8 +3458,8 @@ class Jqhtml_Component {
|
||||
this._log_lifecycle('load', 'complete (use_cached_data - skipped on_load)');
|
||||
this.trigger('load');
|
||||
if (!this._load_only && !this._load_render_only) {
|
||||
await this._call_lifecycle('after_load');
|
||||
this.trigger('after_load');
|
||||
await this._call_lifecycle('on_loaded');
|
||||
this.trigger('on_loaded');
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -3531,8 +3538,8 @@ class Jqhtml_Component {
|
||||
this._log_lifecycle('load', 'complete (follower)');
|
||||
this.trigger('load');
|
||||
if (!this._load_only && !this._load_render_only) {
|
||||
await this._call_lifecycle('after_load');
|
||||
this.trigger('after_load');
|
||||
await this._call_lifecycle('on_loaded');
|
||||
this.trigger('on_loaded');
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -3601,18 +3608,18 @@ class Jqhtml_Component {
|
||||
// Used by HTML cache mode for synchronization - static parents don't block children
|
||||
this._is_dynamic = data_changed && data_after_load !== '{}';
|
||||
// CACHE WRITE - @see component-cache.ts
|
||||
write_cache_after_load(this, this._is_dynamic);
|
||||
write_cache_on_loaded(this, this._is_dynamic);
|
||||
this._ready_state = 2;
|
||||
this._update_debug_attrs();
|
||||
this._log_lifecycle('load', 'complete');
|
||||
// Emit lifecycle event
|
||||
this.trigger('load');
|
||||
// Call after_load() - runs on the REAL component (not detached proxy)
|
||||
// Call on_loaded() - runs on the REAL component (not detached proxy)
|
||||
// this.data is frozen (read-only), but this.$, this.state, this.args are accessible
|
||||
// Suppressed by _load_only and _load_render_only flags (preloading mode)
|
||||
if (!this._load_only && !this._load_render_only) {
|
||||
await this._call_lifecycle('after_load');
|
||||
this.trigger('after_load');
|
||||
await this._call_lifecycle('on_loaded');
|
||||
this.trigger('on_loaded');
|
||||
}
|
||||
}
|
||||
finally {
|
||||
@@ -4017,7 +4024,7 @@ class Jqhtml_Component {
|
||||
on_render() { }
|
||||
on_create() { }
|
||||
on_load() { } // Override to fetch data asynchronously
|
||||
after_load() { } // Override to process loaded data (e.g., clone this.data to this.state)
|
||||
on_loaded() { } // Override to process loaded data (e.g., clone this.data to this.state)
|
||||
async on_ready() { }
|
||||
on_stop() { }
|
||||
/**
|
||||
@@ -5269,7 +5276,7 @@ function init(jQuery) {
|
||||
}
|
||||
}
|
||||
// Version - will be replaced during build with actual version from package.json
|
||||
const version = '2.3.38';
|
||||
const version = '2.3.39';
|
||||
// Default export with all functionality
|
||||
const jqhtml = {
|
||||
// Core
|
||||
|
||||
2
node_modules/@jqhtml/core/dist/index.js.map
generated
vendored
2
node_modules/@jqhtml/core/dist/index.js.map
generated
vendored
File diff suppressed because one or more lines are too long
61
node_modules/@jqhtml/core/dist/jqhtml-core.esm.js
generated
vendored
61
node_modules/@jqhtml/core/dist/jqhtml-core.esm.js
generated
vendored
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* JQHTML Core v2.3.38
|
||||
* JQHTML Core v2.3.39
|
||||
* (c) 2025 JQHTML Team
|
||||
* Released under the MIT License
|
||||
*/
|
||||
@@ -40,8 +40,8 @@ class LifecycleManager {
|
||||
* Called when component is created
|
||||
*
|
||||
* Supports lifecycle truncation flags:
|
||||
* - _load_only: on_create + on_load only. No render, no children, no on_render, no after_load, no on_ready.
|
||||
* - _load_render_only: on_create + render + on_load + re-render. No on_render, no after_load, no on_ready.
|
||||
* - _load_only: on_create + on_load only. No render, no children, no on_render, no on_loaded, no on_ready.
|
||||
* - _load_render_only: on_create + render + on_load + re-render. No on_render, no on_loaded, no on_ready.
|
||||
*/
|
||||
async boot_component(component) {
|
||||
this.active_components.add(component);
|
||||
@@ -56,9 +56,10 @@ class LifecycleManager {
|
||||
// Check for lifecycle truncation flags
|
||||
const load_only = component._load_only;
|
||||
const load_render_only = component._load_render_only;
|
||||
const is_detached = component._is_detached;
|
||||
let render_id;
|
||||
if (load_only) {
|
||||
// _load_only: skip render entirely, no children created
|
||||
if (load_only || is_detached) {
|
||||
// _load_only or detached: skip initial render entirely
|
||||
render_id = 0;
|
||||
component._render_count = 0;
|
||||
}
|
||||
@@ -85,9 +86,6 @@ class LifecycleManager {
|
||||
// even if on_load() itself completes instantly (deterministic behavior)
|
||||
await Promise.resolve();
|
||||
}
|
||||
// Trigger 'loaded' event - fires after on_load completes (or would have completed)
|
||||
// This happens before the render/on_render second step
|
||||
component.trigger('loaded');
|
||||
// Check if stopped during load
|
||||
if (component._stopped)
|
||||
return;
|
||||
@@ -99,8 +97,9 @@ class LifecycleManager {
|
||||
component.trigger('ready');
|
||||
return;
|
||||
}
|
||||
// If data changed during load, re-render
|
||||
if (component._should_rerender()) {
|
||||
// Detached elements always render here (initial render was skipped)
|
||||
// Normal elements re-render only if data changed during on_load
|
||||
if (is_detached || component._should_rerender()) {
|
||||
// Disable animations during re-render to prevent visual artifacts
|
||||
// from CSS transitions/animations firing on newly rendered elements
|
||||
const $el = component.$;
|
||||
@@ -2660,7 +2659,7 @@ function write_html_cache_snapshot(component) {
|
||||
* @param component - The component instance
|
||||
* @param data_changed - Whether this.data changed during on_load
|
||||
*/
|
||||
function write_cache_after_load(component, data_changed) {
|
||||
function write_cache_on_loaded(component, data_changed) {
|
||||
if (!data_changed || !component._cache_key) {
|
||||
return;
|
||||
}
|
||||
@@ -2843,10 +2842,13 @@ class Jqhtml_Component {
|
||||
this._on_render_complete = false; // True after on_render() has been called post-on_load
|
||||
// use_cached_data feature - skip on_load() when cache hit occurs
|
||||
this._use_cached_data_hit = false; // True if use_cached_data=true AND cache was used
|
||||
// _load_only: create + load only. No render, no children, no on_render, no after_load, no on_ready.
|
||||
// _load_only: create + load only. No render, no children, no on_render, no on_loaded, no on_ready.
|
||||
this._load_only = false;
|
||||
// _load_render_only: create + render + load + re-render. No on_render, no after_load, no on_ready.
|
||||
// _load_render_only: create + render + load + re-render. No on_render, no on_loaded, no on_ready.
|
||||
this._load_render_only = false;
|
||||
// Detached optimization: true when element is not in the DOM at boot time.
|
||||
// Skips initial render and cache read — just on_load then render.
|
||||
this._is_detached = false;
|
||||
// rendered event - fires once after the synchronous render chain completes
|
||||
// (after on_load's re-render if applicable, or after first render if no on_load)
|
||||
this._has_rendered = false;
|
||||
@@ -3391,11 +3393,11 @@ class Jqhtml_Component {
|
||||
Jqhtml_Local_Storage.set(cache_key, this.data);
|
||||
}
|
||||
}
|
||||
// Call after_load() on the real component (this.data frozen, full access to this.$, this.state)
|
||||
// Call on_loaded() on the real component (this.data frozen, full access to this.$, this.state)
|
||||
// Suppressed by _load_only and _load_render_only flags (preloading mode)
|
||||
if (!this._load_only && !this._load_render_only) {
|
||||
await this._call_lifecycle('after_load');
|
||||
this.trigger('after_load');
|
||||
await this._call_lifecycle('on_loaded');
|
||||
this.trigger('on_loaded');
|
||||
}
|
||||
});
|
||||
return data_changed;
|
||||
@@ -3415,12 +3417,17 @@ class Jqhtml_Component {
|
||||
`on_create() must be synchronous code. Remove 'async' from the function declaration.`);
|
||||
// Don't await - on_create MUST be sync. The warning is enough.
|
||||
}
|
||||
// Detect detached elements — skip cache and initial render for elements not in DOM
|
||||
this._is_detached = !this.$[0].isConnected;
|
||||
// OPTIMIZATION: Skip cache operations and snapshot if no custom on_load()
|
||||
// Components without on_load() don't fetch data, so nothing to cache or restore
|
||||
if (this.__has_custom_on_load) {
|
||||
// CACHE CHECK - Read from cache based on cache mode ('data' or 'html')
|
||||
// Skip cache for detached elements — no point hydrating from cache when not in DOM
|
||||
// @see component-cache.ts for full implementation
|
||||
read_cache_in_create(this);
|
||||
if (!this._is_detached) {
|
||||
read_cache_in_create(this);
|
||||
}
|
||||
// Snapshot this.data after on_create() completes
|
||||
// This will be restored before each on_load() execution to reset state
|
||||
this.__initial_data_snapshot = JSON.parse(JSON.stringify(this.data));
|
||||
@@ -3456,8 +3463,8 @@ class Jqhtml_Component {
|
||||
this._log_lifecycle('load', 'complete (use_cached_data - skipped on_load)');
|
||||
this.trigger('load');
|
||||
if (!this._load_only && !this._load_render_only) {
|
||||
await this._call_lifecycle('after_load');
|
||||
this.trigger('after_load');
|
||||
await this._call_lifecycle('on_loaded');
|
||||
this.trigger('on_loaded');
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -3536,8 +3543,8 @@ class Jqhtml_Component {
|
||||
this._log_lifecycle('load', 'complete (follower)');
|
||||
this.trigger('load');
|
||||
if (!this._load_only && !this._load_render_only) {
|
||||
await this._call_lifecycle('after_load');
|
||||
this.trigger('after_load');
|
||||
await this._call_lifecycle('on_loaded');
|
||||
this.trigger('on_loaded');
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -3606,18 +3613,18 @@ class Jqhtml_Component {
|
||||
// Used by HTML cache mode for synchronization - static parents don't block children
|
||||
this._is_dynamic = data_changed && data_after_load !== '{}';
|
||||
// CACHE WRITE - @see component-cache.ts
|
||||
write_cache_after_load(this, this._is_dynamic);
|
||||
write_cache_on_loaded(this, this._is_dynamic);
|
||||
this._ready_state = 2;
|
||||
this._update_debug_attrs();
|
||||
this._log_lifecycle('load', 'complete');
|
||||
// Emit lifecycle event
|
||||
this.trigger('load');
|
||||
// Call after_load() - runs on the REAL component (not detached proxy)
|
||||
// Call on_loaded() - runs on the REAL component (not detached proxy)
|
||||
// this.data is frozen (read-only), but this.$, this.state, this.args are accessible
|
||||
// Suppressed by _load_only and _load_render_only flags (preloading mode)
|
||||
if (!this._load_only && !this._load_render_only) {
|
||||
await this._call_lifecycle('after_load');
|
||||
this.trigger('after_load');
|
||||
await this._call_lifecycle('on_loaded');
|
||||
this.trigger('on_loaded');
|
||||
}
|
||||
}
|
||||
finally {
|
||||
@@ -4022,7 +4029,7 @@ class Jqhtml_Component {
|
||||
on_render() { }
|
||||
on_create() { }
|
||||
on_load() { } // Override to fetch data asynchronously
|
||||
after_load() { } // Override to process loaded data (e.g., clone this.data to this.state)
|
||||
on_loaded() { } // Override to process loaded data (e.g., clone this.data to this.state)
|
||||
async on_ready() { }
|
||||
on_stop() { }
|
||||
/**
|
||||
@@ -5274,7 +5281,7 @@ function init(jQuery) {
|
||||
}
|
||||
}
|
||||
// Version - will be replaced during build with actual version from package.json
|
||||
const version = '2.3.38';
|
||||
const version = '2.3.39';
|
||||
// Default export with all functionality
|
||||
const jqhtml = {
|
||||
// Core
|
||||
|
||||
2
node_modules/@jqhtml/core/dist/jqhtml-core.esm.js.map
generated
vendored
2
node_modules/@jqhtml/core/dist/jqhtml-core.esm.js.map
generated
vendored
File diff suppressed because one or more lines are too long
4
node_modules/@jqhtml/core/dist/lifecycle-manager.d.ts
generated
vendored
4
node_modules/@jqhtml/core/dist/lifecycle-manager.d.ts
generated
vendored
@@ -27,8 +27,8 @@ export declare class LifecycleManager {
|
||||
* Called when component is created
|
||||
*
|
||||
* Supports lifecycle truncation flags:
|
||||
* - _load_only: on_create + on_load only. No render, no children, no on_render, no after_load, no on_ready.
|
||||
* - _load_render_only: on_create + render + on_load + re-render. No on_render, no after_load, no on_ready.
|
||||
* - _load_only: on_create + on_load only. No render, no children, no on_render, no on_loaded, no on_ready.
|
||||
* - _load_render_only: on_create + render + on_load + re-render. No on_render, no on_loaded, no on_ready.
|
||||
*/
|
||||
boot_component(component: Jqhtml_Component): Promise<void>;
|
||||
/**
|
||||
|
||||
2
node_modules/@jqhtml/core/dist/lifecycle-manager.d.ts.map
generated
vendored
2
node_modules/@jqhtml/core/dist/lifecycle-manager.d.ts.map
generated
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"lifecycle-manager.d.ts","sourceRoot":"","sources":["../src/lifecycle-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAEpE,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAmB;IAC1C,OAAO,CAAC,iBAAiB,CAAoC;IAE7D,MAAM,CAAC,YAAY,IAAI,gBAAgB;;IAevC;;;;;;;OAOG;IACG,cAAc,CAAC,SAAS,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IA+IhE;;OAEG;IACH,oBAAoB,CAAC,SAAS,EAAE,gBAAgB,GAAG,IAAI;IAIvD;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;CAetC"}
|
||||
{"version":3,"file":"lifecycle-manager.d.ts","sourceRoot":"","sources":["../src/lifecycle-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAEpE,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAmB;IAC1C,OAAO,CAAC,iBAAiB,CAAoC;IAE7D,MAAM,CAAC,YAAY,IAAI,gBAAgB;;IAevC;;;;;;;OAOG;IACG,cAAc,CAAC,SAAS,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IA6IhE;;OAEG;IACH,oBAAoB,CAAC,SAAS,EAAE,gBAAgB,GAAG,IAAI;IAIvD;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;CAetC"}
|
||||
2
node_modules/@jqhtml/core/package.json
generated
vendored
2
node_modules/@jqhtml/core/package.json
generated
vendored
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@jqhtml/core",
|
||||
"version": "2.3.38",
|
||||
"version": "2.3.39",
|
||||
"description": "Core runtime library for JQHTML",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
|
||||
2
node_modules/@jqhtml/parser/dist/codegen.js
generated
vendored
2
node_modules/@jqhtml/parser/dist/codegen.js
generated
vendored
@@ -1385,7 +1385,7 @@ export class CodeGenerator {
|
||||
for (const [name, component] of this.components) {
|
||||
code += `// Component: ${name}\n`;
|
||||
code += `jqhtml_components.set('${name}', {\n`;
|
||||
code += ` _jqhtml_version: '2.3.38',\n`; // Version will be replaced during build
|
||||
code += ` _jqhtml_version: '2.3.39',\n`; // Version will be replaced during build
|
||||
code += ` name: '${name}',\n`;
|
||||
code += ` tag: '${component.tagName}',\n`;
|
||||
code += ` defaultAttributes: ${this.serializeAttributeObject(component.defaultAttributes)},\n`;
|
||||
|
||||
2
node_modules/@jqhtml/parser/package.json
generated
vendored
2
node_modules/@jqhtml/parser/package.json
generated
vendored
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@jqhtml/parser",
|
||||
"version": "2.3.38",
|
||||
"version": "2.3.39",
|
||||
"description": "JQHTML template parser - converts templates to JavaScript",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
|
||||
2
node_modules/@jqhtml/ssr/package.json
generated
vendored
2
node_modules/@jqhtml/ssr/package.json
generated
vendored
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@jqhtml/ssr",
|
||||
"version": "2.3.38",
|
||||
"version": "2.3.39",
|
||||
"description": "Server-Side Rendering for JQHTML components - renders components to HTML for SEO",
|
||||
"main": "src/index.js",
|
||||
"bin": {
|
||||
|
||||
2
node_modules/@jqhtml/vscode-extension/.version
generated
vendored
2
node_modules/@jqhtml/vscode-extension/.version
generated
vendored
@@ -1 +1 @@
|
||||
2.3.38
|
||||
2.3.39
|
||||
|
||||
BIN
node_modules/@jqhtml/vscode-extension/jqhtml-vscode-extension-2.3.38.vsix → node_modules/@jqhtml/vscode-extension/jqhtml-vscode-extension-2.3.39.vsix
generated
vendored
Normal file → Executable file
BIN
node_modules/@jqhtml/vscode-extension/jqhtml-vscode-extension-2.3.38.vsix → node_modules/@jqhtml/vscode-extension/jqhtml-vscode-extension-2.3.39.vsix
generated
vendored
Normal file → Executable file
Binary file not shown.
2
node_modules/@jqhtml/vscode-extension/package.json
generated
vendored
2
node_modules/@jqhtml/vscode-extension/package.json
generated
vendored
@@ -2,7 +2,7 @@
|
||||
"name": "@jqhtml/vscode-extension",
|
||||
"displayName": "JQHTML",
|
||||
"description": "Syntax highlighting and language support for JQHTML template files",
|
||||
"version": "2.3.38",
|
||||
"version": "2.3.39",
|
||||
"publisher": "jqhtml",
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
|
||||
24
package-lock.json
generated
24
package-lock.json
generated
@@ -2676,9 +2676,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jqhtml/core": {
|
||||
"version": "2.3.38",
|
||||
"resolved": "http://npm.internal.hanson.xyz/@jqhtml/core/-/core-2.3.38.tgz",
|
||||
"integrity": "sha512-7yjkqgYAPuyl9bjw67nm7+NwDTR4nCuem9IHmW99SO5o/iJIuJUB9txuBQBo8l2g+pNbDFFI4wvUxJbFn3fznA==",
|
||||
"version": "2.3.39",
|
||||
"resolved": "http://npm.internal.hanson.xyz/@jqhtml/core/-/core-2.3.39.tgz",
|
||||
"integrity": "sha512-qyxOBcoFCaf35etqvNOSJppqT4WQLfD9O2b8bAv5la4oSpRUmXSjVJFdv3cSMIK8qClXbupN8bm4FLbAalJqog==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
@@ -2702,9 +2702,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jqhtml/parser": {
|
||||
"version": "2.3.38",
|
||||
"resolved": "http://npm.internal.hanson.xyz/@jqhtml/parser/-/parser-2.3.38.tgz",
|
||||
"integrity": "sha512-kIb9u3p01FDTvQbq7LKmSBaGd5JZzJGZNL7oHQXzjSkvpL6/iTE2NXkOHI/0yiSfMR5s8tsMABnHQX8M8bzZJw==",
|
||||
"version": "2.3.39",
|
||||
"resolved": "http://npm.internal.hanson.xyz/@jqhtml/parser/-/parser-2.3.39.tgz",
|
||||
"integrity": "sha512-DLPwZf1X7enf2lVOaFaIWlu8vQYMgk/+Lioup2w4F07oXFx2+MnFgcJ/Ie9Pf6VUnMT1IOIZQxOd/5QugwFFDA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/jest": "^29.5.11",
|
||||
@@ -2742,9 +2742,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jqhtml/ssr": {
|
||||
"version": "2.3.38",
|
||||
"resolved": "http://npm.internal.hanson.xyz/@jqhtml/ssr/-/ssr-2.3.38.tgz",
|
||||
"integrity": "sha512-C0hFuzMVwAoKGGj2UvBkUqvNa2Q2Q95DYHuGMx+4cD0kCwxA2bpo5MnjcmtSQh2YX4UODKTaqmpHL+03MYOp7g==",
|
||||
"version": "2.3.39",
|
||||
"resolved": "http://npm.internal.hanson.xyz/@jqhtml/ssr/-/ssr-2.3.39.tgz",
|
||||
"integrity": "sha512-//MaIub8tel8w6l3AiqvoW021Aj9JR8BlVrZsezAO7svAIgsMFTeFdLKUud1+rg8I5Nxe4DE8CiGHz+f3Ts0kA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jquery": "^3.7.1",
|
||||
@@ -2838,9 +2838,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jqhtml/vscode-extension": {
|
||||
"version": "2.3.38",
|
||||
"resolved": "http://npm.internal.hanson.xyz/@jqhtml/vscode-extension/-/vscode-extension-2.3.38.tgz",
|
||||
"integrity": "sha512-/npaSwR6ibKl3z8xFlnMO1salWyzQIgs+1D7JH2QFi62o9TtIJWDhKKO/n2njc6PdUrJcDy6ElT1jYZh4eoNGg==",
|
||||
"version": "2.3.39",
|
||||
"resolved": "http://npm.internal.hanson.xyz/@jqhtml/vscode-extension/-/vscode-extension-2.3.39.tgz",
|
||||
"integrity": "sha512-Zi0iS5/t+5IhQoZP54J1/OOFB2OdoM6TM3g37SMJmPKjIDBUt883M3POszKFJwfj8+lrBV5OeJPOmPu3m9RYOQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"vscode": "^1.74.0"
|
||||
|
||||
Reference in New Issue
Block a user