@jqhtml/core 2.3.11 → 2.3.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1122,6 +1122,15 @@ class Jqhtml_Component {
1122
1122
  this.__data_frozen = false; // Track if this.data is currently frozen
1123
1123
  this.next_reload_force_refresh = null; // State machine for reload()/refresh() debounce precedence
1124
1124
  this.__lifecycle_authorized = false; // Flag for lifecycle method protection
1125
+ // Cache mode properties
1126
+ this._cache_key = null; // Cache key for caching
1127
+ // 'html' mode caching
1128
+ this._cached_html = null; // Cached HTML to inject on first render
1129
+ this._used_cached_html = false; // Flag if cached HTML was used (forces re-render after on_load)
1130
+ this._should_cache_html_after_ready = false; // Flag to cache HTML after on_ready lifecycle
1131
+ this._is_dynamic = false; // True if this.data changed during on_load() (used for HTML cache sync)
1132
+ // on_render synchronization (HTML cache mode)
1133
+ this._on_render_complete = false; // True after on_render() has been called post-on_load
1125
1134
  this._cid = this._generate_cid();
1126
1135
  this._lifecycle_manager = LifecycleManager.get_instance();
1127
1136
  // Create or wrap element
@@ -1352,6 +1361,40 @@ class Jqhtml_Component {
1352
1361
  `The framework will automatically re-render if this.data changes during on_load().`);
1353
1362
  }
1354
1363
  this._log_lifecycle('render', 'start');
1364
+ // HTML CACHE MODE - If we have cached HTML, inject it directly and skip template rendering
1365
+ if (this._cached_html !== null) {
1366
+ if (window.jqhtml?.debug?.verbose) {
1367
+ console.log(`[Cache html] Component ${this._cid} (${this.component_name()}) injecting cached HTML`, { html_length: this._cached_html.length });
1368
+ }
1369
+ // Inject cached HTML directly
1370
+ this.$[0].innerHTML = this._cached_html;
1371
+ // Mark that we used cached HTML (forces re-render after on_load)
1372
+ this._used_cached_html = true;
1373
+ // Clear cached HTML so next render uses template
1374
+ this._cached_html = null;
1375
+ // Mark first render complete
1376
+ this._did_first_render = true;
1377
+ this._log_lifecycle('render', 'complete (cached HTML)');
1378
+ // NEW ARCHITECTURE: Call on_render() even after cache inject
1379
+ // This ensures consistent behavior - on_render() always runs after DOM update
1380
+ // Note: this.data has defaults from on_create(), not fresh data yet
1381
+ const cacheRenderResult = this._call_lifecycle_sync('on_render');
1382
+ if (cacheRenderResult && typeof cacheRenderResult.then === 'function') {
1383
+ console.warn(`[JQHTML] Component "${this.component_name()}" returned a Promise from on_render(). ` +
1384
+ `on_render() must be synchronous code. Remove 'async' from the function declaration.`);
1385
+ }
1386
+ // Emit lifecycle event
1387
+ this.trigger('render');
1388
+ // Store args/data snapshots for later comparison
1389
+ try {
1390
+ this._args_on_last_render = JSON.parse(JSON.stringify(this.args));
1391
+ }
1392
+ catch (error) {
1393
+ this._args_on_last_render = null;
1394
+ }
1395
+ this._data_on_last_render = JSON.stringify(this.data);
1396
+ return current_render_id;
1397
+ }
1355
1398
  // Determine child-finding strategy: If component is off-DOM, children can't register
1356
1399
  // via _find_dom_parent() (no parent in DOM to find), so we'll need find() fallback later.
1357
1400
  // If attached to DOM, children register normally and we can use the fast _dom_children path.
@@ -1519,6 +1562,12 @@ class Jqhtml_Component {
1519
1562
  this._log_lifecycle('render', 'complete');
1520
1563
  // Call on_render() with authorization (sync) immediately after render completes
1521
1564
  // This ensures event handlers are always re-attached after DOM updates
1565
+ //
1566
+ // NEW ARCHITECTURE: on_render() can now access this.data normally
1567
+ // The HTML cache mode now properly synchronizes - on_render() runs after both:
1568
+ // 1. Cache inject (with on_create() defaults)
1569
+ // 2. Second render (with fresh data from on_load())
1570
+ // Since on_render() always runs with appropriate data, no proxy restriction needed
1522
1571
  const renderResult = this._call_lifecycle_sync('on_render');
1523
1572
  if (renderResult && typeof renderResult.then === 'function') {
1524
1573
  console.warn(`[JQHTML] Component "${this.component_name()}" returned a Promise from on_render(). ` +
@@ -1539,6 +1588,12 @@ class Jqhtml_Component {
1539
1588
  }
1540
1589
  // Store data snapshot for refresh() comparison
1541
1590
  this._data_on_last_render = JSON.stringify(this.data);
1591
+ // HTML CACHE MODE: Mark on_render complete after second render (post-on_load)
1592
+ // This signals to parent components that this component's DOM is fully rendered
1593
+ // with fresh data and ready for HTML snapshot
1594
+ if (this._ready_state >= 2) {
1595
+ this._on_render_complete = true;
1596
+ }
1542
1597
  // Return the render ID so callers can check if this render is still current
1543
1598
  return current_render_id;
1544
1599
  }
@@ -1612,8 +1667,8 @@ class Jqhtml_Component {
1612
1667
  `on_create() must be synchronous code. Remove 'async' from the function declaration.`);
1613
1668
  await result; // Still wait for it to avoid breaking existing code
1614
1669
  }
1615
- // CACHE READ - Hydrate this.data from cache BEFORE first render
1616
- // This happens after on_create() but before render, allowing instant first render with cached data
1670
+ // CACHE CHECK - Read from cache based on cache mode ('data' or 'html')
1671
+ // This happens after on_create() but before render, allowing instant first render
1617
1672
  const { Load_Coordinator } = await Promise.resolve().then(function () { return loadCoordinator; });
1618
1673
  const { Jqhtml_Local_Storage } = await Promise.resolve().then(function () { return localStorage$1; });
1619
1674
  // Check if component implements cache_id() for custom cache key
@@ -1644,21 +1699,48 @@ class Jqhtml_Component {
1644
1699
  if (window.jqhtml?.debug?.verbose) {
1645
1700
  console.log(`[Cache] Component ${this._cid} (${this.component_name()}) has non-serializable args - caching disabled`, { uncacheable_property });
1646
1701
  }
1647
- return; // Skip cache check
1648
- }
1649
- if (window.jqhtml?.debug?.verbose) {
1650
- console.log(`[Cache] Component ${this._cid} (${this.component_name()}) checking cache in create()`, { cache_key, has_cache_key_set: Jqhtml_Local_Storage.has_cache_key() });
1651
- }
1652
- const cached_data = Jqhtml_Local_Storage.get(cache_key);
1653
- if (cached_data !== null) {
1654
- this.data = cached_data;
1655
- if (window.jqhtml?.debug?.verbose) {
1656
- console.log(`[Cache] Component ${this._cid} (${this.component_name()}) hydrated from cache in create()`, { cache_key, data: this.data });
1657
- }
1702
+ // Don't return - continue to snapshot this.data for on_load restoration
1658
1703
  }
1659
1704
  else {
1705
+ // Store cache key for later use
1706
+ this._cache_key = cache_key;
1707
+ // Get cache mode
1708
+ const cache_mode = Jqhtml_Local_Storage.get_cache_mode();
1660
1709
  if (window.jqhtml?.debug?.verbose) {
1661
- console.log(`[Cache] Component ${this._cid} (${this.component_name()}) cache miss in create()`, { cache_key });
1710
+ console.log(`[Cache ${cache_mode}] Component ${this._cid} (${this.component_name()}) checking cache in create()`, { cache_key, cache_mode, has_cache_key_set: Jqhtml_Local_Storage.has_cache_key() });
1711
+ }
1712
+ if (cache_mode === 'html') {
1713
+ // HTML cache mode - check for cached HTML to inject on first render
1714
+ const html_cache_key = `${cache_key}::html`;
1715
+ const cached_html = Jqhtml_Local_Storage.get(html_cache_key);
1716
+ if (cached_html !== null && typeof cached_html === 'string') {
1717
+ // Store cached HTML for injection in _render()
1718
+ this._cached_html = cached_html;
1719
+ if (window.jqhtml?.debug?.verbose) {
1720
+ console.log(`[Cache html] Component ${this._cid} (${this.component_name()}) found cached HTML`, { cache_key: html_cache_key, html_length: cached_html.length });
1721
+ }
1722
+ }
1723
+ else {
1724
+ if (window.jqhtml?.debug?.verbose) {
1725
+ console.log(`[Cache html] Component ${this._cid} (${this.component_name()}) cache miss`, { cache_key: html_cache_key });
1726
+ }
1727
+ }
1728
+ }
1729
+ else {
1730
+ // Data cache mode (default) - check for cached data to hydrate this.data
1731
+ const cached_data = Jqhtml_Local_Storage.get(cache_key);
1732
+ if (cached_data !== null && typeof cached_data === 'object') {
1733
+ // Hydrate this.data with cached data
1734
+ this.data = cached_data;
1735
+ if (window.jqhtml?.debug?.verbose) {
1736
+ console.log(`[Cache data] Component ${this._cid} (${this.component_name()}) hydrated from cache`, { cache_key, data: cached_data });
1737
+ }
1738
+ }
1739
+ else {
1740
+ if (window.jqhtml?.debug?.verbose) {
1741
+ console.log(`[Cache data] Component ${this._cid} (${this.component_name()}) cache miss`, { cache_key });
1742
+ }
1743
+ }
1662
1744
  }
1663
1745
  }
1664
1746
  // Snapshot this.data after on_create() completes
@@ -1848,8 +1930,7 @@ class Jqhtml_Component {
1848
1930
  // Always clear loading flag and complete coordination
1849
1931
  this.__loading = false;
1850
1932
  complete_coordination();
1851
- // Freeze this.data after on_load() completes
1852
- this.__data_frozen = true;
1933
+ // Note: this.data stays unfrozen until after normalization below
1853
1934
  }
1854
1935
  // Validate that on_load() only modified this.data
1855
1936
  let argsAfterLoad = null;
@@ -1878,12 +1959,43 @@ class Jqhtml_Component {
1878
1959
  ` ❌ this.${newProperties[0]} = value;\n` +
1879
1960
  ` ✅ this.data.${newProperties[0]} = value;`);
1880
1961
  }
1881
- // Check if data changed during on_load() - if so, update cache (but not if empty)
1882
- const data_after_load = JSON.stringify(this.data);
1883
- if (data_after_load !== data_before_load && data_after_load !== '{}') {
1884
- Jqhtml_Local_Storage.set(cache_key, this.data);
1962
+ // DATA MODE: Normalize this.data through serialize/deserialize round-trip
1963
+ // This ensures "hot" data (fresh from on_load) behaves identically to "cold" data
1964
+ // (restored from cache). Unregistered class instances become plain objects immediately,
1965
+ // so developers catch missing class registrations during development rather than
1966
+ // only after a page reload when data comes from cache.
1967
+ const cache_mode = Jqhtml_Local_Storage.get_cache_mode();
1968
+ if (cache_mode === 'data') {
1969
+ const normalized = Jqhtml_Local_Storage.normalize_for_cache(this.data);
1970
+ this.data = normalized;
1885
1971
  if (window.jqhtml?.debug?.verbose) {
1886
- console.log(`[Cache] Component ${this._cid} (${this.component_name()}) updated cache after on_load()`, { cache_key, data: this.data });
1972
+ console.log(`[Cache data] Component ${this._cid} (${this.component_name()}) normalized this.data after on_load()`, { data: this.data });
1973
+ }
1974
+ }
1975
+ // Freeze this.data after normalization completes
1976
+ this.__data_frozen = true;
1977
+ // CACHE WRITE - If data changed during on_load(), write to cache based on mode
1978
+ const data_after_load = JSON.stringify(this.data);
1979
+ const data_changed = data_after_load !== data_before_load;
1980
+ // Track if component is "dynamic" (this.data changed during on_load)
1981
+ // Used by HTML cache mode for synchronization - static parents don't block children
1982
+ this._is_dynamic = data_changed && data_after_load !== '{}';
1983
+ if (this._is_dynamic) {
1984
+ if (this._cache_key) {
1985
+ if (cache_mode === 'html') {
1986
+ // HTML cache mode - flag to cache HTML after children ready in _ready()
1987
+ this._should_cache_html_after_ready = true;
1988
+ if (window.jqhtml?.debug?.verbose) {
1989
+ console.log(`[Cache html] Component ${this._cid} (${this.component_name()}) will cache HTML after ready`, { cache_key: this._cache_key });
1990
+ }
1991
+ }
1992
+ else {
1993
+ // Data cache mode (default) - write this.data to cache
1994
+ Jqhtml_Local_Storage.set(this._cache_key, this.data);
1995
+ if (window.jqhtml?.debug?.verbose) {
1996
+ console.log(`[Cache data] Component ${this._cid} (${this.component_name()}) cached data after on_load()`, { cache_key: this._cache_key, data: this.data });
1997
+ }
1998
+ }
1887
1999
  }
1888
2000
  }
1889
2001
  this._ready_state = 2;
@@ -1901,6 +2013,34 @@ class Jqhtml_Component {
1901
2013
  if (this._stopped || this._ready_state >= 4)
1902
2014
  return;
1903
2015
  this._log_lifecycle('ready', 'start');
2016
+ // HTML CACHE MODE - New synchronization architecture:
2017
+ // 1. Wait for all children to complete on_render (post-on_load)
2018
+ // 2. Take HTML snapshot BEFORE waiting for children ready
2019
+ // 3. This ensures we capture the DOM after on_render but before on_ready manipulations
2020
+ if (this._should_cache_html_after_ready && this._cache_key) {
2021
+ // Wait for all children to complete their on_render
2022
+ await this._wait_for_children_on_render();
2023
+ // Only cache if this component is dynamic (data changed during on_load)
2024
+ // Static parents don't cache - they just let children proceed
2025
+ if (this._is_dynamic) {
2026
+ this._should_cache_html_after_ready = false;
2027
+ // Cache the rendered HTML (async import to avoid circular dependency)
2028
+ const { Jqhtml_Local_Storage } = await Promise.resolve().then(function () { return localStorage$1; });
2029
+ const html = this.$.html();
2030
+ const html_cache_key = `${this._cache_key}::html`;
2031
+ Jqhtml_Local_Storage.set(html_cache_key, html);
2032
+ if (window.jqhtml?.debug?.verbose) {
2033
+ console.log(`[Cache html] Component ${this._cid} (${this.component_name()}) cached HTML after children on_render complete`, { cache_key: html_cache_key, html_length: html.length });
2034
+ }
2035
+ }
2036
+ else {
2037
+ // Static component - just clear the flag, don't cache
2038
+ this._should_cache_html_after_ready = false;
2039
+ if (window.jqhtml?.debug?.verbose) {
2040
+ console.log(`[Cache html] Component ${this._cid} (${this.component_name()}) is static (no data change) - skipping HTML cache`);
2041
+ }
2042
+ }
2043
+ }
1904
2044
  // Wait for all children to reach ready state (bottom-up execution)
1905
2045
  await this._wait_for_children_ready();
1906
2046
  await this._call_lifecycle('on_ready');
@@ -1981,6 +2121,47 @@ class Jqhtml_Component {
1981
2121
  // Wait for all children to be ready
1982
2122
  await Promise.all(ready_promises);
1983
2123
  }
2124
+ /**
2125
+ * Wait for all child components to complete on_render (post-on_load)
2126
+ * Used by HTML cache mode to ensure DOM is fully rendered before taking snapshot
2127
+ *
2128
+ * HTML CACHE ARCHITECTURE:
2129
+ * - Parent waits for all children to complete their on_render after on_load
2130
+ * - This ensures the HTML snapshot captures fully rendered DOM
2131
+ * - Static parents (is_dynamic=false) don't block - they immediately let children proceed
2132
+ *
2133
+ * @private
2134
+ */
2135
+ async _wait_for_children_on_render() {
2136
+ const children = this._get_dom_children();
2137
+ if (children.length === 0) {
2138
+ return; // No children, nothing to wait for
2139
+ }
2140
+ // Create promises for each child that hasn't completed on_render yet
2141
+ const render_promises = [];
2142
+ for (const child of children) {
2143
+ // If child already completed on_render post-on_load, skip
2144
+ if (child._on_render_complete) {
2145
+ continue;
2146
+ }
2147
+ // Create promise that resolves when child completes on_render
2148
+ const render_promise = new Promise((resolve) => {
2149
+ // Poll for completion (simple approach - could use events for more efficiency)
2150
+ const check = () => {
2151
+ if (child._on_render_complete || child._stopped) {
2152
+ resolve();
2153
+ }
2154
+ else {
2155
+ setTimeout(check, 10);
2156
+ }
2157
+ };
2158
+ check();
2159
+ });
2160
+ render_promises.push(render_promise);
2161
+ }
2162
+ // Wait for all children to complete on_render
2163
+ await Promise.all(render_promises);
2164
+ }
1984
2165
  /**
1985
2166
  * Reload component - re-fetch data and re-render (debounced)
1986
2167
  *
@@ -2093,19 +2274,38 @@ class Jqhtml_Component {
2093
2274
  const result = Load_Coordinator.generate_invocation_key(this.component_name(), this.args);
2094
2275
  cache_key = result.key;
2095
2276
  }
2096
- // Only use cache if args are serializable
2277
+ // Check for cached data/html when args changed
2097
2278
  if (cache_key !== null) {
2098
- const cached_data = Jqhtml_Local_Storage.get(cache_key);
2099
- if (cached_data !== null && JSON.stringify(cached_data) !== '{}') {
2100
- if (window.jqhtml?.debug?.verbose) {
2101
- console.log(`[Cache] reload() - Component ${this._cid} (${this.component_name()}) hydrated from cache (args changed)`, { cache_key, data: cached_data });
2279
+ const cache_mode = Jqhtml_Local_Storage.get_cache_mode();
2280
+ this._cache_key = cache_key;
2281
+ if (cache_mode === 'html') {
2282
+ // HTML cache mode - check for cached HTML
2283
+ const html_cache_key = `${cache_key}::html`;
2284
+ const cached_html = Jqhtml_Local_Storage.get(html_cache_key);
2285
+ if (cached_html !== null && typeof cached_html === 'string') {
2286
+ if (window.jqhtml?.debug?.verbose) {
2287
+ console.log(`[Cache html] reload() - Component ${this._cid} (${this.component_name()}) found cached HTML (args changed)`, { cache_key: html_cache_key, html_length: cached_html.length });
2288
+ }
2289
+ // Store cached HTML for injection in _render()
2290
+ this._cached_html = cached_html;
2291
+ this.render();
2292
+ rendered_from_cache = true;
2293
+ }
2294
+ }
2295
+ else {
2296
+ // Data cache mode (default) - check for cached data
2297
+ const cached_data = Jqhtml_Local_Storage.get(cache_key);
2298
+ if (cached_data !== null && typeof cached_data === 'object' && JSON.stringify(cached_data) !== '{}') {
2299
+ if (window.jqhtml?.debug?.verbose) {
2300
+ console.log(`[Cache data] reload() - Component ${this._cid} (${this.component_name()}) hydrated from cache (args changed)`, { cache_key, data: cached_data });
2301
+ }
2302
+ // Hydrate this.data with cached data
2303
+ this.__data_frozen = false;
2304
+ this.data = cached_data;
2305
+ this.__data_frozen = true;
2306
+ this.render();
2307
+ rendered_from_cache = true;
2102
2308
  }
2103
- // Unfreeze to set cached data, then re-freeze
2104
- this.__data_frozen = false;
2105
- this.data = cached_data;
2106
- this.__data_frozen = true;
2107
- await this.render();
2108
- rendered_from_cache = true;
2109
2309
  }
2110
2310
  }
2111
2311
  }
@@ -2125,12 +2325,25 @@ class Jqhtml_Component {
2125
2325
  // Freeze this.data after on_load() completes
2126
2326
  this.__data_frozen = true;
2127
2327
  }
2328
+ // Import for cache operations
2329
+ const { Load_Coordinator } = await Promise.resolve().then(function () { return loadCoordinator; });
2330
+ const { Jqhtml_Local_Storage } = await Promise.resolve().then(function () { return localStorage$1; });
2331
+ // DATA MODE: Normalize this.data through serialize/deserialize round-trip
2332
+ // (Same as in _load() - ensures hot/cold cache parity)
2333
+ const reload_cache_mode = Jqhtml_Local_Storage.get_cache_mode();
2334
+ if (reload_cache_mode === 'data') {
2335
+ this.__data_frozen = false;
2336
+ const normalized = Jqhtml_Local_Storage.normalize_for_cache(this.data);
2337
+ this.data = normalized;
2338
+ this.__data_frozen = true;
2339
+ if (window.jqhtml?.debug?.verbose) {
2340
+ console.log(`[Cache data] Component ${this._cid} (${this.component_name()}) normalized this.data after on_load() in reload()`, { data: this.data });
2341
+ }
2342
+ }
2128
2343
  // Check if data changed during on_load() - if so, update cache (but not if empty)
2129
2344
  const data_after_load = JSON.stringify(this.data);
2130
2345
  const data_changed = data_after_load !== data_before_load;
2131
2346
  if (data_changed && data_after_load !== '{}') {
2132
- const { Load_Coordinator } = await Promise.resolve().then(function () { return loadCoordinator; });
2133
- const { Jqhtml_Local_Storage } = await Promise.resolve().then(function () { return localStorage$1; });
2134
2347
  // Check if component implements cache_id() for custom cache key
2135
2348
  let cache_key = null;
2136
2349
  if (typeof this.cache_id === 'function') {
@@ -2148,11 +2361,22 @@ class Jqhtml_Component {
2148
2361
  const result = Load_Coordinator.generate_invocation_key(this.component_name(), this.args);
2149
2362
  cache_key = result.key;
2150
2363
  }
2151
- // Only update cache if args are serializable
2364
+ // Write to cache based on mode
2152
2365
  if (cache_key !== null) {
2153
- Jqhtml_Local_Storage.set(cache_key, this.data);
2154
- if (window.jqhtml?.debug?.verbose) {
2155
- console.log(`[Cache] Component ${this._cid} (${this.component_name()}) updated cache after on_load() in reload()`, { cache_key, data: this.data });
2366
+ this._cache_key = cache_key;
2367
+ if (reload_cache_mode === 'html') {
2368
+ // HTML cache mode - flag to cache HTML after children ready in _ready()
2369
+ this._should_cache_html_after_ready = true;
2370
+ if (window.jqhtml?.debug?.verbose) {
2371
+ console.log(`[Cache html] Component ${this._cid} (${this.component_name()}) will cache HTML after ready in reload()`, { cache_key });
2372
+ }
2373
+ }
2374
+ else {
2375
+ // Data cache mode (default) - write this.data to cache
2376
+ Jqhtml_Local_Storage.set(cache_key, this.data);
2377
+ if (window.jqhtml?.debug?.verbose) {
2378
+ console.log(`[Cache data] Component ${this._cid} (${this.component_name()}) cached data after on_load() in reload()`, { cache_key, data: this.data });
2379
+ }
2156
2380
  }
2157
2381
  }
2158
2382
  }
@@ -2278,6 +2502,15 @@ class Jqhtml_Component {
2278
2502
  * @private
2279
2503
  */
2280
2504
  _should_rerender() {
2505
+ // HTML CACHE MODE - If we used cached HTML, always re-render to get live components
2506
+ if (this._used_cached_html) {
2507
+ if (window.jqhtml?.debug?.verbose) {
2508
+ console.log(`[Cache html] Component ${this._cid} (${this.component_name()}) forcing re-render after cached HTML`);
2509
+ }
2510
+ // Clear the flag
2511
+ this._used_cached_html = false;
2512
+ return true;
2513
+ }
2281
2514
  // Compare current data state with data state before initial render
2282
2515
  const currentDataState = JSON.stringify(this.data);
2283
2516
  const dataChanged = this._data_before_render !== currentDataState;
@@ -3505,6 +3738,13 @@ if (typeof window !== 'undefined' && window.jQuery) {
3505
3738
  * - **Quota management**: Auto-clears storage when full and retries operation
3506
3739
  * - **Scope validation**: Clears storage when cache key changes
3507
3740
  * - **Developer-friendly keys**: Scoped suffix allows easy inspection in dev tools
3741
+ * - **Class-aware serialization**: ES6 class instances serialize and restore properly
3742
+ *
3743
+ * Class-Aware Serialization:
3744
+ * ES6 class instances can be serialized and deserialized if registered via
3745
+ * register_cache_class(). Classes are wrapped as {__jqhtml_class__: "Name", __jqhtml_props__: {...}}
3746
+ * and restored to proper instances on retrieval. Nested class instances in objects
3747
+ * and arrays are handled recursively.
3508
3748
  *
3509
3749
  * Scoping Strategy:
3510
3750
  * Storage is scoped by a user-provided cache key (typically a session identifier,
@@ -3526,13 +3766,20 @@ if (typeof window !== 'undefined' && window.jQuery) {
3526
3766
  * once. This ensures the application continues functioning even when storage is full.
3527
3767
  *
3528
3768
  * Usage:
3769
+ * // Register classes that need to be cached (call once at startup)
3770
+ * jqhtml.register_cache_class(Contact_Model);
3771
+ * jqhtml.register_cache_class(User_Profile);
3772
+ *
3529
3773
  * // Must set cache key first (typically done once on page load)
3530
3774
  * Jqhtml_Local_Storage.set_cache_key('user_123');
3531
3775
  *
3532
- * // Then use storage normally
3533
- * Jqhtml_Local_Storage.set('cached_component', {html: '...', timestamp: Date.now()});
3776
+ * // Then use storage normally - ES6 classes serialize automatically
3777
+ * Jqhtml_Local_Storage.set('cached_component', {
3778
+ * contact: new Contact_Model(),
3779
+ * timestamp: Date.now()
3780
+ * });
3534
3781
  * const cached = Jqhtml_Local_Storage.get('cached_component');
3535
- * Jqhtml_Local_Storage.remove('cached_component');
3782
+ * // cached.contact instanceof Contact_Model === true
3536
3783
  *
3537
3784
  * IMPORTANT - Volatile Storage:
3538
3785
  * Storage can be cleared at any time due to:
@@ -3548,14 +3795,290 @@ if (typeof window !== 'undefined' && window.jQuery) {
3548
3795
  *
3549
3796
  * @internal This class is not exposed in the public API
3550
3797
  */
3798
+ // ============================================================================
3799
+ // Class Registry for Serialization
3800
+ // ============================================================================
3801
+ // Registry mapping class names to constructors
3802
+ const class_registry = Object.create(null);
3803
+ // Markers for serialized class instances (unique to avoid collisions with user data)
3804
+ const CLASS_MARKER = '__jqhtml_class__';
3805
+ const PROPS_MARKER = '__jqhtml_props__';
3806
+ /**
3807
+ * Register a class for cache serialization/deserialization.
3808
+ * Must be called before attempting to cache instances of this class.
3809
+ *
3810
+ * @param klass - The class constructor to register
3811
+ * @throws Error if klass is not a named function/class
3812
+ */
3813
+ function register_cache_class(klass) {
3814
+ if (typeof klass !== 'function' || !klass.name) {
3815
+ throw new Error('[JQHTML Cache] register_cache_class requires a named class constructor');
3816
+ }
3817
+ class_registry[klass.name] = klass;
3818
+ }
3819
+ // ============================================================================
3820
+ // Serialization (Object → String)
3821
+ // ============================================================================
3822
+ /**
3823
+ * Serialize a value to JSON string, handling ES6 class instances recursively.
3824
+ * Returns null if serialization fails for any reason.
3825
+ *
3826
+ * @param value - The value to serialize
3827
+ * @param verbose - Whether to log warnings
3828
+ * @returns Serialized JSON string, or null if serialization failed
3829
+ */
3830
+ function serialize_value(value, verbose) {
3831
+ try {
3832
+ const seen = new WeakSet();
3833
+ const processed = process_for_serialization(value, verbose, seen);
3834
+ if (processed === undefined) {
3835
+ // Serialization failed - undefined signals failure
3836
+ return null;
3837
+ }
3838
+ return JSON.stringify(processed);
3839
+ }
3840
+ catch (e) {
3841
+ if (verbose) {
3842
+ console.warn('[JQHTML Cache] Serialization failed:', e);
3843
+ }
3844
+ return null;
3845
+ }
3846
+ }
3847
+ /**
3848
+ * Recursively process a value for serialization.
3849
+ * Returns undefined if serialization should fail (unserializable value encountered).
3850
+ *
3851
+ * @param value - The value to process
3852
+ * @param verbose - Whether to log warnings
3853
+ * @param seen - WeakSet to detect circular references
3854
+ * @returns Processed value ready for JSON.stringify, or undefined if failed
3855
+ */
3856
+ function process_for_serialization(value, verbose, seen) {
3857
+ // Handle null and undefined
3858
+ if (value === null) {
3859
+ return null;
3860
+ }
3861
+ if (value === undefined) {
3862
+ return undefined; // JSON.stringify will omit this property
3863
+ }
3864
+ // Handle primitives
3865
+ if (typeof value !== 'object') {
3866
+ // string, number, boolean are fine
3867
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
3868
+ return value;
3869
+ }
3870
+ // bigint, symbol, function cannot be serialized
3871
+ if (verbose) {
3872
+ console.warn(`[JQHTML Cache] Cannot serialize ${typeof value} - value will be omitted`);
3873
+ }
3874
+ // Return undefined to omit this value (not fail entire serialization)
3875
+ return undefined;
3876
+ }
3877
+ // Handle circular references
3878
+ if (seen.has(value)) {
3879
+ if (verbose) {
3880
+ console.warn('[JQHTML Cache] Circular reference detected - cannot serialize');
3881
+ }
3882
+ return undefined; // Fail entire serialization
3883
+ }
3884
+ seen.add(value);
3885
+ // Handle arrays
3886
+ if (Array.isArray(value)) {
3887
+ const result = [];
3888
+ for (let i = 0; i < value.length; i++) {
3889
+ const item = value[i];
3890
+ const processed = process_for_serialization(item, verbose, seen);
3891
+ // For arrays, we keep undefined as null to preserve indices
3892
+ result.push(processed === undefined ? null : processed);
3893
+ }
3894
+ return result;
3895
+ }
3896
+ // Handle Date objects specially
3897
+ if (value instanceof Date) {
3898
+ return {
3899
+ [CLASS_MARKER]: 'Date',
3900
+ [PROPS_MARKER]: value.toISOString()
3901
+ };
3902
+ }
3903
+ // Handle Map objects
3904
+ if (value instanceof Map) {
3905
+ const entries = [];
3906
+ for (const [k, v] of value) {
3907
+ const processedKey = process_for_serialization(k, verbose, seen);
3908
+ const processedValue = process_for_serialization(v, verbose, seen);
3909
+ entries.push([processedKey, processedValue]);
3910
+ }
3911
+ return {
3912
+ [CLASS_MARKER]: 'Map',
3913
+ [PROPS_MARKER]: entries
3914
+ };
3915
+ }
3916
+ // Handle Set objects
3917
+ if (value instanceof Set) {
3918
+ const items = [];
3919
+ for (const item of value) {
3920
+ items.push(process_for_serialization(item, verbose, seen));
3921
+ }
3922
+ return {
3923
+ [CLASS_MARKER]: 'Set',
3924
+ [PROPS_MARKER]: items
3925
+ };
3926
+ }
3927
+ // Get the constructor
3928
+ const ctor = value.constructor;
3929
+ // Handle registered class instances
3930
+ if (ctor && ctor.name && class_registry[ctor.name]) {
3931
+ const props = {};
3932
+ // Extract own enumerable properties (bypass any toJSON method)
3933
+ for (const key of Object.keys(value)) {
3934
+ const propValue = value[key];
3935
+ const processed = process_for_serialization(propValue, verbose, seen);
3936
+ // Only include if not undefined (failed serialization)
3937
+ if (processed !== undefined || propValue === undefined) {
3938
+ props[key] = processed;
3939
+ }
3940
+ }
3941
+ return {
3942
+ [CLASS_MARKER]: ctor.name,
3943
+ [PROPS_MARKER]: props
3944
+ };
3945
+ }
3946
+ // Handle plain objects (constructor is Object or no constructor)
3947
+ if (ctor === Object || ctor === undefined || ctor === null) {
3948
+ const result = {};
3949
+ for (const key of Object.keys(value)) {
3950
+ const propValue = value[key];
3951
+ const processed = process_for_serialization(propValue, verbose, seen);
3952
+ // Only include if not undefined (unless original was undefined)
3953
+ if (processed !== undefined || propValue === undefined) {
3954
+ result[key] = processed;
3955
+ }
3956
+ }
3957
+ return result;
3958
+ }
3959
+ // Unregistered class instance - convert to plain object for hot/cold parity
3960
+ // This ensures developers catch missing class registrations during development
3961
+ // (methods will be lost, only properties preserved)
3962
+ if (verbose) {
3963
+ console.warn(`[JQHTML Cache] Converting unregistered class "${ctor.name}" to plain object. ` +
3964
+ `Methods will be lost. Call jqhtml.register_cache_class(${ctor.name}) to preserve the class.`);
3965
+ }
3966
+ // Convert to plain object - properties are preserved, prototype methods are lost
3967
+ const result = {};
3968
+ for (const key of Object.keys(value)) {
3969
+ const propValue = value[key];
3970
+ const processed = process_for_serialization(propValue, verbose, seen);
3971
+ // Only include if not undefined (unless original was undefined)
3972
+ if (processed !== undefined || propValue === undefined) {
3973
+ result[key] = processed;
3974
+ }
3975
+ }
3976
+ return result;
3977
+ }
3978
+ // ============================================================================
3979
+ // Deserialization (String → Object)
3980
+ // ============================================================================
3981
+ /**
3982
+ * Deserialize a JSON string, restoring ES6 class instances.
3983
+ * Returns null if deserialization fails.
3984
+ *
3985
+ * @param str - The JSON string to deserialize
3986
+ * @param verbose - Whether to log warnings
3987
+ * @returns Deserialized value with class instances restored, or null if failed
3988
+ */
3989
+ function deserialize_value(str, verbose) {
3990
+ try {
3991
+ const parsed = JSON.parse(str);
3992
+ return process_for_deserialization(parsed, verbose);
3993
+ }
3994
+ catch (e) {
3995
+ if (verbose) {
3996
+ console.warn('[JQHTML Cache] Deserialization failed:', e);
3997
+ }
3998
+ return null;
3999
+ }
4000
+ }
4001
+ /**
4002
+ * Recursively process a parsed value, restoring class instances.
4003
+ *
4004
+ * @param value - The parsed value to process
4005
+ * @param verbose - Whether to log warnings
4006
+ * @returns Processed value with class instances restored
4007
+ */
4008
+ function process_for_deserialization(value, verbose) {
4009
+ // Handle primitives and null
4010
+ if (value === null || value === undefined || typeof value !== 'object') {
4011
+ return value;
4012
+ }
4013
+ // Handle arrays
4014
+ if (Array.isArray(value)) {
4015
+ return value.map(item => process_for_deserialization(item, verbose));
4016
+ }
4017
+ // Check for class marker
4018
+ if (value[CLASS_MARKER] !== undefined && value[PROPS_MARKER] !== undefined) {
4019
+ const class_name = value[CLASS_MARKER];
4020
+ const props = value[PROPS_MARKER];
4021
+ // Handle built-in types
4022
+ if (class_name === 'Date') {
4023
+ return new Date(props);
4024
+ }
4025
+ if (class_name === 'Map') {
4026
+ const map = new Map();
4027
+ for (const [k, v] of props) {
4028
+ map.set(process_for_deserialization(k, verbose), process_for_deserialization(v, verbose));
4029
+ }
4030
+ return map;
4031
+ }
4032
+ if (class_name === 'Set') {
4033
+ const set = new Set();
4034
+ for (const item of props) {
4035
+ set.add(process_for_deserialization(item, verbose));
4036
+ }
4037
+ return set;
4038
+ }
4039
+ // Look up registered class
4040
+ const klass = class_registry[class_name];
4041
+ if (!klass) {
4042
+ if (verbose) {
4043
+ console.warn(`[JQHTML Cache] Cannot restore class "${class_name}" - not registered. ` +
4044
+ `Data will be returned as plain object. ` +
4045
+ `Call jqhtml.register_cache_class(${class_name}) to restore class instances.`);
4046
+ }
4047
+ // Return as plain object rather than failing completely
4048
+ return process_for_deserialization(props, verbose);
4049
+ }
4050
+ // Create instance without calling constructor and assign properties
4051
+ try {
4052
+ const instance = Object.create(klass.prototype);
4053
+ const processed_props = process_for_deserialization(props, verbose);
4054
+ Object.assign(instance, processed_props);
4055
+ return instance;
4056
+ }
4057
+ catch (e) {
4058
+ if (verbose) {
4059
+ console.warn(`[JQHTML Cache] Failed to restore class "${class_name}":`, e);
4060
+ }
4061
+ // Return null to signal cache should be treated as miss
4062
+ return null;
4063
+ }
4064
+ }
4065
+ // Plain object - process recursively
4066
+ const result = {};
4067
+ for (const key of Object.keys(value)) {
4068
+ result[key] = process_for_deserialization(value[key], verbose);
4069
+ }
4070
+ return result;
4071
+ }
3551
4072
  class Jqhtml_Local_Storage {
3552
4073
  /**
3553
4074
  * Set the cache key and initialize storage
3554
4075
  * Must be called before any get/set operations
3555
4076
  * @param {string} cache_key - Unique identifier for this cache scope
4077
+ * @param {CacheMode} cache_mode - Cache strategy: 'data' (default, recommended) or 'html'
3556
4078
  */
3557
- static set_cache_key(cache_key) {
4079
+ static set_cache_key(cache_key, cache_mode = 'data') {
3558
4080
  this._cache_key = cache_key;
4081
+ this._cache_mode = cache_mode;
3559
4082
  this._init();
3560
4083
  }
3561
4084
  /**
@@ -3565,6 +4088,13 @@ class Jqhtml_Local_Storage {
3565
4088
  static has_cache_key() {
3566
4089
  return this._cache_key !== null;
3567
4090
  }
4091
+ /**
4092
+ * Get the current cache mode
4093
+ * @returns {CacheMode} Current cache mode ('data' or 'html')
4094
+ */
4095
+ static get_cache_mode() {
4096
+ return this._cache_mode;
4097
+ }
3568
4098
  /**
3569
4099
  * Initialize storage system and validate scope
3570
4100
  * Called automatically after cache key is set
@@ -3599,6 +4129,13 @@ class Jqhtml_Local_Storage {
3599
4129
  return false;
3600
4130
  }
3601
4131
  }
4132
+ /**
4133
+ * Check if verbose mode is enabled
4134
+ * @private
4135
+ */
4136
+ static _is_verbose() {
4137
+ return window.jqhtml?.debug?.verbose === true;
4138
+ }
3602
4139
  /**
3603
4140
  * Validate storage scope and clear JQHTML keys if cache key changed
3604
4141
  * Only clears keys prefixed with 'jqhtml::' to preserve other libraries' data
@@ -3677,34 +4214,90 @@ class Jqhtml_Local_Storage {
3677
4214
  return this._storage_available === true && this._cache_key !== null && this._initialized;
3678
4215
  }
3679
4216
  /**
3680
- * Set item in localStorage
4217
+ * Set item in localStorage with class-aware serialization.
4218
+ *
4219
+ * If serialization fails (e.g., unregistered class instances, circular refs),
4220
+ * the existing cache entry is removed and nothing is stored.
4221
+ *
3681
4222
  * @param {string} key - Storage key
3682
- * @param {*} value - Value to store (will be JSON serialized)
4223
+ * @param {*} value - Value to store (primitives, objects, arrays, or registered class instances)
3683
4224
  */
3684
4225
  static set(key, value) {
3685
4226
  if (!this._is_ready()) {
3686
4227
  return;
3687
4228
  }
4229
+ const verbose = this._is_verbose();
4230
+ const scoped_key = this._build_key(key);
4231
+ // Serialize with class-aware processing
4232
+ const serialized = serialize_value(value, verbose);
4233
+ if (serialized === null) {
4234
+ // Serialization failed - remove any existing cache entry
4235
+ if (verbose) {
4236
+ console.warn(`[JQHTML Cache] Failed to serialize value for key "${key}". ` +
4237
+ `Removing existing cache entry if present.`);
4238
+ }
4239
+ try {
4240
+ localStorage.removeItem(scoped_key);
4241
+ }
4242
+ catch (e) {
4243
+ // Ignore removal errors
4244
+ }
4245
+ return;
4246
+ }
3688
4247
  // Check size before attempting to store (1MB limit)
3689
- const serialized = JSON.stringify(value);
3690
4248
  const size_bytes = new Blob([serialized]).size;
3691
4249
  const size_mb = size_bytes / (1024 * 1024);
3692
4250
  if (size_mb > 1) {
3693
- console.warn(`[JQHTML Local Storage] Skipping set - value too large (${size_mb.toFixed(2)}MB > 1MB limit)`, { key, size_bytes, size_mb });
4251
+ if (verbose) {
4252
+ console.warn(`[JQHTML Cache] Skipping set - value too large (${size_mb.toFixed(2)}MB > 1MB limit)`, { key, size_bytes, size_mb });
4253
+ }
4254
+ // Remove existing cache entry since we can't update it
4255
+ try {
4256
+ localStorage.removeItem(scoped_key);
4257
+ }
4258
+ catch (e) {
4259
+ // Ignore removal errors
4260
+ }
3694
4261
  return;
3695
4262
  }
3696
- this._set_item(key, value, serialized);
4263
+ this._set_item(key, serialized);
3697
4264
  }
3698
4265
  /**
3699
- * Get item from localStorage
4266
+ * Get item from localStorage with class-aware deserialization.
4267
+ *
4268
+ * If deserialization fails, returns null (as if no cache exists).
4269
+ *
3700
4270
  * @param {string} key - Storage key
3701
- * @returns {*|null} Parsed value or null if not found/unavailable
4271
+ * @returns {*|null} Deserialized value with class instances restored, or null if not found/failed
3702
4272
  */
3703
4273
  static get(key) {
3704
4274
  if (!this._is_ready()) {
3705
4275
  return null;
3706
4276
  }
3707
- return this._get_item(key);
4277
+ const verbose = this._is_verbose();
4278
+ const scoped_key = this._build_key(key);
4279
+ try {
4280
+ const serialized = localStorage.getItem(scoped_key);
4281
+ if (serialized === null) {
4282
+ return null;
4283
+ }
4284
+ const result = deserialize_value(serialized, verbose);
4285
+ if (result === null) {
4286
+ // Deserialization failed - treat as cache miss
4287
+ if (verbose) {
4288
+ console.warn(`[JQHTML Cache] Failed to deserialize value for key "${key}". ` +
4289
+ `Treating as cache miss.`);
4290
+ }
4291
+ return null;
4292
+ }
4293
+ return result;
4294
+ }
4295
+ catch (e) {
4296
+ if (verbose) {
4297
+ console.warn('[JQHTML Cache] Failed to get item:', e);
4298
+ }
4299
+ return null;
4300
+ }
3708
4301
  }
3709
4302
  /**
3710
4303
  * Remove item from localStorage
@@ -3716,14 +4309,49 @@ class Jqhtml_Local_Storage {
3716
4309
  }
3717
4310
  this._remove_item(key);
3718
4311
  }
4312
+ /**
4313
+ * Perform a serialize/deserialize round-trip on a value.
4314
+ *
4315
+ * This ensures "hot" data (fresh from on_load) behaves identically to "cold" data
4316
+ * (restored from cache). Unregistered class instances will be converted to plain
4317
+ * objects, exactly as they would be if restored from cache.
4318
+ *
4319
+ * Use this to normalize data after on_load() so developers catch missing class
4320
+ * registrations immediately rather than only after a page reload.
4321
+ *
4322
+ * @param {any} value - The value to normalize
4323
+ * @returns {any} The value after serialize/deserialize round-trip, or original if serialization fails
4324
+ */
4325
+ static normalize_for_cache(value) {
4326
+ const verbose = this._is_verbose();
4327
+ // Serialize to JSON string
4328
+ const serialized = serialize_value(value, verbose);
4329
+ if (serialized === null) {
4330
+ // Serialization failed completely - return original
4331
+ // (This happens with circular references or other fatal issues)
4332
+ if (verbose) {
4333
+ console.warn('[JQHTML Cache] normalize_for_cache: Serialization failed, returning original value');
4334
+ }
4335
+ return value;
4336
+ }
4337
+ // Deserialize back to object
4338
+ const deserialized = deserialize_value(serialized, verbose);
4339
+ if (deserialized === null) {
4340
+ // Deserialization failed - return original
4341
+ if (verbose) {
4342
+ console.warn('[JQHTML Cache] normalize_for_cache: Deserialization failed, returning original value');
4343
+ }
4344
+ return value;
4345
+ }
4346
+ return deserialized;
4347
+ }
3719
4348
  /**
3720
4349
  * Internal set implementation with scope validation and quota handling
3721
4350
  * @param {string} key
3722
- * @param {*} value - Original value (not used, kept for clarity)
3723
4351
  * @param {string} serialized - Pre-serialized JSON string
3724
4352
  * @private
3725
4353
  */
3726
- static _set_item(key, value, serialized) {
4354
+ static _set_item(key, serialized) {
3727
4355
  // Validate scope before every write
3728
4356
  this._validate_scope();
3729
4357
  const scoped_key = this._build_key(key);
@@ -3749,26 +4377,6 @@ class Jqhtml_Local_Storage {
3749
4377
  }
3750
4378
  }
3751
4379
  }
3752
- /**
3753
- * Internal get implementation
3754
- * @param {string} key
3755
- * @returns {*|null}
3756
- * @private
3757
- */
3758
- static _get_item(key) {
3759
- const scoped_key = this._build_key(key);
3760
- try {
3761
- const serialized = localStorage.getItem(scoped_key);
3762
- if (serialized === null) {
3763
- return null;
3764
- }
3765
- return JSON.parse(serialized);
3766
- }
3767
- catch (e) {
3768
- console.error('[JQHTML Local Storage] Failed to get item:', e);
3769
- return null;
3770
- }
3771
- }
3772
4380
  /**
3773
4381
  * Internal remove implementation
3774
4382
  * @param {string} key
@@ -3785,12 +4393,14 @@ class Jqhtml_Local_Storage {
3785
4393
  }
3786
4394
  }
3787
4395
  Jqhtml_Local_Storage._cache_key = null;
4396
+ Jqhtml_Local_Storage._cache_mode = 'data';
3788
4397
  Jqhtml_Local_Storage._storage_available = null;
3789
4398
  Jqhtml_Local_Storage._initialized = false;
3790
4399
 
3791
4400
  var localStorage$1 = /*#__PURE__*/Object.freeze({
3792
4401
  __proto__: null,
3793
- Jqhtml_Local_Storage: Jqhtml_Local_Storage
4402
+ Jqhtml_Local_Storage: Jqhtml_Local_Storage,
4403
+ register_cache_class: register_cache_class
3794
4404
  });
3795
4405
 
3796
4406
  /**
@@ -4053,7 +4663,7 @@ function init(jQuery) {
4053
4663
  }
4054
4664
  }
4055
4665
  // Version - will be replaced during build with actual version from package.json
4056
- const version = '2.3.11';
4666
+ const version = '2.3.13';
4057
4667
  // Default export with all functionality
4058
4668
  const jqhtml = {
4059
4669
  // Core
@@ -4137,9 +4747,17 @@ const jqhtml = {
4137
4747
  return version;
4138
4748
  },
4139
4749
  // Cache key setter - enables component caching via local storage
4140
- set_cache_key(cache_key) {
4141
- Jqhtml_Local_Storage.set_cache_key(cache_key);
4750
+ // cache_mode: 'data' (default, recommended) or 'html'
4751
+ set_cache_key(cache_key, cache_mode = 'data') {
4752
+ Jqhtml_Local_Storage.set_cache_key(cache_key, cache_mode);
4753
+ },
4754
+ // Get current cache mode
4755
+ get_cache_mode() {
4756
+ return Jqhtml_Local_Storage.get_cache_mode();
4142
4757
  },
4758
+ // Register a class for cache serialization/deserialization
4759
+ // Required for ES6 class instances to be stored and restored from localStorage
4760
+ register_cache_class,
4143
4761
  // Boot - hydrate server-rendered component placeholders
4144
4762
  boot
4145
4763
  };
@@ -4159,5 +4777,5 @@ if (typeof window !== 'undefined' && !window.jqhtml) {
4159
4777
  }
4160
4778
  }
4161
4779
 
4162
- export { Jqhtml_Component, LifecycleManager as Jqhtml_LifecycleManager, Jqhtml_Local_Storage, LifecycleManager, Load_Coordinator, applyDebugDelay, boot, create_component, jqhtml as default, devWarn, escape_html, 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_component, register_template, render_template, version };
4780
+ export { Jqhtml_Component, LifecycleManager as Jqhtml_LifecycleManager, Jqhtml_Local_Storage, LifecycleManager, Load_Coordinator, applyDebugDelay, boot, create_component, jqhtml as default, devWarn, escape_html, 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 };
4163
4781
  //# sourceMappingURL=index.js.map