From 376a02f88aecd62950eb1b01dd3eac9ceeefefc8 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 22 Oct 2025 04:07:04 +0000 Subject: [PATCH] Reduce docs.dist/CLAUDE.dist.md from 74KB to 26KB for LLM efficiency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- docs/CLAUDE.dist-13k.md | 537 +++++++ docs/CLAUDE.dist.md | 3088 ++++++++++----------------------------- 2 files changed, 1282 insertions(+), 2343 deletions(-) create mode 100755 docs/CLAUDE.dist-13k.md diff --git a/docs/CLAUDE.dist-13k.md b/docs/CLAUDE.dist-13k.md new file mode 100755 index 000000000..7215b1280 --- /dev/null +++ b/docs/CLAUDE.dist-13k.md @@ -0,0 +1,537 @@ +# RSpade Framework - AI/LLM Development Guide + +**PURPOSE**: Essential directives for AI/LLM assistants developing RSX applications with RSpade. + +## What is RSpade? + +**Visual Basic-like development for PHP/Laravel.** Think: VB6 apps → VB6 runtime → Windows = RSX apps → RSpade runtime → Laravel. + +**Philosophy**: Modern anti-modernization. While JavaScript fragments into complexity and React demands quarterly paradigm shifts, RSpade asks: "What if coding was easy again?" Business apps need quick builds and easy maintenance, not bleeding-edge architecture. + +**Important**: RSpade is built on Laravel but diverges significantly. Do not assume Laravel patterns work in RSX without verification. + +**Terminology**: **RSpade** = Complete framework | **RSX** = Your application code in `/rsx/` + +--- + +## CRITICAL RULES + +### 🔴 Framework Updates + +```bash +php artisan rsx:framework:pull # 5-minute timeout required +``` + +Updates take 2-5 minutes. Includes code pull, manifest rebuild, bundle recompilation. **For AI: Always use 5-minute timeout.** + +### 🔴 Fail Loud - No Silent Fallbacks + +**ALWAYS fail visibly.** No redundant fallbacks, silent failures, or alternative code paths. + +```php +// ❌ CATASTROPHIC +try { $clean = Sanitizer::sanitize($input); } +catch (Exception $e) { $clean = $input; } // DISASTER + +// ✅ CORRECT +$clean = Sanitizer::sanitize($input); // Let it throw +``` + +**SECURITY-CRITICAL**: If sanitization/validation/auth fails, NEVER continue. Always throw immediately. + +### 🔴 No Defensive Coding + +Core classes ALWAYS exist. Never check. + +```javascript +// ❌ BAD: if (typeof Rsx !== 'undefined') { Rsx.Route(...) } +// ✅ GOOD: Rsx.Route(...) +``` + +### 🔴 Static-First Philosophy + +Classes are namespacing tools. Use static unless instances needed (models, resources). Avoid dependency injection. + +### 🔴 Git Workflow - Framework is READ-ONLY + +**NEVER modify `/var/www/html/system/`** - It's like node_modules or the Linux kernel. + +- **App repo**: `/var/www/html/.git` (you control) +- **Framework**: `/var/www/html/system/` (submodule, don't touch) +- **Your code**: `/var/www/html/rsx/` (all changes here) + +**Commit discipline**: ONLY commit when explicitly asked. Commits are milestones, not individual changes. + +### 🔴 DO NOT RUN `rsx:clean` + +**RSpade's cache auto-invalidates on file changes.** Running `rsx:clean` causes 30-60 second rebuilds with zero benefit. + +**When to use**: Only on catastrophic corruption, after framework updates (automatic), or when explicitly instructed. + +**Correct workflow**: Edit → Save → Reload browser → See changes (< 1 second) + +--- + +## NAMING CONVENTIONS + +**Enforced by `rsx:check`**: + +| Context | Convention | Example | +|---------|------------|---------| +| PHP Methods/Variables | `underscore_case` | `user_name` | +| PHP Classes | `Like_This` | `User_Controller` | +| JavaScript Classes | `Like_This` | `User_Card` | +| Files | `lowercase_underscore` | `user_controller.php` | +| Database Tables | `lowercase_plural` | `users` | +| Constants | `UPPERCASE` | `MAX_SIZE` | + +**Critical**: Never create same-name different-case files. + +--- + +## DIRECTORY STRUCTURE + +``` +/var/www/html/ +├── rsx/ # YOUR CODE +│ ├── app/ # Modules +│ ├── models/ # Database models +│ ├── public/ # Static files (web-accessible) +│ ├── resource/ # Framework-ignored +│ └── theme/ # Global assets +└── system/ # FRAMEWORK (read-only) +``` + +### Special Directories (Path-Agnostic) + +**`resource/`** - ANY directory named this is framework-ignored. Store helpers, docs, third-party code. Exception: `/rsx/resource/config/` IS processed. + +**`public/`** - ANY directory named this is web-accessible, framework-ignored. 5min cache, 30d with `?v=`. + +### Path-Agnostic Loading + +Classes found by name, not path. No imports needed. + +```php +$user = User_Model::find(1); // Framework finds it +// NOT: use Rsx\Models\User_Model; // Auto-generated +``` + +--- + +## CONFIGURATION + +**Two-tier system**: +- **Framework**: `/system/config/rsx.php` (never modify) +- **User**: `/rsx/resource/config/rsx.php` (your overrides) + +Merged via `array_merge_deep()`. Common overrides: `development.auto_rename_files`, `bundle_aliases`, `console_debug`. + +--- + +## ROUTING & CONTROLLERS + +```php +class Frontend_Controller extends Rsx_Controller_Abstract +{ + #[Auth('Permission::anybody()')] + #[Route('/', methods: ['GET'])] + public static function index(Request $request, array $params = []) + { + return rsx_view('Frontend_Index', [ + 'bundle' => Frontend_Bundle::render() + ]); + } +} +``` + +**Rules**: Only GET/POST allowed. Use `:param` syntax. All routes MUST have `#[Auth]`. + +### #[Auth] Attribute + +```php +#[Auth('Permission::anybody()')] // Public +#[Auth('Permission::authenticated()')] // Require login +#[Auth('Permission::has_role("admin")')] // Custom +``` + +**Controller-wide**: Add to `pre_dispatch()`. Multiple attributes = all must pass. + +### Type-Safe URLs + +```php +// PHP +Rsx::Route('User_Controller', 'show')->url(['id' => 123]); + +// JavaScript (identical) +Rsx.Route('User_Controller', 'show').url({id: 123}); +``` + +--- + +## BLADE & VIEWS + +```blade +@rsx_id('Frontend_Index') {{-- Every view starts with this --}} + + {{-- Adds view class --}} + +@rsx_include('Component_Name') {{-- Include by name, not path --}} +``` + +### SCSS Pairing + +```scss +/* frontend_index.scss - Same directory as view */ +.Frontend_Index { /* Matches @rsx_id */ + .content { padding: 20px; } +} +``` + +--- + +## JAVASCRIPT + +### Auto-Initialization + +```javascript +class Frontend_Index { + static async on_app_ready() { + // DOM ready + } + + static async on_jqhtml_ready() { + // Components ready + } +} +``` + +**CRITICAL**: JavaScript only executes when bundle rendered. + +--- + +## BUNDLE SYSTEM + +**One bundle per page required.** + +```php +class Frontend_Bundle extends Rsx_Bundle_Abstract +{ + public static function define(): array + { + return [ + 'include' => [ + 'jquery', // Required + 'lodash', // Required + 'rsx/theme/variables.scss', // Order matters + 'rsx/app/frontend', // Directory + 'rsx/models', // For JS stubs + ], + ]; + } +} +``` + +Auto-compiles on page reload in development. + +```blade + + {!! Frontend_Bundle::render() !!} + +``` + +--- + +## JQHTML COMPONENTS + +### Philosophy + +For mechanical thinkers who see structure, not visuals. Write `` not `
`. Name what things ARE. + +### Template Syntax + +**🔴 CRITICAL: `` IS the element, not a wrapper** + +```jqhtml + + + Save + + + + +``` + +**Interpolation**: `<%= escaped %>` | `<%== unescaped %>` | `<% javascript %>` + +### 🔴 CRITICAL on_load() Rules + +**ONLY modify `this.data` - NO other properties, NO DOM manipulation** + +```javascript +class User_Card extends Jqhtml_Component { + async on_load() { + // ✅ ONLY this.data modifications + this.data = await User_Controller.get_data({id: this.args.id}); + this.data.loaded = true; + + // ❌ FORBIDDEN + // this.state = {loading: false}; // NO other properties + // this.$id('title').text(...); // NO DOM manipulation + // $.ajax({url: '/api/...'}); // NO direct ajax + } + + on_ready() { + // ✅ DOM manipulation here + this.$id('title').text(this.data.name); + } +} +``` + +### Lifecycle + +1. **render** → Template executes, `this.data = {}` (empty) +2. **on_render()** → Hide uninitialized UI (sync) +3. **on_create()** → Quick setup (sync) +4. **on_load()** → Fetch data into `this.data` (async) +5. **on_ready()** → DOM manipulation safe (async) + +**Double-render**: If `on_load()` modifies `this.data`, component renders twice (empty → populated). + +### Loading Pattern + +```javascript +async on_load() { + const result = await Product_Controller.list({page: 1}); + this.data.products = result.products; + this.data.loaded = true; // Simple flag at END +} +``` + +```jqhtml +<% if (!this.data.loaded) { %> + Loading... +<% } else { %> + +<% } %> +``` + +**NEVER call `this.render()` in `on_load()` - automatic re-render happens.** + +### Attributes + +- **`$quoted="string"`** → String literal +- **`$unquoted=expression`** → JavaScript expression +- **`$id="name"`** → Scoped element ID + +```javascript +this.$id('button').on('click', ...); // Access scoped element +``` + +### Common Pitfalls + +1. `` IS the element - use `tag=""` attribute +2. `this.data` starts empty `{}` +3. ONLY modify `this.data` in `on_load()` +4. Use `Controller.method()` not `$.ajax()` +5. Blade components self-closing only +6. `on_create/render/destroy` must be sync + +### Bundle Integration Required + +```blade +{!! Frontend_Bundle::render() !!} {{-- Required for JS --}} + {{-- Now JS executes --}} +``` + +For advanced topics: `php artisan rsx:man jqhtml` + +--- + +## MODELS & DATABASE + +### No Mass Assignment + +```php +// ✅ CORRECT +$user = new User_Model(); +$user->email = $email; +$user->save(); + +// ❌ WRONG +User_Model::create(['email' => $email]); +``` + +### Enums + +```php +public static $enums = [ + 'status_id' => [ + 1 => ['constant' => 'STATUS_ACTIVE', 'label' => 'Active'], + ], +]; + +// Usage +$user->status_id = User_Model::STATUS_ACTIVE; +echo $user->status_label; // "Active" +``` + +### Migrations + +**Forward-only, no rollbacks.** + +```bash +php artisan make:migration:safe create_users_table +php artisan migrate:begin +php artisan migrate +php artisan migrate:commit +``` + +--- + +## AJAX ENDPOINTS + +```php +#[Ajax_Endpoint] +public static function get_data(Request $request, array $params = []) +{ + return ['success' => true, 'data' => ...]; +} +``` + +```javascript +const result = await Demo_Controller.get_data({user_id: 123}); +``` + +### Model Fetch + +```php +#[Ajax_Endpoint_Model_Fetch] +public static function fetch($id) +{ + if (!RsxAuth::check()) return false; + return static::find($id); +} +``` + +```javascript +const user = await User_Model.fetch(1); +``` + +--- + +## AUTHENTICATION + +**Always use RsxAuth**, never Laravel Auth or $_SESSION. + +```php +RsxAuth::check(); // Is authenticated +RsxAuth::user(); // User model +RsxAuth::id(); // User ID +``` + +Sessions persist 365 days. Never implement "Remember Me". + +--- + +## JAVASCRIPT DECORATORS + +```javascript +/** @decorator */ +function logCalls(target, key, descriptor) { /* ... */ } + +class Service { + @logCalls + @mutex + async save() { /* ... */ } +} +``` + +--- + +## COMMANDS + +### Module Creation + +```bash +rsx:app:module:create # /name +rsx:app:module:feature:create # /m/f +rsx:app:component:create --name=x # Component +``` + +### Development + +```bash +rsx:check # Code quality +rsx:debug /page # Test routes +rsx:man # Documentation +db:query "SQL" --json +``` + +### Debugging + +- **rsx_dump_die()** - Debug output +- **console_debug("CHANNEL", ...)** - Channel logging +- **CONSOLE_DEBUG_FILTER=CHANNEL** - Filter output + +--- + +## ERROR HANDLING + +```php +if (!$expected) { + shouldnt_happen("Class {$expected} missing"); +} +``` + +Use for "impossible" conditions that indicate broken assumptions. + +--- + +## CODE QUALITY + +**Professional UI**: Hover effects ONLY on buttons, links, form fields. Static elements remain static. + +Run `rsx:check` before commits. Enforces naming, prohibits animations on non-actionable elements. + +--- + +## MAIN_ABSTRACT MIDDLEWARE + +Optional `/rsx/main.php`: + +```php +class Main extends Main_Abstract +{ + public function init() { } // Bootstrap once + public function pre_dispatch($request, $params) { return null; } // Before routes + public function unhandled_route($request, $params) { } // 404s +} +``` + +--- + +## KEY REMINDERS + +1. **Fail loud** - No silent failures +2. **Static by default** - Unless instances needed +3. **Path-agnostic** - Reference by name +4. **Bundles required** - For JavaScript +5. **Use RsxAuth** - Never Laravel Auth +6. **No mass assignment** - Explicit only +7. **Forward migrations** - No rollbacks +8. **Don't run rsx:clean** - Cache auto-invalidates +9. **All routes need #[Auth]** - No exceptions + +--- + +## GETTING HELP + +```bash +php artisan rsx:man # Detailed docs +php artisan list rsx # All commands +``` + +**Topics**: bundle_api, jqhtml, routing, migrations, console_debug, model_fetch, vs_code_extension, deployment, framework_divergences + +**Remember**: RSpade prioritizes simplicity and rapid development. When in doubt, choose the straightforward approach. \ No newline at end of file diff --git a/docs/CLAUDE.dist.md b/docs/CLAUDE.dist.md index d2316c92f..bbd85fc60 100644 --- a/docs/CLAUDE.dist.md +++ b/docs/CLAUDE.dist.md @@ -24,546 +24,139 @@ This separation ensures: # RSpade Framework - AI/LLM Development Guide -**PURPOSE**: This document provides comprehensive directives for AI/LLM assistants developing RSX applications with the RSpade framework. +**PURPOSE**: Essential directives for AI/LLM assistants developing RSX applications with RSpade. ## What is RSpade? -RSpade is a **Visual Basic-like development environment for PHP/Laravel** that prioritizes simplicity, straightforwardness, and rapid development. Think of it as: -- **VB6 apps** run in the **VB6 runtime** which runs in **Windows** -- **RSX apps** run in the **RSpade runtime** which runs in **Laravel** +**Visual Basic-like development for PHP/Laravel.** Think: VB6 apps → VB6 runtime → Windows = RSX apps → RSpade runtime → Laravel. -**Core Philosophy**: RSpade is the modern anti-modernization framework - a deliberate rejection of contemporary complexity in favor of straightforward, understandable patterns. While the JavaScript ecosystem fragments into ever-more-complex build pipelines and the React world demands you learn new paradigms every quarter, RSpade asks: "What if we just made coding easy again?" Like Visual Basic democratized Windows development in the 90s by hiding COM complexity, RSpade hides Laravel's architectural complexity behind simple, intuitive patterns. This isn't nostalgia - it's recognition that **business applications don't need bleeding-edge architecture; they need to be built quickly and maintained easily**. +**Philosophy**: Modern anti-modernization. While JavaScript fragments into complexity and React demands quarterly paradigm shifts, RSpade asks: "What if coding was easy again?" Business apps need quick builds and easy maintenance, not bleeding-edge architecture. -**Important**: RSpade is built on Laravel but diverges significantly from Laravel conventions. Do not assume Laravel patterns work in RSX without verification. +**Important**: RSpade is built on Laravel but diverges significantly. Do not assume Laravel patterns work in RSX without verification. -### Terminology: RSpade vs RSX - -**RSpade** - The complete framework (formal product name) - -**RSX** - Your application code in `/rsx/`. Also appears in: -- Class names (e.g., `Rsx_Controller_Abstract`) -- Commands (e.g., `rsx:check`) -- Configuration keys (e.g., `config('rsx.bundle_aliases')`) +**Terminology**: **RSpade** = Complete framework | **RSX** = Your application code in `/rsx/` --- -## FRAMEWORK UPDATES +## CRITICAL RULES -### Updating Framework +### 🔴 Framework Updates ```bash -php artisan rsx:framework:pull +php artisan rsx:framework:pull # 5-minute timeout required ``` -**IMPORTANT: Framework updates take 2-5 minutes.** When the user requests a framework update, execute the command with a 5-minute timeout to ensure it completes successfully. The update process includes: -- Pulling latest framework code from upstream -- Cleaning and rebuilding the manifest -- Recompiling all bundles -- Updating dependencies +Updates take 2-5 minutes. Includes code pull, manifest rebuild, bundle recompilation. **For AI: Always use 5-minute timeout.** -**For AI/LLM assistants:** Always use a 5-minute timeout when executing framework updates to prevent premature termination. +### 🔴 Fail Loud - No Silent Fallbacks -### Detailed Documentation - -```bash -php artisan rsx:man rspade_upstream -``` - ---- - -## CRITICAL DIRECTIVES - -### Fail Loud - No Silent Fallbacks - -**CRITICAL: Always fail loudly and visibly when errors occur.** - -- **NO redundant fallback systems** - Fail clearly, not silently -- **NO silent failures** - Errors must be immediately apparent -- **Exception handlers ONLY format display** - Never provide alternative functionality -- **NO alternative code paths** - ONE deterministic way - -Examples: -- ❌ BAD: JavaScript fails → fall back to server-side rendering -- ✅ GOOD: JavaScript fails → display clear error message -- ❌ BAD: New API fails → silently use old API -- ✅ GOOD: New API fails → throw exception with clear error -- ❌ BAD: Sanitizer fails → skip and continue -- ✅ GOOD: Sanitizer fails → throw exception and halt - -**SECURITY-CRITICAL**: If any subsystem fails (sanitization, validation, authentication), NEVER continue. Always throw immediately. +**ALWAYS fail visibly.** No redundant fallbacks, silent failures, or alternative code paths. ```php -// ❌ CATASTROPHIC - Silent security failure -try { - $clean = Sanitizer::sanitize($user_input); -} catch (Exception $e) { - $clean = $user_input; // DISASTER -} +// ❌ CATASTROPHIC +try { $clean = Sanitizer::sanitize($input); } +catch (Exception $e) { $clean = $input; } // DISASTER -// ✅ CORRECT - Fail loudly -$clean = Sanitizer::sanitize($user_input); // Let it throw +// ✅ CORRECT +$clean = Sanitizer::sanitize($input); // Let it throw ``` -### No Defensive Coding for Core Classes +**SECURITY-CRITICAL**: If sanitization/validation/auth fails, NEVER continue. Always throw immediately. -**CRITICAL: Core framework classes are ALWAYS present. Never check if they exist.** +### 🔴 No Defensive Coding -- **Assume core classes exist** -- **NO typeof checks** - Don't check `if (typeof Rsx !== 'undefined')` -- **Just use directly** - `Rsx.Route(...)` not wrapped in checks -- **Core classes**: Rsx, Rsx_Cache, all framework-bundled classes +Core classes ALWAYS exist. Never check. -Examples: -- ❌ BAD: `if (typeof Rsx !== 'undefined') { Rsx.Route(...) }` -- ✅ GOOD: `Rsx.Route(...)` -- ❌ BAD: `try { a = 2 } catch(e) { console.log('Failed') }` -- ✅ GOOD: `a = 2` - -**The build system guarantees core classes are present. Trust it.** - -### Static-First Philosophy - -**Classes are static by default** - Classes are namespacing tools, not OOP for OOP's sake: - -- **Use static methods** - Unless class represents data needing multiple instances -- **Avoid dependency injection** - Direct access everywhere -- **Exceptions**: Models (database rows), resources, service connectors needing mocking - -This keeps code simple and straightforward - the Visual Basic philosophy. - -## GIT WORKFLOW - -### 🔴 CRITICAL: Framework Code is READ-ONLY - -**AI AGENTS: You must NEVER modify files in `/var/www/html/system/` or commit to the framework submodule** - -The framework code in `/var/www/html/system/` is managed by the RSpade team. It's equivalent to the Linux kernel or node_modules - external code that you don't modify directly. - -**Forbidden actions in `/var/www/html/system/`:** -- ❌ NEVER edit framework files -- ❌ NEVER commit framework changes -- ❌ NEVER remove framework files from git tracking -- ❌ NEVER stage framework changes -- ❌ NEVER fix issues in framework code (report them instead) - -**Only exception:** Updating framework via `php artisan rsx:framework:pull` (this is automated and safe) - -### Git Repository Structure - -**Application repo:** `/var/www/html/.git` (your code, you control) -**Framework submodule:** `/var/www/html/system/` (read-only, managed by RSpade team - DO NOT TOUCH) - -### Commit Discipline - -**NEVER commit unless explicitly asked** - You are UNQUALIFIED to decide when to commit. ONLY commit when the user explicitly says "commit" or gives a clear instruction to commit. Commits are MAJOR MILESTONES (like completing all history homework), NOT individual changes (like changing one answer on one assignment). Wait for the user to tell you when to commit. - -### Working Directory Rules - -**All code changes shall be made in `/var/www/html/rsx` for application code.** Do not make any changes outside of `/var/www/html/rsx`. - -**Run artisan commands from `/var/www/html`:** - -```bash -cd /var/www/html -php artisan rsx:check # ✅ Framework commands run from here +```javascript +// ❌ BAD: if (typeof Rsx !== 'undefined') { Rsx.Route(...) } +// ✅ GOOD: Rsx.Route(...) ``` -**Commit from `/var/www/html`:** +### 🔴 Static-First Philosophy -```bash -cd /var/www/html # ✅ CORRECT -git add -A -git commit -m "Snapshot: description" -git push origin master -``` +Classes are namespacing tools. Use static unless instances needed (models, resources). Avoid dependency injection. +### 🔴 Git Workflow - Framework is READ-ONLY + +**NEVER modify `/var/www/html/system/`** - It's like node_modules or the Linux kernel. + +- **App repo**: `/var/www/html/.git` (you control) +- **Framework**: `/var/www/html/system/` (submodule, don't touch) +- **Your code**: `/var/www/html/rsx/` (all changes here) + +**Commit discipline**: ONLY commit when explicitly asked. Commits are milestones, not individual changes. + +### 🔴 DO NOT RUN `rsx:clean` + +**RSpade's cache auto-invalidates on file changes.** Running `rsx:clean` causes 30-60 second rebuilds with zero benefit. + +**When to use**: Only on catastrophic corruption, after framework updates (automatic), or when explicitly instructed. + +**Correct workflow**: Edit → Save → Reload browser → See changes (< 1 second) --- ## NAMING CONVENTIONS -All conventions **enforced by code quality checker** (`php artisan rsx:check`). +**Enforced by `rsx:check`**: -### PHP Naming +| Context | Convention | Example | +|---------|------------|---------| +| PHP Methods/Variables | `underscore_case` | `user_name` | +| PHP Classes | `Like_This` | `User_Controller` | +| JavaScript Classes | `Like_This` | `User_Card` | +| Files | `lowercase_underscore` | `user_controller.php` | +| Database Tables | `lowercase_plural` | `users` | +| Constants | `UPPERCASE` | `MAX_SIZE` | -| Context | Convention | Examples | -|---------|------------|----------| -| Methods/Variables | `underscore_case` | `user_name`, `calculate_total()` | -| Constants | `UPPERCASE_WITH_UNDERSCORES` | `MAX_UPLOAD_SIZE` | -| RSX Classes | `Like_This_With_Underscores` | `Frontend_Index_Controller` | -| Files | `lowercase_with_underscores` | `frontend_index_controller.php` | -| Temp Files | `name-temp.extension` | `test-temp.php` | - -### JavaScript Naming - -| Context | Convention | Examples | -|---------|------------|----------| -| Classes | `Like_This_With_Underscores` | `Demo_Index`, `User_Card` | -| Methods/Variables | `underscore_case` | `init_sidebar()`, `user_data` | -| Constants | `UPPERCASE_WITH_UNDERSCORES` | `API_URL` | -| Files | `lowercase_with_underscores` | `demo_index.js` | - -### Database Naming - -| Element | Convention | Examples | -|---------|------------|----------| -| Tables | `lowercase_plural` | `users`, `products` | -| Primary Keys | Always `id` | `id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY` | -| Foreign Keys | `table_id` suffix | `user_id`, `site_id` | -| Booleans | `is_` prefix | `is_active`, `is_verified` | -| Timestamps | `_at` suffix | `created_at`, `updated_at` | - -### Class Naming Philosophy - -All class names must identify: -1. **Module** (e.g., `Frontend_`, `Backend_`) -2. **Feature** (e.g., `Users`, `Settings`) -3. **Type** (e.g., `_Controller`, `_Model`) - -Example: `Frontend_Users_Index_Controller` → Module: Frontend, Feature: Users, Subfeature: Index, Type: Controller - -### Suffix Enforcement - -Classes extending abstracts **must match suffix**: -- Extends `Rsx_Controller_Abstract` → ends in `_Controller` -- Extends `Rsx_Model_Abstract` → ends in `_Model` -- Extends `Rsx_Bundle_Abstract` → ends in `_Bundle` - -Enforced automatically by `rsx:check`. - -### Critical Filename Rule - -**NEVER create files with same name but different case** (e.g., `Helpers.php` and `helpers.php`). Breaks Windows/macOS compatibility. Flagged as critical error by `rsx:check`. +**Critical**: Never create same-name different-case files. --- ## DIRECTORY STRUCTURE -### Directory Structure - -**CRITICAL**: The framework uses a split directory structure. - -**Important**: The structure shown below is **conventional, not mandatory**. Files in `/rsx/` are path-agnostic (referenced by name, not path) and can be organized however you prefer. The only special directories with framework-enforced behavior are `resource/` and `public/` (explained below). - ``` -/var/www/html/ # Project root -├── rsx/ # YOUR APPLICATION CODE -│ ├── app/ # Application modules -│ │ ├── frontend/ # Public website (/) -│ │ ├── backend/ # Admin panel (/admin) -│ │ ├── dashboard/ # User dashboard -│ │ ├── login/ # Authentication -│ │ └── api/ # API endpoints -│ ├── lib/ # Shared libraries -│ ├── models/ # Database models -│ ├── public/ # Static files (5min cache, 30d with ?v=) -│ │ └── public_ignore.json # Patterns blocked from HTTP -│ ├── resource/config/ # User config overrides -│ │ └── rsx.php # Merged with /system/config/rsx.php -│ ├── theme/ # Global theme assets -│ │ ├── variables.scss -│ │ ├── layouts/ -│ │ └── components/ -│ ├── main.php # App-wide middleware -│ └── permission.php # Authorization methods -│ -└── system/ # FRAMEWORK CODE (do not modify) - ├── app/RSpade/ # RSpade framework runtime - ├── config/ # Framework configuration - ├── storage/ # Build artifacts and caches - ├── bin/ # CLI scripts - ├── docs.dist/ # Documentation templates - └── artisan # Laravel's artisan CLI +/var/www/html/ +├── rsx/ # YOUR CODE +│ ├── app/ # Modules +│ ├── models/ # Database models +│ ├── public/ # Static files (web-accessible) +│ ├── resource/ # Framework-ignored +│ └── theme/ # Global assets +└── system/ # FRAMEWORK (read-only) ``` -### /system/app/RSpade/ - Framework Runtime +### Special Directories (Path-Agnostic) -**DO NOT MODIFY** - Framework runtime that executes your RSX application. +**`resource/`** - ANY directory named this is framework-ignored. Store helpers, docs, third-party code. Exception: `/rsx/resource/config/` IS processed. -### Special Directory Names - Path-Agnostic Rules +**`public/`** - ANY directory named this is web-accessible, framework-ignored. 5min cache, 30d with `?v=`. -**These rules apply ANYWHERE in `/rsx/`**, not just at specific paths: +### Path-Agnostic Loading -#### `resource/` Directories (Framework-Ignored) - -**ANY directory named `resource/` is ignored by the framework** - files will not be loaded or processed. - -Use for: -- Helper PHP files not following RSX conventions -- External reference files and documentation -- Third-party code that doesn't fit RSX patterns -- Any files you want to store but not execute - -Examples: -- `/rsx/resource/` - Project-level resources -- `/rsx/app/module/resource/` - Module-specific resources -- `/rsx/resource/config/` - Configuration overrides (exception: config files ARE processed) - -**Exception**: `/rsx/resource/config/` files ARE processed for configuration overrides. - -#### `public/` Directories (Web-Accessible) - -**ANY directory named `public/` is:** -- Ignored by framework (files not loaded/processed) -- Exposed via HTTP (downloadable by browsers) -- Served with caching headers (5min default, 30 days with `?v=` query string) - -Use for: -- Static files (images, PDFs, downloads) -- Uploaded user content -- Any files that need direct HTTP access - -Examples: -- `/rsx/public/` - Project-level static files (accessible at `/filename`) -- `/rsx/app/module/public/` - Module-specific static files -- `/rsx/public/public_ignore.json` - Patterns blocked from HTTP access - -**Security**: Use `public_ignore.json` to block sensitive files like README.md from HTTP access. - -#### `vendor/` Directory - -**`/vendor/` is for Composer dependencies only** - excluded from automatic processing. - ---- - -## CONFIGURATION SYSTEM - -### Two-Tier Configuration - -**Framework Config**: `/system/config/rsx.php` - Framework defaults, **NEVER modify** - -**User Config**: `/rsx/resource/config/rsx.php` - Your overrides, merged via `array_merge_deep()` - -### Merging Example +Classes found by name, not path. No imports needed. ```php -// Framework: /system/config/rsx.php -'bundle_aliases' => ['core' => CoreBundle::class] - -// User: /rsx/resource/config/rsx.php -'bundle_aliases' => ['my-app' => MyAppBundle::class] - -// Result: Both present -config('rsx.bundle_aliases'); // ['core' => CoreBundle, 'my-app' => MyAppBundle] +$user = User_Model::find(1); // Framework finds it +// NOT: use Rsx\Models\User_Model; // Auto-generated ``` -### Common Overrides - -- `development.auto_rename_files` -- `code_quality.root_whitelist` -- `bundle_aliases` -- `gatekeeper` -- `console_debug` - --- -## FILE ORGANIZATION PHILOSOPHY +## CONFIGURATION -### Files of Similar Concern Together +**Two-tier system**: +- **Framework**: `/system/config/rsx.php` (never modify) +- **User**: `/rsx/resource/config/rsx.php` (your overrides) -Related files (view, JS, SCSS, controller) stored **in same folder, same base name**, differing by extension: - -``` -/rsx/app/dashboard/ -├── dashboard_controller.php -├── dashboard_bundle.php -├── dashboard_layout.blade.php -├── dashboard_index.blade.php -├── dashboard_index.js -├── dashboard_index.scss -``` - -### Hierarchy Describes Logical Position - -- **Outer layer**: Shared layouts, global components -- **Middle layers**: Modules and features -- **Inner layers**: Specific pages - -### Theme and Library Directories - -- **`/rsx/theme/`** - Reusable SCSS and jqhtml components -- **`/rsx/lib/`** - Reusable JavaScript utilities +Merged via `array_merge_deep()`. Common overrides: `development.auto_rename_files`, `bundle_aliases`, `console_debug`. --- -## PATH-AGNOSTIC CLASS LOADING - -**CRITICAL CONCEPT**: Classes found by name, not path. - -### How It Works - -1. Framework automatically indexes `/rsx/` -2. Classes referenced by name -3. Framework locates files -4. Automatic loading on first reference - -### Implications - -- **Move files freely** - Location doesn't matter -- **No path management** - No `use` statements needed (auto-generated) -- **Name-based referencing** -- **Uniqueness required** - -### Why This Matters - -- **Path independence** - Move files without breaking references -- **Refactoring safety** - Rename files without updating imports -- **Simplicity** - No import management overhead +## ROUTING & CONTROLLERS ```php -// ✅ GOOD -$user = User_Model::find(1); -$route = Rsx::Route('Frontend_Index_Controller'); - -// ❌ BAD -use Rsx\Models\User_Model; // Auto-generated -require_once 'models/user_model.php'; // Never -``` - ---- - -## BLADE DIRECTIVES - -### @rsx_id - Define View Identity - -**Every Blade view starts with `@rsx_id()`** - path-independent identity: - -```blade -@rsx_id('Frontend_Index') - - - - - @yield('content') - - -``` - -**Convention**: Match related class names. - -### @rsx_include - Include Views - -**Include by RSX ID, not path:** - -```blade -{{-- ✅ GOOD --}} -@rsx_include('Layouts_Main_Footer') - -{{-- ❌ BAD --}} -@include('theme.layouts.footer') -``` - -### Why This Matters - -- **Path independence** - Move files without breaking includes -- **Refactoring safety** - Rename without updating references -- **Clear dependencies** - See view relationships - ---- - -## SCSS FILE CONVENTIONS - -### Paired with Views - -SCSS files paired with views: -1. Match view name (snake_case) -2. Wrap rules in class matching @rsx_id -3. Store in same directory - -```blade - -@rsx_id('Frontend_Index') -``` - -```scss -/* frontend_index.scss */ -.Frontend_Index { - .content { - padding: 20px; - } -} -``` - -### Why Wrap in Class? - -- **Scoping** - Prevents conflicts -- **Specificity** - Applies only when view rendered -- **Organization** - Clear ownership - -### rsx_body_class() Integration - -`{{ rsx_body_class() }}` adds RSX ID as body class: - -```blade - -``` - ---- - -## JAVASCRIPT FILE CONVENTIONS - -### Auto-Initialization Lifecycle - -#### on_app_ready() - -DOM ready, scripts loaded: - -```javascript -class Frontend_Index { - static async on_app_ready() { - // jQuery's $(document).ready() equivalent - Frontend_Index.init_sidebar(); - } -} -``` - -#### on_jqhtml_ready() - -All jqhtml components loaded: - -```javascript -class Dashboard_Index { - static async on_jqhtml_ready() { - // Safe to interact with components - const card = $('#user-card').component(); - } -} -``` - -### JavaScript Requires Bundles - -**CRITICAL**: JS only executes when bundle rendered in HTML. - -- Without bundles → No JS execution -- JSON responses → No JS -- Views without bundles → No JS - -Always render bundle: - -```php -return rsx_view('Frontend_Index', [ - 'bundle' => Frontend_Bundle::render() -]); -``` - -**View Functions:** -- `rsx_view()` - Use RSX ID (Upper_Case matching class name) -- Laravel's `view()` - Not recommended, use `rsx_view()` instead - -**Naming Pattern:** -- Filename: `frontend_index.blade.php` (snake_case) -- RSX ID: `@rsx_id('Frontend_Index')` (Upper_Case) -- Controller: `Frontend_Index_Controller` (Upper_Case) -- View call: `rsx_view('Frontend_Index')` (Uses RSX ID, not filename) - ---- - -## ROUTING SYSTEM - -### Route Definition - -```php -class Frontend_Index_Controller extends Rsx_Controller_Abstract +class Frontend_Controller extends Rsx_Controller_Abstract { #[Auth('Permission::anybody()')] #[Route('/', methods: ['GET'])] @@ -576,159 +169,74 @@ class Frontend_Index_Controller extends Rsx_Controller_Abstract } ``` -### Route Parameters +**Rules**: Only GET/POST allowed. Use `:param` syntax. All routes MUST have `#[Auth]`. -Use `:param` syntax (not Laravel's `{param}`): +### #[Auth] Attribute ```php -#[Route('/users/:id')] -public static function show_user(Request $request, array $params = []) -{ - $user_id = $params['id']; // Route parameter -} +#[Auth('Permission::anybody()')] // Public +#[Auth('Permission::authenticated()')] // Require login +#[Auth('Permission::has_role("admin")')] // Custom ``` -### Method Restrictions +**Controller-wide**: Add to `pre_dispatch()`. Multiple attributes = all must pass. -**Only GET and POST allowed.** PUT, PATCH, DELETE throw exceptions. - -### Type-Safe URL Generation - -**PHP:** -```php -$url = Rsx::Route('Frontend_Index_Controller')->url(); -$url = Rsx::Route('Users_Controller', 'show')->url(['id' => 123]); -Rsx::Route('Dashboard_Controller')->navigate(); // Redirect -``` - -**JavaScript:** -```javascript -const url = Rsx.Route('Frontend_Index_Controller').url(); -Rsx.Route('Dashboard_Controller').navigate(); // Sets location.href -``` - -### $params Array - -Contains route parameters and query string parameters: +### Type-Safe URLs ```php -// Route: /users/:id -// Request: /users/123?tab=profile +// PHP +Rsx::Route('User_Controller', 'show')->url(['id' => 123]); -$params['id']; // "123" (route) -$params['tab']; // "profile" (query) +// JavaScript (identical) +Rsx.Route('User_Controller', 'show').url({id: 123}); ``` --- -## AUTHENTICATION & AUTHORIZATION +## BLADE & VIEWS -### RsxAuth API +```blade +@rsx_id('Frontend_Index') {{-- Every view starts with this --}} -**Always use RsxAuth** - Never Laravel Auth or $_SESSION: + {{-- Adds view class --}} -```php -use App\RSpade\Core\Auth\RsxAuth; - -RsxAuth::check(); // Is authenticated -RsxAuth::user(); // User model or null -RsxAuth::id(); // User ID or null -RsxAuth::session(); // Session model or null +@rsx_include('Component_Name') {{-- Include by name, not path --}} ``` -### Session System +### SCSS Pairing -**Sessions always persist (365 days)** - Never implement "Remember Me" checkboxes. - -### #[Auth] Attribute - -**CRITICAL: All routes MUST have at least one `#[Auth]` attribute.** - -#### Syntax - -```php -#[Auth('Permission::method_name()', - message: 'Optional error', - redirect: '/optional/url')] // HTTP only -``` - -#### Built-in Methods - -```php -Permission::anybody() // Public routes -Permission::authenticated() // Require login -``` - -#### Examples - -```php -#[Auth('Permission::anybody()')] -#[Route('/')] -public static function index(Request $request, array $params = []) {} - -#[Auth('Permission::authenticated()', redirect: '/login')] -#[Route('/dashboard')] -public static function dashboard(Request $request, array $params = []) {} - -// Multiple requirements (all must pass) -#[Auth('Permission::authenticated()')] -#[Auth('Permission::has_role("admin")')] -#[Route('/admin')] -public static function admin_panel(Request $request, array $params = []) {} -``` - -#### Controller-Wide Protection - -```php -#[Auth('Permission::authenticated()')] -public static function pre_dispatch(Request $request, array $params = []) -{ - return null; // Continue +```scss +/* frontend_index.scss - Same directory as view */ +.Frontend_Index { /* Matches @rsx_id */ + .content { padding: 20px; } } ``` -#### Custom Permission Methods +--- -Add to `/rsx/permission.php`: +## JAVASCRIPT -```php -class Permission extends Permission_Abstract -{ - public static function has_role(Request $request, array $params, string $role): mixed - { - if (!RsxAuth::check()) return false; - return RsxAuth::user()->user_role_id === constant('User_Model::ROLE_' . strtoupper($role)); +### Auto-Initialization + +```javascript +class Frontend_Index { + static async on_app_ready() { + // DOM ready + } + + static async on_jqhtml_ready() { + // Components ready } } ``` -Usage: `#[Auth('Permission::has_role("admin")')]` - -#### Ajax Endpoint Auth - -For Ajax, `redirect` ignored. Returns JSON: - -```json -{ - "success": false, - "error": "Login required", - "error_type": "permission_denied" -} -``` - -### Where to Check Authentication - -1. **Controller pre_dispatch()** - All routes in controller -2. **`/rsx/main.php` pre_dispatch()** - URL pattern matching -3. **Route method** - Route-specific +**CRITICAL**: JavaScript only executes when bundle rendered. --- ## BUNDLE SYSTEM -**One bundle mandatory per page.** Contains all assets and model stubs. - -### Creating Bundle +**One bundle per page required.** ```php class Frontend_Bundle extends Rsx_Bundle_Abstract @@ -737,25 +245,484 @@ class Frontend_Bundle extends Rsx_Bundle_Abstract { return [ 'include' => [ - 'jquery', // Required - 'lodash', // Required - 'bootstrap5_src', // Bundle alias - 'rsx/theme/variables.scss', // Specific file - 'rsx/app/frontend', // Directory (all files) - 'rsx/models', // Models (for JS stubs) + 'jquery', // Required + 'lodash', // Required + 'rsx/theme/variables.scss', // Order matters + 'rsx/app/frontend', // Directory + 'rsx/models', // For JS stubs ], ]; } } ``` +Auto-compiles on page reload in development. + +```blade + + {!! Frontend_Bundle::render() !!} + +``` + +--- + +## JQHTML COMPONENTS + +### Philosophy + +For mechanical thinkers who see structure, not visuals. Write `` not `
`. Name what things ARE. + +### Template Syntax + +**🔴 CRITICAL: `` IS the element, not a wrapper** + +```jqhtml + + + Save + + + + +``` + +**Interpolation**: `<%= escaped %>` | `<%== unescaped %>` | `<% javascript %>` + +### 🔴 CRITICAL on_load() Rules + +**ONLY modify `this.data` - NO other properties, NO DOM manipulation** + +```javascript +class User_Card extends Jqhtml_Component { + async on_load() { + // ✅ ONLY this.data modifications + this.data = await User_Controller.get_data({id: this.args.id}); + this.data.loaded = true; + + // ❌ FORBIDDEN + // this.state = {loading: false}; // NO other properties + // this.$id('title').text(...); // NO DOM manipulation + // $.ajax({url: '/api/...'}); // NO direct ajax + } + + on_ready() { + // ✅ DOM manipulation here + this.$id('title').text(this.data.name); + } +} +``` + +### Lifecycle + +1. **render** → Template executes, `this.data = {}` (empty) +2. **on_render()** → Hide uninitialized UI (sync) +3. **on_create()** → Quick setup (sync) +4. **on_load()** → Fetch data into `this.data` (async) +5. **on_ready()** → DOM manipulation safe (async) + +**Double-render**: If `on_load()` modifies `this.data`, component renders twice (empty → populated). + +### Loading Pattern + +```javascript +async on_load() { + const result = await Product_Controller.list({page: 1}); + this.data.products = result.products; + this.data.loaded = true; // Simple flag at END +} +``` + +```jqhtml +<% if (!this.data.loaded) { %> + Loading... +<% } else { %> + +<% } %> +``` + +**NEVER call `this.render()` in `on_load()` - automatic re-render happens.** + +### Attributes + +- **`$quoted="string"`** → String literal +- **`$unquoted=expression`** → JavaScript expression +- **`$id="name"`** → Scoped element ID + +```javascript +this.$id('button').on('click', ...); // Access scoped element +``` + +### Common Pitfalls + +1. `` IS the element - use `tag=""` attribute +2. `this.data` starts empty `{}` +3. ONLY modify `this.data` in `on_load()` +4. Use `Controller.method()` not `$.ajax()` +5. Blade components self-closing only +6. `on_create/render/destroy` must be sync + +### Bundle Integration Required + +```blade +{!! Frontend_Bundle::render() !!} {{-- Required for JS --}} + {{-- Now JS executes --}} +``` + +For advanced topics: `php artisan rsx:man jqhtml` + +--- + +## MODELS & DATABASE + +### No Mass Assignment + +```php +// ✅ CORRECT +$user = new User_Model(); +$user->email = $email; +$user->save(); + +// ❌ WRONG +User_Model::create(['email' => $email]); +``` + +### Enums + +```php +public static $enums = [ + 'status_id' => [ + 1 => ['constant' => 'STATUS_ACTIVE', 'label' => 'Active'], + ], +]; + +// Usage +$user->status_id = User_Model::STATUS_ACTIVE; +echo $user->status_label; // "Active" +``` + +### Migrations + +**Forward-only, no rollbacks.** + +```bash +php artisan make:migration:safe create_users_table +php artisan migrate:begin +php artisan migrate +php artisan migrate:commit +``` + +--- + +## AJAX ENDPOINTS + +```php +#[Ajax_Endpoint] +public static function get_data(Request $request, array $params = []) +{ + return ['success' => true, 'data' => ...]; +} +``` + +```javascript +const result = await Demo_Controller.get_data({user_id: 123}); +``` + +### Model Fetch + +```php +#[Ajax_Endpoint_Model_Fetch] +public static function fetch($id) +{ + if (!RsxAuth::check()) return false; + return static::find($id); +} +``` + +```javascript +const user = await User_Model.fetch(1); +``` + +--- + +## AUTHENTICATION + +**Always use RsxAuth**, never Laravel Auth or $_SESSION. + +```php +RsxAuth::check(); // Is authenticated +RsxAuth::user(); // User model +RsxAuth::id(); // User ID +``` + +Sessions persist 365 days. Never implement "Remember Me". + +--- + +## JAVASCRIPT DECORATORS + +```javascript +/** @decorator */ +function logCalls(target, key, descriptor) { /* ... */ } + +class Service { + @logCalls + @mutex + async save() { /* ... */ } +} +``` + +--- + +## COMMANDS + +### Module Creation + +```bash +rsx:app:module:create # /name +rsx:app:module:feature:create # /m/f +rsx:app:component:create --name=x # Component +``` + +### Development + +```bash +rsx:check # Code quality +rsx:debug /page # Test routes +rsx:man # Documentation +db:query "SQL" --json +``` + +### Debugging + +- **rsx_dump_die()** - Debug output +- **console_debug("CHANNEL", ...)** - Channel logging +- **CONSOLE_DEBUG_FILTER=CHANNEL** - Filter output + +--- + +## ERROR HANDLING + +```php +if (!$expected) { + shouldnt_happen("Class {$expected} missing"); +} +``` + +Use for "impossible" conditions that indicate broken assumptions. + +--- + +## CODE QUALITY + +**Professional UI**: Hover effects ONLY on buttons, links, form fields. Static elements remain static. + +Run `rsx:check` before commits. Enforces naming, prohibits animations on non-actionable elements. + +--- + +## MAIN_ABSTRACT MIDDLEWARE + +Optional `/rsx/main.php`: + +```php +class Main extends Main_Abstract +{ + public function init() { } // Bootstrap once + public function pre_dispatch($request, $params) { return null; } // Before routes + public function unhandled_route($request, $params) { } // 404s +} +``` + +--- + +## KEY REMINDERS + +1. **Fail loud** - No silent failures +2. **Static by default** - Unless instances needed +3. **Path-agnostic** - Reference by name +4. **Bundles required** - For JavaScript +5. **Use RsxAuth** - Never Laravel Auth +6. **No mass assignment** - Explicit only +7. **Forward migrations** - No rollbacks +8. **Don't run rsx:clean** - Cache auto-invalidates +9. **All routes need #[Auth]** - No exceptions + +--- + +## GETTING HELP + +```bash +php artisan rsx:man # Detailed docs +php artisan list rsx # All commands +``` + +**Topics**: bundle_api, jqhtml, routing, migrations, console_debug, model_fetch, vs_code_extension, deployment, framework_divergences + +**Remember**: RSpade prioritizes simplicity and rapid development. When in doubt, choose the straightforward approach. +## CRITICAL RULES (EXPANDED) + +### Git Working Directory Rules + +```bash +# All code changes in /var/www/html/rsx +cd /var/www/html +php artisan rsx:check # Run commands from project root +git add -A # Stage from project root +git commit -m "Snapshot: description" +``` + +### When rsx:clean Actually Fails + +**The Cost of Running 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) + +## NAMING CONVENTIONS (EXPANDED) + +### Class Naming Philosophy + +All class names must identify: +1. **Module** (e.g., Frontend_, Backend_) +2. **Feature** (e.g., Users, Settings) +3. **Type** (e.g., _Controller, _Model) + +Example: Frontend_Users_Index_Controller → Module: Frontend, Feature: Users, Subfeature: Index, Type: Controller + +## DIRECTORY STRUCTURE (EXPANDED) + +``` +/var/www/html/ # Project root +├── rsx/ # YOUR APPLICATION CODE +│ ├── app/ # Application modules +│ │ ├── frontend/ # Public website (/) +│ │ ├── backend/ # Admin panel (/admin) +│ │ └── api/ # API endpoints +│ ├── lib/ # Shared libraries +│ ├── models/ # Database models +│ ├── public/ # Static files (web-accessible) +│ │ └── public_ignore.json # Patterns blocked from HTTP +│ ├── resource/ # Framework-ignored files +│ │ └── config/ # Configuration overrides +│ │ └── rsx.php # Merged with /system/config/rsx.php +│ ├── theme/ # Global theme assets +│ │ ├── variables.scss +│ │ ├── layouts/ +│ │ └── components/ +│ ├── main.php # App-wide middleware (optional) +│ └── permission.php # Authorization methods +│ +└── system/ # FRAMEWORK CODE (do not modify) + ├── app/RSpade/ # RSpade framework runtime + ├── config/ # Framework configuration + ├── storage/ # Build artifacts and caches + └── artisan # Laravel's artisan CLI +``` + +### Path-Agnostic Implications + +- **Move files freely** - Location doesn't matter +- **No path management** - No use statements needed (auto-generated) +- **Name-based referencing** - Just use the class name +- **Uniqueness required** - Each class name must be unique + +## CONFIGURATION (EXPANDED) + +### Merging Example + +```php +// Framework: /system/config/rsx.php +'bundle_aliases' => ['core' => CoreBundle::class] + +// User: /rsx/resource/config/rsx.php +'bundle_aliases' => ['my-app' => MyAppBundle::class] + +// Result: Both present +config('rsx.bundle_aliases'); // ['core' => CoreBundle, 'my-app' => MyAppBundle] +``` + +## ROUTING & CONTROLLERS (EXPANDED) + +### Route Parameters + +Use :param syntax (not Laravel's {param}): + +```php +#[Route('/users/:id')] +public static function show_user(Request $request, array $params = []) +{ + $user_id = $params['id']; // Route parameter + $tab = $params['tab']; // Query string parameter +} +``` + +### Controller-Wide Auth + +```php +#[Auth('Permission::authenticated()')] +public static function pre_dispatch(Request $request, array $params = []) +{ + return null; // Continue to route +} +``` + +### Custom Permission Methods + +Add to /rsx/permission.php: + +```php +class Permission extends Permission_Abstract +{ + public static function has_role(Request $request, array $params, string $role): mixed + { + if (!RsxAuth::check()) return false; + return RsxAuth::user()->role === $role; + } +} +``` + +## BLADE & VIEWS (EXPANDED) + +### View Functions + +```php +// ✅ GOOD +return rsx_view('Frontend_Index', ['data' => $data]); + +// ❌ AVOID +return view('rsx.app.frontend.frontend_index', ['data' => $data]); +``` + +### Why SCSS Class Wrapping + +- **Scoping** - Prevents style conflicts between views +- **Specificity** - Styles only apply when that view is rendered +- **Organization** - Clear ownership of styles + +## JAVASCRIPT (EXPANDED) + +### JavaScript File Structure Rules + +```javascript +// ✅ ALLOWED +const API_URL = "https://api.example.com"; +function processData(data) { /* ... */ } + +// ❌ NOT ALLOWED +const RANDOM = Math.random(); // Function call +let variable = 42; // Use const +``` + +## BUNDLE SYSTEM (EXPANDED) + ### Unified Include System -Auto-detects: -1. **Module Aliases** - `jquery`, `lodash`, `bootstrap5`, `jqhtml` -2. **Bundle Aliases** - From `config/rsx.php` -3. **Bundle Classes** - Full class names -4. **Files** - Specific files +The include array auto-detects: +1. **Module Aliases** - jquery, lodash, bootstrap5, jqhtml +2. **Bundle Aliases** - From config('rsx.bundle_aliases') +3. **Bundle Classes** - Full class names like Shared_Bundle +4. **Files** - Specific file paths 5. **Directories** - All files in directory ### Include Order Matters @@ -768,1262 +735,45 @@ Auto-detects: ], ``` -### Automatic Compilation - -**Bundles compile automatically when you reload the page.** No manual build steps required during development. - -To verify a bundle compiles successfully (troubleshooting only): -```bash -php artisan rsx:bundle:compile Frontend_Bundle -``` - -### Rendering - -```blade - - - - {!! Frontend_Bundle::render() !!} - - -``` - ---- -## JQHTML COMPONENTS - -### Philosophy: Semantic-First Design - -**JQHTML is designed for mechanical thinkers** - developers who think in terms of structure, logic, and data flow rather than visual design. - -From the creator: -> "I think much more mechanically than visually, so UI has always been a struggle for me, which is why I designed JQHTML. The goal is to composite **concepts** in HTML documents, rather than actual HTML elements with cryptic class names and mental mapping overhead." - -**Traditional approach**: -```html -
-
-
-
User Profile
-
-
-
-``` - -**JQHTML approach**: -```blade - - - -``` - -Write `` not `
`. 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) { %> - -
-
- Loading... -
-

Loading products...

-
- <% } else if (this.data.records && this.data.records.length > 0) { %> - -
- - <% for (let record of this.data.records) { %> - - <% } %> -
<%= record.name %>
-
- <% } else { %> - -
-

No records found

-
- <% } %> -
-``` - -**Why this pattern works:** - -1. **First render**: `this.data = {}` (empty) → Template shows loading state -2. **`on_load()` executes**: Populates `this.data.records` and sets `this.data.loaded = true` -3. **Automatic re-render**: Framework detects `this.data` changed and re-renders -4. **Second render**: `this.data.loaded === true` → Template shows data - -**Key principles:** - -- **Trust the framework** - Automatic re-rendering when `this.data` changes -- **Simple is better** - Use flat `this.data.loaded` flag, not nested state objects -- **Check at template level** - `!this.data.loaded` in jqhtml, not JS-side logic -- **Never call `this.render()` manually** in `on_load()` - breaks the lifecycle - -### Double-Render Pattern - -**Components may render TWICE** if `on_load()` modifies `this.data`: - -1. **First render**: `this.data = {}` (empty) -2. **on_load()** populates `this.data` -3. **Automatic re-render** with populated data -4. **on_ready()** fires after second render - -**Use for loading states**: -```jqhtml - - <% if (Object.keys(this.data).length === 0) { %> - -
Loading products...
- <% } else { %> - - <% for (let product of this.data.products) { %> - - <% } %> - <% } %> -
-``` - -```javascript -class Product_List extends Jqhtml_Component { - on_render() { - // Fires TWICE: before and after data load - console.log('Rendered, empty?', Object.keys(this.data).length === 0); - } - - async on_load() { - // ✅ CORRECT - Use Ajax endpoint pattern - const result = await Product_Controller.list_products({page: 1, per_page: 25}); - this.data.products = result.products; - this.data.total = result.total; - // Automatic re-render happens because this.data changed - } - - on_ready() { - // Fires ONCE after second render - console.log('Ready with', this.data.products.length, 'products'); - } -} -``` - -### Using in Blade - -```blade - {{-- Self-closing only --}} -``` - -### Attribute Syntax - CRITICAL - -**`$` prefix = passed to `this.args`** - -#### Quoted = Literal Strings - -```jqhtml - - -``` - -#### Unquoted = JavaScript Expressions - -```jqhtml - - - - - - - - -``` - -#### Complex Expressions - -```jqhtml - - -``` - -**Implementation Detail**: `$` attributes also create corresponding `data-` HTML attributes (vestigial from v1). - -#### Event Binding with @ Prefix - -**Bind DOM events directly in templates** using `@event=this.method` syntax: - -```jqhtml - - <%= content() %> - -``` - -```javascript -class Button extends Jqhtml_Component { - handle_click(event) { - console.log('Clicked', event); - this.$.addClass('clicked'); - } - - handle_hover(event) { - this.$.addClass('hovered'); - } -} -``` - -**Common events**: @click, @change, @submit, @focus, @blur, @keyup, @keydown, @mouseover - -⚠️ **Note**: Verify @event binding functionality in your jqhtml version. +- **Top-down:** render, on_render (parent before children) +- **Bottom-up:** on_create, on_load, on_ready (children before parent) +- **Parallel:** Siblings at same depth process simultaneously during on_load() ### this.args vs this.data -**this.args** - Input parameters (read-only) -- Source: Component attributes -- Purpose: Configuration, IDs, callbacks - -**this.data** - Loaded data (mutable, **starts as `{}`**) -- Source: `on_load()` method -- Purpose: API data, computed values -- **Starts empty**: Check `Object.keys(this.data).length === 0` for loading state - ```javascript -// +// class User_Card extends Jqhtml_Component { async on_load() { - // ✅ CRITICAL: this.data starts as {} (empty object) - // ✅ ONLY modify this.data - NO other properties - this.data = await User_Controller.get_user_data({user_id: this.args.user_id}); - // Now this.data = {name: "John", email: "..."} - - // ❌ FORBIDDEN: Setting other properties - // this.state = {loading: false}; // WRONG - not allowed - // this.loaded = true; // WRONG - not allowed - } - - on_ready() { - console.log(this.data.name); // ✅ Loaded data - console.log(this.args.theme); // ✅ Input param (read-only) + // this.args.user_id = "123" (from attribute) + // this.data starts as {} (empty) + this.data = await User_Controller.get_user({id: this.args.user_id}); } } ``` -### Components ARE jQuery - -**`this.$` is a genuine jQuery object**, not a wrapper. All jQuery methods work directly. - -```javascript -class Dashboard extends Jqhtml_Component { - on_ready() { - // All jQuery methods available - this.$.addClass('active'); - this.$.css('background', '#f0f0f0'); - this.$.fadeIn(300); - this.$.on('click', () => this.handle_click()); - - // Traversal - this.$.find('.item').addClass('selected'); - this.$.parent().addClass('has-dashboard'); - - // Manipulation - this.$.append('
New content
'); - this.$.empty(); - } -} -``` - -No abstraction layers, no virtual DOM - direct jQuery manipulation. - -### Scoped IDs with this.$id() - -Use `$id` attribute for component-scoped element IDs. - -**Template**: -```jqhtml - -

Name

-

Email

- -
-``` - -**Rendered HTML** (automatic scoping): -```html -
-

Name

-

Email

- -
-``` - -**Access with this.$id()**: -```javascript -class User_Card extends Jqhtml_Component { - on_ready() { - // ✅ CORRECT - Use logical name - this.$id('title').text('John Doe'); - this.$id('email').text('john@example.com'); - this.$id('edit_btn').on('click', () => this.edit()); - - // ❌ WRONG - Don't construct full ID manually - $('#title:c123').text('John Doe'); // Fragile - } -} -``` - -**Why scoped IDs**: Multiple component instances need unique IDs without conflicts. - -### Nesting Components - -```jqhtml - - <% for (let user of this.data.users) { %> - - <% } %> - -``` - -### Parent-Child Communication - -**Pass callbacks as component parameters** for child-to-parent communication: - -```jqhtml - - <% for (let product of this.data.products) { %> - - <% } %> - -``` - -```javascript -class Product_List extends Jqhtml_Component { - async on_load() { - // ✅ CORRECT - Use Ajax endpoint pattern - const result = await Product_Controller.list_products({page: 1}); - this.data.products = result.products; - } - - async handle_delete(product_id) { - // ✅ CORRECT - Use Ajax endpoint for deletion - await Product_Controller.delete_product({id: product_id}); - - // Update this.data and re-render - this.data.products = this.data.products.filter(p => p.id !== product_id); - this.render(); - } - - handle_edit(product_id) { - Rsx.Route('Product_Edit_Controller').navigate({id: product_id}); - } -} -``` - -```javascript -class Product_Card extends Jqhtml_Component { - on_ready() { - this.$id('delete_btn').on('click', () => { - if (confirm('Delete this product?')) { - // Call parent's callback - this.args.on_delete(this.args.product.id); - } - }); - - this.$id('edit_btn').on('click', () => { - this.args.on_edit(this.args.product.id); - }); - } -} -``` - -### content() - Inner Content and Named Slots - -**Default content** (single content area - 95% of use cases): - -```jqhtml - -
<%= this.args.title %>
-
<%= content() %>
-
- - - -

Content goes here

-
-``` - -**Named slots** (multiple content areas - 5% of use cases): - -Child template uses `content('slotname')` to render named slots: - -```jqhtml - -
-
<%= content('header') %>
-
<%= content('body') %>
- -
-
-``` - -Parent provides content using `<#slotname>` tags: - -```blade - - <#header>

User Profile

- <#body> -

Name: <%= this.data.name %>

-

Email: <%= this.data.email %>

- - <#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) { %> - <%= content('row', record) %> - <% } %> - -
-
-``` - -**Parent Class** (`Users_DataGrid.js`): -```javascript -class Users_DataGrid extends DataGrid_Abstract { - async on_load() { - const result = await User_Controller.list_users({page: 1}); - this.data.records = result.users; - this.data.loaded = true; - } -} -``` - -**Child Template - Slot-Only** (`Users_DataGrid.jqhtml`): -```jqhtml - - <#header> - ID - Name - Email - - - <#row> - <%= row.id %> - <%= row.name %> - <%= row.email %> - - -``` - -**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 %> - -``` - -**Reserved Word Validation**: -Slot names cannot be JavaScript reserved words. Parser rejects with fatal error: - -```jqhtml -<#function>Content -<#if>Content -<#header>Content -``` - -**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.