odac 1.4.3 → 1.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent/rules/coding.md +2 -2
- package/.agent/rules/memory.md +5 -1
- package/.github/workflows/release.yml +2 -0
- package/.husky/pre-push +0 -1
- package/.kiro/steering/coding.md +27 -0
- package/.kiro/steering/memory.md +56 -0
- package/.kiro/steering/project.md +30 -0
- package/.kiro/steering/workflow.md +16 -0
- package/CHANGELOG.md +98 -0
- package/README.md +2 -1
- package/client/odac.js +121 -2
- package/docs/ai/skills/backend/authentication.md +7 -5
- package/docs/ai/skills/backend/controllers.md +24 -3
- package/docs/ai/skills/backend/forms.md +8 -6
- package/docs/ai/skills/backend/image-processing.md +93 -0
- package/docs/ai/skills/backend/request_response.md +2 -2
- package/docs/ai/skills/backend/routing.md +11 -0
- package/docs/ai/skills/backend/structure.md +1 -1
- package/docs/ai/skills/backend/views.md +34 -9
- package/docs/ai/skills/frontend/navigation.md +45 -1
- package/docs/ai/skills/frontend/realtime.md +18 -2
- package/docs/backend/05-controllers/02-your-trusty-odac-assistant.md +24 -0
- package/docs/backend/07-views/03-template-syntax.md +65 -15
- package/docs/backend/07-views/03-variables.md +22 -7
- package/docs/backend/07-views/11-image-optimization.md +197 -0
- package/docs/frontend/02-ajax-navigation/01-quick-start.md +22 -0
- package/docs/frontend/02-ajax-navigation/03-advanced-usage.md +51 -0
- package/package.json +5 -2
- package/src/Auth.js +8 -4
- package/src/Config.js +5 -0
- package/src/Database/ConnectionFactory.js +16 -0
- package/src/Ipc.js +3 -2
- package/src/Lang.js +17 -10
- package/src/Odac.js +1 -0
- package/src/Request.js +20 -20
- package/src/Route.js +39 -3
- package/src/Validator.js +5 -5
- package/src/View/Image.js +495 -0
- package/src/View.js +4 -0
- package/template/controller/page/about.js +3 -3
- package/template/controller/page/index.js +2 -2
- package/template/public/assets/js/app.js +38 -54
- package/template/skeleton/main.html +4 -4
- package/template/view/content/about.html +64 -60
- package/template/view/content/home.html +148 -175
- package/template/view/css/app.css +46 -0
- package/template/view/footer/main.html +10 -9
- package/template/view/header/main.html +34 -11
- package/test/Auth/verifyMagicLink.test.js +281 -0
- package/test/Client/load.test.js +306 -0
- package/test/Lang/get.test.js +37 -11
- package/test/Odac/image.test.js +61 -0
- package/test/Route/set.test.js +102 -0
- package/test/View/Image/buildFilename.test.js +62 -0
- package/test/View/Image/hash.test.js +59 -0
- package/test/View/Image/isAvailable.test.js +15 -0
- package/test/View/Image/parse.test.js +83 -0
- package/test/View/Image/process.test.js +38 -0
- package/test/View/Image/render.test.js +117 -0
- package/test/View/Image/serve.test.js +56 -0
- package/test/View/Image/url.test.js +53 -0
- package/test/View/constructor.test.js +10 -0
- package/template/public/assets/css/style.css +0 -1835
package/.agent/rules/coding.md
CHANGED
|
@@ -21,7 +21,7 @@ trigger: always_on
|
|
|
21
21
|
|
|
22
22
|
## 4. Modern JavaScript
|
|
23
23
|
- **Standard:** Use ES6+ features (Async/Await, Arrow functions, Destructuring).
|
|
24
|
-
- **Modules:**
|
|
24
|
+
- **Modules:** The current codebase uses CommonJS (`require` / `module.exports`). Follow CommonJS for existing modules to keep the codebase consistent. A gradual migration to ES Modules is planned; prefer ESM only for new, isolated packages or tools with clear interop boundaries.
|
|
25
25
|
|
|
26
26
|
## 5. Route & Session Logic
|
|
27
|
-
- **Session Initialization:** `Odac.Request.setSession()` MUST be called before any logic that attempts to access `Odac.Request.session()`. This includes global middleware or form processing logic in `Route.js` that runs before the specific controller is resolved.
|
|
27
|
+
- **Session Initialization:** `Odac.Request.setSession()` MUST be called before any logic that attempts to access `Odac.Request.session()`. This includes global middleware or form processing logic in `Route.js` that runs before the specific controller is resolved.
|
package/.agent/rules/memory.md
CHANGED
|
@@ -38,6 +38,7 @@ trigger: always_on
|
|
|
38
38
|
|
|
39
39
|
## Documentation Standards
|
|
40
40
|
- **AI Skill Front Matter:** Every file under `docs/ai/skills/**/*.md` must start with YAML front matter containing `name`, `description`, and `metadata.tags`; values must be specific to that document's topic (never copied from generic examples).
|
|
41
|
+
- **Template Syntax Documentation:** `{{ }}` and `<odac var>` are equal-status syntaxes, NOT legacy vs modern. `{{ }}` is preferred inside HTML attributes and inline text; `<odac var>` is preferred for standalone block output. Never label `{{ }}` as "legacy" or "backward compatibility" in docs.
|
|
41
42
|
|
|
42
43
|
## Testing & Validation
|
|
43
44
|
- **Mandatory Test Coverage:** Every new feature, method, or significant logic change MUST be accompanied by a corresponding unit or integration test.
|
|
@@ -53,4 +54,7 @@ trigger: always_on
|
|
|
53
54
|
- **Automatic JSON Parsing:** The `#ajax` method (and by extension `odac.get`) must automatically parse the response if the `Content-Type` header contains `application/json`, even if `dataType` is not explicitly set to `json`.
|
|
54
55
|
|
|
55
56
|
## Security Logic & Authentication
|
|
56
|
-
- **Enterprise Token Rotation:** The `Auth.js` system utilizes a non-blocking refresh token rotation mechanism for cookies (`odac_x`/`odac_y`). To prevent race conditions during concurrent requests in high-throughput SPAs, rotated tokens are **not** immediately deleted. Instead, their `active` timestamp is set to naturally expire in 60 seconds (Grace Period), and their `date` timestamp is set to the Unix Epoch (`new Date(0)`) as an identifier mark. Never delete rotated tokens immediately.
|
|
57
|
+
- **Enterprise Token Rotation:** The `Auth.js` system utilizes a non-blocking refresh token rotation mechanism for cookies (`odac_x`/`odac_y`). To prevent race conditions during concurrent requests in high-throughput SPAs, rotated tokens are **not** immediately deleted. Instead, their `active` timestamp is set to naturally expire in 60 seconds (Grace Period), and their `date` timestamp is set to the Unix Epoch (`new Date(0)`) as an identifier mark. Never delete rotated tokens immediately.
|
|
58
|
+
|
|
59
|
+
## View Engine & SSR
|
|
60
|
+
- **Auto-Navigation Injection:** The ODAC View engine (`src/View.js`) automatically parses skeleton HTML files and dynamically injects `data-odac-navigate="content"` into the element immediately wrapping `{{ CONTENT }}`. Do NOT manually add this attribute to HTML templates or assume it is missing based on static analysis, as it is handled seamlessly at runtime via SSR.
|
|
@@ -30,6 +30,7 @@ jobs:
|
|
|
30
30
|
with:
|
|
31
31
|
node-version: '24'
|
|
32
32
|
registry-url: 'https://registry.npmjs.org'
|
|
33
|
+
always-auth: false
|
|
33
34
|
|
|
34
35
|
- name: Update npm
|
|
35
36
|
run: npm install -g npm@latest
|
|
@@ -40,6 +41,7 @@ jobs:
|
|
|
40
41
|
- name: Release
|
|
41
42
|
env:
|
|
42
43
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
44
|
+
HUSKY: 0
|
|
43
45
|
run: npx semantic-release
|
|
44
46
|
- name: Get version
|
|
45
47
|
id: version
|
package/.husky/pre-push
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
inclusion: always
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Coding Standards & Best Practices
|
|
6
|
+
|
|
7
|
+
## 1. Testing Strategy
|
|
8
|
+
- **Rule:** No feature is complete without tests.
|
|
9
|
+
- **Goal:** Maintain stability and prevent regressions.
|
|
10
|
+
- **Action:** Write unit tests for logic and integration tests for API endpoints.
|
|
11
|
+
|
|
12
|
+
## 2. Dependency Management
|
|
13
|
+
- **Philosophy:** "Less is more."
|
|
14
|
+
- **Rule:** Avoid external dependencies unless absolutely necessary.
|
|
15
|
+
- **Preference:** Prioritize native Node.js modules (`fs`, `http`, `crypto`, etc.) to reduce bundle size and security attack surface.
|
|
16
|
+
|
|
17
|
+
## 3. Error Handling
|
|
18
|
+
- **Rule:** Fail loudly and clearly.
|
|
19
|
+
- **Practice:** Use custom Error classes where possible.
|
|
20
|
+
- **Message:** Error messages should guide the developer on how to fix the issue, not just say "Error".
|
|
21
|
+
|
|
22
|
+
## 4. Modern JavaScript
|
|
23
|
+
- **Standard:** Use ES6+ features (Async/Await, Arrow functions, Destructuring).
|
|
24
|
+
- **Modules:** The current codebase uses CommonJS (`require` / `module.exports`). Follow CommonJS for existing modules to keep the codebase consistent. A gradual migration to ES Modules is planned; prefer ESM only for new, isolated packages or tools with clear interop boundaries.
|
|
25
|
+
|
|
26
|
+
## 5. Route & Session Logic
|
|
27
|
+
- **Session Initialization:** `Odac.Request.setSession()` MUST be called before any logic that attempts to access `Odac.Request.session()`. This includes global middleware or form processing logic in `Route.js` that runs before the specific controller is resolved.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
inclusion: always
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Project Memory & Rules
|
|
6
|
+
|
|
7
|
+
## Configuration & Environment
|
|
8
|
+
- **Debug Mode Logic:** The `debug` configuration in `src/Config.js` defaults to `process.env.NODE_ENV !== 'production'`. This ensures that `odac dev` (undefined NODE_ENV) enables debug/hot-reload, while `odac start` (NODE_ENV=production) disables it to use caching.
|
|
9
|
+
- **Logging Strategy:**
|
|
10
|
+
- **Development (`debug: true`):** Enable verbose logging, hot-reloading notifications, and detailed stack traces for easier debugging.
|
|
11
|
+
- **Production (`debug: false`):** Minimize logging to essential operational events (Start/Stop) and Fatal Errors only. Avoid `console.log` for per-request information to preserve performance and disk space. Sensitive error details must not be exposed to the user.
|
|
12
|
+
|
|
13
|
+
## Development Standards & Integrity
|
|
14
|
+
- **NO QUICK/LAZY FIXES:** Explicitly prohibited.
|
|
15
|
+
- Never implement truncated solutions (e.g., `substring(0, 32)` on a hash) or temporary workarounds just to make code run.
|
|
16
|
+
- Always implement the mathematically and architecturally correct "Enterprise-Grade" solution (e.g., using raw `Buffer` for crypto keys instead of hex strings).
|
|
17
|
+
- If a proper solution requires refactoring, do the refactoring. Do not patch holes.
|
|
18
|
+
- **Prioritize Correctness over Speed:** It is better to verify documentation or think for a minute than to output a sub-par patch.
|
|
19
|
+
|
|
20
|
+
## Code Quality & Modern Standards
|
|
21
|
+
- **No Legacy Syntax:**
|
|
22
|
+
- **Strictly Prohibited:** The use of `var` is forbidden. Use `const` (preferred) or `let` (only if mutation is needed).
|
|
23
|
+
- **Variable Scope:** Ensure variables are block-scoped to prevent leakage.
|
|
24
|
+
- **Anti-Spaghetti Code:**
|
|
25
|
+
- **Fail-Fast Pattern:** Avoid deeply nested `if/else` logic. Use early returns (`return`, `break`, `continue`) to handle negative cases immediately.
|
|
26
|
+
- **Promise Handling:** Resolve Promises upfront (e.g., `Promise.all` or strict `await` before loops) rather than mixing `await` inside deep logic or mutating input objects.
|
|
27
|
+
- **Strict Equality:** Always use strict equality checks (`===`) instead of loose ones.
|
|
28
|
+
- **Loop Optimization:** Use labeled loops (`label: for`) for efficient control flow in nested structures. Eliminate intermediate "flag" variables (`isMatch`, `found`) by using direct `return` or `continue label`.
|
|
29
|
+
- **Direct Returns:** Return a value as soon as it is determined. Avoid assigning to a temporary variable (e.g. `matchedUser`) and breaking the loop, unless post-loop processing is strictly necessary.
|
|
30
|
+
- **Async State Safety:** When an async function depends on mutable class state (like `pendingMiddlewares`), capture that state into a local `const` *synchronously* before triggering any async operations. This prevents race conditions where the state changes before the async task consumes it.
|
|
31
|
+
- **Async I/O Preference:** Prefer asynchronous file system operations (`fs.promises` or `await fs.promises.*`) over synchronous methods (`fs.readFileSync`, `fs.writeFileSync`) to prevent blocking the event loop and ensure high concurrency, especially in request handling paths.
|
|
32
|
+
|
|
33
|
+
## Dependency Management
|
|
34
|
+
- **Prefer Native Fetch:** Use the native `fetch` API for network requests in both Node.js (18+) and browser environments to reduce dependencies and bundle size.
|
|
35
|
+
|
|
36
|
+
## Naming & Text Conventions
|
|
37
|
+
- **ODAC Casing:** Always write "ODAC" in uppercase letters when referring to the framework name in strings, comments, log messages, or user-facing text. **EXCEPTION:** The class name itself (`class Odac`) and variable references to it should remain `Odac` (PascalCase) as per code conventions.
|
|
38
|
+
|
|
39
|
+
## Documentation Standards
|
|
40
|
+
- **AI Skill Front Matter:** Every file under `docs/ai/skills/**/*.md` must start with YAML front matter containing `name`, `description`, and `metadata.tags`; values must be specific to that document's topic (never copied from generic examples).
|
|
41
|
+
|
|
42
|
+
## Testing & Validation
|
|
43
|
+
- **Mandatory Test Coverage:** Every new feature, method, or significant logic change MUST be accompanied by a corresponding unit or integration test.
|
|
44
|
+
- **Verify Correctness:** do not assume code works; prove it with a test that covers both success and failure scenarios (e.g., edge cases, error conditions).
|
|
45
|
+
- **Update Existing Tests:** If a feature modifies existing behavior, update the relevant tests to reflect the new logic and ensure they pass.
|
|
46
|
+
- **Atomic Test Structure:**
|
|
47
|
+
- **Directory Mapping:** Each source class/module must have its own directory under `test/` (e.g., `src/Auth.js` -> `test/Auth/`).
|
|
48
|
+
- **Method-Level Files:** Every public method should have its own test file within the class directory (e.g., `test/Auth/check.test.js`).
|
|
49
|
+
- **Sub-module Context:** Nested modules should follow the same pattern (e.g., `src/View/Form.js` -> `test/View/Form/generateFieldHtml.test.js`).
|
|
50
|
+
- **Isolation & Parallelism:** This structure is mandatory to leverage Jest's multi-threaded execution and ensure strict isolation between test cases.
|
|
51
|
+
|
|
52
|
+
## Client Library (odac.js)
|
|
53
|
+
- **Automatic JSON Parsing:** The `#ajax` method (and by extension `odac.get`) must automatically parse the response if the `Content-Type` header contains `application/json`, even if `dataType` is not explicitly set to `json`.
|
|
54
|
+
|
|
55
|
+
## Security Logic & Authentication
|
|
56
|
+
- **Enterprise Token Rotation:** The `Auth.js` system utilizes a non-blocking refresh token rotation mechanism for cookies (`odac_x`/`odac_y`). To prevent race conditions during concurrent requests in high-throughput SPAs, rotated tokens are **not** immediately deleted. Instead, their `active` timestamp is set to naturally expire in 60 seconds (Grace Period), and their `date` timestamp is set to the Unix Epoch (`new Date(0)`) as an identifier mark. Never delete rotated tokens immediately.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
inclusion: always
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Project Context & Design Philosophy
|
|
6
|
+
|
|
7
|
+
## Project Identity
|
|
8
|
+
- **Type:** Node.js Framework
|
|
9
|
+
- **Goal:** To provide a robust, enterprise-ready backbone for web applications.
|
|
10
|
+
|
|
11
|
+
## Core Priorities (The "Big 3")
|
|
12
|
+
1. **Enterprise-Level Security:**
|
|
13
|
+
- Security is not an afterthought; it is foundational.
|
|
14
|
+
- Default to secure settings.
|
|
15
|
+
- Validate all inputs.
|
|
16
|
+
- Sanitize outputs.
|
|
17
|
+
- Use established cryptographic standards.
|
|
18
|
+
2. **Zero-Config:**
|
|
19
|
+
- The framework should work "out of the box" with sensible defaults.
|
|
20
|
+
- Configuration should be optional, not mandatory for getting started.
|
|
21
|
+
- "Convention over Configuration" is key.
|
|
22
|
+
3. **High Performance:**
|
|
23
|
+
- Code must be optimized for throughput and low latency.
|
|
24
|
+
- Avoid unnecessary overhead.
|
|
25
|
+
- Profile and benchmark critical paths.
|
|
26
|
+
- Memory management is crucial.
|
|
27
|
+
|
|
28
|
+
## Interaction Guidelines for AI
|
|
29
|
+
- Always assume the user wants the most efficient and secure solution unless specified otherwise.
|
|
30
|
+
- When suggesting architecture, prioritize scalability and maintainability.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
inclusion: always
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Development Workflow Rules
|
|
6
|
+
|
|
7
|
+
## 1. Quality Assurance (Linting)
|
|
8
|
+
- **Rule:** **ALWAYS** runs lint checks after writing or modifying code.
|
|
9
|
+
- **Action:** Execute the project's linting command (e.g., `npm run lint` or `eslint .`) to verify code compliance.
|
|
10
|
+
- **Strictness:** Do not mark a task as complete if lint errors persist. Fix them immediately.
|
|
11
|
+
|
|
12
|
+
## 2. Documentation Hygiene
|
|
13
|
+
- **Rule:** Documentation must be kept in sync with code changes.
|
|
14
|
+
- **Trigger:** Adding a new feature, modifying an API, or changing configuration behavior.
|
|
15
|
+
- **Action:** Update the relevant `.md` files (README, API docs, etc.) or JSDoc comments.
|
|
16
|
+
- **Goal:** Ensure that the documentation is never stale and accurately reflects the current state of the codebase.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,101 @@
|
|
|
1
|
+
### security
|
|
2
|
+
|
|
3
|
+
- **template:** add noopener to external footer links to mitigate reverse tabnabbing
|
|
4
|
+
- **template:** add rel="noopener" to all external links to prevent tabnabbing without losing referer analytics
|
|
5
|
+
|
|
6
|
+
### ✨ What's New
|
|
7
|
+
|
|
8
|
+
- **client:** add native View Transition API support via odac-transition attribute
|
|
9
|
+
- Implement a complete UI redesign using Tailwind CSS, introduce new components, and update branding to ODAC.
|
|
10
|
+
|
|
11
|
+
### 📚 Documentation
|
|
12
|
+
|
|
13
|
+
- Add documentation for auto-navigation injection in the View Engine and SSR.
|
|
14
|
+
- **template:** correct casing for Odac object api references in comments
|
|
15
|
+
- **views:** clarify template syntax as equal-status with usage guidelines
|
|
16
|
+
|
|
17
|
+
### 🛠️ Fixes & Improvements
|
|
18
|
+
|
|
19
|
+
- **client:** clear stale view transition names on aborted navigation promises
|
|
20
|
+
- **client:** decode HTML entities in document title to prevent XSS vulnerabilities
|
|
21
|
+
- **template:** sync active nav state properly across desktop and mobile menus
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
Powered by [⚡ ODAC](https://odac.run)
|
|
28
|
+
|
|
29
|
+
### doc
|
|
30
|
+
|
|
31
|
+
- Introduce WebSocket routing and controllers, update request handling, and refactor language and validator modules to use async operations.
|
|
32
|
+
|
|
33
|
+
### ⚙️ Engine Tuning
|
|
34
|
+
|
|
35
|
+
- **view:** extract <odac:img> parsing to Image.parse() method
|
|
36
|
+
|
|
37
|
+
### ✨ What's New
|
|
38
|
+
|
|
39
|
+
- **image:** add Odac.image() API for programmatic image URL generation
|
|
40
|
+
- **view:** add <odac:img> tag with on-demand image processing
|
|
41
|
+
- **view:** add warning message when sharp dependency is unavailable
|
|
42
|
+
- **view:** implement human-readable cache filenames and mtime-based cache busting
|
|
43
|
+
|
|
44
|
+
### 📚 Documentation
|
|
45
|
+
|
|
46
|
+
- **steering:** correct module system standard to explicitly dictate CommonJS preserving architectural consistency
|
|
47
|
+
- **View/Image:** correct cache eviction jsdoc from LRU to FIFO reflecting O(1) performance trait
|
|
48
|
+
|
|
49
|
+
### 🛠️ Fixes & Improvements
|
|
50
|
+
|
|
51
|
+
- **auth:** prevent duplicate login tokens during magic link verification
|
|
52
|
+
- Refactor `fs` module usage to `node:fs` and `fs.promises`, and update string manipulation from `substr` to `slice`.
|
|
53
|
+
- **request:** use global.Odac namespace for consistent access
|
|
54
|
+
- **route:** improve controller loading error handling with specific error messages
|
|
55
|
+
- **route:** update inline function reference on hot reload
|
|
56
|
+
- **View/Image:** clamp unrequested output formats to supported whitelist to prevent processing crashes
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
Powered by [⚡ ODAC](https://odac.run)
|
|
63
|
+
|
|
64
|
+
### doc
|
|
65
|
+
|
|
66
|
+
- Introduce WebSocket routing and controllers, update request handling, and refactor language and validator modules to use async operations.
|
|
67
|
+
|
|
68
|
+
### ⚙️ Engine Tuning
|
|
69
|
+
|
|
70
|
+
- **view:** extract <odac:img> parsing to Image.parse() method
|
|
71
|
+
|
|
72
|
+
### ✨ What's New
|
|
73
|
+
|
|
74
|
+
- **image:** add Odac.image() API for programmatic image URL generation
|
|
75
|
+
- **view:** add <odac:img> tag with on-demand image processing
|
|
76
|
+
- **view:** add warning message when sharp dependency is unavailable
|
|
77
|
+
- **view:** implement human-readable cache filenames and mtime-based cache busting
|
|
78
|
+
|
|
79
|
+
### 📚 Documentation
|
|
80
|
+
|
|
81
|
+
- **steering:** correct module system standard to explicitly dictate CommonJS preserving architectural consistency
|
|
82
|
+
- **View/Image:** correct cache eviction jsdoc from LRU to FIFO reflecting O(1) performance trait
|
|
83
|
+
|
|
84
|
+
### 🛠️ Fixes & Improvements
|
|
85
|
+
|
|
86
|
+
- **auth:** prevent duplicate login tokens during magic link verification
|
|
87
|
+
- Refactor `fs` module usage to `node:fs` and `fs.promises`, and update string manipulation from `substr` to `slice`.
|
|
88
|
+
- **request:** use global.Odac namespace for consistent access
|
|
89
|
+
- **route:** improve controller loading error handling with specific error messages
|
|
90
|
+
- **route:** update inline function reference on hot reload
|
|
91
|
+
- **View/Image:** clamp unrequested output formats to supported whitelist to prevent processing crashes
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
Powered by [⚡ ODAC](https://odac.run)
|
|
98
|
+
|
|
1
99
|
### ⚙️ Engine Tuning
|
|
2
100
|
|
|
3
101
|
- **client:** extract ws connection logic and fix recursive sharedworker reconnects reconnect bug
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* 🚀 **Developer Friendly:** Simple setup and intuitive API design let you start building immediately.
|
|
9
9
|
* 🎨 **Built-in Tailwind CSS:** Zero-config integration with Tailwind CSS v4. Automatic compilation and optimization out of the box.
|
|
10
10
|
* 🔗 **Powerful Routing:** Create clean, custom URLs and manage infinite pages with a flexible routing system.
|
|
11
|
-
* ✨ **Seamless SPA Experience:** Automatic AJAX handling for forms and page transitions
|
|
11
|
+
* ✨ **Seamless SPA Experience:** Automatic AJAX handling for forms and page transitions with native **View Transition API** support. Add a single HTML attribute for smooth, browser-native animations — no client-side code required.
|
|
12
12
|
* 🛡️ **Built-in Security:** Enterprise-grade security out of the box. Includes secure default headers and a **Multi-tab Safe, Single-Use CSRF Protection (Nonce)**. Tokens self-replenish in the background, ensuring maximum defense without ever interrupting the user experience.
|
|
13
13
|
* 🔐 **Authentication:** Ready-to-use session management with enterprise-grade **Refresh Token Rotation**, secure password hashing, and authentication helpers.
|
|
14
14
|
* 🗄️ **Database Agnostic:** Integrated support for major databases (PostgreSQL, MySQL, SQLite) and Redis via Knex.js.
|
|
@@ -72,6 +72,7 @@ project/
|
|
|
72
72
|
├── middleware/ # Route middlewares
|
|
73
73
|
├── public/ # Static assets
|
|
74
74
|
├── route/ # Route definitions
|
|
75
|
+
├── schema/ # Database schemas (auto-migrate)
|
|
75
76
|
├── view/ # HTML templates
|
|
76
77
|
├── .env # Environment variables
|
|
77
78
|
└── odac.json # App configuration
|
package/client/odac.js
CHANGED
|
@@ -195,6 +195,33 @@ if (typeof window !== 'undefined') {
|
|
|
195
195
|
xhr.send(data)
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Assigns `view-transition-name` CSS properties to all elements carrying
|
|
200
|
+
* the `odac-transition` attribute, enabling the browser's native View
|
|
201
|
+
* Transition API to animate them individually during navigation.
|
|
202
|
+
*
|
|
203
|
+
* @returns {Element[]} The list of elements that received transition names.
|
|
204
|
+
*/
|
|
205
|
+
#applyTransitionNames() {
|
|
206
|
+
const elements = document.querySelectorAll('[odac-transition]')
|
|
207
|
+
elements.forEach(el => {
|
|
208
|
+
el.style.viewTransitionName = el.getAttribute('odac-transition')
|
|
209
|
+
})
|
|
210
|
+
return Array.from(elements)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Removes `view-transition-name` from previously tagged elements to
|
|
215
|
+
* prevent stale names from conflicting with future transitions.
|
|
216
|
+
*
|
|
217
|
+
* @param {Element[]} elements - Elements to clean up.
|
|
218
|
+
*/
|
|
219
|
+
#clearTransitionNames(elements) {
|
|
220
|
+
elements.forEach(el => {
|
|
221
|
+
el.style.viewTransitionName = ''
|
|
222
|
+
})
|
|
223
|
+
}
|
|
224
|
+
|
|
198
225
|
#fade(element, type, duration = 400, callback) {
|
|
199
226
|
const isIn = type === 'in'
|
|
200
227
|
const startOpacity = isIn ? 0 : 1
|
|
@@ -720,6 +747,20 @@ if (typeof window !== 'undefined') {
|
|
|
720
747
|
.replace(/\n/g, '<br>')
|
|
721
748
|
}
|
|
722
749
|
|
|
750
|
+
/**
|
|
751
|
+
* Decodes HTML entities back to their plain-text representation.
|
|
752
|
+
* Uses a textarea element as a safe decoder — no script execution risk.
|
|
753
|
+
*
|
|
754
|
+
* @param {string} str - HTML-encoded string (e.g. "&" → "&").
|
|
755
|
+
* @returns {string} Decoded plain-text string.
|
|
756
|
+
*/
|
|
757
|
+
#decodeHtmlEntities(str) {
|
|
758
|
+
if (typeof str !== 'string') return str
|
|
759
|
+
const textarea = document.createElement('textarea')
|
|
760
|
+
textarea.innerHTML = str
|
|
761
|
+
return textarea.value
|
|
762
|
+
}
|
|
763
|
+
|
|
723
764
|
load(url, callback, push = true) {
|
|
724
765
|
if (this.#isNavigating) return false
|
|
725
766
|
|
|
@@ -739,6 +780,84 @@ if (typeof window !== 'undefined') {
|
|
|
739
780
|
if (element) elementsToUpdate.push({key, element})
|
|
740
781
|
})
|
|
741
782
|
|
|
783
|
+
const useViewTransition = document.startViewTransition && document.querySelectorAll('[odac-transition]').length > 0
|
|
784
|
+
|
|
785
|
+
if (useViewTransition) {
|
|
786
|
+
this.#loadWithViewTransition(url, callback, push, currentUrl, currentSkeleton, elementsToUpdate)
|
|
787
|
+
} else {
|
|
788
|
+
this.#loadWithFade(url, callback, push, currentUrl, currentSkeleton, elementsToUpdate)
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
/**
|
|
793
|
+
* Performs page navigation using the browser's native View Transition API.
|
|
794
|
+
* Elements with `odac-transition` attributes receive individual transition
|
|
795
|
+
* names, enabling per-element morphing animations orchestrated by the browser.
|
|
796
|
+
* Non-transition elements still update their content within the transition frame.
|
|
797
|
+
*/
|
|
798
|
+
#loadWithViewTransition(url, callback, push, currentUrl, currentSkeleton, elementsToUpdate) {
|
|
799
|
+
const oldTransitionElements = this.#applyTransitionNames()
|
|
800
|
+
|
|
801
|
+
this.#ajax({
|
|
802
|
+
url,
|
|
803
|
+
type: 'GET',
|
|
804
|
+
headers: {
|
|
805
|
+
'X-Odac': 'ajaxload',
|
|
806
|
+
'X-Odac-Load': Object.keys(this.#loader.elements).join(','),
|
|
807
|
+
'X-Odac-Skeleton': currentSkeleton || ''
|
|
808
|
+
},
|
|
809
|
+
dataType: 'json',
|
|
810
|
+
success: (data, status, xhr) => {
|
|
811
|
+
const finalUrl = xhr.responseURL || url
|
|
812
|
+
if (data.skeletonChanged) {
|
|
813
|
+
this.#clearTransitionNames(oldTransitionElements)
|
|
814
|
+
window.location.href = finalUrl
|
|
815
|
+
return
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
const transition = document.startViewTransition(() => {
|
|
819
|
+
if (finalUrl !== currentUrl && push) window.history.pushState(null, document.title, finalUrl)
|
|
820
|
+
|
|
821
|
+
const newPage = xhr.getResponseHeader('X-Odac-Page')
|
|
822
|
+
if (newPage !== null) {
|
|
823
|
+
this.#page = newPage
|
|
824
|
+
document.documentElement.dataset.odacPage = newPage
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
if (data.data) this.#data = data.data
|
|
828
|
+
if (data.title) document.title = this.#decodeHtmlEntities(data.title)
|
|
829
|
+
|
|
830
|
+
elementsToUpdate.forEach(({key, element}) => {
|
|
831
|
+
if (data.output && data.output[key] !== undefined) element.innerHTML = data.output[key]
|
|
832
|
+
})
|
|
833
|
+
|
|
834
|
+
this.#applyTransitionNames()
|
|
835
|
+
})
|
|
836
|
+
|
|
837
|
+
transition.finished
|
|
838
|
+
.then(() => {
|
|
839
|
+
this.#clearTransitionNames(document.querySelectorAll('[odac-transition]'))
|
|
840
|
+
this.#handleLoadComplete(data, callback)
|
|
841
|
+
})
|
|
842
|
+
.catch(() => {
|
|
843
|
+
this.#clearTransitionNames(document.querySelectorAll('[odac-transition]'))
|
|
844
|
+
this.#isNavigating = false
|
|
845
|
+
})
|
|
846
|
+
},
|
|
847
|
+
error: () => {
|
|
848
|
+
this.#clearTransitionNames(oldTransitionElements)
|
|
849
|
+
this.#isNavigating = false
|
|
850
|
+
window.location.replace(url)
|
|
851
|
+
}
|
|
852
|
+
})
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
/**
|
|
856
|
+
* Performs page navigation using the legacy fade-in/fade-out animation.
|
|
857
|
+
* This is the fallback path when the View Transition API is unavailable
|
|
858
|
+
* or no `odac-transition` elements exist in the DOM.
|
|
859
|
+
*/
|
|
860
|
+
#loadWithFade(url, callback, push, currentUrl, currentSkeleton, elementsToUpdate) {
|
|
742
861
|
let ajaxData = null,
|
|
743
862
|
ajaxXhr = null,
|
|
744
863
|
fadeOutComplete = false,
|
|
@@ -761,7 +880,7 @@ if (typeof window !== 'undefined') {
|
|
|
761
880
|
}
|
|
762
881
|
|
|
763
882
|
if (ajaxData.data) this.#data = ajaxData.data
|
|
764
|
-
if (ajaxData.title) document.title = ajaxData.title
|
|
883
|
+
if (ajaxData.title) document.title = this.#decodeHtmlEntities(ajaxData.title)
|
|
765
884
|
|
|
766
885
|
if (elementsToUpdate.length === 0) {
|
|
767
886
|
this.#handleLoadComplete(ajaxData, callback)
|
|
@@ -794,7 +913,7 @@ if (typeof window !== 'undefined') {
|
|
|
794
913
|
}
|
|
795
914
|
|
|
796
915
|
this.#ajax({
|
|
797
|
-
url
|
|
916
|
+
url,
|
|
798
917
|
type: 'GET',
|
|
799
918
|
headers: {
|
|
800
919
|
'X-Odac': 'ajaxload',
|
|
@@ -59,13 +59,15 @@ const val = Odac.session('key');
|
|
|
59
59
|
Odac.session('key', null);
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
### 4. Realtime
|
|
62
|
+
### 4. Realtime Broadcasting (Ipc)
|
|
63
63
|
```javascript
|
|
64
|
-
// Broadcast to everyone in
|
|
65
|
-
Odac.
|
|
64
|
+
// Broadcast to everyone subscibed in this worker cluster
|
|
65
|
+
await Odac.Ipc.publish('lobby', { type: 'chat', text: 'Hello!' });
|
|
66
66
|
|
|
67
|
-
//
|
|
68
|
-
Odac.
|
|
67
|
+
// In your WS handler, listen for Ipc messages
|
|
68
|
+
Odac.Ipc.subscribe('lobby', (msg) => {
|
|
69
|
+
Odac.ws.send(msg);
|
|
70
|
+
});
|
|
69
71
|
```
|
|
70
72
|
|
|
71
73
|
## Security Best Practices
|
|
@@ -32,7 +32,7 @@ class User {
|
|
|
32
32
|
|
|
33
33
|
// GET /users/{id}
|
|
34
34
|
async show(Odac) {
|
|
35
|
-
const id = Odac.
|
|
35
|
+
const id = await Odac.request('id');
|
|
36
36
|
const user = await Odac.Service.get('User').find(id);
|
|
37
37
|
return Odac.return(user);
|
|
38
38
|
}
|
|
@@ -48,7 +48,28 @@ class User {
|
|
|
48
48
|
module.exports = User;
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
### 2.
|
|
51
|
+
### 2. WebSocket Controller
|
|
52
|
+
```javascript
|
|
53
|
+
// controller/ws/Chat.js
|
|
54
|
+
module.exports = function(Odac) {
|
|
55
|
+
const ws = Odac.ws; // The WebSocket client instance
|
|
56
|
+
|
|
57
|
+
ws.on('message', (data) => {
|
|
58
|
+
console.log('Received:', data);
|
|
59
|
+
// Broadcast to everyone else
|
|
60
|
+
ws.broadcast({ sender: ws.id, text: data.text });
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
ws.on('close', () => {
|
|
64
|
+
console.log('Client disconnected:', ws.id);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Optional: send welcome message
|
|
68
|
+
ws.send({ type: 'info', message: 'Connected to Chat!' });
|
|
69
|
+
};
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 3. Simple Function-Based Controller
|
|
52
73
|
```javascript
|
|
53
74
|
// controller/Home.js
|
|
54
75
|
module.exports = function(Odac) {
|
|
@@ -56,7 +77,7 @@ module.exports = function(Odac) {
|
|
|
56
77
|
};
|
|
57
78
|
```
|
|
58
79
|
|
|
59
|
-
###
|
|
80
|
+
### 4. Usage in Routes
|
|
60
81
|
```javascript
|
|
61
82
|
// route/web.js
|
|
62
83
|
Odac.Route.get('/users', 'User@index');
|
|
@@ -109,12 +109,14 @@ module.exports = class Contact {
|
|
|
109
109
|
|
|
110
110
|
## Patterns
|
|
111
111
|
```javascript
|
|
112
|
-
//
|
|
113
|
-
|
|
112
|
+
// Custom form action in class/Contact.js
|
|
113
|
+
module.exports = class Contact {
|
|
114
114
|
async submit(form) {
|
|
115
|
-
const {email, message} = form.data
|
|
116
|
-
if (!email || !message) return form.error('
|
|
117
|
-
|
|
115
|
+
const { email, message } = form.data;
|
|
116
|
+
if (!email || !message) return form.error('email', 'Required fields missing');
|
|
117
|
+
|
|
118
|
+
// Process data...
|
|
119
|
+
return form.success('Message received!');
|
|
118
120
|
}
|
|
119
|
-
}
|
|
121
|
+
}
|
|
120
122
|
```
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: backend-image-processing-skill
|
|
3
|
+
description: ODAC on-demand image optimization via the odac:img template tag with automatic resize, format conversion, and aggressive caching.
|
|
4
|
+
metadata:
|
|
5
|
+
tags: backend, image, optimization, sharp, webp, resize, template, view
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Backend Image Processing Skill
|
|
9
|
+
|
|
10
|
+
ODAC provides on-demand image processing through the `<odac:img>` template tag. Images are automatically resized, converted to modern formats (WebP, AVIF), and cached to disk for sub-millisecond subsequent responses.
|
|
11
|
+
|
|
12
|
+
## Architectural Approach
|
|
13
|
+
|
|
14
|
+
Processing happens at render time via `src/View/Image.js`. The first request triggers sharp-based transformation; all subsequent requests serve from `storage/.cache/img/`. Sharp is an optional dependency — when absent, `<odac:img>` gracefully degrades to a standard `<img>` tag.
|
|
15
|
+
|
|
16
|
+
## Core Rules
|
|
17
|
+
|
|
18
|
+
1. **Optional Dependency**: Sharp must be installed separately (`npm install sharp`). The framework functions without it.
|
|
19
|
+
2. **Security**: Path traversal is blocked. Only files under `public/` are processable.
|
|
20
|
+
3. **Cache**: Processed images are stored in `storage/.cache/img/` with human-readable filenames (`{name}-{dimension}-{hash}.{ext}`) for easy debugging and CDN log analysis.
|
|
21
|
+
4. **Max Dimension**: 4096px cap prevents resource exhaustion from oversized requests.
|
|
22
|
+
|
|
23
|
+
## Reference Patterns
|
|
24
|
+
|
|
25
|
+
### 1. Basic Resize + Format Conversion
|
|
26
|
+
```html
|
|
27
|
+
<odac:img src="/images/hero.jpg" width="800" height="600" format="webp"/>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 2. Format Conversion Only (No Resize)
|
|
31
|
+
```html
|
|
32
|
+
<odac:img src="/images/logo.png" format="webp"/>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 3. Custom Quality
|
|
36
|
+
```html
|
|
37
|
+
<odac:img src="/images/banner.jpg" width="1200" format="webp" quality="90"/>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 4. Dynamic Source from Controller
|
|
41
|
+
```html
|
|
42
|
+
<odac:img src="{{ product.image }}" width="200" height="200" format="webp" alt="Product"/>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 5. With Standard HTML Attributes
|
|
46
|
+
```html
|
|
47
|
+
<odac:img src="/images/avatar.jpg" width="64" height="64" format="webp"
|
|
48
|
+
alt="User avatar" class="rounded-full" loading="lazy" decoding="async"/>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Supported Attributes
|
|
52
|
+
|
|
53
|
+
| Attribute | Type | Description |
|
|
54
|
+
|-----------|------|-------------|
|
|
55
|
+
| `src` | string | Source path relative to `public/` (required) |
|
|
56
|
+
| `width` | number | Target width in pixels |
|
|
57
|
+
| `height` | number | Target height in pixels |
|
|
58
|
+
| `format` | string | Output format: `webp`, `avif`, `png`, `jpeg`, `tiff` |
|
|
59
|
+
| `quality` | number | Compression quality 1-100 (default: 80) |
|
|
60
|
+
| All others | — | Passed through to the `<img>` tag as-is |
|
|
61
|
+
|
|
62
|
+
## Configuration
|
|
63
|
+
|
|
64
|
+
Default settings in `odac.json`:
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"image": {
|
|
68
|
+
"quality": 80,
|
|
69
|
+
"maxDimension": 4096,
|
|
70
|
+
"format": "webp"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Best Practices
|
|
76
|
+
|
|
77
|
+
- Prefer `webp` format for the best size/quality ratio across modern browsers.
|
|
78
|
+
- Set explicit `width` and `height` to prevent layout shift (CLS).
|
|
79
|
+
- Use `loading="lazy"` for below-the-fold images.
|
|
80
|
+
- Keep source images at high resolution in `public/`; let ODAC handle the downsizing.
|
|
81
|
+
|
|
82
|
+
## Programmatic API (`Odac.image()`)
|
|
83
|
+
|
|
84
|
+
For cases where a raw URL is needed instead of an `<img>` tag (div backgrounds, JSON APIs, mail templates, cron jobs):
|
|
85
|
+
|
|
86
|
+
```js
|
|
87
|
+
const url = await Odac.image('/images/hero.jpg', { width: 800, format: 'webp' })
|
|
88
|
+
// → "/_odac/img/hero-800-a1b2c3d4.webp"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
- Available on every `Odac` instance (controller, cron, middleware).
|
|
92
|
+
- Same processing pipeline and cache as `<odac:img>`.
|
|
93
|
+
- Returns original `src` as fallback when sharp is unavailable.
|