Fix bin/publish: copy docs.dist from project root

Fix bin/publish: use correct .env path for rspade_system
Fix bin/publish script: prevent grep exit code 1 from terminating script

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-10-21 02:08:33 +00:00
commit f6fac6c4bc
79758 changed files with 10547827 additions and 0 deletions

21
node_modules/@jqhtml/core/LICENSE generated vendored Executable file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 JQHTML Team
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

369
node_modules/@jqhtml/core/LLM_REFERENCE.md generated vendored Executable file
View File

@@ -0,0 +1,369 @@
# JQHTML Template & Component System - LLM Reference
## Component-Template Association
- Template files (.jqhtml) compile to ES6 classes extending Component base class
- Template name matches component class name: `UserCard.jqhtml``class UserCard extends Component`
- Components are jQuery objects: `this.$` is the component's root jQuery element
- No virtual DOM, no reconciliation, direct DOM manipulation only
## Template Syntax
### Basic Structure
```jqhtml
<Define:ComponentName tag="tagname">
<!-- template content -->
</Define:ComponentName>
```
- `tag` attribute optional, defaults to "div"
- Component instantiation: `<ComponentName />` or `<ComponentName tag="span" />`
- Tag can be overridden at invocation time
- Self-closing for no children, paired tags for slots/children
### Data Binding & Expressions
- `<%= expression %>` - Escaped output (HTML entities encoded)
- `<%! expression %>` - Raw output (no escaping)
- `<% statement; %>` - JavaScript statements (no output)
- Access component data via `this.data.property`
- Access component methods via `this.methodName()`
### Attribute System
- `$id="name"``id="name:_cid"` (component-scoped ID)
- `$attr="value"``data-attr="value"` (data attributes)
- `@click="handler"` → jQuery event binding to component method
- `@submit.prevent="handler"` → Event with preventDefault
- Regular attributes pass through unchanged
### Control Flow
Two equivalent syntaxes supported:
**Colon Syntax:**
```jqhtml
<% for (let item of items): %>
<div><%= item %></div>
<% endfor; %>
<% if (condition): %>
<div>True branch</div>
<% else: %>
<div>False branch</div>
<% endif; %>
```
**Brace Syntax:**
```jqhtml
<% for (let item of items) { %>
<div><%= item %></div>
<% } %>
```
### Component Content & innerHTML (Primary Pattern)
**Most components use the simple `content()` function** to output innerHTML:
```jqhtml
<!-- Define a component that outputs innerHTML -->
<Define:Card>
<div class="card">
<div class="card-body">
<%= content() %> <!-- Outputs whatever innerHTML was passed -->
</div>
</div>
</Define:Card>
<!-- Usage - just pass innerHTML like regular HTML -->
<Card>
<h3>Card Title</h3>
<p>Card content goes here</p>
<button>Action</button>
</Card>
```
This is the **recommended pattern for 95% of components**. It's simple, intuitive, and works like regular HTML.
### Slots (Advanced Feature)
**Slots are only needed when you have multiple distinct content areas:**
```jqhtml
<Define:ComplexCard>
<div class="card">
<div class="card-header">
<%= content('header') %>
</div>
<div class="body">
<%= content() %>
</div>
<div class="card-footer">
<%= content('footer') %>
</div>
</div>
</Define:ComplexCard>
<!-- Usage with slots (advanced): -->
<ComplexCard>
<#header>Title</#header>
Main content
<#footer>Actions</#footer>
</ComplexCard>
```
**Important:** You cannot mix modes - if using slots, ALL content must be in slots.
## Component Lifecycle
### Execution Order
1. **constructor** - Synchronous, sets up jQuery wrapper
2. **render** - Creates DOM structure (top-down traversal)
3. **create** - Quick initialization (bottom-up traversal)
4. **load** - Async data fetching (parallel execution)
5. **ready** - Final setup, DOM complete (bottom-up traversal)
### Phase Characteristics
- **render**: Executes parent before children, can access `this.$` and `this.data`
- **create**: Executes children before parents, component visible but may lack data
- **load**: All components at same level run in parallel, NO DOM manipulation allowed
- **ready**: Executes children before parents, all data loaded, safe for DOM queries
### Lifecycle Methods
```javascript
class MyComponent extends Component {
async on_render() {
// Called during render phase, after template DOM created
// Parent's on_render completes before children render
}
async on_create() {
// Bottom-up, children complete before parent
// Used for quick setup, event binding
}
async on_load() {
// Parallel execution, data fetching only
// MUST NOT touch DOM or component will break
this.data.users = await fetch('/api/users').then(r => r.json());
}
async on_ready() {
// Bottom-up, all components fully initialized
// Safe to query children, cross-component communication
}
}
```
## Args vs Data: Critical Distinction
### this.args - Input Parameters
Input parameters passed to the component during invocation. These come from:
- `$` attributes: `$user=this.user` (expression) or `$title="literal"` (string)
- `data-*` attributes: `data-role="admin"`
- Regular attributes: `name="John"` (becomes `data-name` in args)
```javascript
// Parent template
<UserCard
$user=this.user // args.user = parent's user object
$title="User Profile" // args.title = "User Profile" (literal string)
data-role="admin" // args.role = "admin"
email="john@example" // args.email = "john@example"
/>
// In UserCard component
on_create() {
console.log(this.args.user); // {name: "Alice", id: 123}
console.log(this.args.title); // "User Profile"
console.log(this.args.role); // "admin"
console.log(this.args.email); // "john@example"
}
```
### this.data - Async Loaded Data
Data loaded asynchronously in the `on_load()` lifecycle method:
```javascript
class UserCard extends Jqhtml_Component {
async on_load() {
// Load data from API - this populates this.data
this.data.posts = await fetch(`/api/users/${this.args.user.id}/posts`)
.then(r => r.json());
// this.args = input parameters (unchanged)
// this.data = fetched data (just loaded)
}
on_ready() {
// Both args and data are now available
console.log('User:', this.args.user); // Input parameter
console.log('Posts:', this.data.posts); // Loaded data
}
}
```
### Key Differences
| Property | `this.args` | `this.data` |
|----------|------------|------------|
| **Source** | Parent component attributes | `on_load()` method |
| **When Set** | Component construction | Load lifecycle phase |
| **Purpose** | Configuration/parameters | Dynamic/async data |
| **Reactivity** | No (static after construction) | Yes (changes can trigger re-render) |
| **Example** | User ID, config options | API responses, computed values |
## Component API
### Core Properties
- `this.$` - jQuery object for component root element
- `this.args` - Input parameters from component invocation (attributes passed from parent)
- `this.data` - Data loaded asynchronously in `on_load()` method
- `this._cid` - Unique component instance ID
- `this._ready_state` - Lifecycle phase (0=created, 1=rendering, 2=creating, 3=loading, 4=ready)
### DOM Access Methods
- `this.$id('name')` - Get element by scoped ID (returns jQuery object)
- `this.$child('name')` - Get child component by name
- `this.$children()` - Get all direct child components
- `this.$parent()` - Get parent component
- `this.$find('.selector')` - Query within component scope
### Data Management
```javascript
// Setting data triggers re-render after current lifecycle phase
this.data.property = value; // Triggers render after load phase
this.set_data({ multiple: values }); // Batch update
// Prevent re-render during load phase
async on_load() {
this.data.items = await fetchItems(); // Won't trigger render
this.data.cache = processItems(this.data.items); // Still won't render
// Render happens once after all load phases complete
}
```
### Component Registration
```javascript
// Global registration (optional)
jqhtml.register_component('UserCard', UserCardClass);
// Template registration (for dynamic components)
jqhtml.register_template('UserCard', {
as: 'article',
render: compiledRenderFunction
});
// Dynamic instantiation
const ComponentClass = jqhtml.get_component_class('UserCard');
const instance = new ComponentClass({ data: initialData });
$('#container').append(instance.$);
```
### jQuery Method Override
```javascript
class CustomInput extends Component {
val(value) {
if (arguments.length === 0) {
// Getter - return processed value
return this.parse_value(this.$id('input').val());
} else {
// Setter - validate and set
if (this.validate(value)) {
this.$id('input').val(this.format_value(value));
this.data.value = value;
}
return this.$; // Maintain jQuery chaining
}
}
}
// Usage: $('#my-input').val() calls component's val method
```
## Instruction Format (Compiled Template Output)
Templates compile to instruction arrays:
```javascript
[
{tag: ['div', {class: 'container', $id: 'root'}, false]},
{text: 'Static content'},
{expr: function() { return this.data.title; }},
{comp: ['ChildComponent', {props: 'values'}, [
{slot: ['header', [...instructions]]}
]]},
{ctrl: ['if', function() { return this.data.show; }, [...instructions]]}
]
```
## Component Instantiation Patterns
### jQuery Plugin
```javascript
$('#target').component(ComponentClass, { data: initialData });
```
### Direct Instantiation
```javascript
const component = new ComponentClass({ data: initialData });
$('#target').append(component.$);
await component.wait_ready(); // Wait for ready state
```
### Within Templates
```jqhtml
<UserCard $data="this.data.user" @click="handleUserClick" />
```
## Re-render Behavior
- Data changes during `load` phase are batched, single render after all loads complete
- Data changes during `ready` phase trigger immediate re-render
- Re-render preserves component instance, only updates DOM
- Child components maintain state through re-render unless key changes
- `should_rerender()` method can prevent unnecessary renders
## Event Handling
```jqhtml
<!-- Method binding -->
<button @click="handleClick">Click</button>
<!-- Inline expression -->
<button @click="this.data.count++">Increment</button>
<!-- With event modifiers -->
<form @submit.prevent="handleSubmit">
```
```javascript
class Component {
handleClick(event) {
// jQuery event object
// 'this' is component instance
event.preventDefault();
this.data.clicked = true;
}
}
```
## Component Communication
```javascript
// Parent to child - via props
<ChildComponent $data="this.data.childData" />
// Child to parent - via events
this.$.trigger('custom-event', [data]);
// Parent listening
this.$child('name').on('custom-event', (e, data) => {});
// Cross-component - via registry
const other = jqhtml.get_component_instance('id');
```
## Performance Characteristics
- Template compilation happens build-time, zero runtime parsing
- jQuery operations are synchronous and immediate
- No virtual DOM overhead, direct DOM manipulation
- Component creation is fast, most cost in load phase
- Re-renders only update changed portions via jQuery
## Critical Invariants
1. Never modify DOM during `load` phase - causes race conditions
2. Always return jQuery object from overridden jQuery methods for chaining
3. Component `this.$` is always valid after constructor
4. Data modifications during load are batched to prevent render thrashing
5. Bottom-up phases (create, ready) guarantee children initialize first
6. Parallel load execution means no component dependencies in load phase
7. Template expressions have access to full component context via `this`

565
node_modules/@jqhtml/core/README.md generated vendored Executable file
View File

@@ -0,0 +1,565 @@
# @jqhtml/core
Core runtime library for JQHTML v2 - jQuery-first component framework.
## Installation
```bash
npm install @jqhtml/core jquery
```
## Module Structure
The core package exports two separate entry points:
### Main Runtime (Production)
```javascript
import { Component, jqhtml } from '@jqhtml/core';
```
### Debug Utilities (Development Only)
```javascript
import { showDebugOverlay, hideDebugOverlay } from '@jqhtml/core/debug';
```
The debug module is kept separate to avoid including debug code in production bundles. It provides:
- Visual debug overlay with performance profiling
- Component flash visualization
- Lifecycle logging
- Slow render detection
## Overview
The core package provides:
- Component base class with 5-stage lifecycle
- Lifecycle manager for phase coordination
- Component registry for dynamic instantiation
- Instruction processor for template rendering
- jQuery plugin integration
- Template rendering with data bindings
## Quick Start
### Basic Component
```javascript
import { Component } from '@jqhtml/core';
class UserCard extends Component {
async init() {
// Quick setup, hide elements
this.$.addClass('loading');
}
async load() {
// Fetch data (runs in parallel with siblings)
this.data.user = await fetch('/api/user').then(r => r.json());
}
async render() {
// Create DOM structure
this.$.html(`
<div class="card">
<h2>${this.data.user.name}</h2>
<p>${this.data.user.email}</p>
</div>
`);
}
async ready() {
// Component fully initialized
this.$.removeClass('loading');
}
}
```
### Using jQuery Plugin
```javascript
// Create component on existing element
$('#user-container').component(UserCard, { userId: 123 });
// Get component instance
const card = $('#user-container').component();
```
## Component Lifecycle
Components follow a multi-stage lifecycle with automatic re-rendering:
1. **constructor** - Instance creation, jQuery element setup (`this.data` is `{}`)
2. **render** - Create initial DOM structure (top-down, atomic/synchronous operation)
3. **create** - Quick setup after DOM creation (bottom-up, siblings parallel)
4. **load** - Fetch data (bottom-up, fully parallel, **ABSOLUTELY NO DOM MODIFICATIONS**)
5. **render** (automatic) - Re-render if data changed during load (empties DOM first)
6. **create** (automatic) - Re-setup if component was re-rendered
7. **ready** - Fully initialized (bottom-up, siblings parallel)
8. **destroy** - Cleanup when removed
### Critical: NO DOM Modifications in load()
**The requirement that `on_load()` must not modify the DOM is ABSOLUTE.** This is not a guideline but a strict architectural requirement. Violating this will cause rendering issues and race conditions.
**Data Initialization Timeline:**
- Before `load()`: `this.data = {}` (empty object)
- After `load()`: `this.data` contains fetched data (remains `{}` if no data loaded)
### Correct Patterns for Loading States
If you need to show different DOM states before and after loading, use these patterns:
**Pattern 1: Conditional Rendering in Template**
```jqhtml
<Define:UserProfile>
<div class="user-profile">
<% if (Object.keys(this.data).length === 0): %>
<!-- Show loading state (no data loaded yet) -->
<div class="loading">
<div class="spinner"></div>
<p>Loading user profile...</p>
</div>
<% else: %>
<!-- Show loaded content -->
<h2><%= this.data.user?.name || 'Unknown User' %></h2>
<p><%= this.data.user?.bio || 'No bio available' %></p>
<% endif; %>
</div>
</Define:UserProfile>
```
**Pattern 2: DOM Changes in create() and ready()**
```javascript
class DataComponent extends Component {
async on_create() {
// Set loading state in the DOM during create phase
this.$id('status').addClass('loading').text('Loading...');
}
async on_load() {
// ONLY fetch data - NO DOM modifications here!
this.data.user = await fetch('/api/user').then(r => r.json());
}
async on_ready() {
// Update DOM after data is loaded
this.$id('status').removeClass('loading').text('Loaded');
this.$id('username').text(this.data.user.name);
}
}
```
**NEVER do this:**
```javascript
class BadComponent extends Component {
async on_load() {
// ❌ WRONG - DOM modification in load()
this.$id('status').text('Loading...'); // VIOLATION!
this.data.user = await fetch('/api/user').then(r => r.json());
// ❌ WRONG - More DOM modification
this.$id('status').text('Loaded'); // VIOLATION!
}
}
```
**Important:** The `render()` method is atomic and essentially synchronous - it empties the DOM and rebuilds it in a single operation. While marked `async` for technical reasons, it completes immediately without yielding control.
### Phase Batching
All components complete each phase before moving to the next:
- Parents render before children
- Children create/load/ready before parents
- Siblings execute in parallel where safe
### Automatic Re-rendering
After the `load` phase, if `this.data` has changed, the component automatically:
1. Calls `render()` again (which empties the DOM with `$.empty()` first)
2. Calls `create()` again to re-setup the new DOM
3. Then proceeds to `ready()`
This ensures components can fetch data and re-render with that data seamlessly.
### Controlling Re-rendering
By default, components automatically re-render if `this.data` changes during `load()`:
```javascript
class DataComponent extends Component {
async on_load() {
// If this modifies this.data, component will re-render
this.data.user = await fetch('/api/user').then(r => r.json());
}
// Override to customize re-render logic
should_rerender() {
// Default: returns true if JSON.stringify(this.data) changed
// Override for custom logic:
return this.data.user && !this.data.cached;
}
}
```
### Manual Re-rendering
You can manually re-render a component at any time:
```javascript
class InteractiveComponent extends Component {
async handleUpdate(newData) {
// Update data
this.data = { ...this.data, ...newData };
// Manually re-render (atomic operation)
await this.render();
// Optionally re-initialize (call create/ready as needed)
await this.on_create(); // Re-setup event handlers
await this.on_ready(); // Re-initialize component state
}
// Lifecycle manipulation methods
redraw() {
// Synchronously re-renders with current data
}
async reload_data() {
// Re-fetches data via on_load(), then redraws
}
async reinitialize() {
// Full lifecycle reset from stage 0
}
destroy() {
// Cleanup component and children
}
}
```
**Note:** The re-render process:
1. Empties the component's DOM with `this.$.empty()`
2. Calls `render()` to rebuild the DOM atomically
3. For automatic re-renders, calls `create()` again for the new DOM
4. Finally calls `ready()`
5. For manual re-renders, you control which lifecycle methods to call
## Scoped IDs
Components use `_cid` for scoped element selection:
```javascript
class TabsComponent extends Component {
async render() {
this.$.html(`
<button id="tab1:${this._cid}">Tab 1</button>
<button id="tab2:${this._cid}">Tab 2</button>
<div id="content:${this._cid}"></div>
`);
}
selectTab(tabId) {
// Use $id() for scoped selection
this.$id('tab1').removeClass('active');
this.$id('tab2').removeClass('active');
this.$id(tabId).addClass('active');
}
}
```
## Template Integration
The core runtime processes templates compiled by `@jqhtml/parser`:
```javascript
import { render_template, with_template } from '@jqhtml/core';
// Template function (usually generated by parser)
const template = function(data, args, content) {
const _output = [];
_output.push({tag: ["h1", {"data-bind-text": "title"}, false]});
_output.push({tag: ["h1", {}, true]});
return [_output, this];
};
// Component with template
class Article extends Component {
async on_render() {
await render_template(this, template);
}
}
// Or use mixin
const Article = with_template(template)(Component);
```
## Attribute Rules
JQHTML has specific rules for attribute quoting and value passing:
- **@ Event attributes**: MUST be unquoted (pass function references)
Example: `@click=this.handleClick`
- **$ Data attributes**: Can be quoted OR unquoted (flexible)
Example: `$id="my-id"` or `$data=this.complexObject`
- **Regular HTML attributes**: MUST be quoted (strings only)
Example: `class="container <%= this.args.theme %>"`
See [ATTRIBUTE_RULES.md](../../docs/ATTRIBUTE_RULES.md) for comprehensive documentation.
## Data Bindings
Templates support reactive data bindings:
- `:text="expression"` - Update text content
- `:value="expression"` - Form input values
- `:class="{active: isActive}"` - Dynamic classes
- `:style="{color: textColor}"` - Dynamic styles
- `@click=handler` - Event handlers (unquoted)
## Browser Bundle
For browser usage without a build tool:
```html
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="jqhtml-bundle.js"></script>
<script>
const { Component } = JQHTML;
class MyComponent extends Component {
// ...
}
</script>
```
## Lifecycle Event Callbacks
JQHTML provides a `.on()` method for registering callbacks that fire after lifecycle events complete. This is useful for external code that needs to know when a component reaches a certain state.
### Supported Events
- `'render'` - Fires after the render phase completes
- `'create'` - Fires after the create phase completes
- `'load'` - Fires after the load phase completes
- `'ready'` - Fires after the ready phase completes (component fully initialized)
### Usage
```javascript
// Get component instance and register callback
const component = $('#my-component').component();
component.on('ready', (comp) => {
console.log('Component is ready!', comp);
// Access component data, DOM, etc.
});
// If the event already occurred, callback fires immediately
// AND still registers for future occurrences (e.g., on re-render)
```
### Behavior
1. **Immediate Execution**: If the lifecycle event has already occurred when you register the callback, it fires immediately
2. **Future Events**: The callback also registers for future occurrences of the same event (useful for re-renders)
3. **Multiple Callbacks**: You can register multiple callbacks for the same event
4. **Error Safety**: Errors in callbacks are caught and logged without breaking the component
5. **Validation**: Only lifecycle events are allowed; other event names trigger a console error
### Example: Wait for Component to be Ready
```javascript
// Register callback before or after component initialization
$('#user-profile').component().on('ready', (component) => {
// Component is fully initialized, data is loaded
console.log('User data:', component.data.user);
// Safe to access all DOM elements
component.$id('email').addClass('verified');
});
```
### Example: Track Multiple Lifecycle Events
```javascript
const component = $('#dashboard').component();
component
.on('render', () => console.log('Dashboard rendered'))
.on('create', () => console.log('Dashboard created'))
.on('load', () => console.log('Dashboard data loaded'))
.on('ready', () => console.log('Dashboard ready'));
```
### Example: Invalid Event (Error)
```javascript
// This will log an error to console
$('#my-component').component().on('click', callback);
// Error: Component.on() only supports lifecycle events: render, create, load, ready
```
### Note on Re-renders
When a component re-renders (manually or automatically after `load()`), lifecycle events fire again:
```javascript
const component = $('#widget').component();
component.on('render', () => {
console.log('Widget rendered');
// Fires on initial render AND every re-render
});
// Later, trigger re-render
await component.render(); // "Widget rendered" logs again
```
## API Reference
### Component Class
- `constructor(element, args)` - Create component instance
- `async render()` - Create DOM structure
- `async init()` - Quick setup phase
- `async load()` - Data fetching phase
- `async ready()` - Final initialization
- `destroy()` - Cleanup
- `should_rerender()` - Control re-rendering after load
- `emit(event, data)` - Emit jQuery events
- `on(event, callback)` - Register lifecycle event callback
- `$id(localId)` - Get scoped jQuery element
- `id(localId)` - Get scoped component instance
- `parent()` - Get parent component
- `children()` - Get direct child components
- `find(selector)` - Find descendant components
### jQuery Plugin
- `$(el).component()` - Get component instance
- `$(el).component(Class, args)` - Create component
- `$.jqhtml.register(name, Class)` - Register component
- `$.jqhtml.create(name, args)` - Create by name
### Template Functions
- `render_template(component, template)` - Render template
- `with_template(template)` - Component mixin
- `process_instructions(instructions, target, context)` - Process instruction array
## Component and Template Registration Flexibility
JQHTML provides complete flexibility in how you define components - you can use JavaScript classes, `.jqhtml` template files, both, or neither. The framework automatically handles all combinations:
### All Possible Combinations
1. **Both JS Class and .jqhtml Template** (Standard approach)
```javascript
// UserCard.js
class UserCard extends Jqhtml_Component {
async load() {
this.data.user = await fetch('/api/user').then(r => r.json());
}
}
jqhtml.register_component('UserCard', UserCard);
```
```html
<!-- UserCard.jqhtml -->
<Define:UserCard>
<div class="card">
<h2><%= this.data.user.name %></h2>
</div>
</Define:UserCard>
```
**Result**: Uses your custom class logic + your custom template
2. **.jqhtml Template Only** (No JS class needed)
```html
<!-- SimpleWidget.jqhtml -->
<Define:SimpleWidget>
<div class="widget">
Hello, <%= this.args.name %>!
</div>
</Define:SimpleWidget>
```
**Result**: Uses default `Jqhtml_Component` class + your template. Perfect for presentational components that don't need custom logic.
3. **JS Class Only** (No .jqhtml template)
```javascript
class DynamicComponent extends Jqhtml_Component {
async render() {
this.$.html(`<div>Rendered at ${Date.now()}</div>`);
}
}
jqhtml.register_component('DynamicComponent', DynamicComponent);
```
**Result**: Uses your class + default passthrough template. Useful when you want full programmatic control.
4. **Neither Class nor Template** (Passthrough)
```html
<!-- In your JQHTML file -->
<FooBar>
<p>This content just passes through!</p>
</FooBar>
```
**Result**: Uses default `Jqhtml_Component` class + default passthrough template. Acts as a simple wrapper that renders its inner content.
### How It Works
When you use `<ComponentName>`:
- **JS Class Resolution**: Looks for registered class, falls back to `Jqhtml_Component`
- **Template Resolution**: Looks for registered template by class, then by name, then uses default passthrough
- **Default Passthrough Template**: Simply renders the component's inner HTML as-is
This means you can start simple and progressively add complexity:
```html
<!-- Start with just markup -->
<UserBadge>Basic HTML content</UserBadge>
<!-- Later, add a .jqhtml template for consistent styling -->
<Define:UserBadge>
<div class="badge"><%= this.args.username %></div>
</Define:UserBadge>
<!-- Finally, add JS class for complex behavior -->
class UserBadge extends Jqhtml_Component {
async load() { /* fetch user data */ }
}
```
## Laravel Integration
This package includes a Laravel bridge for error handling and source map support. After installing via npm, you can load it directly from `node_modules` in your Laravel application:
```php
// In app/Providers/AppServiceProvider.php
public function register()
{
$jqhtmlBridge = base_path('node_modules/@jqhtml/core/laravel-bridge/autoload.php');
if (file_exists($jqhtmlBridge)) {
require_once $jqhtmlBridge;
}
}
```
See [laravel-bridge/LARAVEL_INTEGRATION.md](./laravel-bridge/LARAVEL_INTEGRATION.md) for complete integration instructions.
## Development
```bash
# Install dependencies
npm install
# Build TypeScript
npm run build
# Run tests
npm test
# Watch mode
npm run watch
```
## License
MIT

62
node_modules/@jqhtml/core/dist/component-registry.d.ts generated vendored Executable file
View File

@@ -0,0 +1,62 @@
/**
* JQHTML v2 Component Registry
*
* Global registry for component classes and templates
* Enables dynamic component instantiation and template association
*/
import { Jqhtml_Component } from './component.js';
export type TemplateFunction = (this: Jqhtml_Component, data: Record<string, any>, args: Record<string, any>, content: any) => [any[], Jqhtml_Component];
export interface TemplateDefinition {
name: string;
tag: string;
defaultAttributes?: Record<string, any>;
render: TemplateFunction;
}
export type ComponentConstructor = new (args: Record<string, any>, element?: any) => Jqhtml_Component;
/**
* Register a component class with optional template
*/
export declare function register_component(name: string, component_class: ComponentConstructor, template?: TemplateDefinition): void;
export declare function register_component(component_class: ComponentConstructor): void;
/**
* Get a component class by name
*/
export declare function get_component_class(name: string): ComponentConstructor | undefined;
/**
* Register a template - name is extracted from template.name property
* Returns true if registered, false if duplicate
*/
export declare function register_template(template_def: TemplateDefinition): boolean;
/**
* Get template for a component by name
*/
export declare function get_template(name: string): TemplateDefinition;
/**
* Get template for a component class - walks up inheritance chain
*/
export declare function get_template_by_class(component_class: ComponentConstructor): TemplateDefinition;
/**
* Create a component instance by name
* If no component class is registered, uses the default Component class
*/
export declare function create_component(name: string, element?: any, args?: Record<string, any>): Jqhtml_Component;
/**
* Check if a component is registered
*/
export declare function has_component(name: string): boolean;
/**
* Get all registered component names
*/
export declare function get_component_names(): string[];
/**
* Get all registered template names
*/
export declare function get_registered_templates(): string[];
/**
* List all registered components with their template status
*/
export declare function list_components(): Record<string, {
has_class: boolean;
has_template: boolean;
}>;
//# sourceMappingURL=component-registry.d.ts.map

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

@@ -0,0 +1 @@
{"version":3,"file":"component-registry.d.ts","sourceRoot":"","sources":["../src/component-registry.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGlD,MAAM,MAAM,gBAAgB,GAAG,CAC7B,IAAI,EAAE,gBAAgB,EACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,OAAO,EAAE,GAAG,KACT,CAAC,GAAG,EAAE,EAAE,gBAAgB,CAAC,CAAC;AAG/B,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACxC,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAGD,MAAM,MAAM,oBAAoB,GAAG,KAAK,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,GAAG,KAAK,gBAAgB,CAAC;AAsCtG;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,eAAe,EAAE,oBAAoB,EACrC,QAAQ,CAAC,EAAE,kBAAkB,GAC5B,IAAI,CAAC;AACR,wBAAgB,kBAAkB,CAAC,eAAe,EAAE,oBAAoB,GAAG,IAAI,CAAC;AA4ChF;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,SAAS,CAElF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,kBAAkB,GAAG,OAAO,CAoC3E;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,CA0B7D;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,eAAe,EAAE,oBAAoB,GAAG,kBAAkB,CAwB/F;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,GAAG,EACb,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,GAC7B,gBAAgB,CAGlB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,EAAE,CAE9C;AAED;;GAEG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,EAAE,CAEnD;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,OAAO,CAAA;CAAE,CAAC,CAsB/F"}

148
node_modules/@jqhtml/core/dist/component.d.ts generated vendored Executable file
View File

@@ -0,0 +1,148 @@
/**
* JQHTML v2 Component Base Class
*
* Core component implementation following v2 specification:
* - 5-stage lifecycle coordinated by LifecycleManager
* - Direct jQuery manipulation (no virtual DOM)
* - Scoped IDs using _cid pattern
* - Event emission and CSS class hierarchy
*/
declare global {
interface Window {
JQHTML_DEBUG?: {
log: (componentName: string, phase: string, message: string, data?: any) => void;
updateTree: () => void;
};
}
}
export declare class Jqhtml_Component {
static template?: any;
$: any;
args: Record<string, any>;
data: Record<string, any>;
_cid: string;
_ready_state: number;
private _lifecycle_manager;
private _parent;
private _children;
private _destroyed;
private _data_before_render;
private _lifecycle_callbacks;
private _lifecycle_states;
private __loading;
constructor(element?: any, args?: Record<string, any>);
/**
* Render phase - Create DOM structure
* Called top-down (parent before children) when part of lifecycle
* Can also be called directly to just re-render DOM without lifecycle hooks
*
* @param id Optional scoped ID - if provided, delegates to child component's render()
*/
render(id?: string | null): Promise<void>;
/**
* Create phase - Quick setup, prepare UI
* Called bottom-up (children before parent)
*/
create(): Promise<void>;
/**
* Load phase - Fetch data from APIs
* Called bottom-up, fully parallel
* NO DOM MODIFICATIONS ALLOWED IN THIS PHASE
*/
load(): Promise<void>;
/**
* Ready phase - Component fully initialized
* Called bottom-up (children before parent)
*/
ready(): Promise<void>;
/**
* Reinitialize the component - full reset and re-initialization
* Wipes the innerHTML, resets data to empty, and runs full lifecycle
*/
reinitialize(): Promise<void>;
/**
* Reload data - re-fetch data and update the component
* Re-runs on_load(), then renders and calls on_ready()
*/
reload_data(): Promise<void>;
/**
* Destroy the component and cleanup
* Synchronously cleans up component and all children before removal
*/
destroy(): void;
on_render(): void | Promise<void>;
on_create(): void | Promise<void>;
on_load(): Promise<void>;
on_ready(): Promise<void>;
on_destroy(): void | Promise<void>;
/**
* Should component re-render after load?
* By default, only re-renders if data has changed
* Override to control re-rendering behavior
*/
should_rerender(): boolean;
/**
* Get component name for debugging
*/
component_name(): string;
/**
* Emit a jQuery event from component root
*/
emit(event_name: string, data?: any): void;
/**
* Register lifecycle event callback
* Allowed events: 'render', 'create', 'load', 'ready', 'destroy'
* Callbacks fire after the lifecycle method completes
* If the event has already occurred, the callback fires immediately AND registers for future occurrences
*/
on(event_name: string, callback: (component: Jqhtml_Component) => void): this;
/**
* Emit lifecycle event - fires all registered callbacks
* @private
*/
private _emit_lifecycle_event;
/**
* Find element by scoped ID
* @param local_id The local ID (without _cid suffix)
* @returns jQuery element with id="local_id:_cid"
*/
$id(local_id: string): any;
/**
* Get component instance by scoped ID
*/
id(local_id: string): Jqhtml_Component | null;
/**
* Get parent component
*/
parent(): Jqhtml_Component | null;
/**
* Get direct child components
*/
children(): Jqhtml_Component[];
/**
* Find descendant components by CSS selector
*/
find(selector: string): Jqhtml_Component[];
/**
* Find closest ancestor component matching selector
*/
closest(selector: string): Jqhtml_Component | null;
/**
* Get CSS class hierarchy for this component type
*/
static get_class_hierarchy(): string[];
private _generate_cid;
/**
* Flatten instruction array - converts ['_content', [...]] markers to flat array
* Recursively flattens nested content from content() calls
*/
private _flatten_instructions;
private _apply_css_classes;
private _apply_default_attributes;
private _set_attributes;
private _update_debug_attrs;
private _find_parent;
private _log_lifecycle;
private _log_debug;
}
//# sourceMappingURL=component.d.ts.map

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

@@ -0,0 +1 @@
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAUH,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,YAAY,CAAC,EAAE;YACb,GAAG,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;YACjF,UAAU,EAAE,MAAM,IAAI,CAAC;SACxB,CAAC;KACH;CACF;AAED,qBAAa,gBAAgB;IAE3B,MAAM,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC;IAGtB,CAAC,EAAE,GAAG,CAAC;IACP,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAM;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAK;IAGzB,OAAO,CAAC,kBAAkB,CAAmB;IAC7C,OAAO,CAAC,OAAO,CAAiC;IAChD,OAAO,CAAC,SAAS,CAAoC;IACrD,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,mBAAmB,CAAuB;IAClD,OAAO,CAAC,oBAAoB,CAAwE;IACpG,OAAO,CAAC,iBAAiB,CAA0B;IACnD,OAAO,CAAC,SAAS,CAAkB;gBAEvB,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM;IA6FzD;;;;;;OAMG;IACG,MAAM,CAAC,EAAE,GAAE,MAAM,GAAG,IAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAmMrD;;;OAGG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAwB7B;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA6D3B;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB5B;;;OAGG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAmCnC;;;OAGG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IA2DlC;;;OAGG;IACH,OAAO,IAAI,IAAI;IA8Df,SAAS,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IACjC,SAAS,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAC3B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IACxB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAC/B,UAAU,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAElC;;;;OAIG;IACH,eAAe,IAAI,OAAO;IAiB1B;;OAEG;IACH,cAAc,IAAI,MAAM;IAIxB;;OAEG;IACH,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI;IAK1C;;;;;OAKG;IACH,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,SAAS,EAAE,gBAAgB,KAAK,IAAI,GAAG,IAAI;IA4B7E;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAiB7B;;;;OAIG;IACH,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG;IAY1B;;OAEG;IACH,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI;IAK7C;;OAEG;IACH,MAAM,IAAI,gBAAgB,GAAG,IAAI;IAIjC;;OAEG;IACH,QAAQ,IAAI,gBAAgB,EAAE;IAI9B;;OAEG;IACH,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,EAAE;IAa1C;;OAEG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI;IAoBlD;;OAEG;IACH,MAAM,CAAC,mBAAmB,IAAI,MAAM,EAAE;IAwCtC,OAAO,CAAC,aAAa;IAIrB;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAkB7B,OAAO,CAAC,kBAAkB;IAyB1B,OAAO,CAAC,yBAAyB;IAsFjC,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,YAAY;IAcpB,OAAO,CAAC,cAAc;IActB,OAAO,CAAC,UAAU;CASnB"}

36
node_modules/@jqhtml/core/dist/debug-entry.d.ts generated vendored Executable file
View File

@@ -0,0 +1,36 @@
/**
* JQHTML Debug Module
*
* Optional debugging utilities for development
* Import separately to keep production bundles small:
*
* import { showDebugOverlay } from '@jqhtml/core/debug';
*/
export { DebugOverlay, showDebugOverlay, hideDebugOverlay } from './debug-overlay.js';
export type { DebugOverlayOptions } from './debug-overlay.js';
export { logLifecycle, applyDebugDelay, logDispatch, logInstruction, logDataChange, isSequentialProcessing, handleComponentError, devWarn } from './debug.js';
export interface DebugSettings {
verbose?: boolean;
logCreationReady?: boolean;
logFullLifecycle?: boolean;
logDispatch?: boolean;
logDispatchVerbose?: boolean;
delayAfterComponent?: number;
delayAfterRender?: number;
delayAfterRerender?: number;
sequentialProcessing?: boolean;
flashComponents?: boolean;
flashDuration?: number;
flashColors?: {
create?: string;
render?: string;
ready?: string;
};
showComponentTree?: boolean;
profilePerformance?: boolean;
breakOnError?: boolean;
traceDataFlow?: boolean;
logInstructionProcessing?: boolean;
highlightSlowRenders?: number;
}
//# sourceMappingURL=debug-entry.d.ts.map

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

@@ -0,0 +1 @@
{"version":3,"file":"debug-entry.d.ts","sourceRoot":"","sources":["../src/debug-entry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAG9D,OAAO,EACL,YAAY,EACZ,eAAe,EACf,WAAW,EACX,cAAc,EACd,aAAa,EACb,sBAAsB,EACtB,oBAAoB,EACpB,OAAO,EACR,MAAM,YAAY,CAAC;AAGpB,MAAM,WAAW,aAAa;IAE5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAG7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAG/B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE;QACZ,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IAGF,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B"}

61
node_modules/@jqhtml/core/dist/debug-overlay.d.ts generated vendored Executable file
View File

@@ -0,0 +1,61 @@
/**
* JQHTML Debug Overlay
*
* Independent debug controls using pure jQuery DOM manipulation.
* Does NOT use JQHTML components so it works even when components are broken.
*/
export interface DebugOverlayOptions {
position?: 'top' | 'bottom';
theme?: 'dark' | 'light';
compact?: boolean;
showStatus?: boolean;
autoHide?: boolean;
}
export declare class DebugOverlay {
private static instance;
private $container;
private $statusIndicator;
private options;
private $;
constructor(options?: DebugOverlayOptions);
/**
* Static method to show debug overlay (singleton pattern)
*/
static show(options?: DebugOverlayOptions): DebugOverlay;
/**
* Static method to hide debug overlay
*/
static hide(): void;
/**
* Static method to toggle debug overlay visibility
*/
static toggle(): void;
/**
* Static method to destroy debug overlay
*/
static destroy(): void;
/**
* Display the debug overlay
*/
private display;
/**
* Hide the debug overlay
*/
hide(): void;
/**
* Remove the debug overlay completely
*/
destroy(): void;
/**
* Update the status indicator
*/
updateStatus(mode: string): void;
private createOverlay;
private createStatusIndicator;
private addStyles;
private toggle;
private executeAction;
}
export declare function showDebugOverlay(options?: DebugOverlayOptions): DebugOverlay;
export declare function hideDebugOverlay(): void;
//# sourceMappingURL=debug-overlay.d.ts.map

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

@@ -0,0 +1 @@
{"version":3,"file":"debug-overlay.d.ts","sourceRoot":"","sources":["../src/debug-overlay.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA8BH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA6B;IACpD,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,OAAO,CAAsB;IACrC,OAAO,CAAC,CAAC,CAAM;gBAEH,OAAO,GAAE,mBAAwB;IAgB7C;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,YAAY;IAQxD;;OAEG;IACH,MAAM,CAAC,IAAI,IAAI,IAAI;IAMnB;;OAEG;IACH,MAAM,CAAC,MAAM,IAAI,IAAI;IAYrB;;OAEG;IACH,MAAM,CAAC,OAAO,IAAI,IAAI;IAOtB;;OAEG;IACH,OAAO,CAAC,OAAO;IAYf;;OAEG;IACH,IAAI,IAAI,IAAI;IASZ;;OAEG;IACH,OAAO,IAAI,IAAI;IAWf;;OAEG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAOhC,OAAO,CAAC,aAAa;IAiDrB,OAAO,CAAC,qBAAqB;IAqB7B,OAAO,CAAC,SAAS;IAoGjB,OAAO,CAAC,MAAM;IAed,OAAO,CAAC,aAAa;CAoEtB;AAGD,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,YAAY,CAE5E;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC"}

19
node_modules/@jqhtml/core/dist/debug.d.ts generated vendored Executable file
View File

@@ -0,0 +1,19 @@
/**
* JQHTML Debug Module
*
* Provides comprehensive debugging capabilities for JQHTML components
*/
import type { Jqhtml_Component } from './component.js';
/**
* Development warning helper
* Warnings are suppressed in production builds or when JQHTML_SUPPRESS_WARNINGS is set
*/
export declare function devWarn(message: string): void;
export declare function logLifecycle(component: Jqhtml_Component, phase: string, status: 'start' | 'complete'): void;
export declare function applyDebugDelay(phase: 'component' | 'render' | 'rerender'): void;
export declare function logInstruction(type: string, data: any): void;
export declare function logDataChange(component: Jqhtml_Component, property: string, oldValue: any, newValue: any): void;
export declare function logDispatch(url: string, route: any, params: any, verbose?: boolean): void;
export declare function isSequentialProcessing(): boolean;
export declare function handleComponentError(component: Jqhtml_Component, phase: string, error: Error): void;
//# sourceMappingURL=debug.d.ts.map

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

@@ -0,0 +1 @@
{"version":3,"file":"debug.d.ts","sourceRoot":"","sources":["../src/debug.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAQvD;;;GAGG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAY7C;AAuDD,wBAAgB,YAAY,CAAC,SAAS,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,UAAU,GAAG,IAAI,CAmD3G;AAGD,wBAAgB,eAAe,CAAC,KAAK,EAAE,WAAW,GAAG,QAAQ,GAAG,UAAU,GAAG,IAAI,CAqBhF;AAGD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,IAAI,CAK5D;AAGD,wBAAgB,aAAa,CAAC,SAAS,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,GAAG,IAAI,CAM/G;AAUD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAE,OAAe,GAAG,IAAI,CAoBhG;AAGD,wBAAgB,sBAAsB,IAAI,OAAO,CAGhD;AAGD,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAQnG"}

2869
node_modules/@jqhtml/core/dist/index.cjs generated vendored Executable file

File diff suppressed because it is too large Load Diff

1
node_modules/@jqhtml/core/dist/index.cjs.map generated vendored Executable file

File diff suppressed because one or more lines are too long

86
node_modules/@jqhtml/core/dist/index.d.ts generated vendored Executable file
View File

@@ -0,0 +1,86 @@
/**
* JQHTML v2 Core Runtime
*
* Main entry point for the JQHTML runtime library
*/
export { Jqhtml_Component } from './component.js';
export { LifecycleManager } from './lifecycle-manager.js';
export type { LifecyclePhase } from './lifecycle-manager.js';
export { register_component, get_component_class, register_template, get_template, get_template_by_class, create_component, has_component, get_component_names, get_registered_templates, list_components } from './component-registry.js';
export type { ComponentConstructor, TemplateFunction, TemplateDefinition } from './component-registry.js';
export { process_instructions, extract_slots } from './instruction-processor.js';
export type { Instruction, TagInstruction, ComponentInstruction, SlotInstruction } from './instruction-processor.js';
export { render_template, escape_html } from './template-renderer.js';
export { logLifecycle, applyDebugDelay, logDispatch, logInstruction, logDataChange, isSequentialProcessing, handleComponentError, devWarn } from './debug.js';
export { DebugOverlay, showDebugOverlay, hideDebugOverlay } from './debug-overlay.js';
export type { DebugOverlayOptions } from './debug-overlay.js';
import { init_jquery_plugin } from './jquery-plugin.js';
export { init_jquery_plugin };
export declare function init(jQuery?: any): void;
export { LifecycleManager as Jqhtml_LifecycleManager } from './lifecycle-manager.js';
import { Jqhtml_Component } from './component.js';
import { LifecycleManager } from './lifecycle-manager.js';
import { register_component, get_component_class, register_template, get_template, get_template_by_class, create_component, has_component, get_component_names, get_registered_templates, list_components } from './component-registry.js';
import { process_instructions, extract_slots } from './instruction-processor.js';
import { render_template, escape_html } from './template-renderer.js';
import { DebugOverlay } from './debug-overlay.js';
import './jquery-plugin.js';
export declare const version = "__VERSION__";
export interface DebugSettings {
verbose?: boolean;
logCreationReady?: boolean;
logFullLifecycle?: boolean;
logDispatch?: boolean;
logDispatchVerbose?: boolean;
delayAfterComponent?: number;
delayAfterRender?: number;
delayAfterRerender?: number;
sequentialProcessing?: boolean;
flashComponents?: boolean;
flashDuration?: number;
flashColors?: {
create?: string;
render?: string;
ready?: string;
};
showComponentTree?: boolean;
profilePerformance?: boolean;
breakOnError?: boolean;
traceDataFlow?: boolean;
logInstructionProcessing?: boolean;
highlightSlowRenders?: number;
}
declare const jqhtml: {
Jqhtml_Component: typeof Jqhtml_Component;
LifecycleManager: typeof LifecycleManager;
register_component: typeof register_component;
get_component_class: typeof get_component_class;
register_template: typeof register_template;
get_template: typeof get_template;
get_template_by_class: typeof get_template_by_class;
create_component: typeof create_component;
has_component: typeof has_component;
get_component_names: typeof get_component_names;
get_registered_templates: typeof get_registered_templates;
list_components: typeof list_components;
process_instructions: typeof process_instructions;
extract_slots: typeof extract_slots;
render_template: typeof render_template;
escape_html: typeof escape_html;
__version: string;
debug: DebugSettings & {
enabled: boolean;
verbose: boolean;
};
setDebugSettings(settings: DebugSettings): void;
enableDebugMode(level?: "basic" | "full"): void;
clearDebugSettings(): void;
showDebugOverlay(options?: any): DebugOverlay;
hideDebugOverlay(): void;
DebugOverlay: typeof DebugOverlay;
installGlobals(): void;
_version(): any;
version(): string;
};
export default jqhtml;
//# sourceMappingURL=index.d.ts.map

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

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,YAAY,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAG7D,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,YAAY,EACZ,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,EACb,mBAAmB,EACnB,wBAAwB,EACxB,eAAe,EAChB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAG1G,OAAO,EACL,oBAAoB,EACpB,aAAa,EACd,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACV,WAAW,EACX,cAAc,EACd,oBAAoB,EACpB,eAAe,EAChB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EACL,eAAe,EACf,WAAW,EACZ,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EACL,YAAY,EACZ,eAAe,EACf,WAAW,EACX,cAAc,EACd,aAAa,EACb,sBAAsB,EACtB,oBAAoB,EACpB,OAAO,EACR,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAM9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAG9B,wBAAgB,IAAI,CAAC,MAAM,CAAC,EAAE,GAAG,GAAG,IAAI,CAUvC;AAID,OAAO,EAAE,gBAAgB,IAAI,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAGrF,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,YAAY,EACZ,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,EACb,mBAAmB,EACnB,wBAAwB,EACxB,eAAe,EAChB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,oBAAoB,EACpB,aAAa,EACd,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,eAAe,EACf,WAAW,EACZ,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,YAAY,EAGb,MAAM,oBAAoB,CAAC;AAG5B,OAAO,oBAAoB,CAAC;AAG5B,eAAO,MAAM,OAAO,gBAAgB,CAAC;AAGrC,MAAM,WAAW,aAAa;IAE5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAG7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAG/B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE;QACZ,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IAGF,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAGD,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;WA8BL,aAAa,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE;+BAGhC,aAAa;4BAIjB,OAAO,GAAG,MAAM;;+BAmBZ,GAAG;;;;;;CA6C/B,CAAC;AAmBF,eAAe,MAAM,CAAC"}

2834
node_modules/@jqhtml/core/dist/index.js generated vendored Executable file

File diff suppressed because it is too large Load Diff

1
node_modules/@jqhtml/core/dist/index.js.map generated vendored Executable file

File diff suppressed because one or more lines are too long

27
node_modules/@jqhtml/core/dist/instruction-processor.d.ts generated vendored Executable file
View File

@@ -0,0 +1,27 @@
/**
* JQHTML v2 Instruction Processor
*
* Processes instruction arrays generated by the parser
* Uses v1's HTML string building approach for performance
*/
import { Jqhtml_Component } from './component.js';
export interface TagInstruction {
tag: [string, Record<string, any>, boolean];
}
export interface ComponentInstruction {
comp: [string, Record<string, any>] | [string, Record<string, any>, (context: any) => [any[], Jqhtml_Component]];
}
export interface SlotInstruction {
slot: [string, Record<string, any>, (context: any) => [any[], Jqhtml_Component]];
}
export type Instruction = TagInstruction | ComponentInstruction | SlotInstruction | string;
/**
* Process an array of instructions and append to target
* Uses v1 approach: build HTML string, set innerHTML, then initialize
*/
export declare function process_instructions(instructions: Instruction[], target: any, context: Jqhtml_Component, slots?: Record<string, SlotInstruction>): Promise<void>;
/**
* Utility to extract slots from instructions
*/
export declare function extract_slots(instructions: Instruction[]): Record<string, SlotInstruction>;
//# sourceMappingURL=instruction-processor.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"instruction-processor.d.ts","sourceRoot":"","sources":["../src/instruction-processor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAIlD,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,GAAG,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC;CAClH;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,GAAG,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC;CAClF;AAED,MAAM,MAAM,WAAW,GAAG,cAAc,GAAG,oBAAoB,GAAG,eAAe,GAAG,MAAM,CAAC;AAqB3F;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,YAAY,EAAE,WAAW,EAAE,EAC3B,MAAM,EAAE,GAAG,EACX,OAAO,EAAE,gBAAgB,EACzB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,GACtC,OAAO,CAAC,IAAI,CAAC,CAuCf;AAwXD;;GAEG;AACH,wBAAgB,aAAa,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAW1F"}

2839
node_modules/@jqhtml/core/dist/jqhtml-core.esm.js generated vendored Executable file

File diff suppressed because it is too large Load Diff

1
node_modules/@jqhtml/core/dist/jqhtml-core.esm.js.map generated vendored Executable file

File diff suppressed because one or more lines are too long

575
node_modules/@jqhtml/core/dist/jqhtml-debug.esm.js generated vendored Executable file
View File

@@ -0,0 +1,575 @@
/**
* JQHTML Debug Overlay
*
* Independent debug controls using pure jQuery DOM manipulation.
* Does NOT use JQHTML components so it works even when components are broken.
*/
// Get global jQuery
function getJQuery() {
if (typeof window !== 'undefined' && window.$) {
return window.$;
}
if (typeof window !== 'undefined' && window.jQuery) {
return window.jQuery;
}
throw new Error('FATAL: jQuery is not defined. jQuery must be loaded before using JQHTML. ' +
'Add <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> before loading JQHTML.');
}
// Get global jqhtml object
function getJqhtml$1() {
if (typeof window !== 'undefined' && window.jqhtml) {
return window.jqhtml;
}
if (typeof globalThis !== 'undefined' && globalThis.jqhtml) {
return globalThis.jqhtml;
}
throw new Error('FATAL: window.jqhtml is not defined. The JQHTML runtime must be loaded before using JQHTML components. ' +
'Ensure @jqhtml/core is imported and initialized before attempting to use debug features.');
}
class DebugOverlay {
constructor(options = {}) {
this.$container = null;
this.$statusIndicator = null;
this.$ = getJQuery();
if (!this.$) {
throw new Error('jQuery is required for DebugOverlay');
}
this.options = {
position: 'bottom',
theme: 'dark',
compact: false,
showStatus: true,
autoHide: false,
...options
};
}
/**
* Static method to show debug overlay (singleton pattern)
*/
static show(options) {
if (!DebugOverlay.instance) {
DebugOverlay.instance = new DebugOverlay(options);
}
DebugOverlay.instance.display();
return DebugOverlay.instance;
}
/**
* Static method to hide debug overlay
*/
static hide() {
if (DebugOverlay.instance) {
DebugOverlay.instance.hide();
}
}
/**
* Static method to toggle debug overlay visibility
*/
static toggle() {
if (DebugOverlay.instance && DebugOverlay.instance.$container) {
if (DebugOverlay.instance.$container.is(':visible')) {
DebugOverlay.hide();
}
else {
DebugOverlay.instance.display();
}
}
else {
DebugOverlay.show();
}
}
/**
* Static method to destroy debug overlay
*/
static destroy() {
if (DebugOverlay.instance) {
DebugOverlay.instance.destroy();
DebugOverlay.instance = null;
}
}
/**
* Display the debug overlay
*/
display() {
if (this.$container) {
this.$container.show();
return;
}
this.createOverlay();
if (this.options.showStatus) {
this.createStatusIndicator();
}
}
/**
* Hide the debug overlay
*/
hide() {
if (this.$container) {
this.$container.hide();
}
if (this.$statusIndicator) {
this.$statusIndicator.hide();
}
}
/**
* Remove the debug overlay completely
*/
destroy() {
if (this.$container) {
this.$container.remove();
this.$container = null;
}
if (this.$statusIndicator) {
this.$statusIndicator.remove();
this.$statusIndicator = null;
}
}
/**
* Update the status indicator
*/
updateStatus(mode) {
if (!this.$statusIndicator)
return;
this.$statusIndicator.text('Debug: ' + mode);
this.$statusIndicator.attr('class', 'jqhtml-debug-status' + (mode !== 'Off' ? ' active' : ''));
}
createOverlay() {
// Add styles first
this.addStyles();
// Create container using jQuery
this.$container = this.$('<div>')
.addClass(`jqhtml-debug-overlay ${this.options.theme} ${this.options.position}`);
// Create content structure
const $content = this.$('<div>').addClass('jqhtml-debug-content');
const $controls = this.$('<div>').addClass('jqhtml-debug-controls');
// Add title
const $title = this.$('<span>')
.addClass('jqhtml-debug-title')
.html('<strong>🐛 JQHTML Debug:</strong>');
$controls.append($title);
// Create buttons
const buttons = [
{ text: 'Slow Motion + Flash', action: 'enableSlowMotionDebug', class: 'success' },
{ text: 'Basic Debug', action: 'enableBasicDebug', class: '' },
{ text: 'Full Debug', action: 'enableFullDebug', class: '' },
{ text: 'Sequential', action: 'enableSequentialMode', class: '' },
{ text: 'Clear Debug', action: 'clearAllDebug', class: 'danger' },
{ text: 'Settings', action: 'showDebugInfo', class: '' }
];
buttons.forEach(btn => {
const $button = this.$('<button>')
.text(btn.text)
.addClass('jqhtml-debug-btn' + (btn.class ? ` ${btn.class}` : ''))
.on('click', () => this.executeAction(btn.action));
$controls.append($button);
});
// Add minimize/close button
const $toggleBtn = this.$('<button>')
.text(this.options.compact ? '▼' : '▲')
.addClass('jqhtml-debug-toggle')
.on('click', () => this.toggle());
$controls.append($toggleBtn);
// Assemble and add to page
$content.append($controls);
this.$container.append($content);
this.$('body').append(this.$container);
}
createStatusIndicator() {
this.$statusIndicator = this.$('<div>')
.addClass('jqhtml-debug-status')
.text('Debug: Off')
.css({
position: 'fixed',
top: '10px',
right: '10px',
background: '#2c3e50',
color: 'white',
padding: '5px 10px',
borderRadius: '4px',
fontSize: '0.75rem',
zIndex: '10001',
opacity: '0.8',
fontFamily: 'monospace'
});
this.$('body').append(this.$statusIndicator);
}
addStyles() {
// Check if styles already exist
if (this.$('#jqhtml-debug-styles').length > 0)
return;
// Create and inject CSS using jQuery - concatenated strings for better minification
const $style = this.$('<style>')
.attr('id', 'jqhtml-debug-styles')
.text('.jqhtml-debug-overlay {' +
'position: fixed;' +
'left: 0;' +
'right: 0;' +
'z-index: 10000;' +
'font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace;' +
'font-size: 0.8rem;' +
'box-shadow: 0 2px 10px rgba(0,0,0,0.2);' +
'}' +
'.jqhtml-debug-overlay.top {' +
'top: 0;' +
'}' +
'.jqhtml-debug-overlay.bottom {' +
'bottom: 0;' +
'}' +
'.jqhtml-debug-overlay.dark {' +
'background: #34495e;' +
'color: #ecf0f1;' +
'}' +
'.jqhtml-debug-overlay.light {' +
'background: #f8f9fa;' +
'color: #333;' +
'border-bottom: 1px solid #dee2e6;' +
'}' +
'.jqhtml-debug-content {' +
'padding: 0.5rem 1rem;' +
'}' +
'.jqhtml-debug-controls {' +
'display: flex;' +
'flex-wrap: wrap;' +
'gap: 8px;' +
'align-items: center;' +
'}' +
'.jqhtml-debug-title {' +
'margin-right: 10px;' +
'font-weight: bold;' +
'}' +
'.jqhtml-debug-btn {' +
'padding: 4px 8px;' +
'border: none;' +
'border-radius: 3px;' +
'background: #3498db;' +
'color: white;' +
'cursor: pointer;' +
'font-size: 0.75rem;' +
'transition: background 0.2s;' +
'}' +
'.jqhtml-debug-btn:hover {' +
'background: #2980b9;' +
'}' +
'.jqhtml-debug-btn.success {' +
'background: #27ae60;' +
'}' +
'.jqhtml-debug-btn.success:hover {' +
'background: #229954;' +
'}' +
'.jqhtml-debug-btn.danger {' +
'background: #e74c3c;' +
'}' +
'.jqhtml-debug-btn.danger:hover {' +
'background: #c0392b;' +
'}' +
'.jqhtml-debug-toggle {' +
'padding: 4px 8px;' +
'border: none;' +
'border-radius: 3px;' +
'background: #7f8c8d;' +
'color: white;' +
'cursor: pointer;' +
'font-size: 0.75rem;' +
'margin-left: auto;' +
'}' +
'.jqhtml-debug-toggle:hover {' +
'background: #6c7b7d;' +
'}' +
'.jqhtml-debug-status.active {' +
'background: #27ae60 !important;' +
'}' +
'@media (max-width: 768px) {' +
'.jqhtml-debug-controls {' +
'flex-direction: column;' +
'align-items: flex-start;' +
'}' +
'.jqhtml-debug-title {' +
'margin-bottom: 5px;' +
'}' +
'}');
this.$('head').append($style);
}
toggle() {
// Toggle between compact and full view
this.options.compact = !this.options.compact;
const $toggleBtn = this.$container.find('.jqhtml-debug-toggle');
$toggleBtn.text(this.options.compact ? '▼' : '▲');
const $buttons = this.$container.find('.jqhtml-debug-btn');
if (this.options.compact) {
$buttons.hide();
}
else {
$buttons.show();
}
}
executeAction(action) {
const jqhtml = getJqhtml$1();
if (!jqhtml) {
console.warn('JQHTML not available - make sure it\'s loaded and exposed globally');
return;
}
switch (action) {
case 'enableSlowMotionDebug':
jqhtml.setDebugSettings({
logFullLifecycle: true,
sequentialProcessing: true,
delayAfterComponent: 150,
delayAfterRender: 200,
delayAfterRerender: 250,
flashComponents: true,
flashDuration: 800,
flashColors: {
create: '#3498db',
render: '#27ae60',
ready: '#9b59b6'
},
profilePerformance: true,
highlightSlowRenders: 30,
logDispatch: true
});
this.updateStatus('Slow Motion');
console.log('🐛 Slow Motion Debug Mode Enabled');
break;
case 'enableBasicDebug':
jqhtml.enableDebugMode('basic');
this.updateStatus('Basic');
console.log('🐛 Basic Debug Mode Enabled');
break;
case 'enableFullDebug':
jqhtml.enableDebugMode('full');
this.updateStatus('Full');
console.log('🐛 Full Debug Mode Enabled');
break;
case 'enableSequentialMode':
jqhtml.setDebugSettings({
logCreationReady: true,
sequentialProcessing: true,
flashComponents: true,
profilePerformance: true
});
this.updateStatus('Sequential');
console.log('🐛 Sequential Processing Mode Enabled');
break;
case 'clearAllDebug':
jqhtml.clearDebugSettings();
this.updateStatus('Off');
console.log('🐛 All Debug Modes Disabled');
break;
case 'showDebugInfo':
const settings = JSON.stringify(jqhtml.debug, null, 2);
console.log('🐛 Current Debug Settings:', settings);
alert('Debug settings logged to console:\n\n' + (Object.keys(jqhtml.debug).length > 0 ? settings : 'No debug settings active'));
break;
}
}
}
DebugOverlay.instance = null;
// Simplified global convenience functions that use static methods
function showDebugOverlay(options) {
return DebugOverlay.show(options);
}
function hideDebugOverlay() {
DebugOverlay.hide();
}
// Auto-initialize if debug query parameter is present
if (typeof window !== 'undefined') {
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.get('debug') === 'true' || urlParams.get('jqhtml-debug') === 'true') {
document.addEventListener('DOMContentLoaded', () => {
DebugOverlay.show();
});
}
}
/**
* JQHTML Debug Module
*
* Provides comprehensive debugging capabilities for JQHTML components
*/
// Global debug state
let performanceMetrics = new Map();
/**
* Development warning helper
* Warnings are suppressed in production builds or when JQHTML_SUPPRESS_WARNINGS is set
*/
function devWarn(message) {
// Check if warnings are suppressed
if (typeof window !== 'undefined' && window.JQHTML_SUPPRESS_WARNINGS) {
return;
}
// Check if in production mode
if (typeof process !== 'undefined' && process.env && process.env.NODE_ENV === 'production') {
return;
}
console.warn(`[JQHTML Dev Warning] ${message}`);
}
// Get global jqhtml object
function getJqhtml() {
if (typeof window !== 'undefined' && window.jqhtml) {
return window.jqhtml;
}
// Fallback: try to get from global if available
if (typeof globalThis !== 'undefined' && globalThis.jqhtml) {
return globalThis.jqhtml;
}
throw new Error('FATAL: window.jqhtml is not defined. The JQHTML runtime must be loaded before using debug features. ' +
'Import and initialize @jqhtml/core before attempting to use debug functionality.');
}
// Visual flash effect
function flashComponent(component, eventType) {
const jqhtml = getJqhtml();
if (!jqhtml?.debug?.flashComponents)
return;
const duration = jqhtml.debug.flashDuration || 500;
const colors = jqhtml.debug.flashColors || {};
const color = colors[eventType] || (eventType === 'create' ? '#3498db' :
eventType === 'render' ? '#27ae60' :
'#9b59b6');
// Store original border
const originalBorder = component.$.css('border');
// Apply flash border
component.$.css({
'border': `2px solid ${color}`,
'transition': `border ${duration}ms ease-out`
});
// Remove after duration
setTimeout(() => {
component.$.css('border', originalBorder || '');
}, duration);
}
// Log lifecycle event
function logLifecycle(component, phase, status) {
const jqhtml = getJqhtml();
if (!jqhtml?.debug)
return;
const shouldLog = jqhtml.debug.logFullLifecycle ||
(jqhtml.debug.logCreationReady && (phase === 'create' || phase === 'ready'));
if (!shouldLog)
return;
const componentName = component.constructor.name;
const timestamp = new Date().toISOString();
const prefix = `[JQHTML ${timestamp}]`;
if (status === 'start') {
console.log(`${prefix} ${componentName}#${component._cid}${phase} starting...`);
// Start performance tracking
if (jqhtml.debug.profilePerformance) {
performanceMetrics.set(`${component._cid}_${phase}`, Date.now());
}
}
else {
let message = `${prefix} ${componentName}#${component._cid}${phase} complete`;
// Add performance data
if (jqhtml.debug.profilePerformance) {
const startTime = performanceMetrics.get(`${component._cid}_${phase}`);
if (startTime) {
const duration = Date.now() - startTime;
message += ` (${duration}ms)`;
// Highlight slow renders
if (phase === 'render' && jqhtml.debug.highlightSlowRenders &&
duration > jqhtml.debug.highlightSlowRenders) {
console.warn(`${prefix} SLOW RENDER: ${componentName}#${component._cid} took ${duration}ms`);
component.$.css('outline', '2px dashed red');
}
}
}
console.log(message);
// Visual feedback
if (jqhtml.debug.flashComponents && (phase === 'create' || phase === 'render' || phase === 'ready')) {
flashComponent(component, phase);
}
}
// Update component tree if enabled
if (jqhtml.debug.showComponentTree) {
updateComponentTree();
}
}
// Apply delays based on lifecycle phase
function applyDebugDelay(phase) {
const jqhtml = getJqhtml();
if (!jqhtml?.debug)
return;
let delayMs = 0;
switch (phase) {
case 'component':
delayMs = jqhtml.debug.delayAfterComponent || 0;
break;
case 'render':
delayMs = jqhtml.debug.delayAfterRender || 0;
break;
case 'rerender':
delayMs = jqhtml.debug.delayAfterRerender || 0;
break;
}
if (delayMs > 0) {
console.log(`[JQHTML Debug] Applying ${delayMs}ms delay after ${phase}`);
}
}
// Log instruction processing
function logInstruction(type, data) {
const jqhtml = getJqhtml();
if (!jqhtml?.debug?.logInstructionProcessing)
return;
console.log(`[JQHTML Instruction] ${type}:`, data);
}
// Log data changes
function logDataChange(component, property, oldValue, newValue) {
const jqhtml = getJqhtml();
if (!jqhtml?.debug?.traceDataFlow)
return;
console.log(`[JQHTML Data] ${component.constructor.name}#${component._cid}.data.${property}:`, { old: oldValue, new: newValue });
}
// Update component tree visualization
function updateComponentTree() {
// This would update a debug panel if implemented
// For now, just log the tree structure periodically
console.log('[JQHTML Tree] Component hierarchy updated');
}
// Router dispatch logging
function logDispatch(url, route, params, verbose = false) {
const jqhtml = getJqhtml();
if (!jqhtml?.debug)
return;
const shouldLog = jqhtml.debug.logDispatch || jqhtml.debug.logDispatchVerbose;
if (!shouldLog)
return;
const isVerbose = jqhtml.debug.logDispatchVerbose || verbose;
if (isVerbose) {
console.group(`[JQHTML Router] Dispatching: ${url}`);
console.log('Matched route:', route);
console.log('Extracted params:', params);
console.log('Route component:', route.component);
console.log('Route layout:', route.layout);
console.log('Route meta:', route.meta);
console.groupEnd();
}
else {
console.log(`[JQHTML Router] ${url}${route.component} (params: ${JSON.stringify(params)})`);
}
}
// Check if sequential processing is enabled
function isSequentialProcessing() {
const jqhtml = getJqhtml();
return jqhtml?.debug?.sequentialProcessing || false;
}
// Error handling with break on error
function handleComponentError(component, phase, error) {
const jqhtml = getJqhtml();
console.error(`[JQHTML Error] ${component.constructor.name}#${component._cid} failed in ${phase}:`, error);
if (jqhtml?.debug?.breakOnError) {
debugger; // This will pause execution in dev tools
}
}
// Additional debug suggestions that could be implemented:
//
// 1. Component Inspector - Click on any component to see its data/args/state
// 2. Time Travel Debugging - Record state changes and replay them
// 3. Network Request Tracking - Log all AJAX calls made during load()
// 4. Memory Leak Detection - Track component creation/destruction
// 5. Template Compilation Debugging - Show compiled template functions
// 6. Event Flow Visualization - Show event bubbling through components
// 7. Dependency Graph - Show which components depend on which data
// 8. Hot Reload Support - Reload components without losing state
// 9. Performance Budgets - Warn when components exceed size/time limits
// 10. Accessibility Auditing - Check for missing ARIA attributes
export { DebugOverlay, applyDebugDelay, devWarn, handleComponentError, hideDebugOverlay, isSequentialProcessing, logDataChange, logDispatch, logInstruction, logLifecycle, showDebugOverlay };
//# sourceMappingURL=jqhtml-debug.esm.js.map

1
node_modules/@jqhtml/core/dist/jqhtml-debug.esm.js.map generated vendored Executable file

File diff suppressed because one or more lines are too long

21
node_modules/@jqhtml/core/dist/jquery-plugin.d.ts generated vendored Executable file
View File

@@ -0,0 +1,21 @@
/**
* JQHTML v2 jQuery Plugin
*
* Extends jQuery with component method:
* - $(el).component() - Get component instance
* - $(el).component(ComponentClass, args) - Create component
*/
import { Jqhtml_Component } from './component.js';
import type { ComponentConstructor } from './component-registry.js';
declare global {
interface JQuery {
/**
* Get or create a component on this element
*/
component(): Jqhtml_Component | null;
component(ComponentClass: ComponentConstructor, args?: Record<string, any>): Jqhtml_Component;
component(componentName: string, args?: Record<string, any>): Jqhtml_Component;
}
}
export declare function init_jquery_plugin(jQuery: any): void;
//# sourceMappingURL=jquery-plugin.d.ts.map

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

@@ -0,0 +1 @@
{"version":3,"file":"jquery-plugin.d.ts","sourceRoot":"","sources":["../src/jquery-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAQpE,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd;;WAEG;QACH,SAAS,IAAI,gBAAgB,GAAG,IAAI,CAAC;QACrC,SAAS,CAAC,cAAc,EAAE,oBAAoB,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,gBAAgB,CAAC;QAC9F,SAAS,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,gBAAgB,CAAC;KAChF;CACF;AAGD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI,CA2JpD"}

70
node_modules/@jqhtml/core/dist/lifecycle-manager.d.ts generated vendored Executable file
View File

@@ -0,0 +1,70 @@
/**
* JQHTML v2 Lifecycle Manager
*
* Coordinates phase batching across all components
* Implements the v2 lifecycle specification:
* 1. render - Create DOM structure (top-down)
* 2. create - Quick setup, hide elements, prepare UI (bottom-up)
* 3. load - Fetch data from APIs (bottom-up, parallelizable)
* 4. ready - Component fully initialized (bottom-up)
*/
import type { Jqhtml_Component } from './component.js';
export type LifecyclePhase = 'render' | 'create' | 'load' | 'ready';
export declare class LifecycleManager {
private static instance;
private active_components;
private phase_queues;
private current_phase;
private processing;
constructor();
static get_instance(): LifecycleManager;
/**
* Register a component for lifecycle processing
*/
register_component(component: Jqhtml_Component): void;
/**
* Unregister a component (called on destroy)
*/
unregister_component(component: Jqhtml_Component): void;
/**
* Process lifecycle phases for all queued components
*/
private process_queues;
/**
* Process a single lifecycle phase
*/
private process_phase;
/**
* Execute a lifecycle phase on a component
*/
private execute_phase;
/**
* Sort components by hierarchy for proper phase execution order
*/
private sort_components_by_hierarchy;
/**
* Process bottom-up with parallel sibling execution
*/
private process_bottom_up_parallel;
/**
* Process top-down sequentially
*/
private process_top_down;
/**
* Group components by their depth in the hierarchy
*/
private group_by_depth;
/**
* Find the parent component of a given component
*/
private find_parent_component;
/**
* Catch up a component to the current lifecycle phase
*/
private catch_up_component;
/**
* Wait for all components to reach ready state
*/
wait_for_ready(): Promise<void>;
}
//# sourceMappingURL=lifecycle-manager.d.ts.map

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

@@ -0,0 +1 @@
{"version":3,"file":"lifecycle-manager.d.ts","sourceRoot":"","sources":["../src/lifecycle-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGvD,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAepE,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAmB;IAC1C,OAAO,CAAC,iBAAiB,CAAoC;IAC7D,OAAO,CAAC,YAAY,CAAyD;IAC7E,OAAO,CAAC,aAAa,CAA+B;IACpD,OAAO,CAAC,UAAU,CAAkB;;IASpC,MAAM,CAAC,YAAY,IAAI,gBAAgB;IAOvC;;OAEG;IACH,kBAAkB,CAAC,SAAS,EAAE,gBAAgB,GAAG,IAAI;IAarD;;OAEG;IACH,oBAAoB,CAAC,SAAS,EAAE,gBAAgB,GAAG,IAAI;IASvD;;OAEG;YACW,cAAc;IAe5B;;OAEG;YACW,aAAa;IA6C3B;;OAEG;YACW,aAAa;IA+C3B;;OAEG;IACH,OAAO,CAAC,4BAA4B;IA+CpC;;OAEG;YACW,0BAA0B;IAiBxC;;OAEG;YACW,gBAAgB;IAS9B;;OAEG;IACH,OAAO,CAAC,cAAc;IAuBtB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAiB7B;;OAEG;YACW,kBAAkB;IAehC;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;CAMtC"}

17
node_modules/@jqhtml/core/dist/template-renderer.d.ts generated vendored Executable file
View File

@@ -0,0 +1,17 @@
/**
* JQHTML v2 Template Renderer
*
* Connects compiled templates to components
* Processes instruction arrays and handles bindings
*/
import type { Jqhtml_Component } from './component.js';
/**
* Render a template for a component
* Templates are functions that return [instructions, context] tuples
*/
export declare function render_template(component: Jqhtml_Component, template_fn?: Function): Promise<void>;
/**
* Helper to escape HTML for safe output
*/
export declare function escape_html(str: string): string;
//# sourceMappingURL=template-renderer.d.ts.map

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

@@ -0,0 +1 @@
{"version":3,"file":"template-renderer.d.ts","sourceRoot":"","sources":["../src/template-renderer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AA2EvD;;;GAGG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,gBAAgB,EAC3B,WAAW,CAAC,EAAE,QAAQ,GACrB,OAAO,CAAC,IAAI,CAAC,CAqDf;AA8KD;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI/C"}

View File

@@ -0,0 +1,339 @@
# JQHTML Laravel Integration
The JQHTML Laravel Bridge is included with the `@jqhtml/core` npm package, making it easy to integrate JQHTML error handling into your Laravel application without requiring a separate Composer package.
## Installation
### Step 1: Install JQHTML via npm
```bash
npm install @jqhtml/core
```
### Step 2: Load the Laravel Bridge in your Laravel application
#### Option A: In `AppServiceProvider` (Recommended)
Add to `app/Providers/AppServiceProvider.php`:
```php
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function register()
{
// Load JQHTML Laravel Bridge from node_modules
$jqhtmlBridge = base_path('node_modules/@jqhtml/core/laravel-bridge/autoload.php');
if (file_exists($jqhtmlBridge)) {
require_once $jqhtmlBridge;
}
}
}
```
#### Option B: In `composer.json` autoload
Add to your `composer.json`:
```json
{
"autoload": {
"files": [
"node_modules/@jqhtml/core/laravel-bridge/autoload.php"
]
}
}
```
Then run:
```bash
composer dump-autoload
```
#### Option C: Manual inclusion in `bootstrap/app.php`
For Laravel 11+, add to `bootstrap/app.php`:
```php
use Illuminate\Foundation\Application;
return Application::configure(basePath: dirname(__DIR__))
->withProviders([
// After creating the app, load JQHTML
function ($app) {
$jqhtmlBridge = base_path('node_modules/@jqhtml/core/laravel-bridge/autoload.php');
if (file_exists($jqhtmlBridge)) {
require_once $jqhtmlBridge;
}
}
])
->create();
```
## Configuration
### Publish the configuration file (optional)
Create `config/jqhtml.php`:
```php
<?php
return [
'source_maps_path' => storage_path('jqhtml-sourcemaps'),
'show_source_context' => env('APP_DEBUG', false),
'context_lines' => 5,
'cache_compiled' => !env('APP_DEBUG', false),
'compiled_path' => storage_path('jqhtml-compiled'),
'enable_source_maps' => env('APP_DEBUG', false),
'source_map_mode' => 'external', // 'inline', 'external', or 'both'
];
```
## Usage
### Basic Error Handling
The bridge automatically catches and formats JQHTML template errors. When a JQHTML error occurs, it will be displayed with:
- Template file location with line and column numbers
- Source code context with error highlighting
- Helpful suggestions for common mistakes
- Source map resolution (if available)
### In Your Blade Templates
If you're compiling JQHTML templates and want to catch errors:
```php
use Jqhtml\LaravelBridge\JqhtmlException;
Route::post('/compile-template', function (Request $request) {
$templatePath = resource_path('jqhtml/' . $request->input('template'));
// Use Node.js to compile (via shell_exec, Process, etc.)
$result = shell_exec("node compile-jqhtml.js " . escapeshellarg($templatePath));
$data = json_decode($result, true);
if (!$data['success']) {
// Create exception from JS error data
throw JqhtmlException::createFromJsError($data['error']);
}
return response()->json(['compiled' => $data['code']]);
});
```
### Middleware Setup
Add to your middleware groups in `app/Http/Kernel.php`:
```php
protected $middlewareGroups = [
'web' => [
// ... other middleware
\Jqhtml\LaravelBridge\Middleware\JqhtmlErrorMiddleware::class,
],
];
```
### Manual Exception Handling
```php
use Jqhtml\LaravelBridge\JqhtmlException;
try {
// Your JQHTML compilation or execution
$compiled = compileJqhtmlTemplate($source);
} catch (\Exception $e) {
// Wrap in JQHTML exception for better display
throw new JqhtmlException(
$e->getMessage(),
'templates/my-template.jqhtml',
$lineNumber,
$columnNumber,
$sourceCode,
'Check your template syntax'
);
}
```
## Node.js Integration
### Compilation Script
Create `compile-jqhtml.js` in your Laravel project root:
```javascript
#!/usr/bin/env node
import { parse, generate } from '@jqhtml/core';
import fs from 'fs';
const templatePath = process.argv[2];
try {
const source = fs.readFileSync(templatePath, 'utf8');
const ast = parse(source, templatePath);
const { code, map } = generate(ast, { sourceMap: true });
console.log(JSON.stringify({
success: true,
code,
map
}));
} catch (error) {
// Format error for Laravel
console.log(JSON.stringify({
success: false,
error: {
message: error.message,
filename: error.filename || templatePath,
line: error.line,
column: error.column,
source: error.source,
suggestion: error.suggestion
}
}));
process.exit(1);
}
```
### Using with Laravel Mix/Vite
In `vite.config.js`:
```javascript
import { defineConfig } from 'vite';
import { parse, generate } from '@jqhtml/core';
export default defineConfig({
plugins: [
{
name: 'jqhtml',
transform(source, id) {
if (id.endsWith('.jqhtml')) {
try {
const ast = parse(source, id);
const { code, map } = generate(ast, { sourceMap: true });
return { code, map };
} catch (error) {
// Error will be caught by Laravel bridge
throw error;
}
}
}
}
]
});
```
## API Endpoint Example
Create an API endpoint for compiling JQHTML templates:
```php
// routes/api.php
use Jqhtml\LaravelBridge\JqhtmlException;
Route::post('/api/jqhtml/compile', function (Request $request) {
$template = $request->input('template');
$filename = $request->input('filename', 'template.jqhtml');
// Execute Node.js compilation
$process = new Process([
'node',
base_path('compile-jqhtml.js'),
'--stdin'
]);
$process->setInput(json_encode([
'template' => $template,
'filename' => $filename
]));
$process->run();
if (!$process->isSuccessful()) {
$output = json_decode($process->getOutput(), true);
if (isset($output['error'])) {
throw JqhtmlException::createFromJsError($output['error']);
}
throw new \Exception('Compilation failed');
}
return response()->json(json_decode($process->getOutput(), true));
});
```
## Laravel Ignition Integration
The bridge automatically integrates with Laravel Ignition (if installed) to provide enhanced error display. No additional configuration needed.
## Troubleshooting
### Bridge not loading
Ensure `node_modules/@jqhtml/core` exists:
```bash
ls -la node_modules/@jqhtml/core/laravel-bridge/
```
### Class not found errors
Clear Laravel's cache:
```bash
php artisan cache:clear
php artisan config:clear
composer dump-autoload
```
### Source maps not working
Ensure the source map directory exists and is writable:
```bash
mkdir -p storage/jqhtml-sourcemaps
chmod 755 storage/jqhtml-sourcemaps
```
## Directory Structure
After installation, your Laravel project will have:
```
your-laravel-project/
├── node_modules/
│ └── @jqhtml/
│ └── core/
│ ├── dist/ # JavaScript runtime
│ └── laravel-bridge/ # PHP integration
│ ├── src/ # PHP classes
│ ├── config/ # Laravel config
│ └── autoload.php # Autoloader
├── config/
│ └── jqhtml.php # Your config (optional)
├── storage/
│ ├── jqhtml-compiled/ # Compiled templates
│ └── jqhtml-sourcemaps/ # Source maps
└── compile-jqhtml.js # Node compilation script
```
## Benefits of This Approach
1. **Single Package**: Everything comes from npm, no Composer package needed
2. **Version Sync**: PHP and JS code always match versions
3. **Simple Updates**: Just `npm update @jqhtml/core`
4. **No Private Packagist**: No need for private Composer repositories
5. **CI/CD Friendly**: Works with standard Node.js deployment pipelines
## License
MIT - Same as @jqhtml/core

242
node_modules/@jqhtml/core/laravel-bridge/README.md generated vendored Executable file
View File

@@ -0,0 +1,242 @@
# JQHTML Laravel Bridge
Laravel integration package for JQHTML template error reporting with source map support.
## Installation
```bash
composer require jqhtml/laravel-bridge
```
## Configuration
Publish the configuration file:
```bash
php artisan vendor:publish --tag=jqhtml-config
```
## Usage
### Basic Setup
The package automatically registers its service provider and exception handler extensions. No additional setup is required for basic functionality.
### Error Handling
When a JQHTML template error occurs, the package will:
1. Parse the error message to extract template location information
2. Load source maps if available
3. Format the error for Laravel's exception handler
4. Display enhanced error information in debug mode
### Middleware
To automatically catch and format JQHTML errors, add the middleware to your route groups:
```php
// In app/Http/Kernel.php
protected $middlewareGroups = [
'web' => [
// ...
\Jqhtml\LaravelBridge\Middleware\JqhtmlErrorMiddleware::class,
],
];
```
### Manual Exception Creation
```php
use Jqhtml\LaravelBridge\JqhtmlException;
// Create from JavaScript error data
$jsError = json_decode($errorJson, true);
$exception = JqhtmlException::createFromJsError($jsError);
// Create manually
$exception = new JqhtmlException(
'Unclosed component definition',
'templates/user-card.jqhtml', // template file
42, // line number
15, // column number
$sourceCode, // template source
'Did you forget </Define:UserCard>?' // suggestion
);
```
### Integration with Node.js Compiler
When compiling JQHTML templates with Node.js, catch errors and pass them to Laravel:
```javascript
// Node.js side
try {
const compiled = jqhtml.compile(template);
} catch (error) {
// Send error details to Laravel
const errorData = {
message: error.message,
filename: error.filename,
line: error.line,
column: error.column,
source: error.source,
suggestion: error.suggestion
};
// Pass to Laravel via API or process communication
sendToLaravel(errorData);
}
```
```php
// Laravel side - Best Practice
use Jqhtml\LaravelBridge\JqhtmlException;
// Parse compiler JSON output
$errorData = json_decode($compiler_output, true);
if ($errorData && isset($errorData['error'])) {
// JqhtmlException extends ViewException, so this will show
// the template file as the error source in Laravel/Ignition
$exception = JqhtmlException::createFromJsError(
$errorData['error'],
$templatePath // Pass the actual template path
);
throw $exception;
}
// Alternative: Direct ViewException usage (if not using the bridge)
if ($errorData && isset($errorData['error'])) {
$error = $errorData['error'];
$line = $error['line'] ?? 1;
throw new \Illuminate\View\ViewException(
$error['message'],
0, // code
1, // severity
$templatePath, // file
$line, // line
null // previous
);
}
```
### Source Map Support
The package supports source maps for mapping compiled JavaScript back to original JQHTML templates:
```php
// Configure source map storage location
config(['jqhtml.source_maps_path' => storage_path('sourcemaps')]);
// The formatter will automatically look for source maps
// when an error includes a compiled file reference
$exception->setCompiledFile('/path/to/compiled.js');
```
### Custom Error Views
Create a custom error view at `resources/views/jqhtml/error.blade.php`:
```blade
@extends('errors::layout')
@section('title', 'JQHTML Template Error')
@section('message')
<div class="jqhtml-error">
<h2>{{ $exception->getMessage() }}</h2>
@if($exception->getSuggestion())
<div class="suggestion">
💡 {{ $exception->getSuggestion() }}
</div>
@endif
@if($exception->getTemplateFile())
<div class="location">
📍 {{ $exception->getTemplateFile() }}:{{ $exception->getTemplateLine() }}:{{ $exception->getTemplateColumn() }}
</div>
@endif
@if(isset($error_data['source_context']))
<div class="source-context">
<pre><code>@foreach($error_data['source_context'] as $line)
@if($line['is_error_line'])<strong>> {{ $line['line_number'] }} | {{ $line['content'] }}
@if($line['error_column']){{ str_repeat(' ', $line['error_column'] + 8) }}{{ str_repeat('^', 20) }}@endif</strong>
@else {{ $line['line_number'] }} | {{ $line['content'] }}
@endif
@endforeach</code></pre>
</div>
@endif
</div>
@endsection
```
## Configuration Options
```php
return [
// Where to store source map files
'source_maps_path' => storage_path('jqhtml-sourcemaps'),
// Show source code context in errors
'show_source_context' => env('APP_DEBUG', false),
// Lines of context to show
'context_lines' => 5,
// Cache compiled templates
'cache_compiled' => !env('APP_DEBUG', false),
// Where to store compiled templates
'compiled_path' => storage_path('jqhtml-compiled'),
// Enable source map generation
'enable_source_maps' => env('APP_DEBUG', false),
// Source map mode: 'inline', 'external', or 'both'
'source_map_mode' => 'external',
];
```
## Laravel Ignition Integration
The package automatically integrates with Laravel Ignition (if installed) to provide enhanced error display:
- Template file location with line and column numbers
- Source code context with error highlighting
- Helpful suggestions for common mistakes
- Source map resolution for compiled files
## API Reference
### JqhtmlException
Main exception class for JQHTML template errors. **Extends Laravel's `ViewException`** for optimal integration with Laravel's error handling and Ignition error pages.
When thrown, this exception ensures:
- The template file appears as the error source in Laravel/Ignition
- Line numbers point to the actual template location
- The error page shows your JQHTML template, not the PHP processor
### JqhtmlErrorFormatter
Formats exceptions for display with source context and source map support.
### JqhtmlExceptionRenderer
Renders exceptions for web and JSON responses.
### JqhtmlServiceProvider
Registers services and extends Laravel's exception handler.
### JqhtmlErrorMiddleware
Middleware for catching and wrapping JQHTML errors.
## License
MIT

51
node_modules/@jqhtml/core/laravel-bridge/autoload.php generated vendored Executable file
View File

@@ -0,0 +1,51 @@
<?php
/**
* JQHTML Laravel Bridge Autoloader
*
* Include this file from your Laravel project to load the JQHTML error handling bridge:
*
* require_once base_path('node_modules/@jqhtml/core/laravel-bridge/autoload.php');
*
* Or if using in a service provider:
*
* require_once dirname(__DIR__, 3) . '/node_modules/@jqhtml/core/laravel-bridge/autoload.php';
*/
// This is a Laravel bridge - it REQUIRES Laravel to function
if (!class_exists('Illuminate\\Support\\ServiceProvider')) {
throw new \RuntimeException(
'JQHTML Laravel Bridge requires Laravel. ' .
'This file should only be included from within a Laravel application.'
);
}
// Register the autoloader for JQHTML Laravel Bridge classes
spl_autoload_register(function ($class) {
// Check if the class is in the Jqhtml\LaravelBridge namespace
$prefix = 'Jqhtml\\LaravelBridge\\';
$len = strlen($prefix);
if (strncmp($prefix, $class, $len) !== 0) {
return;
}
// Get the relative class name
$relative_class = substr($class, $len);
// Replace namespace separators with directory separators
$file = __DIR__ . '/src/' . str_replace('\\', '/', $relative_class) . '.php';
// If the file exists, require it
if (file_exists($file)) {
require $file;
}
});
// Auto-register the service provider if in Laravel application context
if (function_exists('app') && app() instanceof \Illuminate\Foundation\Application) {
app()->register(\Jqhtml\LaravelBridge\JqhtmlServiceProvider::class);
}
// Return the namespace for convenience
return 'Jqhtml\\LaravelBridge';

34
node_modules/@jqhtml/core/laravel-bridge/composer.json generated vendored Executable file
View File

@@ -0,0 +1,34 @@
{
"name": "jqhtml/laravel-bridge",
"description": "Laravel integration bridge for JQHTML template error reporting and source maps",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "JQHTML Team"
}
],
"require": {
"php": "^7.4|^8.0",
"illuminate/support": "^8.0|^9.0|^10.0|^11.0"
},
"autoload": {
"psr-4": {
"Jqhtml\\LaravelBridge\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Jqhtml\\LaravelBridge\\Tests\\": "tests/"
}
},
"extra": {
"laravel": {
"providers": [
"Jqhtml\\LaravelBridge\\JqhtmlServiceProvider"
]
}
},
"minimum-stability": "stable",
"prefer-stable": true
}

82
node_modules/@jqhtml/core/laravel-bridge/config/jqhtml.php generated vendored Executable file
View File

@@ -0,0 +1,82 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| JQHTML Source Maps Path
|--------------------------------------------------------------------------
|
| This option defines where JQHTML source map files should be stored.
| Source maps help map compiled JavaScript back to original JQHTML templates
| for better error reporting and debugging.
|
*/
'source_maps_path' => storage_path('jqhtml-sourcemaps'),
/*
|--------------------------------------------------------------------------
| Show Source Context
|--------------------------------------------------------------------------
|
| When enabled, error messages will include the surrounding source code
| context to help identify the exact location and nature of the error.
|
*/
'show_source_context' => env('APP_DEBUG', false),
/*
|--------------------------------------------------------------------------
| Error Context Lines
|--------------------------------------------------------------------------
|
| Number of lines to show before and after the error line when displaying
| source context in error messages.
|
*/
'context_lines' => 5,
/*
|--------------------------------------------------------------------------
| Cache Compiled Templates
|--------------------------------------------------------------------------
|
| When enabled, compiled JQHTML templates will be cached to improve
| performance. Disable during development for immediate template updates.
|
*/
'cache_compiled' => env('JQHTML_CACHE', !env('APP_DEBUG', false)),
/*
|--------------------------------------------------------------------------
| Compiled Templates Path
|--------------------------------------------------------------------------
|
| Directory where compiled JQHTML templates should be stored.
|
*/
'compiled_path' => storage_path('jqhtml-compiled'),
/*
|--------------------------------------------------------------------------
| Enable Source Maps
|--------------------------------------------------------------------------
|
| Whether to generate source maps for compiled templates. Source maps
| increase compilation time slightly but provide better debugging.
|
*/
'enable_source_maps' => env('APP_DEBUG', false),
/*
|--------------------------------------------------------------------------
| Source Map Mode
|--------------------------------------------------------------------------
|
| How source maps should be generated:
| - 'inline': Embed source map directly in compiled file
| - 'external': Save source map as separate .map file
| - 'both': Generate both inline and external source maps
|
*/
'source_map_mode' => 'external',
];

View File

@@ -0,0 +1,201 @@
#!/usr/bin/env node
/**
* Example Node.js integration for sending JQHTML errors to Laravel
*
* This shows how to compile JQHTML templates and send any errors
* to a Laravel backend for proper error handling and display.
*/
import { Lexer, Parser, CodeGenerator, JQHTMLParseError } from '@jqhtml/parser';
import fs from 'fs';
import fetch from 'node-fetch'; // or axios, etc.
/**
* Compile a JQHTML template and handle errors
*/
async function compileTemplate(templatePath, laravelEndpoint) {
try {
// Read template file
const source = fs.readFileSync(templatePath, 'utf8');
const filename = templatePath;
// Compile with source maps
const lexer = new Lexer(source);
const tokens = lexer.tokenize();
const parser = new Parser(tokens, source, filename);
const ast = parser.parse();
const generator = new CodeGenerator();
const result = generator.generateWithSourceMap(ast, filename, source);
// Success - return compiled code
return {
success: true,
code: result.code,
sourceMap: result.map
};
} catch (error) {
// Format error for Laravel
const errorData = formatErrorForLaravel(error, templatePath);
// Send to Laravel if endpoint provided
if (laravelEndpoint) {
await sendErrorToLaravel(errorData, laravelEndpoint);
}
// Return error response
return {
success: false,
error: errorData
};
}
}
/**
* Format a JavaScript error for Laravel consumption
*/
function formatErrorForLaravel(error, templatePath) {
// Check if it's a JQHTML parse error with full details
if (error instanceof JQHTMLParseError || error.name === 'JQHTMLParseError') {
return {
message: error.message,
filename: error.filename || templatePath,
line: error.line,
column: error.column,
source: error.source,
suggestion: error.suggestion,
severity: error.severity || 'error',
endLine: error.endLine,
endColumn: error.endColumn
};
}
// Generic error - try to extract what we can
const errorData = {
message: error.message || String(error),
filename: templatePath,
severity: 'error'
};
// Try to parse location from error message
const locationMatch = error.message.match(/at line (\d+), column (\d+)/);
if (locationMatch) {
errorData.line = parseInt(locationMatch[1]);
errorData.column = parseInt(locationMatch[2]);
}
return errorData;
}
/**
* Send error data to Laravel backend
*/
async function sendErrorToLaravel(errorData, endpoint) {
try {
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
error: errorData,
context: {
node_version: process.version,
timestamp: new Date().toISOString(),
environment: process.env.NODE_ENV || 'development'
}
})
});
if (!response.ok) {
console.error('Failed to send error to Laravel:', response.statusText);
}
return response.json();
} catch (err) {
console.error('Error communicating with Laravel:', err);
}
}
/**
* Express/HTTP endpoint for template compilation
*
* This could be used in a Node.js service that compiles templates
* for a Laravel application.
*/
export function createCompilationEndpoint(app, laravelErrorEndpoint) {
app.post('/compile-jqhtml', async (req, res) => {
const { template, filename, source } = req.body;
try {
// Compile template
const lexer = new Lexer(source || template);
const tokens = lexer.tokenize();
const parser = new Parser(tokens, source || template, filename);
const ast = parser.parse();
const generator = new CodeGenerator();
const result = generator.generateWithSourceMap(
ast,
filename || 'template.jqhtml',
source || template
);
// Success response
res.json({
success: true,
compiled: {
code: result.code,
map: result.map
}
});
} catch (error) {
// Format error for Laravel
const errorData = formatErrorForLaravel(error, filename);
// Send error to Laravel for logging/display
if (laravelErrorEndpoint) {
sendErrorToLaravel(errorData, laravelErrorEndpoint);
}
// Return error response
res.status(400).json({
success: false,
error: errorData
});
}
});
}
/**
* CLI usage example
*/
if (import.meta.url === `file://${process.argv[1]}`) {
const templatePath = process.argv[2];
const laravelEndpoint = process.argv[3] || process.env.LARAVEL_ERROR_ENDPOINT;
if (!templatePath) {
console.error('Usage: node-integration.js <template-file> [laravel-endpoint]');
process.exit(1);
}
compileTemplate(templatePath, laravelEndpoint).then(result => {
if (result.success) {
console.log('Compilation successful!');
console.log('Code length:', result.code.length);
console.log('Source map:', result.sourceMap ? 'Generated' : 'Not generated');
} else {
console.error('Compilation failed!');
console.error(result.error);
process.exit(1);
}
});
}
// Export for use as module
export { compileTemplate, formatErrorForLaravel, sendErrorToLaravel };

View File

@@ -0,0 +1,187 @@
<?php
namespace Jqhtml\LaravelBridge;
use Throwable;
class JqhtmlErrorFormatter
{
protected $sourceMapPath;
protected $showSourceContext;
protected $sourceMapCache = [];
public function __construct(?string $sourceMapPath = null, bool $showSourceContext = true)
{
$this->sourceMapPath = $sourceMapPath;
$this->showSourceContext = $showSourceContext;
}
/**
* Format a JQHTML exception for Laravel's error handler
*/
public function format(JqhtmlException $exception): array
{
$data = [
'message' => $exception->getMessage(),
'exception' => get_class($exception),
'file' => $exception->getTemplateFile() ?? $exception->getFile(),
'line' => $exception->getTemplateLine() ?? $exception->getLine(),
'column' => $exception->getTemplateColumn(),
'error_type' => $exception->getErrorType(),
];
if ($exception->getSuggestion()) {
$data['suggestion'] = $exception->getSuggestion();
}
if ($this->showSourceContext && $exception->getSourceCode()) {
$data['source_context'] = $this->getSourceContext($exception);
}
// Try to resolve source map if we have a compiled file location
if ($exception->getCompiledFile() && $this->sourceMapPath) {
$data['source_map'] = $this->resolveSourceMap($exception);
}
return $data;
}
/**
* Format for JSON responses
*/
public function formatForJson(JqhtmlException $exception): array
{
return [
'error' => true,
'type' => 'jqhtml_error',
'message' => $exception->getMessage(),
'details' => $this->format($exception),
];
}
/**
* Get source code context for display
*/
protected function getSourceContext(JqhtmlException $exception): array
{
$source = $exception->getSourceCode();
$line = $exception->getTemplateLine();
$column = $exception->getTemplateColumn();
if (!$source || !$line) {
return [];
}
$lines = explode("\n", $source);
$lineIndex = $line - 1;
// Get 5 lines before and after for context
$contextSize = 5;
$start = max(0, $lineIndex - $contextSize);
$end = min(count($lines) - 1, $lineIndex + $contextSize);
$context = [];
for ($i = $start; $i <= $end; $i++) {
$context[] = [
'line_number' => $i + 1,
'content' => $lines[$i],
'is_error_line' => $i === $lineIndex,
'error_column' => $i === $lineIndex ? $column : null,
];
}
return $context;
}
/**
* Attempt to resolve source map for better error location
*/
protected function resolveSourceMap(JqhtmlException $exception): ?array
{
$compiledFile = $exception->getCompiledFile();
if (!$compiledFile || !file_exists($compiledFile)) {
return null;
}
// Look for source map file
$mapFile = $compiledFile . '.map';
if (!file_exists($mapFile)) {
// Try in the configured source map directory
if ($this->sourceMapPath) {
$mapFile = $this->sourceMapPath . '/' . basename($compiledFile) . '.map';
if (!file_exists($mapFile)) {
return null;
}
} else {
return null;
}
}
// Load and parse source map
if (!isset($this->sourceMapCache[$mapFile])) {
$mapContent = file_get_contents($mapFile);
$this->sourceMapCache[$mapFile] = json_decode($mapContent, true);
}
$sourceMap = $this->sourceMapCache[$mapFile];
if (!$sourceMap) {
return null;
}
return [
'version' => $sourceMap['version'] ?? null,
'sources' => $sourceMap['sources'] ?? [],
'file' => $sourceMap['file'] ?? null,
'has_mappings' => !empty($sourceMap['mappings']),
];
}
/**
* Convert a generic exception to JQHTML exception if it contains JQHTML error data
*/
public function wrapException(Throwable $exception): Throwable
{
// Check if the exception message contains JQHTML error data
$message = $exception->getMessage();
// Look for JQHTML error patterns
if (strpos($message, 'JQHTMLParseError') !== false ||
strpos($message, 'at line') !== false && strpos($message, 'column') !== false) {
// Try to extract error details from the message
$templateFile = null;
$templateLine = null;
$templateColumn = null;
// Extract location info from "at filename:line:column" format
if (preg_match('/at\s+([^:]+):(\d+):(\d+)/', $message, $matches)) {
$templateFile = $matches[1];
$templateLine = (int)$matches[2];
$templateColumn = (int)$matches[3];
} elseif (preg_match('/at\s+line\s+(\d+),\s+column\s+(\d+)/', $message, $matches)) {
$templateLine = (int)$matches[1];
$templateColumn = (int)$matches[2];
}
// Extract suggestion if present
$suggestion = null;
if (preg_match('/Did you\s+(.+?)\?/', $message, $matches)) {
$suggestion = 'Did you ' . $matches[1] . '?';
}
return new JqhtmlException(
$message,
$templateFile,
$templateLine,
$templateColumn,
null,
$suggestion,
'parse',
$exception->getCode(),
$exception
);
}
return $exception;
}
}

View File

@@ -0,0 +1,173 @@
<?php
namespace Jqhtml\LaravelBridge;
use Illuminate\View\ViewException;
use Throwable;
class JqhtmlException extends ViewException
{
protected $templateFile;
protected $templateLine;
protected $templateColumn;
protected $sourceCode;
protected $suggestion;
protected $errorType;
protected $compiledFile;
public function __construct(
string $message,
?string $templateFile = null,
?int $templateLine = null,
?int $templateColumn = null,
?string $sourceCode = null,
?string $suggestion = null,
string $errorType = 'parse',
int $code = 0,
?Throwable $previous = null
) {
// Call ViewException constructor with template file and line info
// ViewException signature: __construct($message, $code = 0, $severity = 1, $filename = '', $lineno = 0, $previous = null)
parent::__construct(
$message,
$code,
1, // severity
$templateFile ?? '',
$templateLine ?? 0,
$previous
);
$this->templateFile = $templateFile;
$this->templateLine = $templateLine;
$this->templateColumn = $templateColumn;
$this->sourceCode = $sourceCode;
$this->suggestion = $suggestion;
$this->errorType = $errorType;
}
/**
* Create from a JavaScript error object or JSON string
*/
public static function createFromJsError($jsError, ?string $compiledFile = null): self
{
if (is_string($jsError)) {
$jsError = json_decode($jsError, true);
}
return new self(
$jsError['message'] ?? 'Unknown JQHTML error',
$jsError['filename'] ?? $jsError['templateFile'] ?? null,
$jsError['line'] ?? null,
$jsError['column'] ?? null,
$jsError['source'] ?? null,
$jsError['suggestion'] ?? null,
$jsError['severity'] ?? 'error'
);
}
public function getTemplateFile(): ?string
{
return $this->templateFile;
}
public function getTemplateLine(): ?int
{
return $this->templateLine;
}
public function getTemplateColumn(): ?int
{
return $this->templateColumn;
}
public function getSourceCode(): ?string
{
return $this->sourceCode;
}
public function getSuggestion(): ?string
{
return $this->suggestion;
}
public function getErrorType(): string
{
return $this->errorType;
}
public function getCompiledFile(): ?string
{
return $this->compiledFile;
}
public function setCompiledFile(string $file): self
{
$this->compiledFile = $file;
return $this;
}
/**
* Get formatted error message with context
*/
public function getFormattedMessage(): string
{
$message = $this->getMessage();
if ($this->suggestion) {
$message .= "\n" . $this->suggestion;
}
if ($this->templateFile) {
$message .= sprintf(
"\n at %s:%d:%d",
$this->templateFile,
$this->templateLine ?? 0,
$this->templateColumn ?? 0
);
}
if ($this->sourceCode && $this->templateLine) {
$message .= "\n\n" . $this->getCodeSnippet();
}
return $message;
}
/**
* Get code snippet with error highlighting
*/
protected function getCodeSnippet(): string
{
if (!$this->sourceCode || !$this->templateLine) {
return '';
}
$lines = explode("\n", $this->sourceCode);
$lineIndex = $this->templateLine - 1;
// Show 3 lines before and after for context
$contextLines = 3;
$startLine = max(0, $lineIndex - $contextLines);
$endLine = min(count($lines) - 1, $lineIndex + $contextLines);
$snippet = '';
for ($i = $startLine; $i <= $endLine; $i++) {
$lineNum = $i + 1;
$isErrorLine = $i === $lineIndex;
$prefix = $isErrorLine ? '>' : ' ';
// Line number with padding
$lineNumStr = str_pad((string)$lineNum, 5, ' ', STR_PAD_LEFT);
$snippet .= sprintf("%s %s | %s\n", $prefix, $lineNumStr, $lines[$i]);
// Add pointer to error column
if ($isErrorLine && $this->templateColumn) {
$spaces = str_repeat(' ', $this->templateColumn + 8);
$carets = str_repeat('^', min(strlen($lines[$i]) - $this->templateColumn + 1, 20));
$snippet .= $spaces . $carets . "\n";
}
}
return $snippet;
}
}

View File

@@ -0,0 +1,93 @@
<?php
namespace Jqhtml\LaravelBridge;
use Illuminate\Contracts\View\View;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Throwable;
class JqhtmlExceptionRenderer
{
protected $formatter;
public function __construct(JqhtmlErrorFormatter $formatter)
{
$this->formatter = $formatter;
}
/**
* Render a JQHTML exception for display
*/
public function render(Request $request, JqhtmlException $exception): ?Response
{
if ($request->expectsJson()) {
return $this->renderJson($exception);
}
// For development, enhance the error page with JQHTML-specific info
if (app()->hasDebugModeEnabled()) {
return $this->renderDebugView($exception);
}
// In production, return null to let Laravel handle it normally
return null;
}
/**
* Render JSON error response
*/
protected function renderJson(JqhtmlException $exception): JsonResponse
{
return response()->json(
$this->formatter->formatForJson($exception),
500
);
}
/**
* Render debug view with enhanced JQHTML error information
*/
protected function renderDebugView(JqhtmlException $exception): ?Response
{
// Get formatted error data
$errorData = $this->formatter->format($exception);
// If using Laravel's Ignition error page, enhance it with our data
if (class_exists(\Spatie\LaravelIgnition\Facades\Flare::class)) {
\Spatie\LaravelIgnition\Facades\Flare::context('JQHTML Error', $errorData);
return null; // Let Ignition handle the rendering
}
// If using older Laravel or custom error handling
try {
// Check if we have a custom view
if (view()->exists('jqhtml::error')) {
return response()->view('jqhtml::error', [
'exception' => $exception,
'error_data' => $errorData,
], 500);
}
} catch (Throwable $e) {
// Fall back to letting Laravel handle it
}
return null;
}
/**
* Check if an exception should be handled by this renderer
*/
public function shouldHandle(Throwable $exception): bool
{
if ($exception instanceof JqhtmlException) {
return true;
}
// Check if it's a wrapped JQHTML error
$message = $exception->getMessage();
return strpos($message, 'JQHTMLParseError') !== false ||
strpos($message, 'JQHTML') !== false;
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace Jqhtml\LaravelBridge;
use Illuminate\Support\ServiceProvider;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
class JqhtmlServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register()
{
// Register the error formatter as a singleton
$this->app->singleton(JqhtmlErrorFormatter::class, function ($app) {
return new JqhtmlErrorFormatter(
$app['config']->get('jqhtml.source_maps_path', storage_path('jqhtml-sourcemaps')),
$app['config']->get('jqhtml.show_source_context', true)
);
});
// Register the exception renderer
$this->app->singleton(JqhtmlExceptionRenderer::class, function ($app) {
return new JqhtmlExceptionRenderer(
$app->make(JqhtmlErrorFormatter::class)
);
});
}
/**
* Bootstrap any application services.
*/
public function boot()
{
// Publish configuration
$this->publishes([
__DIR__ . '/../config/jqhtml.php' => config_path('jqhtml.php'),
], 'jqhtml-config');
// Extend Laravel's exception handler
$this->extendExceptionHandler();
}
/**
* Extend Laravel's exception handler to handle JQHTML exceptions
*/
protected function extendExceptionHandler()
{
$this->app->extend(ExceptionHandler::class, function ($handler, $app) {
// Hook into the exception rendering process
$handler->reportable(function (JqhtmlException $e) {
// Custom reporting logic if needed
});
$handler->renderable(function (JqhtmlException $e, $request) use ($app) {
if ($request->expectsJson()) {
return response()->json(
$app->make(JqhtmlErrorFormatter::class)->formatForJson($e),
500
);
}
// For web requests, let Laravel's default HTML handler display it
// with our enhanced error information
return null;
});
return $handler;
});
}
}

View File

@@ -0,0 +1,90 @@
<?php
namespace Jqhtml\LaravelBridge\Middleware;
use Closure;
use Illuminate\Http\Request;
use Jqhtml\LaravelBridge\JqhtmlErrorFormatter;
use Jqhtml\LaravelBridge\JqhtmlException;
use Throwable;
class JqhtmlErrorMiddleware
{
protected $formatter;
public function __construct(JqhtmlErrorFormatter $formatter)
{
$this->formatter = $formatter;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
try {
return $next($request);
} catch (Throwable $exception) {
// Check if this might be a JQHTML error
if ($this->isJqhtmlError($exception)) {
// Wrap it in our exception type for better handling
$wrapped = $this->formatter->wrapException($exception);
if ($wrapped instanceof JqhtmlException) {
// Add request context
$wrapped->setCompiledFile(
$this->getCompiledFileFromRequest($request)
);
throw $wrapped;
}
}
// Not a JQHTML error, rethrow as-is
throw $exception;
}
}
/**
* Check if an exception appears to be JQHTML-related
*/
protected function isJqhtmlError(Throwable $exception): bool
{
$message = $exception->getMessage();
return strpos($message, 'JQHTML') !== false ||
strpos($message, 'jqhtml') !== false ||
strpos($message, 'at line') !== false && strpos($message, 'column') !== false ||
strpos($message, 'Unclosed component') !== false ||
strpos($message, 'Mismatched tags') !== false;
}
/**
* Try to determine the compiled file from the request
*/
protected function getCompiledFileFromRequest(Request $request): ?string
{
// Check if the request has a reference to a compiled template
$route = $request->route();
if ($route && method_exists($route, 'getAction')) {
$action = $route->getAction();
// Look for JQHTML template reference in route action
if (isset($action['jqhtml_template'])) {
return $action['jqhtml_template'];
}
}
// Check request attributes
if ($request->has('_jqhtml_compiled')) {
return $request->get('_jqhtml_compiled');
}
return null;
}
}

View File

@@ -0,0 +1,219 @@
<?php
namespace Jqhtml\LaravelBridge\Tests;
use PHPUnit\Framework\TestCase;
use Jqhtml\LaravelBridge\JqhtmlException;
use Jqhtml\LaravelBridge\JqhtmlErrorFormatter;
class ExceptionFormattingTest extends TestCase
{
protected $formatter;
protected function setUp(): void
{
parent::setUp();
$this->formatter = new JqhtmlErrorFormatter();
}
public function testBasicExceptionCreation()
{
$exception = new JqhtmlException(
'Unclosed component definition',
'templates/test.jqhtml',
10,
15
);
$this->assertEquals('Unclosed component definition', $exception->getMessage());
$this->assertEquals('templates/test.jqhtml', $exception->getTemplateFile());
$this->assertEquals(10, $exception->getTemplateLine());
$this->assertEquals(15, $exception->getTemplateColumn());
}
public function testExceptionWithSuggestion()
{
$exception = new JqhtmlException(
'Unclosed component definition',
'templates/test.jqhtml',
10,
15,
null,
'Did you forget </Define:ComponentName>?'
);
$this->assertEquals('Did you forget </Define:ComponentName>?', $exception->getSuggestion());
$formatted = $exception->getFormattedMessage();
$this->assertStringContainsString('Did you forget', $formatted);
}
public function testExceptionFromJsError()
{
$jsError = [
'message' => 'Syntax error: unexpected token',
'filename' => 'app.jqhtml',
'line' => 42,
'column' => 8,
'suggestion' => 'Check for missing closing tags',
'severity' => 'error'
];
$exception = JqhtmlException::createFromJsError($jsError);
$this->assertEquals('Syntax error: unexpected token', $exception->getMessage());
$this->assertEquals('app.jqhtml', $exception->getTemplateFile());
$this->assertEquals(42, $exception->getTemplateLine());
$this->assertEquals(8, $exception->getTemplateColumn());
$this->assertEquals('Check for missing closing tags', $exception->getSuggestion());
}
public function testExceptionFromJsonString()
{
$json = json_encode([
'message' => 'Parse error',
'templateFile' => 'template.jqhtml',
'line' => 5,
'column' => 10
]);
$exception = JqhtmlException::createFromJsError($json);
$this->assertEquals('Parse error', $exception->getMessage());
$this->assertEquals('template.jqhtml', $exception->getTemplateFile());
$this->assertEquals(5, $exception->getTemplateLine());
$this->assertEquals(10, $exception->getTemplateColumn());
}
public function testFormatterBasicFormat()
{
$exception = new JqhtmlException(
'Test error',
'test.jqhtml',
20,
5
);
$formatted = $this->formatter->format($exception);
$this->assertArrayHasKey('message', $formatted);
$this->assertArrayHasKey('file', $formatted);
$this->assertArrayHasKey('line', $formatted);
$this->assertArrayHasKey('column', $formatted);
$this->assertArrayHasKey('error_type', $formatted);
$this->assertEquals('Test error', $formatted['message']);
$this->assertEquals('test.jqhtml', $formatted['file']);
$this->assertEquals(20, $formatted['line']);
$this->assertEquals(5, $formatted['column']);
}
public function testFormatterWithSourceContext()
{
$sourceCode = "line 1\nline 2\nline 3 with error\nline 4\nline 5";
$exception = new JqhtmlException(
'Error on line 3',
'test.jqhtml',
3,
10,
$sourceCode
);
$formatter = new JqhtmlErrorFormatter(null, true);
$formatted = $formatter->format($exception);
$this->assertArrayHasKey('source_context', $formatted);
$context = $formatted['source_context'];
$this->assertIsArray($context);
// Find the error line in context
$errorLine = null;
foreach ($context as $line) {
if ($line['is_error_line']) {
$errorLine = $line;
break;
}
}
$this->assertNotNull($errorLine);
$this->assertEquals(3, $errorLine['line_number']);
$this->assertEquals('line 3 with error', $errorLine['content']);
$this->assertEquals(10, $errorLine['error_column']);
}
public function testFormatterJsonFormat()
{
$exception = new JqhtmlException('JSON test error');
$json = $this->formatter->formatForJson($exception);
$this->assertArrayHasKey('error', $json);
$this->assertArrayHasKey('type', $json);
$this->assertArrayHasKey('message', $json);
$this->assertArrayHasKey('details', $json);
$this->assertTrue($json['error']);
$this->assertEquals('jqhtml_error', $json['type']);
$this->assertEquals('JSON test error', $json['message']);
}
public function testWrapGenericException()
{
$genericException = new \Exception(
'JQHTMLParseError: Unclosed tag at line 10, column 5'
);
$wrapped = $this->formatter->wrapException($genericException);
$this->assertInstanceOf(JqhtmlException::class, $wrapped);
$this->assertEquals(10, $wrapped->getTemplateLine());
$this->assertEquals(5, $wrapped->getTemplateColumn());
}
public function testWrapExceptionWithFilename()
{
$genericException = new \Exception(
'Error at component.jqhtml:15:20 - syntax error'
);
$wrapped = $this->formatter->wrapException($genericException);
$this->assertInstanceOf(JqhtmlException::class, $wrapped);
$this->assertEquals('component.jqhtml', $wrapped->getTemplateFile());
$this->assertEquals(15, $wrapped->getTemplateLine());
$this->assertEquals(20, $wrapped->getTemplateColumn());
}
public function testCodeSnippetGeneration()
{
$source = implode("\n", [
'line 1',
'line 2',
'line 3',
'error is here', // line 4
'line 5',
'line 6',
'line 7'
]);
$exception = new JqhtmlException(
'Error message',
'test.jqhtml',
4,
8,
$source
);
$formatted = $exception->getFormattedMessage();
// Should show context lines
$this->assertStringContainsString('line 3', $formatted);
$this->assertStringContainsString('error is here', $formatted);
$this->assertStringContainsString('line 5', $formatted);
// Should have error pointer
$this->assertStringContainsString('^', $formatted);
}
}

76
node_modules/@jqhtml/core/package.json generated vendored Executable file
View File

@@ -0,0 +1,76 @@
{
"name": "@jqhtml/core",
"version": "2.2.137",
"description": "Core runtime library for JQHTML",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.cjs",
"types": "./dist/index.d.ts"
},
"./debug": {
"import": "./dist/jqhtml-debug.esm.js",
"types": "./dist/debug.d.ts"
},
"./bundle": {
"import": "./dist/jqhtml-core.esm.js"
}
},
"scripts": {
"build": "rollup -c",
"build:tsc": "tsc",
"watch": "rollup -c -w",
"test": "jest",
"clean": "rm -rf dist"
},
"peerDependencies": {
"jquery": "^3.7.0"
},
"browser": true,
"publishConfig": {
"access": "public",
"registry": "https://privatenpm.hanson.xyz/"
},
"files": [
"dist",
"laravel-bridge",
"README.md",
"LLM_REFERENCE.md",
"LICENSE"
],
"keywords": [
"jquery",
"components",
"framework",
"jqhtml",
"templating"
],
"author": "JQHTML Team",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/jqhtml/jqhtml.git",
"directory": "packages/core"
},
"dependencies": {
"@rollup/plugin-node-resolve": "^16.0.1",
"@rollup/plugin-replace": "^6.0.2",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^12.1.4",
"@types/jest": "^29.5.14",
"@types/jquery": "^3.5.32",
"esbuild": "^0.25.9",
"jest": "^29.5.0",
"jsdom": "^26.1.0",
"playwright": "^1.53.2",
"rollup": "^4.49.0",
"rollup-plugin-dts": "^6.2.3",
"ts-jest": "^29.1.0",
"tslib": "^2.8.1",
"typescript": "^5.9.2"
},
"devDependencies": null
}