Reduce docs.dist/CLAUDE.dist.md from 74KB to 26KB for LLM efficiency
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
537
docs/CLAUDE.dist-13k.md
Executable file
537
docs/CLAUDE.dist-13k.md
Executable file
@@ -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 --}}
|
||||
|
||||
<body class="{{ rsx_body_class() }}"> {{-- 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
|
||||
<head>
|
||||
{!! Frontend_Bundle::render() !!}
|
||||
</head>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## JQHTML COMPONENTS
|
||||
|
||||
### Philosophy
|
||||
|
||||
For mechanical thinkers who see structure, not visuals. Write `<User_Card>` not `<div class="card">`. Name what things ARE.
|
||||
|
||||
### Template Syntax
|
||||
|
||||
**🔴 CRITICAL: `<Define>` IS the element, not a wrapper**
|
||||
|
||||
```jqhtml
|
||||
<!-- ✅ CORRECT - Define becomes button -->
|
||||
<Define:Save_Button tag="button" class="btn btn-primary">
|
||||
Save
|
||||
</Define:Save_Button>
|
||||
|
||||
<!-- Renders as: -->
|
||||
<button class="Save_Button Jqhtml_Component btn btn-primary">Save</button>
|
||||
```
|
||||
|
||||
**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 { %>
|
||||
<!-- Show data -->
|
||||
<% } %>
|
||||
```
|
||||
|
||||
**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. `<Define>` 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 --}}
|
||||
<User_Card user_id="123" /> {{-- 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> # /name
|
||||
rsx:app:module:feature:create <m> <f> # /m/f
|
||||
rsx:app:component:create --name=x # Component
|
||||
```
|
||||
|
||||
### Development
|
||||
|
||||
```bash
|
||||
rsx:check # Code quality
|
||||
rsx:debug /page # Test routes
|
||||
rsx:man <topic> # 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 <topic> # 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.
|
||||
3088
docs/CLAUDE.dist.md
3088
docs/CLAUDE.dist.md
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user