`. Name things what they ARE, not how they look.
+## JQHTML COMPONENTS (EXPANDED)
### Incremental Scaffolding
-**Undefined components work immediately** - they render as `
` with the component name as a class.
+**Undefined components work immediately** - they render as div with the component name as a class.
```blade
-
-
- Client Details
-
-
-
-
-
-
-
+
+
+
+
```
-**Renders as**:
-```html
-
-
Client Details
-
-
-
-
-
-
-
-```
+### Why on_load() Restrictions Exist
-Define components incrementally as needed. Page structure is separate from visual implementation.
-
-### Overview
-
-jQuery-based component system with templates (`.jqhtml`), classes (`Jqhtml_Component`), lifecycle management, and data binding.
-
-### Purpose
-
-Abstract logical elements into reusable components for consistent design and composable architecture.
-
-### Creating Components
-
-```bash
-php artisan rsx:app:component:create --name=user_card --path=rsx/theme/components
-```
-
-Creates `user_card.jqhtml` and `User_Card.js`.
-
-### Template Syntax
-
-Templates compile to JavaScript render functions at build time (not runtime parsing).
-
-**🔴 CRITICAL: `` IS the Element (Not a Wrapper)**
-
-The `` tag **becomes the HTML element itself**, not a wrapper around content. This is fundamentally different from React/Vue where components return wrapped JSX.
-
-```jqhtml
-
-
- Save Changes
-
-
-
-
-```
-
-```jqhtml
-
-
-
-
-
-
-
-
-
-```
-
-**Key Behaviors:**
-- **Defaults to `
`** if no `tag=""` attribute specified
-- **Component name and `Jqhtml_Component` classes added automatically** - never add these manually
-- **Bootstrap/styling classes go directly on `` tag** - not on inner elements
-- **Never add wrapper divs unless semantically necessary** (layout grids, semantic HTML)
-
-**Common `tag=""` Values:**
-- `tag="button"` - For clickable actions
-- `tag="a"` - For links (use with `href=""` attribute)
-- `tag="span"` - For inline text elements
-- `tag="article"` - For content cards
-- `tag="section"` - For page sections
-- `tag="ul"` or `tag="ol"` - For lists
-- `tag="form"` - For form components
-
-**Define Tag Attribute Types:**
-
-The `` tag supports three types of attributes:
-
-1. **`extends="Parent_Component"`** - Explicit template inheritance (alternative to class-based inheritance)
-2. **`$property=value`** - Set default `this.args` values in the template
-3. **Regular HTML attributes** - Standard attributes like `class`, `id`, `data-*`, etc.
-
-**Setting Default Args with $ Attributes:**
-
-Use `$` prefix on `` tag to set default `this.args` values:
-
-```jqhtml
-
-
-
-
<%= this.data.name %>
-
-
-```
-
-```javascript
-// No backing class needed - template configures component
-// When instantiated:
-// Component automatically has:
-// this.args.handler = User_Controller.fetch_user (function reference)
-// this.args.theme = "light" (string literal)
-// this.args.user_id = "123" (from instantiation)
-```
-
-**Quoted vs Unquoted $ Attributes:**
-
-- **Unquoted** = Raw JavaScript expression (function refs, identifiers)
-- **Quoted** = String literal
-
-```jqhtml
-
- $per_page=25
- $theme="dark"
->
- ...
-
-```
-
-**Use Cases:**
-
-- **Template-only components** - Full component behavior without JavaScript class
-- **Reusable configurations** - Base component with configurable controller
-- **Default values** - Sensible defaults overridable at instantiation
-
-**Component Definition**:
-```jqhtml
-
-
<%= this.data.name %>
-
- <%= this.data.status_label %>
-
-
-```
-
-**Interpolation**:
-- `<%= expression %>` - Escaped output (safe, use by default)
-- `<%== expression %>` - Unescaped HTML (only for pre-sanitized content)
-- `<%-- comment --%>` - Template comments (not rendered)
-
-**Control Flow** (compiles to JavaScript):
-```jqhtml
-<% if (this.data.active) { %>
-
Active User
-<% } %>
-
-<% for (let item of this.data.items) { %>
-
<%= item.name %>
-<% } %>
-```
-
-Similar to PHP templating - write JavaScript directly in templates for loops and conditionals.
-
-### JavaScript Class
-
-```javascript
-class User_Card extends Jqhtml_Component {
- async on_load() {
- // ✅ CRITICAL: ONLY modify this.data in on_load()
- // ✅ Use Ajax endpoint pattern: await Controller.method()
- this.data = await User_Controller.get_user_data({user_id: this.args.userId});
-
- // ❌ FORBIDDEN: No DOM manipulation in on_load()
- // this.$id('title').text(this.data.name); // WRONG
- // this.$.addClass('loaded'); // WRONG
-
- // ❌ FORBIDDEN: No other properties besides this.data
- // this.state = {loading: false}; // WRONG
- // this.config = {...}; // WRONG
- }
-
- on_ready() {
- // ✅ CORRECT: DOM manipulation in on_ready()
- this.$id('title').text(this.data.name);
- this.$.on('click', () => {
- console.log('Card clicked:', this.data.name);
- });
- }
-}
-```
-
-### 🔴 CRITICAL on_load() RULES
-
-**HARD REQUIREMENTS - NO EXCEPTIONS:**
-
-1. **ONLY modify `this.data`** - This is the ONLY property you may set in `on_load()`
- - ✅ CORRECT: `this.data = await Controller.method()`
- - ✅ CORRECT: `this.data.field = value`
- - ❌ FORBIDDEN: `this.state = ...` (not allowed)
- - ❌ FORBIDDEN: `this.config = ...` (not allowed)
- - ❌ FORBIDDEN: `this.cache = ...` (not allowed)
- - ❌ FORBIDDEN: `this.loaded = ...` (not allowed)
-
-2. **Use Ajax endpoint pattern** - NEVER use `$.ajax()` or `fetch()` for local server calls
- - ✅ CORRECT: `await Controller_Name.method_name(params)`
- - ❌ FORBIDDEN: `await $.ajax({url: '/api/...'})`
- - ❌ FORBIDDEN: `await fetch('/api/...').then(r => r.json())`
-
-3. **NO DOM manipulation** - Save ALL DOM operations for `on_ready()`
- - ❌ FORBIDDEN: `this.$id('element').text(...)`
- - ❌ FORBIDDEN: `this.$.addClass(...)`
- - ❌ FORBIDDEN: Any jQuery/DOM operations
-
-**Why these restrictions exist:**
-- `this.data` triggers automatic re-render when modified
-- DOM may not be fully initialized during `on_load()`
-- Siblings execute in parallel - unpredictable DOM state
+- this.data triggers automatic re-render when modified
+- DOM may not be fully initialized during on_load()
- Setting other properties breaks the component lifecycle
-**If you violate these rules, components will behave unpredictably.**
+### Execution Order
-### Component Lifecycle
-
-**Five-stage lifecycle**: **render → on_render → create → load → ready**
-
-#### Execution Flow
-
-**render** (top-down)
-- Template executes
-- DOM created
-- **First render: `this.data = {}` (empty)**
-- Not overridable
-
-**on_render()** (top-down, immediately after render)
-- Fires BEFORE children ready
-- Hide uninitialized elements
-- Set initial visual state
-- Prevents flash of uninitialized content
-
-**on_create()** (bottom-up)
-- Sync initialization
-- Set instance properties
-- Quick setup only
-
-**on_load()** (bottom-up, siblings in parallel)
-- **PRIMARY PURPOSE**: Load async data into `this.data`
-- **HARD REQUIREMENT**: ONLY modify `this.data` - NO other properties allowed
-- **CRITICAL: NO DOM manipulation** - Save for `on_ready()`
-- **NO child component access** - Children may not be ready yet
-- **Use Ajax endpoint pattern**: `await Controller.method()` NOT `$.ajax()`
-- **NO this.state, NO this.config, NO this.cache** - ONLY `this.data`
-- Siblings execute in parallel
-
-**on_ready()** (bottom-up)
-- All children guaranteed ready
-- Safe for DOM manipulation
-- Attach event handlers
-- Initialize plugins
-
-#### Depth-Ordered Execution
-
-- **Top-down**: render, on_render (parent before children)
-- **Bottom-up**: on_create, on_load, on_ready (children before parent)
-- **Parallel**: Siblings at same depth level process simultaneously during `on_load()`
-
-```javascript
-class User_Card extends Jqhtml_Component {
- on_render() {
- // Fires immediately after render, before children
- // Hide until data loads to prevent visual glitches
- this.$.css('opacity', '0');
- }
-
- async on_load() {
- // ✅ CORRECT - Use Ajax endpoint pattern
- this.data = await User_Controller.get_user_data({user_id: this.args.user_id});
-
- // ❌ FORBIDDEN - No $.ajax() calls
- // this.data = await $.ajax({url: '/api/users/...'}); // WRONG
-
- // ❌ FORBIDDEN - No fetch() calls to local server
- // this.data = await fetch('/api/users/...').then(r => r.json()); // WRONG
-
- // ❌ FORBIDDEN - No DOM manipulation
- // this.$id('title').text(this.data.name); // WRONG
- // this.$.addClass('loaded'); // WRONG
-
- // ❌ FORBIDDEN - No other properties besides this.data
- // this.state = {loading: false}; // WRONG
- // this.cache = this.data; // WRONG
- }
-
- on_ready() {
- // All children ready, safe for DOM
- this.$id('title').text(this.data.name);
- this.$.animate({opacity: 1}, 300);
- }
-}
-```
-
-### 🔴 CRITICAL: Loading State Pattern
-
-**NEVER manually call `this.render()` in `on_load()`** - The framework handles re-rendering automatically.
-
-**The correct pattern for loading states:**
-
-1. **Use simple `this.data.loaded` flag** at the END of `on_load()`
-2. **Check `!this.data.loaded` in template** for loading state
-3. **Trust automatic re-rendering** - Don't call `this.render()` manually
-4. **NO nested state objects** - Don't use `this.data.state.loading`
-
-**❌ INCORRECT Pattern** (common mistakes):
-```javascript
-class Product_List extends Jqhtml_Component {
- async on_load() {
- // ❌ WRONG: Setting loading state at START
- this.data.state = {loading: true};
- this.render(); // ❌ WRONG: Manual render call
-
- const response = await $.ajax({...}); // ❌ WRONG: $.ajax() instead of controller stub
-
- if (response.success) {
- this.data = {
- records: response.records,
- state: {loading: false} // ❌ WRONG: Complex nested state
- };
- }
- }
-}
-```
-
-Template for incorrect pattern:
-```jqhtml
-<% if (this.data.state && this.data.state.loading) { %>
-
- Loading...
-<% } %>
-```
-
-**✅ CORRECT Pattern:**
-```javascript
-class Product_List extends Jqhtml_Component {
- async on_load() {
- // ✅ CORRECT: NO loading flags at start
- // ✅ CORRECT: NO manual this.render() calls
-
- // ✅ CORRECT: Use Ajax endpoint pattern
- const response = await Product_Controller.list_products({
- page: 1,
- per_page: 25
- });
-
- // ✅ CORRECT: Populate this.data directly
- this.data.records = response.records;
- this.data.total = response.total;
- this.data.page = response.page;
-
- // ✅ CORRECT: Simple flag at END
- this.data.loaded = true;
-
- // ✅ Automatic re-render happens because this.data changed
- }
-}
-```
-
-Template for correct pattern:
-```jqhtml
-
- <% if (!this.data || !this.data.loaded) { %>
-
-
- #body>
- <#footer>
-
- #footer>
-
-```
-
-**Critical**: Cannot mix `content()` with named slots. If using named slots, ALL content must be in `<#slotname>` tags.
-
-### Slot-Based Template Inheritance (v2.2.108+)
-
-**When a component template contains ONLY slots (no HTML), it automatically inherits the parent class template structure.**
-
-This enables abstract base components with customizable slots, allowing child classes to extend parent templates without duplicating HTML structure.
-
-**Parent Template** (`DataGrid_Abstract.jqhtml`):
-```jqhtml
-
-
-
<%= content('header') %>
-
- <% for (let record of this.data.records) { %>
-
- #row>
-
-```
-
-**Result**: `Users_DataGrid` renders using `DataGrid_Abstract` structure with customized slot content.
-
-**Data Passing to Slots**:
-Parents pass data to slots via second parameter: `content('slotname', data)`
-
-```jqhtml
-
-<% for (let record of this.data.records) { %>
-
<%= content('row', record) %>
-<% } %>
-
-
-<#row>
-
<%= row.id %>
-
<%= row.name %>
-#row>
-```
-
-**Reserved Word Validation**:
-Slot names cannot be JavaScript reserved words. Parser rejects with fatal error:
-
-```jqhtml
-<#function>Content#function>
-<#if>Content#if>
-<#header>Content#header>
-```
-
-**Requirements**:
-- Child template contains ONLY slot definitions (no HTML wrapper)
-- JavaScript class extends parent class: `class Child extends Parent`
-- Parent template defines slots using `content('slotname')` or `content('slotname', data)`
-
-**Use Cases**:
-- Abstract data tables with customizable columns
-- Page layouts with variable content sections
-- Card variations with different headers/bodies/footers
-- Form patterns with customizable field sets
-
-### DOM Class Convention
-
-All components have `Jqhtml_Component` class automatically:
-
-```javascript
-const components = $('.Jqhtml_Component');
-if ($element.hasClass('Jqhtml_Component')) {
- const component = $element.component();
-}
-```
-
-Component names also added as classes for CSS targeting.
-
-### Getting Component Instance from DOM
-
-Use `.component()` method on jQuery objects to get component instance:
-
-```javascript
-// From element reference
-const card = $('#user-card-123').component();
-card.data.name; // Access component data
-card.reload_data(); // Call component methods
-
-// From any parent
-$('.container').find('.User_Card').each(function() {
- const component = $(this).component();
- console.log(component.args.user_id);
-});
-
-// Check if element is a component
-if ($element.hasClass('Jqhtml_Component')) {
- const component = $element.component();
- // Access component properties and methods
-}
-```
-
-### Lifecycle Event Callbacks
-
-External code can register callbacks:
-
-```javascript
-$('#my-component').component().on('ready', (component) => {
- console.log('Ready:', component);
-});
-
-// Chain events
-component
- .on('render', () => console.log('Rendered'))
- .on('ready', () => console.log('Ready'));
-```
-
-**Supported events**: `render`, `create`, `load`, `ready`
-
-### Lifecycle Manipulation Methods
-
-Components provide methods to control lifecycle and state after initial render.
-
-#### render()
-
-**Synchronous template re-render** - Updates DOM with current `this.data`.
-
-```javascript
-class Product_Card extends Jqhtml_Component {
- update_price(new_price) {
- this.data.price = new_price;
- this.render(); // Template re-renders immediately
- }
-}
-```
-
-- Re-executes template with current `this.data`
-- Calls `on_render()` hook after render
-- Does NOT trigger `on_load()` or `on_ready()`
-- Synchronous - completes immediately
-- Use when: Data changed, UI needs updating
-
-#### reload_data()
-
-**Re-fetch data and update** - Re-runs `on_load()`, re-renders, calls `on_ready()`.
-
-```javascript
-class User_Card extends Jqhtml_Component {
- async refresh_user_data() {
- await this.reload_data();
- console.log('Updated');
- }
-}
-```
-
-- Re-runs `on_load()` to fetch fresh data
-- Automatically re-renders template
-- Calls `on_ready()` after re-render
-- Returns promise - await for completion
-- Use when: Need fresh data from API
-
-#### reinitialize()
-
-**Full component reset** - Restarts entire lifecycle from stage 0.
-
-```javascript
-class Dashboard extends Jqhtml_Component {
- async switch_user(new_user_id) {
- this.args.user_id = new_user_id;
- await this.reinitialize();
- }
-}
-```
-
-- Destroys current component state
-- Re-runs full lifecycle: render → on_render → create → load → ready
-- Use when: Component needs complete rebuild
-- Rare use case - usually `reload_data()` or `render()` sufficient
-
-#### destroy()
-
-**Component destruction** - Removes component and all children from DOM.
-
-```javascript
-class Modal extends Jqhtml_Component {
- close() {
- this.destroy();
- }
-}
-```
-
-- Calls `on_destroy()` hook if defined
-- Recursively destroys all child components
-- Removes DOM element completely
-- Adds `_Component_Destroyed` class before removal
-- Synchronous - completes immediately
-
-**on_destroy() hook**:
-```javascript
-class Chat_Widget extends Jqhtml_Component {
- on_destroy() {
- this.socket.disconnect();
- console.log('Chat destroyed');
- }
-}
-```
-
-#### Synchronous Requirements
-
-**CRITICAL**: These lifecycle methods MUST be synchronous (no `async`, no `await`):
-
-| Method | Synchronous Required |
-|--------|---------------------|
-| `on_create()` | ✅ YES |
-| `on_render()` | ✅ YES |
-| `on_destroy()` | ✅ YES |
-| `on_load()` | ❌ NO (async allowed) |
-| `on_ready()` | ❌ NO (async allowed) |
-
-Framework needs predictable execution order for lifecycle coordination.
-
-```javascript
-// ✅ CORRECT
-class MyComponent extends Jqhtml_Component {
- on_create() {
- this.counter = 0;
- }
-
- on_destroy() {
- console.log('Destroyed');
- }
-}
-
-// ❌ WRONG
-class MyComponent extends Jqhtml_Component {
- async on_create() { // DON'T DO THIS
- await this.setup();
- }
-}
-```
-
-### Common Pitfalls and Non-Intuitive Behaviors
-
-**Most Important:**
-- **`` IS the element itself, not a wrapper** - Use `tag=""` to specify element type (defaults to `
`)
-- **Component name and `Jqhtml_Component` classes added automatically** - Never add these manually to `` tag
-
-**Data and Lifecycle:**
-- **`this.data` is the ONLY property allowed in `on_load()`** - NO `this.state`, `this.config`, etc.
-- **`this.data` starts as empty object `{}`** - Check `!this.data.loaded` or `Object.keys(this.data).length === 0` for loading state
-- **Components render twice if `on_load()` modifies `this.data`** - First with empty data, second after load
-- **Only modify `this.data` in `on_load()`, NEVER DOM** - DOM manipulation belongs in `on_ready()`
-- **Use Ajax endpoint pattern in `on_load()`** - `await Controller.method()` NOT `$.ajax()` or `fetch()`
-- **`on_render()` fires before children ready** - Use to hide uninitialized UI (prevent flash)
-
-**Loading Pattern Anti-Patterns:**
-- **NEVER call `this.render()` manually in `on_load()`** - Framework re-renders automatically when `this.data` changes
-- **NEVER use nested state objects** - Use flat `this.data.loaded = true` NOT `this.data.state.loading = false`
-- **NEVER set loading flags at START of `on_load()`** - Only set `this.data.loaded = true` at END
-- **Check loading in template** - Use `<% if (!this.data.loaded) %>` for loading state, trust automatic re-rendering
-
-**Attributes and Syntax:**
-- **Quoted vs unquoted `$` attributes behave completely differently** - Quoted = string literal, unquoted = JS expression
-- **Component names must be PascalCase** - `User_Card` not `user_card` in component definitions
-- **Filenames should be snake_case** - `user_card.jqhtml` not `UserCard.jqhtml`
-- **Blade tags are self-closing only** - `` works, `` doesn't
-
-**Synchronous Requirements:**
-- **`on_create()`, `on_render()`, `on_destroy()` MUST be synchronous** - No `async`/`await` allowed
-- **`render()` and `destroy()` are synchronous** - Complete before next line executes
-- **`reload_data()` and `reinitialize()` are async** - Must `await` for completion
-
-### RSpade Integration
-
-**⚠️ CRITICAL: Components require bundles to function**
-
-JavaScript classes and lifecycle methods **only execute when included in a bundle** rendered in the HTML. Without bundles:
-- No JavaScript execution
-- No `on_load()`, `on_ready()`, or other lifecycle methods
-- Components render as static HTML only
-
-**Bundle inclusion**:
-```php
-class Frontend_Bundle extends Rsx_Bundle_Abstract
-{
- public static function define(): array
- {
- return [
- 'include' => [
- 'jquery', // Required for jqhtml
- 'jqhtml', // jqhtml runtime
- 'rsx/theme/components', // All .jqhtml components in directory
- 'rsx/app/frontend', // Frontend-specific components
- ],
- ];
- }
-}
-```
-
-**Automatic discovery**: Components in bundle-included directories are auto-discovered and compiled.
-
-**Rendering bundles** (required for JavaScript to execute):
-```php
-return rsx_view('Frontend_Index', [
- 'bundle' => Frontend_Bundle::render()
-]);
-```
-
-```blade
-
-
-
- {!! Frontend_Bundle::render() !!}
-
-
- {{-- JS will execute --}}
-
-
-```
-
-**Blade usage**: Components self-closing only, attributes with `$` prefix passed to `this.args`.
-
-**Build-time compilation**: Templates compile to JavaScript functions with working sourcemaps. Bundles recompile automatically when you reload the page during development.
-
-### $redrawable Attribute - Lightweight Components
-
-**Convert any HTML element into a re-renderable component** using the `$redrawable` attribute. This parser-level transformation enables selective re-rendering without creating separate component classes.
-
-**How it works:**
-```blade
-
-
- Count: <%= this.data.count %>
-
-
-
-
- Count: <%= this.data.count %>
-
-```
-
-The `Redrawable` component renders as the specified tag type (default: `div`) while providing full component functionality including lifecycle hooks, data management, and selective re-rendering.
-
-**Selective re-rendering from parent:**
-```javascript
-class Dashboard extends Jqhtml_Component {
- async increment_counter() {
- this.data.count++;
- // Re-render only the counter element, not entire dashboard
- this.render('counter'); // Finds child with $id="counter"
- }
-}
-```
-
-**render(id) delegation syntax:**
-- Finds child element with matching `$id`
-- Verifies element is a component (has `$redrawable` or is proper component class)
-- Calls its `render()` method
-- Perfect for live data displays, counters, status indicators, real-time feeds
-
-**Error handling:**
-- Clear error if ID doesn't exist
-- Clear error if element isn't configured as component
-- Guides developers to correct usage
-
-**Use cases:**
-- Live counters and metrics
-- Status indicators that update independently
-- Real-time data feeds
-- Dynamic lists that change frequently
-- Any element needing selective updates without full page re-render
-
-### Component Documentation Standard
-
-Document jqhtml components using HTML comments at top of `.jqhtml` files. Treat `` like a function signature - document inputs (arguments), outputs (data/methods), and extension points (content blocks).
-
-**Basic format**:
-```html
-
-```
-
-**Simple components need minimal docs, complex ones need more detail.** Only document what's relevant. Complete documentation standard: `php artisan rsx:man jqhtmldoc`
-
-**Examples**:
-
-Simple component:
-```html
-
-
-```
-
-Complex component:
-```html
-
-```
-
----
-
-## MODELS & DATABASE
+## MODELS & DATABASE (EXPANDED)
### Model Definition
@@ -2031,97 +781,17 @@ CONTENT BLOCKS:
class User_Model extends Rsx_Model_Abstract
{
protected $table = 'users';
- protected $fillable = []; // Always empty
+ protected $fillable = []; // Always empty - no mass assignment
public static $enums = [
'status_id' => [
1 => ['constant' => 'STATUS_ACTIVE', 'label' => 'Active'],
- 2 => ['constant' => 'STATUS_INACTIVE', 'label' => 'Inactive'],
],
];
}
```
-### No Mass Assignment
-
-**Always explicit:**
-
-```php
-// ✅ CORRECT
-$user = new User_Model();
-$user->email = $email;
-$user->status_id = User_Model::STATUS_ACTIVE;
-$user->save();
-
-// ❌ WRONG
-$user = User_Model::create(['email' => $email]);
-```
-
-### Model Enums
-
-Type-safe field values with constants, labels, custom properties.
-
-#### Defining
-
-```php
-public static $enums = [
- 'status_id' => [
- 1 => [
- 'constant' => 'STATUS_PUBLISHED',
- 'label' => 'Published',
- 'badge' => 'bg-success', // Custom
- 'selectable' => true, // Show in dropdowns
- ],
- ],
-];
-```
-
-#### PHP Constants (Auto-Generated)
-
-```bash
-php artisan rsx:migrate:document_models
-```
-
-```php
-const STATUS_PUBLISHED = 1;
-$article->status_id = Article_Model::STATUS_PUBLISHED;
-```
-
-#### Magic Properties
-
-```php
-$article->status_id = 1;
-echo $article->status_label; // "Published"
-echo $article->status_badge; // "bg-success"
-```
-
-#### Static Methods
-
-```php
-Article_Model::status_id_enum(); // All definitions
-Article_Model::status_id_enum_select(); // Dropdown options
-Article_Model::status_id_enum_ids(); // All IDs
-```
-
-#### JavaScript Integration
-
-```javascript
-const article = await Article_Model.fetch(1);
-console.log(article.status_label);
-if (article.status_id === Article_Model.STATUS_PUBLISHED) { }
-```
-
-### Migrations
-
-**Forward-only** - No rollbacks, no `down()`.
-
-```bash
-php artisan make:migration:safe create_articles_table
-```
-
-**Never create manually** - always use `make:migration:safe`.
-
-#### Migration Example
+### Migration Example
```php
public function up()
@@ -2138,118 +808,72 @@ public function up()
}
```
-#### Commands
+## AJAX ENDPOINTS (EXPANDED)
-```bash
-php artisan migrate:begin # Start session
-php artisan migrate # Run
-php artisan migrate:commit # Commit
-php artisan migrate:rollback # Rollback to snapshot (within session)
-```
-
----
-
-## INTERNAL API (AJAX ENDPOINTS)
-
-### Creating Endpoints
+### Creating Detailed Endpoints
```php
-class Demo_Controller extends Rsx_Controller_Abstract
+#[Auth('Permission::anybody()')]
+#[Ajax_Endpoint]
+public static function get_user_data(Request $request, array $params = [])
{
- #[Auth('Permission::anybody()')]
- #[Ajax_Endpoint]
- public static function get_user_data(Request $request, array $params = [])
- {
- $user = User_Model::find($params['user_id']);
- return $user ? ['success' => true, 'user' => $user] : ['success' => false];
+ $user = User_Model::find($params['user_id']);
+ if (!$user) {
+ return ['success' => false, 'error' => 'User not found'];
}
+ return ['success' => true, 'user' => $user];
}
```
-### Calling from JavaScript
-
-Auto-generated stubs:
-
-```javascript
-const result = await Demo_Controller.get_user_data({user_id: 123});
-if (result.success) {
- console.log('User:', result.user);
-}
-```
-
-### Model Fetch System
-
-Enable secure fetching:
+### Model Fetch Security
```php
-class Product_Model extends Rsx_Model_Abstract
+#[Ajax_Endpoint_Model_Fetch]
+public static function fetch($id)
{
- #[Ajax_Endpoint_Model_Fetch]
- public static function fetch($id)
- {
- if (!RsxAuth::check()) return false;
- $product = static::find($id);
- return $product && Permission::can_view($product) ? $product : false;
+ if (!RsxAuth::check()) return false;
+ $product = static::find($id);
+ if ($product && !Permission::can_view_product($product)) {
+ return false;
}
+ return $product;
}
```
-**JavaScript:**
+## AUTHENTICATION (EXPANDED)
-```javascript
-const product = await Product_Model.fetch(1);
-const products = await Product_Model.fetch([1, 2, 3]); // Auto-splits
-console.log(product.status_label); // Enum properties available
-```
+### RsxAuth Full API
-**Security**: Each ID authorized individually. Models opt-in with attribute.
+```php
+use App\RSpade\Core\Auth\RsxAuth;
----
-
-## JAVASCRIPT DECORATORS
-
-### Creating Decorators
-
-Mark with `@decorator`:
-
-```javascript
-/**
- * @decorator
- */
-function logCalls(target, key, descriptor) {
- const original = descriptor.value;
- descriptor.value = function(...args) {
- console.log(`${key} called:`, args);
- return original.apply(this, args);
- };
- return descriptor;
+if (RsxAuth::check()) {
+ $user = RsxAuth::user(); // User model or null
+ $user_id = RsxAuth::id(); // User ID or null
+ $session = RsxAuth::session(); // Session model or null
}
+
+RsxAuth::login($user);
+RsxAuth::logout();
```
-### Using Decorators
+### Where to Check Authentication
-```javascript
-class UserService {
- @logCalls
- static async fetchUser(id) {
- return await fetch(`/api/users/${id}`).then(r => r.json());
- }
-}
-```
+1. **Controller pre_dispatch()** - Protects all routes in controller
+2. **/rsx/main.php pre_dispatch()** - Global/pattern-based protection
+3. **Route method** - Individual route protection
+
+## JAVASCRIPT DECORATORS (EXPANDED)
### Built-in @mutex Decorator
-**Ensures exclusive method execution**:
-
```javascript
class DataService {
- // Per-instance locking (each instance has its own lock)
@mutex
async save_data() {
// Only one save_data() call per instance at a time
}
- // Global locking by ID (all instances share the lock)
@mutex('critical_operation')
async update_shared_resource() {
// Only one update across all instances
@@ -2257,383 +881,161 @@ class DataService {
}
```
-### Rules
+## COMMANDS (EXPANDED)
-1. **Static and instance methods supported**
-2. **Whitelist required** - Must have `@decorator` in JSDoc
-3. **Build-time validation**
-
----
-
-## MODULE CREATION COMMANDS
-
-### Hierarchy
-
-**Module > Submodule > Feature > Subfeature**
+### Module Hierarchy
| Command | Creates | Route |
|---------|---------|-------|
-| `rsx:app:module:create ` | Module + index | `/name` |
-| `rsx:app:module:feature:create ` | Feature | `/m/f` |
-| `rsx:app:submodule:create ` | Submodule + layout | `/m/s` |
-| `rsx:app:subfeature:create ` | Subfeature | `/m/f/s` |
-| `rsx:app:component:create --name=x` | jqhtml component | N/A |
+| rsx:app:module:create | Module + index | /name |
+| rsx:app:module:feature:create | Feature | /m/f |
+| rsx:app:submodule:create | Submodule + layout | /m/s |
+| rsx:app:component:create --name=x | jqhtml component | N/A |
-### When to Use
-
-- **Module** - Major sections (frontend, backend, dashboard)
-- **Feature** - Distinct functionality (users, products, settings)
-- **Submodule** - Semi-independent with own layout
-- **Subfeature** - Break complex features into pages
-- **Component** - Reusable UI (cards, forms, modals)
-
----
-
-## CODE QUALITY & STANDARDS
-
-### rsx:check
+### Migration Commands
```bash
-php artisan rsx:check
+make:migration:safe name # Create whitelisted migration
+migrate:begin # Start snapshot session
+migrate # Run migrations
+migrate:commit # Commit changes
```
-**Blocks git commits** until violations resolved.
+## DEBUGGING TOOLS (EXPANDED)
-### Key Rules
-
-- **NoAnimationsRule** - No CSS animations/hover on non-actionable elements
-- **ThisUsageRule** - Enforce `that = this` in ES6 classes
-- **DuplicateCaseFilesRule** - Detect same-name different-case files
-- **MassAssignmentRule** - Prohibit `$fillable`
-- **VarUsageRule** - Prohibit `var`, require `let`/`const`
-
-### Professional UI Philosophy
-
-**RSX applications are serious business tools, not interactive toys.**
-
-Hover effects ONLY on actionable elements:
-- Buttons, links, form fields, images, table rows
-
-All other elements remain static. No hover on cards, containers, panels.
-
----
-
-## ERROR HANDLING
-
-### shouldnt_happen()
-
-For "impossible" conditions:
-
-```php
-if (!class_exists($expected)) {
- shouldnt_happen("Class {$expected} should be loaded");
-}
-```
-
-```javascript
-if (!element) {
- shouldnt_happen(`Element #${id} not found`);
-}
-```
-
-**Use for**: Code paths that should never execute if system works correctly.
-
----
-
-## CRITICAL: RSPADE CACHE SYSTEM - AUTOMATIC INVALIDATION
-
-### DO NOT RUN `rsx:clean` DURING DEVELOPMENT
-
-**❌ STOP: You almost certainly don't need to run this command.**
-
-**RSpade's caching system automatically invalidates when files change.** You do NOT need to clear caches after editing code.
-
-Traditional frameworks (Laravel, Symfony) require manual cache clearing:
-- Laravel: `php artisan cache:clear`
-- Symfony: `php bin/console cache:clear`
-
-**RSpade is different.** The cache system:
-- Auto-detects file changes
-- Invalidates cache entries automatically
-- Provides instant feedback (< 1 second)
-- Rebuilds only what's needed
-
-### The Cost of Running `rsx:clean`
-
-When you run `php artisan rsx:clean`:
-- ✅ **Entire cache is destroyed** (not just invalidated)
-- ⏱️ **30-60 second rebuild** on next request
-- ⚠️ **Timeouts during development**
-- 🔄 **No benefit** (changes already visible without clearing)
-
-### Correct Development Workflow
-
-```bash
-# ✅ CORRECT - Instant feedback
-1. Edit file (component, controller, view)
-2. Save file
-3. Reload page in browser
-4. See changes immediately (< 1 second)
-
-# ❌ WRONG - 30-60 second delay
-1. Edit file
-2. php artisan rsx:clean # DON'T DO THIS
-3. Reload page
-4. Wait 30-60 seconds for cache rebuild
-5. Finally see changes
-```
-
-### When to Actually Use rsx:clean
-
-ONLY run this command when:
-- ✅ Catastrophic cache corruption (extremely rare)
-- ✅ Framework itself was updated (`php artisan rsx:framework:pull` runs this automatically)
-- ✅ Explicitly instructed by error message or RSpade documentation
-
-NEVER use during normal development:
-- ❌ After editing a component
-- ❌ After fixing an error
-- ❌ After adding a route
-- ❌ After modifying a bundle
-- ❌ "Just to be safe"
-
-### Mental Model: Trust the Automation
-
-Think of RSpade cache like:
-- ✅ Modern browsers - auto-refresh on file changes (webpack/vite)
-- ✅ VS Code intellisense - auto-updates on file save
-- ✅ Linux kernel modules - auto-loaded when needed
-
-NOT like:
-- ❌ Laravel cache (requires manual clearing)
-- ❌ Compiled languages (require rebuild)
-- ❌ Docker containers (require restart)
-
-### Error Handling
-
-If you get an error after making changes:
-
-```bash
-# ❌ WRONG RESPONSE
-php artisan rsx:clean # Won't fix code errors!
-
-# ✅ CORRECT RESPONSE
-1. Read the error message
-2. Fix the error in your code
-3. Save the file
-4. Reload the page
-```
-
-The error will still be there after clearing cache because **it's a code error, not a cache error**.
-
-### Performance Impact
-
-Typical development session WITHOUT unnecessary rsx:clean:
-- 100 page reloads × 0.2 seconds = 20 seconds total
-- ✅ Fast iteration, high productivity
-
-Typical session WITH unnecessary rsx:clean (3 times):
-- 3 cache rebuilds × 40 seconds = 120 seconds wasted
-- 100 page reloads × 0.2 seconds = 20 seconds
-- ⏱️ Total: 140 seconds (2.3 minutes wasted waiting)
-
-### Summary for AI Assistants
-
-**Core principle**: RSpade's cache system is automatic and transparent. Treat it like the Linux kernel - it just works, don't touch it.
-
-**Default assumption**: If you're thinking about running `rsx:clean`, **don't**. The cache doesn't need clearing.
-
-**Exception handling**: Errors are in YOUR code, not the cache. Fix the code, don't clear the cache.
-
-## DEBUGGING TOOLS
-
-### rsx:clean - Emergency Cache Clear
-
-**⚠️ RARE USE ONLY** - Read the section above before using this command.
-
-Clears all caches and forces complete rebuild (30-60 seconds):
-
-```bash
-php artisan rsx:clean
-```
-
-**This is NOT a routine debugging tool.** Only use when specifically instructed or after framework updates.
-
-### rsx:debug
-
-**Use instead of curl.** Headless browser with full JS:
+### rsx:debug - Route Testing
```bash
php artisan rsx:debug /dashboard
-php artisan rsx:debug /dashboard --user=1 # Test as user
-php artisan rsx:debug /page --expect-element="#btn"
-php artisan rsx:debug /page --full # Max info
+php artisan rsx:debug /dashboard --user=1 # Test as specific user
+php artisan rsx:debug /page --expect-element="#btn" # Verify element exists
```
-### console_debug()
-
-Channel-based logging:
+### console_debug() - Channel-Based Logging
```php
console_debug("AUTH", "Login attempt", $user->id);
```
```javascript
-console_debug('AJAX', 'Request sent', url);
+console_debug('AJAX', 'Request sent', url, params);
```
-```bash
-CONSOLE_DEBUG_FILTER=AUTH php artisan serve
+## CODE QUALITY (EXPANDED)
+
+### Key Quality Rules
+
+- **NoAnimationsRule** - No CSS animations/hover on non-actionable elements
+- **DuplicateCaseFilesRule** - Detect same-name different-case files (critical)
+- **MassAssignmentRule** - Prohibit $fillable arrays
+
+## MAIN_ABSTRACT MIDDLEWARE (EXPANDED)
+
+### Execution Order
+
+1. Main::init() - Once at bootstrap
+2. Main::pre_dispatch() - Before every route
+3. Controller::pre_dispatch() - Before specific controller
+4. Route method - If both return null
+5. Main::unhandled_route() - If no route matches
+
+## FRAMEWORK DIVERGENCES FROM LARAVEL
+
+**CRITICAL**: RSpade diverges significantly from Laravel. Key differences:
+
+- **No Eloquent ORM features** - Use explicit field assignment only
+- **No mass assignment** - $fillable arrays always empty
+- **No resource routes** - Only explicit GET/POST routes
+- **No middleware classes** - Use Main_Abstract and pre_dispatch()
+- **No service providers** - Framework handles all bootstrapping
+- **No facades** - Direct class access only
+- **No dependency injection** - Static methods preferred
+
+When Laravel documentation conflicts with this guide, **this guide takes precedence**.
+
+## ATTRIBUTES PHILOSOPHY
+
+**PHP attributes are metadata only** - never define actual attribute classes.
+
+```php
+#[Route('/')] // Framework reads via reflection
+#[Auth('...')] // Never create Route or Auth classes
+#[Ajax_Endpoint] // Just metadata markers
```
-### db:query
+Add stubs to `.vscode/attribute-stubs.php` for IDE support, but **never create the actual classes**.
-```bash
-php artisan db:query "SELECT * FROM users" --json
+## SILENT SUCCESS PHILOSOPHY
+
+**Supplemental operations succeed silently** - Unix principle "no news is good news".
+
+- Primary operations report success (what user explicitly requested)
+- Supplemental checks/validations are silent when successful
+- Maintenance operations quiet unless they fail
+
+```php
+// Primary operation - reports success
+$this->info("Migration completed successfully");
+
+// Supplemental operation - silent on success
+$this->validate_cache(); // Only outputs if problem found
```
-**Always use `--json`** for compact output.
+## BROWSER ERROR LOGGING
----
+JavaScript errors auto-captured when `LOG_BROWSER_ERRORS=true` in `.env`.
+
+```javascript
+// Manual error logging
+Debugger.log_error('Component failed', error, {user_id: 123});
+
+// Automatic capture of uncaught errors
+// Batched and rate-limited to prevent spam
+```
## REFACTORING COMMANDS
```bash
-php artisan rsx:refactor:rename_php_class Old_Name New_Name
-php artisan rsx:refactor:rename_php_class_function Class old new
-php artisan rsx:refactor:sort_php_class_functions rsx/path/to/class.php
+# Rename PHP classes across entire codebase
+php artisan rsx:refactor:rename_php_class Old_Class_Name New_Class_Name
+
+# Automatically updates:
+# - All PHP file references
+# - All Blade file references
+# - Renames the file itself
```
-**#[Instantiatable]**: Applied to abstract classes to whitelist all child classes for instantiation by the framework.
+## BUNDLE include_routes
----
-
-## RSX:MAN DOCUMENTATION
-
-```bash
-php artisan rsx:man
-```
-
-**Topics**: ast_sourcecode_parsers, bundle_api, code_quality, coding_standards, console_debug, controller, enums, error_handling, jqhtml, js_decorators, migrations, model_fetch, routing, session
-
----
-
-## MAIN_ABSTRACT MIDDLEWARE
-
-`/rsx/main.php` for app-wide hooks:
+For scanning additional directories for routes:
```php
-class Main extends Main_Abstract
+class Admin_Bundle extends Rsx_Bundle_Abstract
{
- public function init() { } // Bootstrap once
- public function pre_dispatch($request, $params) { return null; } // Before routes
- public function unhandled_route($request, $params) { } // 404s
+ public static function define(): array
+ {
+ return [
+ 'include' => [...],
+ 'include_routes' => [
+ 'rsx/lib/admin_tools', // Scan for #[Route] attributes
+ ],
+ ];
+ }
}
```
-### Execution Order
+## DEVELOPMENT VS PRODUCTION
-1. `Main::init()` - Once at bootstrap
-2. `Main::pre_dispatch()` - Before every route
-3. `Controller::pre_dispatch()` - Before controller
-4. Route method - If both return null
+**Development (auto-detected)**:
+- Cache auto-invalidates on file changes
+- Detailed error messages with stack traces
+- Source maps enabled for debugging
+- Bundle recompilation on every request
----
+**Production**:
+- Pre-compiled bundles from cache
+- Minimal error messages
+- No source maps
+- Optimized performance
-## LARAVEL AVAILABILITY
-
-**Base Laravel available but discouraged:**
-- Hidden from IDE by default
-- Use only for specific circumstances
-- Everything should be built in `/rsx/`
-
-**Prefer RSX equivalents:**
-- RsxAuth instead of Auth
-- Rsx::Route() instead of route()
-- RSX models instead of direct Eloquent
-
----
-
-## DEPLOYMENT
-
-### Storage Directories
-
-| Directory | Action | Purpose |
-|-----------|--------|---------|
-| `system/storage/rsx-build/` | **INCLUDE** | Compiled assets and build cache |
-| `system/storage/rsx-tmp/` | **EXCLUDE** | Temporary caches (auto-recreates) |
-| `system/storage/rsx-locks/` | **CLEAR** | Process locks |
-
-### Checklist
-
-1. `php artisan rsx:check` - Fix all violations
-2. Include `system/storage/rsx-build/` in deployment
-3. Exclude `system/storage/rsx-tmp/` from deployment
-4. Clear `system/storage/rsx-locks/` before starting services
-
-**Note**: Bundles and framework indexes compile automatically on first page load in production.
-
----
-
-## GATEKEEPER
-
-Password protection for dev/preview:
-
-```env
-GATEKEEPER_ENABLED=true
-GATEKEEPER_PASSWORD=your_password
-GATEKEEPER_TITLE="Development Preview"
-```
-
-Disable in production: `GATEKEEPER_ENABLED=false`
-
----
-
-## VS CODE EXTENSION
-
-- **LLMDIRECTIVE folding** - Auto-collapse generated code
-- **RSX:USE protection** - Warnings for auto-generated sections
-- **Smart namespace updates** - Auto-update on file moves
-- **Integrated formatting** - Docker-based
-
-Install from `.vscode/extensions/`.
-
----
-
-## CRITICAL REMINDERS
-
-1. **Fail loud** - No fallback, no silent failures
-2. **Static by default** - Use static unless instances needed
-3. **No defensive coding** - Trust core classes exist
-4. **One way to do things** - No alternative paths
-5. **Path-agnostic** - Reference by name, not path
-6. **Bundles required** - JS won't execute without
-7. **Run rsx:check** - Before committing
-8. **Use RsxAuth** - Never Laravel Auth or $_SESSION
-9. **No mass assignment** - Explicit only
-10. **Forward-only migrations** - No rollbacks
-
----
-
-## GETTING HELP
-
-**Documentation:**
-```bash
-php artisan rsx:man
-php artisan rsx:routes
-php artisan list rsx
-```
-
-**Debugging:**
-```bash
-php artisan rsx:debug /page
-php artisan rsx:check
-php artisan db:query "SQL" --json
-```
-
-**Development:**
-```bash
-php artisan rsx:app:module:create name
-php artisan rsx:app:component:create
-php artisan rsx:clean # Clear caches (troubleshooting)
-```
+Never manually switch modes - framework auto-detects based on environment.