Framework updates

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2026-03-12 19:09:07 +00:00
parent 3294fc7337
commit daa9bb2fb1
47 changed files with 2495 additions and 525 deletions

24
node_modules/.package-lock.json generated vendored
View File

@@ -2224,9 +2224,9 @@
}
},
"node_modules/@jqhtml/core": {
"version": "2.3.41",
"resolved": "http://npm.internal.hanson.xyz/@jqhtml/core/-/core-2.3.41.tgz",
"integrity": "sha512-Owf8Rf7yjG+WSRCPTXtTg+pFpWbTB+MnB/g2Clo6rVWZ5JxEqFZfmKIDx6lSX30pz16ph3RShe9Ijjc8V89S3w==",
"version": "2.3.42",
"resolved": "http://npm.internal.hanson.xyz/@jqhtml/core/-/core-2.3.42.tgz",
"integrity": "sha512-am+iSzLuM3yuTQIaguep3kQ1eFB/jOAeB+C26aYoQyuH4E15K3AvPvy7VfBLP5+80WeIZP7oFiNHeJ6OMKg8iA==",
"license": "MIT",
"dependencies": {
"@rollup/plugin-node-resolve": "^16.0.1",
@@ -2250,9 +2250,9 @@
}
},
"node_modules/@jqhtml/parser": {
"version": "2.3.41",
"resolved": "http://npm.internal.hanson.xyz/@jqhtml/parser/-/parser-2.3.41.tgz",
"integrity": "sha512-q6pT+eqWQf0qEgxzb61nERro5NkIeBnu/DQPUqRNZdywAqam8AHYlwzA5n54BlghJ6m/61DVeRMSHoVu1UV6lA==",
"version": "2.3.42",
"resolved": "http://npm.internal.hanson.xyz/@jqhtml/parser/-/parser-2.3.42.tgz",
"integrity": "sha512-sIj+wG6X3SdJLNchL3NpptN6Osh+WdB8CQR8SkzTcjZ+AvI7O/WNdxdDnQWRU3N51sIfk413v0searcjlMZKcg==",
"license": "MIT",
"dependencies": {
"@types/jest": "^29.5.11",
@@ -2290,9 +2290,9 @@
}
},
"node_modules/@jqhtml/ssr": {
"version": "2.3.41",
"resolved": "http://npm.internal.hanson.xyz/@jqhtml/ssr/-/ssr-2.3.41.tgz",
"integrity": "sha512-9uNQ7QaaBBU49ncEKxv9uoajfxe3/vt1wLOMrex81oqKB1PHFIkfQbQ1QcNakYgDTXMFkXKinH0O3qEROH9Lxw==",
"version": "2.3.42",
"resolved": "http://npm.internal.hanson.xyz/@jqhtml/ssr/-/ssr-2.3.42.tgz",
"integrity": "sha512-0glVS5mIPD+mB6X1hyzwQSVFXNs4UoUj/dUDZ/nfClsP2jdy+nLcpHxO0NL5N4phA3NOytOQ34sePp5Sx0a/gw==",
"license": "MIT",
"dependencies": {
"jquery": "^3.7.1",
@@ -2386,9 +2386,9 @@
}
},
"node_modules/@jqhtml/vscode-extension": {
"version": "2.3.41",
"resolved": "http://npm.internal.hanson.xyz/@jqhtml/vscode-extension/-/vscode-extension-2.3.41.tgz",
"integrity": "sha512-CB3tIppMT3cVLiOIAAxymMtLAae2FJfkf6aFSkQOiONK47h10k2/QkkXFJwXyRRnzbw+ijuhBCDodiLlJtt8aw==",
"version": "2.3.42",
"resolved": "http://npm.internal.hanson.xyz/@jqhtml/vscode-extension/-/vscode-extension-2.3.42.tgz",
"integrity": "sha512-+FASm90uV9SgSRtFRQiUelQ7JShx646XHg/SNzf43TaNaOMzz2SDz7fQS/4IbkYD2Qwa9vFPs5vTUKVc34rtRA==",
"license": "MIT",
"engines": {
"vscode": "^1.74.0"

View File

@@ -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":"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"}

View File

@@ -2837,6 +2837,141 @@ class Component_Queue {
}
}
/**
* JQHTML SSR Preload Data System
*
* Captures component data during SSR rendering and replays it on the client
* to skip redundant on_load() calls during hydration.
*
* Server-side: start_data_capture() → render → get_captured_data()
* Client-side: set_preload_data(entries) → components boot with preloaded data → clear_preload_data()
*/
// --- Capture (Server Side) ---
let _capture_enabled = false;
let _capture_buffer = [];
// Track captured keys to avoid duplicates from Load Coordinator followers
let _captured_keys = new Set();
/**
* Enable data capture mode. After this call, every component that completes
* on_load() has its final this.data recorded.
*
* Idempotent — calling twice is fine.
*/
function start_data_capture() {
_capture_enabled = true;
_capture_buffer = [];
_captured_keys.clear();
}
/**
* Record a component's data after on_load() completes.
* Called internally from _apply_load_result().
*
* @param component_name - Component name
* @param args - Component args at time of on_load()
* @param data - this.data after on_load() completed
* @param cache_key - The cache key for deduplication (null = always capture)
*/
function capture_component_data(component_name, args, data, cache_key) {
if (!_capture_enabled)
return;
// Deduplicate: if Load Coordinator already captured this key, skip
if (cache_key !== null) {
if (_captured_keys.has(cache_key))
return;
_captured_keys.add(cache_key);
}
// Deep clone args and data to plain objects (no proxies, no class instances)
try {
const cloned_args = JSON.parse(JSON.stringify(args));
const cloned_data = JSON.parse(JSON.stringify(data));
_capture_buffer.push({
component: component_name,
args: cloned_args,
data: cloned_data,
});
}
catch (error) {
// Non-serializable data — skip capture for this component
if (typeof console !== 'undefined') {
console.warn(`[JQHTML SSR Capture] Failed to serialize data for ${component_name}:`, error);
}
}
}
/**
* Returns all captured component data and resets the capture buffer.
* One-shot: calling clears the buffer.
*/
function get_captured_data() {
const result = _capture_buffer;
_capture_buffer = [];
_captured_keys.clear();
return result;
}
/**
* Check if data capture is currently enabled.
*/
function is_capture_enabled() {
return _capture_enabled;
}
/**
* Stop data capture and clear all state.
*/
function stop_data_capture() {
_capture_enabled = false;
_capture_buffer = [];
_captured_keys.clear();
}
// --- Preload (Client Side) ---
let _preload_cache = new Map();
/**
* Seed the preload cache with SSR-captured data.
* Must be called before any component _load() runs.
*
* @param entries - Array of { component, args, data } entries from get_captured_data()
*/
function set_preload_data(entries) {
if (!entries || entries.length === 0)
return;
_preload_cache.clear();
for (const entry of entries) {
// Generate key using the same algorithm as Load Coordinator
const result = Load_Coordinator.generate_invocation_key(entry.component, entry.args);
if (result.key !== null) {
_preload_cache.set(result.key, entry.data);
}
}
}
/**
* Check if preloaded data exists for a given cache key.
* If found, returns the data and removes the entry (one-shot).
*
* @param cache_key - The cache key (same format as Load Coordinator)
* @returns The preloaded data, or null if no match
*/
function consume_preload_data(cache_key) {
if (_preload_cache.size === 0)
return null;
const data = _preload_cache.get(cache_key);
if (data !== undefined) {
_preload_cache.delete(cache_key);
return data;
}
return null;
}
/**
* Clear any remaining unconsumed preload entries.
* Call after initial page hydration is complete.
*/
function clear_preload_data() {
_preload_cache.clear();
}
/**
* Check if any preload data is available (for fast early exit in _load).
*/
function has_preload_data() {
return _preload_cache.size > 0;
}
/**
* JQHTML v2 Component Base Class
*
@@ -3515,7 +3650,7 @@ class Jqhtml_Component {
}
return;
}
// Check if component implements cache_id() for custom cache key
// Generate cache key (needed for both preload check and load deduplication)
let cache_key = null;
let uncacheable_property;
if (typeof this.cache_id === 'function') {
@@ -3534,6 +3669,19 @@ class Jqhtml_Component {
cache_key = result.key;
uncacheable_property = result.uncacheable_property;
}
// SSR preload check: if preloaded data exists for this component+args, use it
if (cache_key !== null && has_preload_data()) {
const preloaded_data = consume_preload_data(cache_key);
if (preloaded_data !== null) {
this._cache_key = cache_key;
if (window.jqhtml?.debug?.verbose) {
console.log(`[SSR Preload] Component ${this._cid} (${this.component_name()}) using preloaded data`, { cache_key });
}
const data_before_load = JSON.stringify(this.data);
await this._apply_load_result(preloaded_data, data_before_load);
return;
}
}
// Store cache key for later use
this._cache_key = cache_key;
// If cache_key is null, args are not serializable - skip load deduplication and caching
@@ -3653,6 +3801,10 @@ class Jqhtml_Component {
}
// Freeze this.data
this.__data_frozen = true;
// SSR data capture: record this component's data for preloading
if (is_capture_enabled() && this._has_on_load()) {
capture_component_data(this.component_name(), this.args, this.data, this._cache_key);
}
// Calculate if data changed
const data_after_load = JSON.stringify(this.data);
const data_changed = data_before_load !== null && data_after_load !== data_before_load;
@@ -5336,7 +5488,7 @@ function init(jQuery) {
}
}
// Version - will be replaced during build with actual version from package.json
const version = '2.3.41';
const version = '2.3.42';
// Default export with all functionality
const jqhtml = {
// Core
@@ -5433,7 +5585,13 @@ const jqhtml = {
// Required for ES6 class instances to be stored and restored from localStorage
register_cache_class,
// Boot - hydrate server-rendered component placeholders
boot
boot,
// SSR Preload - capture data during SSR, replay on client to skip on_load()
start_data_capture,
get_captured_data,
stop_data_capture,
set_preload_data,
clear_preload_data
};
// Auto-register on window for browser environments
// This is REQUIRED for compiled templates which use window.jqhtml.register_template()
@@ -5470,12 +5628,14 @@ exports.LifecycleManager = LifecycleManager;
exports.Load_Coordinator = Load_Coordinator;
exports.applyDebugDelay = applyDebugDelay;
exports.boot = boot;
exports.clear_preload_data = clear_preload_data;
exports.create_component = create_component;
exports.default = jqhtml;
exports.devWarn = devWarn;
exports.escape_html = escape_html;
exports.escape_html_nl2br = escape_html_nl2br;
exports.extract_slots = extract_slots;
exports.get_captured_data = get_captured_data;
exports.get_component_class = get_component_class;
exports.get_component_names = get_component_names;
exports.get_registered_templates = get_registered_templates;
@@ -5497,5 +5657,8 @@ exports.register_cache_class = register_cache_class;
exports.register_component = register_component;
exports.register_template = register_template;
exports.render_template = render_template;
exports.set_preload_data = set_preload_data;
exports.start_data_capture = start_data_capture;
exports.stop_data_capture = stop_data_capture;
exports.version = version;
//# sourceMappingURL=index.cjs.map

File diff suppressed because one or more lines are too long

View File

@@ -29,6 +29,9 @@ export { Jqhtml_Local_Storage, register_cache_class };
export type { CacheMode } from './local-storage.js';
import { Load_Coordinator } from './load-coordinator.js';
export { Load_Coordinator };
import { start_data_capture, get_captured_data, stop_data_capture, set_preload_data, clear_preload_data } from './preload-data.js';
export { start_data_capture, get_captured_data, stop_data_capture, set_preload_data, clear_preload_data };
export type { PreloadEntry } from './preload-data.js';
export declare const version = "__VERSION__";
export interface DebugSettings {
verbose?: boolean;
@@ -89,6 +92,11 @@ declare const jqhtml: {
get_cache_mode(): import("./local-storage.js").CacheMode;
register_cache_class: typeof register_cache_class;
boot: typeof boot;
start_data_capture: typeof start_data_capture;
get_captured_data: typeof get_captured_data;
stop_data_capture: typeof stop_data_capture;
set_preload_data: typeof set_preload_data;
clear_preload_data: typeof clear_preload_data;
};
export default jqhtml;
//# sourceMappingURL=index.d.ts.map

View File

@@ -1 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}

View File

@@ -2833,6 +2833,141 @@ class Component_Queue {
}
}
/**
* JQHTML SSR Preload Data System
*
* Captures component data during SSR rendering and replays it on the client
* to skip redundant on_load() calls during hydration.
*
* Server-side: start_data_capture() → render → get_captured_data()
* Client-side: set_preload_data(entries) → components boot with preloaded data → clear_preload_data()
*/
// --- Capture (Server Side) ---
let _capture_enabled = false;
let _capture_buffer = [];
// Track captured keys to avoid duplicates from Load Coordinator followers
let _captured_keys = new Set();
/**
* Enable data capture mode. After this call, every component that completes
* on_load() has its final this.data recorded.
*
* Idempotent — calling twice is fine.
*/
function start_data_capture() {
_capture_enabled = true;
_capture_buffer = [];
_captured_keys.clear();
}
/**
* Record a component's data after on_load() completes.
* Called internally from _apply_load_result().
*
* @param component_name - Component name
* @param args - Component args at time of on_load()
* @param data - this.data after on_load() completed
* @param cache_key - The cache key for deduplication (null = always capture)
*/
function capture_component_data(component_name, args, data, cache_key) {
if (!_capture_enabled)
return;
// Deduplicate: if Load Coordinator already captured this key, skip
if (cache_key !== null) {
if (_captured_keys.has(cache_key))
return;
_captured_keys.add(cache_key);
}
// Deep clone args and data to plain objects (no proxies, no class instances)
try {
const cloned_args = JSON.parse(JSON.stringify(args));
const cloned_data = JSON.parse(JSON.stringify(data));
_capture_buffer.push({
component: component_name,
args: cloned_args,
data: cloned_data,
});
}
catch (error) {
// Non-serializable data — skip capture for this component
if (typeof console !== 'undefined') {
console.warn(`[JQHTML SSR Capture] Failed to serialize data for ${component_name}:`, error);
}
}
}
/**
* Returns all captured component data and resets the capture buffer.
* One-shot: calling clears the buffer.
*/
function get_captured_data() {
const result = _capture_buffer;
_capture_buffer = [];
_captured_keys.clear();
return result;
}
/**
* Check if data capture is currently enabled.
*/
function is_capture_enabled() {
return _capture_enabled;
}
/**
* Stop data capture and clear all state.
*/
function stop_data_capture() {
_capture_enabled = false;
_capture_buffer = [];
_captured_keys.clear();
}
// --- Preload (Client Side) ---
let _preload_cache = new Map();
/**
* Seed the preload cache with SSR-captured data.
* Must be called before any component _load() runs.
*
* @param entries - Array of { component, args, data } entries from get_captured_data()
*/
function set_preload_data(entries) {
if (!entries || entries.length === 0)
return;
_preload_cache.clear();
for (const entry of entries) {
// Generate key using the same algorithm as Load Coordinator
const result = Load_Coordinator.generate_invocation_key(entry.component, entry.args);
if (result.key !== null) {
_preload_cache.set(result.key, entry.data);
}
}
}
/**
* Check if preloaded data exists for a given cache key.
* If found, returns the data and removes the entry (one-shot).
*
* @param cache_key - The cache key (same format as Load Coordinator)
* @returns The preloaded data, or null if no match
*/
function consume_preload_data(cache_key) {
if (_preload_cache.size === 0)
return null;
const data = _preload_cache.get(cache_key);
if (data !== undefined) {
_preload_cache.delete(cache_key);
return data;
}
return null;
}
/**
* Clear any remaining unconsumed preload entries.
* Call after initial page hydration is complete.
*/
function clear_preload_data() {
_preload_cache.clear();
}
/**
* Check if any preload data is available (for fast early exit in _load).
*/
function has_preload_data() {
return _preload_cache.size > 0;
}
/**
* JQHTML v2 Component Base Class
*
@@ -3511,7 +3646,7 @@ class Jqhtml_Component {
}
return;
}
// Check if component implements cache_id() for custom cache key
// Generate cache key (needed for both preload check and load deduplication)
let cache_key = null;
let uncacheable_property;
if (typeof this.cache_id === 'function') {
@@ -3530,6 +3665,19 @@ class Jqhtml_Component {
cache_key = result.key;
uncacheable_property = result.uncacheable_property;
}
// SSR preload check: if preloaded data exists for this component+args, use it
if (cache_key !== null && has_preload_data()) {
const preloaded_data = consume_preload_data(cache_key);
if (preloaded_data !== null) {
this._cache_key = cache_key;
if (window.jqhtml?.debug?.verbose) {
console.log(`[SSR Preload] Component ${this._cid} (${this.component_name()}) using preloaded data`, { cache_key });
}
const data_before_load = JSON.stringify(this.data);
await this._apply_load_result(preloaded_data, data_before_load);
return;
}
}
// Store cache key for later use
this._cache_key = cache_key;
// If cache_key is null, args are not serializable - skip load deduplication and caching
@@ -3649,6 +3797,10 @@ class Jqhtml_Component {
}
// Freeze this.data
this.__data_frozen = true;
// SSR data capture: record this component's data for preloading
if (is_capture_enabled() && this._has_on_load()) {
capture_component_data(this.component_name(), this.args, this.data, this._cache_key);
}
// Calculate if data changed
const data_after_load = JSON.stringify(this.data);
const data_changed = data_before_load !== null && data_after_load !== data_before_load;
@@ -5332,7 +5484,7 @@ function init(jQuery) {
}
}
// Version - will be replaced during build with actual version from package.json
const version = '2.3.41';
const version = '2.3.42';
// Default export with all functionality
const jqhtml = {
// Core
@@ -5429,7 +5581,13 @@ const jqhtml = {
// Required for ES6 class instances to be stored and restored from localStorage
register_cache_class,
// Boot - hydrate server-rendered component placeholders
boot
boot,
// SSR Preload - capture data during SSR, replay on client to skip on_load()
start_data_capture,
get_captured_data,
stop_data_capture,
set_preload_data,
clear_preload_data
};
// Auto-register on window for browser environments
// This is REQUIRED for compiled templates which use window.jqhtml.register_template()
@@ -5459,5 +5617,5 @@ if (typeof window !== 'undefined' && !window.jqhtml) {
}
}
export { Jqhtml_Component, LifecycleManager as Jqhtml_LifecycleManager, Jqhtml_Local_Storage, LifecycleManager, Load_Coordinator, applyDebugDelay, boot, create_component, jqhtml as default, devWarn, escape_html, escape_html_nl2br, extract_slots, get_component_class, get_component_names, get_registered_templates, get_template, get_template_by_class, handleComponentError, has_component, init, init_jquery_plugin, isSequentialProcessing, list_components, logDataChange, logDispatch, logInstruction, logLifecycle, process_instructions, register, register_cache_class, register_component, register_template, render_template, version };
export { Jqhtml_Component, LifecycleManager as Jqhtml_LifecycleManager, Jqhtml_Local_Storage, LifecycleManager, Load_Coordinator, applyDebugDelay, boot, clear_preload_data, create_component, jqhtml as default, devWarn, escape_html, escape_html_nl2br, extract_slots, get_captured_data, get_component_class, get_component_names, get_registered_templates, get_template, get_template_by_class, handleComponentError, has_component, init, init_jquery_plugin, isSequentialProcessing, list_components, logDataChange, logDispatch, logInstruction, logLifecycle, process_instructions, register, register_cache_class, register_component, register_template, render_template, set_preload_data, start_data_capture, stop_data_capture, version };
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
/**
* JQHTML Core v2.3.41
* JQHTML Core v2.3.42
* (c) 2025 JQHTML Team
* Released under the MIT License
*/
@@ -2838,6 +2838,141 @@ class Component_Queue {
}
}
/**
* JQHTML SSR Preload Data System
*
* Captures component data during SSR rendering and replays it on the client
* to skip redundant on_load() calls during hydration.
*
* Server-side: start_data_capture() → render → get_captured_data()
* Client-side: set_preload_data(entries) → components boot with preloaded data → clear_preload_data()
*/
// --- Capture (Server Side) ---
let _capture_enabled = false;
let _capture_buffer = [];
// Track captured keys to avoid duplicates from Load Coordinator followers
let _captured_keys = new Set();
/**
* Enable data capture mode. After this call, every component that completes
* on_load() has its final this.data recorded.
*
* Idempotent — calling twice is fine.
*/
function start_data_capture() {
_capture_enabled = true;
_capture_buffer = [];
_captured_keys.clear();
}
/**
* Record a component's data after on_load() completes.
* Called internally from _apply_load_result().
*
* @param component_name - Component name
* @param args - Component args at time of on_load()
* @param data - this.data after on_load() completed
* @param cache_key - The cache key for deduplication (null = always capture)
*/
function capture_component_data(component_name, args, data, cache_key) {
if (!_capture_enabled)
return;
// Deduplicate: if Load Coordinator already captured this key, skip
if (cache_key !== null) {
if (_captured_keys.has(cache_key))
return;
_captured_keys.add(cache_key);
}
// Deep clone args and data to plain objects (no proxies, no class instances)
try {
const cloned_args = JSON.parse(JSON.stringify(args));
const cloned_data = JSON.parse(JSON.stringify(data));
_capture_buffer.push({
component: component_name,
args: cloned_args,
data: cloned_data,
});
}
catch (error) {
// Non-serializable data — skip capture for this component
if (typeof console !== 'undefined') {
console.warn(`[JQHTML SSR Capture] Failed to serialize data for ${component_name}:`, error);
}
}
}
/**
* Returns all captured component data and resets the capture buffer.
* One-shot: calling clears the buffer.
*/
function get_captured_data() {
const result = _capture_buffer;
_capture_buffer = [];
_captured_keys.clear();
return result;
}
/**
* Check if data capture is currently enabled.
*/
function is_capture_enabled() {
return _capture_enabled;
}
/**
* Stop data capture and clear all state.
*/
function stop_data_capture() {
_capture_enabled = false;
_capture_buffer = [];
_captured_keys.clear();
}
// --- Preload (Client Side) ---
let _preload_cache = new Map();
/**
* Seed the preload cache with SSR-captured data.
* Must be called before any component _load() runs.
*
* @param entries - Array of { component, args, data } entries from get_captured_data()
*/
function set_preload_data(entries) {
if (!entries || entries.length === 0)
return;
_preload_cache.clear();
for (const entry of entries) {
// Generate key using the same algorithm as Load Coordinator
const result = Load_Coordinator.generate_invocation_key(entry.component, entry.args);
if (result.key !== null) {
_preload_cache.set(result.key, entry.data);
}
}
}
/**
* Check if preloaded data exists for a given cache key.
* If found, returns the data and removes the entry (one-shot).
*
* @param cache_key - The cache key (same format as Load Coordinator)
* @returns The preloaded data, or null if no match
*/
function consume_preload_data(cache_key) {
if (_preload_cache.size === 0)
return null;
const data = _preload_cache.get(cache_key);
if (data !== undefined) {
_preload_cache.delete(cache_key);
return data;
}
return null;
}
/**
* Clear any remaining unconsumed preload entries.
* Call after initial page hydration is complete.
*/
function clear_preload_data() {
_preload_cache.clear();
}
/**
* Check if any preload data is available (for fast early exit in _load).
*/
function has_preload_data() {
return _preload_cache.size > 0;
}
/**
* JQHTML v2 Component Base Class
*
@@ -3516,7 +3651,7 @@ class Jqhtml_Component {
}
return;
}
// Check if component implements cache_id() for custom cache key
// Generate cache key (needed for both preload check and load deduplication)
let cache_key = null;
let uncacheable_property;
if (typeof this.cache_id === 'function') {
@@ -3535,6 +3670,19 @@ class Jqhtml_Component {
cache_key = result.key;
uncacheable_property = result.uncacheable_property;
}
// SSR preload check: if preloaded data exists for this component+args, use it
if (cache_key !== null && has_preload_data()) {
const preloaded_data = consume_preload_data(cache_key);
if (preloaded_data !== null) {
this._cache_key = cache_key;
if (window.jqhtml?.debug?.verbose) {
console.log(`[SSR Preload] Component ${this._cid} (${this.component_name()}) using preloaded data`, { cache_key });
}
const data_before_load = JSON.stringify(this.data);
await this._apply_load_result(preloaded_data, data_before_load);
return;
}
}
// Store cache key for later use
this._cache_key = cache_key;
// If cache_key is null, args are not serializable - skip load deduplication and caching
@@ -3654,6 +3802,10 @@ class Jqhtml_Component {
}
// Freeze this.data
this.__data_frozen = true;
// SSR data capture: record this component's data for preloading
if (is_capture_enabled() && this._has_on_load()) {
capture_component_data(this.component_name(), this.args, this.data, this._cache_key);
}
// Calculate if data changed
const data_after_load = JSON.stringify(this.data);
const data_changed = data_before_load !== null && data_after_load !== data_before_load;
@@ -5337,7 +5489,7 @@ function init(jQuery) {
}
}
// Version - will be replaced during build with actual version from package.json
const version = '2.3.41';
const version = '2.3.42';
// Default export with all functionality
const jqhtml = {
// Core
@@ -5434,7 +5586,13 @@ const jqhtml = {
// Required for ES6 class instances to be stored and restored from localStorage
register_cache_class,
// Boot - hydrate server-rendered component placeholders
boot
boot,
// SSR Preload - capture data during SSR, replay on client to skip on_load()
start_data_capture,
get_captured_data,
stop_data_capture,
set_preload_data,
clear_preload_data
};
// Auto-register on window for browser environments
// This is REQUIRED for compiled templates which use window.jqhtml.register_template()
@@ -5464,5 +5622,5 @@ if (typeof window !== 'undefined' && !window.jqhtml) {
}
}
export { Jqhtml_Component, LifecycleManager as Jqhtml_LifecycleManager, Jqhtml_Local_Storage, LifecycleManager, Load_Coordinator, applyDebugDelay, boot, create_component, jqhtml as default, devWarn, escape_html, escape_html_nl2br, extract_slots, get_component_class, get_component_names, get_registered_templates, get_template, get_template_by_class, handleComponentError, has_component, init, init_jquery_plugin, isSequentialProcessing, list_components, logDataChange, logDispatch, logInstruction, logLifecycle, process_instructions, register, register_cache_class, register_component, register_template, render_template, version };
export { Jqhtml_Component, LifecycleManager as Jqhtml_LifecycleManager, Jqhtml_Local_Storage, LifecycleManager, Load_Coordinator, applyDebugDelay, boot, clear_preload_data, create_component, jqhtml as default, devWarn, escape_html, escape_html_nl2br, extract_slots, get_captured_data, get_component_class, get_component_names, get_registered_templates, get_template, get_template_by_class, handleComponentError, has_component, init, init_jquery_plugin, isSequentialProcessing, list_components, logDataChange, logDispatch, logInstruction, logLifecycle, process_instructions, register, register_cache_class, register_component, register_template, render_template, set_preload_data, start_data_capture, stop_data_capture, version };
//# sourceMappingURL=jqhtml-core.esm.js.map

File diff suppressed because one or more lines are too long

69
node_modules/@jqhtml/core/dist/preload-data.d.ts generated vendored Executable file
View File

@@ -0,0 +1,69 @@
/**
* JQHTML SSR Preload Data System
*
* Captures component data during SSR rendering and replays it on the client
* to skip redundant on_load() calls during hydration.
*
* Server-side: start_data_capture() → render → get_captured_data()
* Client-side: set_preload_data(entries) → components boot with preloaded data → clear_preload_data()
*/
export interface PreloadEntry {
component: string;
args: Record<string, any>;
data: Record<string, any>;
}
/**
* Enable data capture mode. After this call, every component that completes
* on_load() has its final this.data recorded.
*
* Idempotent — calling twice is fine.
*/
export declare function start_data_capture(): void;
/**
* Record a component's data after on_load() completes.
* Called internally from _apply_load_result().
*
* @param component_name - Component name
* @param args - Component args at time of on_load()
* @param data - this.data after on_load() completed
* @param cache_key - The cache key for deduplication (null = always capture)
*/
export declare function capture_component_data(component_name: string, args: Record<string, any>, data: Record<string, any>, cache_key: string | null): void;
/**
* Returns all captured component data and resets the capture buffer.
* One-shot: calling clears the buffer.
*/
export declare function get_captured_data(): PreloadEntry[];
/**
* Check if data capture is currently enabled.
*/
export declare function is_capture_enabled(): boolean;
/**
* Stop data capture and clear all state.
*/
export declare function stop_data_capture(): void;
/**
* Seed the preload cache with SSR-captured data.
* Must be called before any component _load() runs.
*
* @param entries - Array of { component, args, data } entries from get_captured_data()
*/
export declare function set_preload_data(entries: PreloadEntry[] | null): void;
/**
* Check if preloaded data exists for a given cache key.
* If found, returns the data and removes the entry (one-shot).
*
* @param cache_key - The cache key (same format as Load Coordinator)
* @returns The preloaded data, or null if no match
*/
export declare function consume_preload_data(cache_key: string): Record<string, any> | null;
/**
* Clear any remaining unconsumed preload entries.
* Call after initial page hydration is complete.
*/
export declare function clear_preload_data(): void;
/**
* Check if any preload data is available (for fast early exit in _load).
*/
export declare function has_preload_data(): boolean;
//# sourceMappingURL=preload-data.d.ts.map

1
node_modules/@jqhtml/core/dist/preload-data.d.ts.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"preload-data.d.ts","sourceRoot":"","sources":["../src/preload-data.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC3B;AASD;;;;;GAKG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAIzC;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,SAAS,EAAE,MAAM,GAAG,IAAI,GACvB,IAAI,CA4BN;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,YAAY,EAAE,CAKlD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAE5C;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAIxC;AAMD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,IAAI,GAAG,IAAI,CAYrE;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CASlF;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C"}

View File

@@ -1,6 +1,6 @@
{
"name": "@jqhtml/core",
"version": "2.3.41",
"version": "2.3.42",
"description": "Core runtime library for JQHTML",
"type": "module",
"main": "./dist/index.js",

View File

@@ -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.41',\n`; // Version will be replaced during build
code += ` _jqhtml_version: '2.3.42',\n`; // Version will be replaced during build
code += ` name: '${name}',\n`;
code += ` tag: '${component.tagName}',\n`;
code += ` defaultAttributes: ${this.serializeAttributeObject(component.defaultAttributes)},\n`;

View File

@@ -1,6 +1,6 @@
{
"name": "@jqhtml/parser",
"version": "2.3.41",
"version": "2.3.42",
"description": "JQHTML template parser - converts templates to JavaScript",
"type": "module",
"main": "dist/index.js",

136
node_modules/@jqhtml/ssr/README.md generated vendored
View File

@@ -361,6 +361,142 @@ Or flush specific bundle:
---
## SSR Preload API (Hydration Acceleration)
The SSR Preload API eliminates redundant `on_load()` calls during client hydration. When the SSR server already fetched data for a component, the client can skip the identical fetch and use the captured data directly.
**Without preload:** SSR fetches data → client boots → client fetches same data again (wasted round-trip).
**With preload:** SSR fetches data → captures it → client receives captured data → client boots with preloaded data → no redundant fetch.
### Server-Side: Capturing Data
During SSR rendering, enable data capture to record each component's loaded data:
```javascript
// 1. Enable capture before rendering
jqhtml.start_data_capture();
// 2. Render the component (on_load() runs, data is captured automatically)
// ... component renders normally ...
// 3. Retrieve captured data (one-shot: clears buffer after retrieval)
const captured = jqhtml.get_captured_data();
// Returns: [{ component: 'DashboardIndex', args: { user_id: 123 }, data: { items: [...] } }, ...]
// 4. Stop capture when done
jqhtml.stop_data_capture();
// 5. Include captured data in SSR response
return { html: renderedHtml, preload: captured };
```
**Capture rules:**
- Only components with `on_load()` are captured (static components excluded)
- Deduplicates by component name + args (Load Coordinator aware)
- `get_captured_data()` is one-shot: clears the buffer on retrieval
- `start_data_capture()` is idempotent (safe to call multiple times)
### Client-Side: Injecting Preloaded Data
Before components boot on the client, seed the preload cache with SSR-captured data:
```javascript
// 1. Inject preloaded data BEFORE components initialize
jqhtml.set_preload_data(ssrPreloadData);
// 2. Components boot → _load() finds preload cache hit → skips on_load() entirely
// ... components initialize normally, but with instant data ...
// 3. Clear unconsumed entries after hydration is complete
jqhtml.clear_preload_data();
```
**Preload rules:**
- `set_preload_data(entries)` must be called before components boot
- Each preload entry is consumed on first use (one-shot per key)
- `set_preload_data(null)` or `set_preload_data([])` is a no-op
- `clear_preload_data()` removes any unconsumed entries
### Complete Data Flow
```
SSR Server:
1. jqhtml.start_data_capture()
2. Render component (on_load() fetches data → CAPTURED)
3. captured = jqhtml.get_captured_data()
4. Return { html, preload: captured }
Client:
1. jqhtml.set_preload_data(ssr_preload)
2. Components boot → _load() hits preload cache → skips on_load()
3. jqhtml.clear_preload_data()
```
### Updated PHP Integration Example (with Preload)
```php
// Usage with preload data
$ssr = new JqhtmlSSR();
$result = $ssr->render('Dashboard_Index_Action', ['user_id' => 123], $bundles);
if ($result['status'] === 'success') {
$html = $result['payload']['html'];
$preload = json_encode($result['payload']['preload'] ?? []);
echo $html;
echo "<script>jqhtml.set_preload_data({$preload});</script>";
echo "<script src='/bundles/vendor.js'></script>";
echo "<script src='/bundles/app.js'></script>";
echo "<script>jqhtml.boot().then(() => jqhtml.clear_preload_data());</script>";
}
```
### Updated Node.js Client Example (with Preload)
```javascript
const response = await sendSSRRequest(9876, {
id: 'render-1',
type: 'render',
payload: {
bundles: [
{ id: 'vendor', content: vendorBundleContent },
{ id: 'app', content: appBundleContent }
],
component: 'Dashboard_Index_Action',
args: { user_id: 123 },
options: { baseUrl: 'http://localhost:3000' }
}
});
const { html, preload } = response.payload;
// Embed preload data in the page for client-side hydration
const pageHtml = `
${html}
<script>
jqhtml.set_preload_data(${JSON.stringify(preload || [])});
</script>
<script src="/bundles/vendor.js"></script>
<script src="/bundles/app.js"></script>
<script>
jqhtml.boot().then(() => jqhtml.clear_preload_data());
</script>
`;
```
### API Reference
| Method | Description |
|--------|-------------|
| `jqhtml.start_data_capture()` | Enable data capture mode (idempotent) |
| `jqhtml.get_captured_data()` | Returns captured entries and clears buffer (one-shot) |
| `jqhtml.stop_data_capture()` | Stops capture and clears all capture state |
| `jqhtml.set_preload_data(entries)` | Seeds preload cache with SSR-captured data |
| `jqhtml.clear_preload_data()` | Clears unconsumed preload entries |
---
## File Structure
```

View File

@@ -544,6 +544,69 @@ jqhtml-ssr --render \
---
## Preload Data Capture and Injection Protocol
The preload system captures component data during SSR and replays it on the client to skip redundant `on_load()` calls during hydration.
### Entry Format
Each captured entry has the following structure:
```json
{
"component": "DashboardIndex",
"args": { "user_id": 123 },
"data": { "items": [...], "total": 42 }
}
```
| Field | Type | Description |
|-------|------|-------------|
| `component` | string | Component name |
| `args` | object | Component arguments (as passed at creation) |
| `data` | object | The component's `this.data` after `on_load()` completed |
### Key Generation
Preload entries are matched by a composite key of `component` + `args`. The key generation algorithm:
1. Component name is used as-is (string)
2. Args are serialized deterministically (stable JSON stringification)
3. Combined key: `component + ":" + serialized_args`
This means two components with the same name and identical args share a preload entry, which is consistent with Load Coordinator deduplication behavior.
### One-Shot Semantics
Both capture and preload use one-shot consumption:
- **`get_captured_data()`** — Returns all captured entries and clears the capture buffer. Calling again returns an empty array until new components are captured.
- **Preload entries** — Each entry is consumed on first use. When `_load()` finds a preload cache hit, it removes that entry. A second component with the same key will not find a preload hit and will call `on_load()` normally.
### Integration Points in `_load()`
The preload system integrates into the component `_load()` method with the following priority:
1. **Preload cache check** (highest priority) — If `set_preload_data()` was called and an entry matches this component's name + args, apply the data via `_apply_load_result()` and skip `on_load()` entirely.
2. **Load Coordinator check** — If another instance of the same component + args is already loading, wait for its result.
3. **Normal `on_load()`** — Call the component's `on_load()` method.
### Capture Integration in `_apply_load_result()`
When data capture is enabled (`start_data_capture()` was called), `_apply_load_result()` records the component's data if the component has an `on_load()` method. Static components (those without `on_load()`) are excluded from capture.
### API Summary
| Method | Side | Description |
|--------|------|-------------|
| `start_data_capture()` | Server | Enable capture mode (idempotent) |
| `get_captured_data()` | Server | Return and clear captured entries |
| `stop_data_capture()` | Server | Stop capture, clear all state |
| `set_preload_data(entries)` | Client | Seed preload cache; null/empty is no-op |
| `clear_preload_data()` | Client | Clear unconsumed preload entries |
---
## Future Considerations
### Streaming SSR

View File

@@ -1,6 +1,6 @@
{
"name": "@jqhtml/ssr",
"version": "2.3.41",
"version": "2.3.42",
"description": "Server-Side Rendering for JQHTML components - renders components to HTML for SEO",
"main": "src/index.js",
"bin": {

View File

@@ -149,6 +149,58 @@ class SSREnvironment {
});
}
/**
* Execute JavaScript code wrapped in an IIFE to prevent class declarations
* from leaking into the global scope across requests.
*
* Without IIFE wrapping, `class Foo {}` in vm.runInThisContext() persists
* in the global context, causing "Identifier already declared" errors on
* subsequent requests that load the same bundles.
*
* @param {string} code - JavaScript code to execute
* @param {string} filename - Filename for error reporting
*/
executeScoped(code, filename = 'scoped-bundle.js') {
if (!this.initialized) {
throw new Error('SSR environment not initialized. Call init() first.');
}
const cleanCode = code.replace(/\/\/# sourceMappingURL=.*/g, '');
// Register Rsx on window inside the IIFE so boot() can access it.
// Class declarations are block-scoped inside the IIFE, but closures
// preserve the scope — so Rsx._rsx_core_boot() can still reference
// Manifest, Ajax, etc. by name when called from outside.
const registration = 'if (typeof Rsx !== "undefined") { window.Rsx = Rsx; }';
const wrappedCode = `(function() {\n${cleanCode}\n${registration}\n})();`;
vm.runInThisContext(wrappedCode, {
filename,
displayErrors: true
});
}
/**
* Boot the RSpade framework lifecycle.
*
* Calls Rsx._rsx_core_boot() which runs the full 10-phase initialization
* sequence: Ajax init, component registration, jqhtml cache setup, etc.
* Framework classes with browser-only APIs detect window.__SSR__ and skip
* their initialization via Rsx.is_ssr() guards.
*
* Must be called after executeScoped() loads the bundles.
*/
async boot() {
if (!this.initialized) {
throw new Error('SSR environment not initialized. Call init() first.');
}
const Rsx = global.Rsx || (global.window && global.window.Rsx);
if (Rsx && typeof Rsx._rsx_core_boot === 'function') {
await Rsx._rsx_core_boot();
}
}
/**
* Render a component to HTML
* @param {string} componentName - Component name to render
@@ -161,6 +213,12 @@ class SSREnvironment {
}
const $ = global.$;
const jqhtml = global.window.jqhtml;
// Start data capture for preload
if (jqhtml && typeof jqhtml.start_data_capture === 'function') {
jqhtml.start_data_capture();
}
// Create container
const $container = $('<div>');
@@ -188,10 +246,16 @@ class SSREnvironment {
// Get rendered HTML
const html = component.$.prop('outerHTML');
// Get captured preload data
let preload = [];
if (jqhtml && typeof jqhtml.get_captured_data === 'function') {
preload = jqhtml.get_captured_data();
}
// Export cache state
const cache = this.storage.exportAll();
return { html, cache };
return { html, cache, preload };
}
/**

View File

@@ -74,7 +74,7 @@ function parseRequest(message) {
};
}
const validTypes = ['render', 'render_spa', 'ping', 'flush_cache'];
const validTypes = ['render', 'render_spa', 'ping', 'flush_cache', 'shutdown'];
if (!validTypes.includes(parsed.type)) {
return {
ok: false,
@@ -396,12 +396,12 @@ function errorResponse(id, code, message, stack) {
* @param {object} timing - Timing info { total_ms, bundle_load_ms, render_ms }
* @returns {string} JSON string with newline
*/
function renderResponse(id, html, cache, timing) {
return successResponse(id, {
html,
cache,
timing
});
function renderResponse(id, html, cache, timing, preload) {
const payload = { html, cache, timing };
if (preload && preload.length > 0) {
payload.preload = preload;
}
return successResponse(id, payload);
}
/**
@@ -424,6 +424,15 @@ function flushCacheResponse(id, flushed) {
return successResponse(id, { flushed });
}
/**
* Create a shutdown response
* @param {string} id - Request ID
* @returns {string} JSON string with newline
*/
function shutdownResponse(id) {
return successResponse(id, { result: 'shutting down' });
}
/**
* MessageBuffer - Handles newline-delimited message framing
*
@@ -483,5 +492,6 @@ module.exports = {
renderSpaResponse,
pingResponse,
flushCacheResponse,
shutdownResponse,
MessageBuffer
};

View File

@@ -17,6 +17,7 @@ const {
renderSpaResponse,
pingResponse,
flushCacheResponse,
shutdownResponse,
errorResponse,
MessageBuffer
} = require('./protocol.js');
@@ -158,6 +159,12 @@ class SSRServer {
response = await this._handleRenderSpa(request);
break;
case 'shutdown':
socket.write(shutdownResponse(request.id));
await this.stop();
process.exit(0);
return;
default:
response = errorResponse(
request.id,
@@ -253,11 +260,15 @@ class SSRServer {
// Initialize environment
env.init();
// Execute bundle code
// Execute bundle code (scoped IIFE prevents class declaration leaks across requests)
const execStartTime = Date.now();
env.execute(bundleCode, `bundles:${cacheKey}`);
env.executeScoped(bundleCode, `bundles:${cacheKey}`);
bundleLoadMs += Date.now() - execStartTime;
// Boot the RSpade framework (runs full 10-phase lifecycle)
// Framework classes detect window.__SSR__ and skip browser-only init
await env.boot();
// Check if jqhtml loaded
if (!env.isJqhtmlReady()) {
throw new Error('jqhtml runtime not available after loading bundles');
@@ -284,7 +295,7 @@ class SSRServer {
total_ms: totalMs,
bundle_load_ms: bundleLoadMs,
render_ms: renderMs
});
}, result.preload);
} catch (err) {
// Determine error type
@@ -499,4 +510,14 @@ Options:
await server.stop();
process.exit(0);
});
process.on('SIGHUP', () => {});
process.on('unhandledRejection', (err) => {
console.error('[SSR] Unhandled rejection:', err);
});
process.on('uncaughtException', (err) => {
console.error('[SSR] Uncaught exception:', err);
});
}

View File

@@ -1 +1 @@
2.3.41
2.3.42

View File

@@ -2,7 +2,7 @@
"name": "@jqhtml/vscode-extension",
"displayName": "JQHTML",
"description": "Syntax highlighting and language support for JQHTML template files",
"version": "2.3.41",
"version": "2.3.42",
"publisher": "jqhtml",
"license": "MIT",
"publishConfig": {