odac 1.3.0 → 1.4.0
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/memory.md +7 -1
- package/.github/workflows/release.yml +0 -4
- package/AGENTS.md +47 -0
- package/CHANGELOG.md +32 -0
- package/README.md +1 -1
- package/bin/odac.js +169 -6
- package/client/odac.js +15 -11
- package/docs/ai/README.md +49 -0
- package/docs/ai/skills/SKILL.md +39 -0
- package/docs/ai/skills/backend/authentication.md +67 -0
- package/docs/ai/skills/backend/config.md +32 -0
- package/docs/ai/skills/backend/controllers.md +62 -0
- package/docs/ai/skills/backend/cron.md +50 -0
- package/docs/ai/skills/backend/database.md +21 -0
- package/docs/ai/skills/backend/forms.md +19 -0
- package/docs/ai/skills/backend/ipc.md +55 -0
- package/docs/ai/skills/backend/mail.md +34 -0
- package/docs/ai/skills/backend/request_response.md +35 -0
- package/docs/ai/skills/backend/routing.md +51 -0
- package/docs/ai/skills/backend/storage.md +43 -0
- package/docs/ai/skills/backend/streaming.md +34 -0
- package/docs/ai/skills/backend/structure.md +57 -0
- package/docs/ai/skills/backend/translations.md +42 -0
- package/docs/ai/skills/backend/utilities.md +24 -0
- package/docs/ai/skills/backend/validation.md +53 -0
- package/docs/ai/skills/backend/views.md +61 -0
- package/docs/ai/skills/frontend/core.md +66 -0
- package/docs/ai/skills/frontend/forms.md +21 -0
- package/docs/ai/skills/frontend/navigation.md +20 -0
- package/docs/ai/skills/frontend/realtime.md +47 -0
- package/docs/backend/10-authentication/01-user-logins-with-authjs.md +2 -0
- package/docs/backend/10-authentication/05-session-management.md +25 -3
- package/package.json +1 -1
- package/src/Auth.js +100 -15
- package/src/Route/Internal.js +21 -18
- package/src/Route/MimeTypes.js +56 -0
- package/src/Route.js +40 -63
- package/src/View/Form.js +91 -51
- package/src/View.js +8 -3
- package/test/Auth.test.js +249 -0
- package/test/Client.test.js +29 -0
- package/test/View/Form.test.js +37 -0
package/.agent/rules/memory.md
CHANGED
|
@@ -39,4 +39,10 @@ trigger: always_on
|
|
|
39
39
|
## Testing & Validation
|
|
40
40
|
- **Mandatory Test Coverage:** Every new feature, method, or significant logic change MUST be accompanied by a corresponding unit or integration test.
|
|
41
41
|
- **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).
|
|
42
|
-
- **Update Existing Tests:** If a feature modifies existing behavior, update the relevant tests to reflect the new logic and ensure they pass.
|
|
42
|
+
- **Update Existing Tests:** If a feature modifies existing behavior, update the relevant tests to reflect the new logic and ensure they pass.
|
|
43
|
+
|
|
44
|
+
## Client Library (odac.js)
|
|
45
|
+
- **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`.
|
|
46
|
+
|
|
47
|
+
## Security Logic & Authentication
|
|
48
|
+
- **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.
|
|
@@ -41,10 +41,6 @@ jobs:
|
|
|
41
41
|
env:
|
|
42
42
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
43
43
|
run: npx semantic-release
|
|
44
|
-
|
|
45
|
-
- name: Publish to npm
|
|
46
|
-
run: npm publish --provenance --access public
|
|
47
|
-
|
|
48
44
|
- name: Get version
|
|
49
45
|
id: version
|
|
50
46
|
run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
|
package/AGENTS.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# ODAC Agent Instructions
|
|
2
|
+
|
|
3
|
+
You are an AI Agent operating within the **ODAC Framework** repository. This document outlines the core principles, architectural standards, and operational guidelines you MUST follow to maintain the integrity and performance of this enterprise-grade system.
|
|
4
|
+
|
|
5
|
+
## 1. Project Identity & Philosophy
|
|
6
|
+
- **Name:** ODAC (Always uppercase in strings/docs/logs).
|
|
7
|
+
- **Core Goal:** To provide a robust, zero-config, high-performance Node.js framework for distributed cloud applications.
|
|
8
|
+
- **The "Big 3" Priorities:**
|
|
9
|
+
1. **Enterprise-Level Security:** Security is foundational. Default to secure, validate all inputs, sanitize all outputs.
|
|
10
|
+
2. **Zero-Config:** Works out-of-the-box. Convention over configuration.
|
|
11
|
+
3. **High Performance:** Optimize for throughput and low latency (Sub-millisecond targets).
|
|
12
|
+
|
|
13
|
+
## 2. Architectural Principles
|
|
14
|
+
- **Asynchronous & Non-Blocking:** Exclusively use non-blocking I/O. Use `fs.promises` instead of sync methods.
|
|
15
|
+
- **Dependency Injection (DI):** Build components with DI for maximum testability.
|
|
16
|
+
- **Single Responsibility Principle (SRP):** Keep classes and functions focused and small.
|
|
17
|
+
- **Memory Management:** Be paranoid about leaks. Clean up listeners, streams, and connections.
|
|
18
|
+
- **O(n log n) Bound:** Prioritize O(1) or O(n log n) algorithms. Justify any O(n²) operations.
|
|
19
|
+
|
|
20
|
+
## 3. Coding Standards & Integrity
|
|
21
|
+
- **Modern JavaScript:** Use ES6+ features, ES Modules (import/export).
|
|
22
|
+
- **Strictly Prohibited:** **No usage of `var`**. Use `const` (preferred) or `let`.
|
|
23
|
+
- **Fail-Fast Pattern:** Implement early returns for negative cases. Avoid deeply nested `if/else`.
|
|
24
|
+
- **Anti-Spaghetti Rules:**
|
|
25
|
+
- Resolve Promises upfront (e.g., `Promise.all`) before loops.
|
|
26
|
+
- Avoid mixing `await` inside deep logic.
|
|
27
|
+
- Capture mutable state synchronously before async operations.
|
|
28
|
+
- **No Quick/Lazy Fixes:** Implement correctly from the start. Refactor if necessary; no "band-aid" patches.
|
|
29
|
+
|
|
30
|
+
## 4. Technical Constraints (Strict Compliance)
|
|
31
|
+
- **Session Safety:** `Odac.Request.setSession()` MUST be called before accessing `Odac.Request.session()`.
|
|
32
|
+
- **Structured Logging:** No `console.log`. Use the internal JSON logger with appropriate levels.
|
|
33
|
+
- **Native APIs:** Prefer native Node.js/Browser APIs (like `fetch`) over external libraries to minimize overhead.
|
|
34
|
+
- **Token Rotation:** In `Auth.js`, use the 60-second grace period for rotated tokens. Never delete them immediately.
|
|
35
|
+
- **Ajax Parsing:** `odac.js` must automatically parse JSON responses if headers allow.
|
|
36
|
+
|
|
37
|
+
## 5. Testing & Documentation
|
|
38
|
+
- **TDD Requirement:** No feature is complete without unit/integration tests covering both success and edge cases.
|
|
39
|
+
- **Documentation:** Every exported member must have JSDoc explaining *Why* it exists, not just *What* it does.
|
|
40
|
+
- **No User Dialogues in Code:** Do not include assistant-user interaction in comments or code files.
|
|
41
|
+
|
|
42
|
+
## 6. Communication Style
|
|
43
|
+
- **Authoritative & Precise:** Be the expert. Do not explain basic concepts.
|
|
44
|
+
- **Proactive Correction:** If the user suggests a sub-optimal or insecure pattern (e.g., synchronous reads), refuse and implement the correct async version, explaining the trade-off.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
*Note: This file is a living document. Updates should be reflected in `memory.md` and subsequent AI interactions.*
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
### ⚙️ Engine Tuning
|
|
2
2
|
|
|
3
|
+
- Extract MIME type definitions into a dedicated module.
|
|
4
|
+
|
|
5
|
+
### ⚡️ Performance Upgrades
|
|
6
|
+
|
|
7
|
+
- prevent redundant database table migration calls by introducing a static cache to track completed migrations.
|
|
8
|
+
|
|
9
|
+
### ✨ What's New
|
|
10
|
+
|
|
11
|
+
- add comprehensive ODAC Agent instructions and guidelines
|
|
12
|
+
- **auth:** implement enterprise refresh token rotation with grace period and session persistence
|
|
13
|
+
- Automatically parse JSON responses in client-side AJAX requests based on the `Content-Type` header.
|
|
14
|
+
- implement comprehensive AI agent skills system and automated CLI setup
|
|
15
|
+
|
|
16
|
+
### 🛠️ Fixes & Improvements
|
|
17
|
+
|
|
18
|
+
- add HTML escaping functionality to Form class and corresponding tests
|
|
19
|
+
- **ai:** correct target path for syncing AI skills
|
|
20
|
+
- **auth:** replace magic number with constant for rotated token threshold
|
|
21
|
+
- **auth:** replace magic number with constant for token rotation grace period
|
|
22
|
+
- enhance odac:form parser with nested quotes and dynamic binding support
|
|
23
|
+
- **route:** support nested property paths in actions and resolve App class conflict
|
|
24
|
+
- **view:** ensure odac:for with 'in' attribute parses correctly as javascript
|
|
25
|
+
- **view:** implement clear attribute in odac:form to control auto-clearing
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
Powered by [⚡ ODAC](https://odac.run)
|
|
32
|
+
|
|
33
|
+
### ⚙️ Engine Tuning
|
|
34
|
+
|
|
3
35
|
- Improve disposable domain cache management by relocating the cache path, ensuring directory existence, and standardizing error logging.
|
|
4
36
|
- Migrate file system operations in Mail and View to use async `fs.promises` for non-blocking I/O, aligning with new memory.md guidelines.
|
|
5
37
|
- remove unused `WebSocketClient` variable assignments in `WebSocket.test.js`
|
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* 🔗 **Powerful Routing:** Create clean, custom URLs and manage infinite pages with a flexible routing system.
|
|
11
11
|
* ✨ **Seamless SPA Experience:** Automatic AJAX handling for forms and page transitions eliminates the need for complex client-side code.
|
|
12
12
|
* 🛡️ **Built-in Security:** Automatic CSRF protection and secure default headers keep your application safe.
|
|
13
|
-
* 🔐 **Authentication:** Ready-to-use session management
|
|
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.
|
|
15
15
|
* 🌍 **i18n Support:** Native multi-language support to help you reach a global audience.
|
|
16
16
|
* ⏰ **Task Scheduling:** Built-in Cron job system for handling background tasks and recurring operations.
|
package/bin/odac.js
CHANGED
|
@@ -16,6 +16,72 @@ const rl = readline.createInterface({
|
|
|
16
16
|
|
|
17
17
|
const ask = question => new Promise(resolve => rl.question(question, answer => resolve(answer.trim())))
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Interactive selection menu for CLI.
|
|
21
|
+
* @param {string} title Menu title
|
|
22
|
+
* @param {string[]} options List of choice strings
|
|
23
|
+
* @returns {Promise<number>} Selected index
|
|
24
|
+
*/
|
|
25
|
+
const select = async (title, options) => {
|
|
26
|
+
if (!process.stdout.isTTY) return 0
|
|
27
|
+
|
|
28
|
+
return new Promise(resolve => {
|
|
29
|
+
let current = 0
|
|
30
|
+
const hideCursor = '\u001B[?25l'
|
|
31
|
+
const showCursor = '\u001B[?25h'
|
|
32
|
+
|
|
33
|
+
// Calculate total lines the title occupies
|
|
34
|
+
const titleLines = title.split('\n')
|
|
35
|
+
const totalLines = titleLines.length + options.length
|
|
36
|
+
|
|
37
|
+
const render = () => {
|
|
38
|
+
// Clear all lines we previously wrote
|
|
39
|
+
titleLines.forEach(line => {
|
|
40
|
+
process.stdout.write('\r\x1b[K' + line + '\n')
|
|
41
|
+
})
|
|
42
|
+
options.forEach((opt, i) => {
|
|
43
|
+
const line = i === current ? `\x1b[36m ❯ ${opt}\x1b[0m` : ` ${opt}`
|
|
44
|
+
process.stdout.write('\r\x1b[K' + line + '\n')
|
|
45
|
+
})
|
|
46
|
+
process.stdout.write(`\x1b[${totalLines}A`) // Move back to the very start
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
process.stdout.write(hideCursor)
|
|
50
|
+
if (!process.stdin.isRaw) {
|
|
51
|
+
process.stdin.setRawMode(true)
|
|
52
|
+
process.stdin.resume()
|
|
53
|
+
}
|
|
54
|
+
readline.emitKeypressEvents(process.stdin)
|
|
55
|
+
|
|
56
|
+
render()
|
|
57
|
+
|
|
58
|
+
const onKey = (str, key) => {
|
|
59
|
+
if (key.name === 'up') {
|
|
60
|
+
current = current > 0 ? current - 1 : options.length - 1
|
|
61
|
+
render()
|
|
62
|
+
} else if (key.name === 'down') {
|
|
63
|
+
current = current < options.length - 1 ? current + 1 : 0
|
|
64
|
+
render()
|
|
65
|
+
} else if (key.name === 'return' || key.name === 'enter') {
|
|
66
|
+
cleanup()
|
|
67
|
+
process.stdout.write(`\x1b[${totalLines}B\n`) // Move down past everything
|
|
68
|
+
resolve(current)
|
|
69
|
+
} else if (key.ctrl && key.name === 'c') {
|
|
70
|
+
cleanup()
|
|
71
|
+
process.exit()
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const cleanup = () => {
|
|
76
|
+
process.stdin.removeListener('keypress', onKey)
|
|
77
|
+
if (process.stdin.isRaw) process.stdin.setRawMode(false)
|
|
78
|
+
process.stdout.write(showCursor)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
process.stdin.on('keypress', onKey)
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
|
|
19
85
|
/**
|
|
20
86
|
* Resolves Tailwind CSS paths and ensures required directories/files exist.
|
|
21
87
|
* Supports multiple CSS entry points from 'view/css'.
|
|
@@ -73,6 +139,94 @@ function getTailwindConfigs() {
|
|
|
73
139
|
return configs
|
|
74
140
|
}
|
|
75
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Manages the AI Agent skills synchronization.
|
|
144
|
+
* @param {string} targetDir The directory to sync skills into.
|
|
145
|
+
*/
|
|
146
|
+
async function manageSkills(targetDir = process.cwd()) {
|
|
147
|
+
const aiSourceDir = path.resolve(__dirname, '../docs/ai')
|
|
148
|
+
|
|
149
|
+
if (!fs.existsSync(aiSourceDir)) {
|
|
150
|
+
console.error('❌ AI components not found in framework.')
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const options = [
|
|
155
|
+
'Antigravity / Cascade (.agent/skills)',
|
|
156
|
+
'Claude / Projects (.claude/skills)',
|
|
157
|
+
'Continue (.continue/skills)',
|
|
158
|
+
'Cursor (.cursor/skills)',
|
|
159
|
+
'Kilo Code (.kilocode/skills)',
|
|
160
|
+
'Kiro CLI (.kiro/skills)',
|
|
161
|
+
'Qwen Code (.qwen/skills)',
|
|
162
|
+
'Windsurf (.windsurf/skills)',
|
|
163
|
+
'Custom Path',
|
|
164
|
+
'Skip / Cancel'
|
|
165
|
+
]
|
|
166
|
+
|
|
167
|
+
const choiceIndex = await select('\n🤖 \x1b[36mODAC AI Agent Skills Manager\x1b[0m\nSelect your AI Agent / IDE for setup:', options)
|
|
168
|
+
|
|
169
|
+
let targetSubDir = ''
|
|
170
|
+
let copySkillsOnly = true
|
|
171
|
+
|
|
172
|
+
const SKIP_INDEX = 9
|
|
173
|
+
const CUSTOM_INDEX = 8
|
|
174
|
+
|
|
175
|
+
if (choiceIndex === SKIP_INDEX) return // Skip / Cancel
|
|
176
|
+
|
|
177
|
+
switch (choiceIndex) {
|
|
178
|
+
case 0:
|
|
179
|
+
targetSubDir = '.agent/skills'
|
|
180
|
+
break
|
|
181
|
+
case 1:
|
|
182
|
+
targetSubDir = '.claude/skills'
|
|
183
|
+
break
|
|
184
|
+
case 2:
|
|
185
|
+
targetSubDir = '.continue/skills'
|
|
186
|
+
break
|
|
187
|
+
case 3:
|
|
188
|
+
targetSubDir = '.cursor/skills'
|
|
189
|
+
break
|
|
190
|
+
case 4:
|
|
191
|
+
targetSubDir = '.kilocode/skills'
|
|
192
|
+
break
|
|
193
|
+
case 5:
|
|
194
|
+
targetSubDir = '.kiro/skills'
|
|
195
|
+
break
|
|
196
|
+
case 6:
|
|
197
|
+
targetSubDir = '.qwen/skills'
|
|
198
|
+
break
|
|
199
|
+
case 7:
|
|
200
|
+
targetSubDir = '.windsurf/skills'
|
|
201
|
+
break
|
|
202
|
+
case CUSTOM_INDEX:
|
|
203
|
+
targetSubDir = await ask('Enter custom path: ')
|
|
204
|
+
copySkillsOnly = false
|
|
205
|
+
break
|
|
206
|
+
default:
|
|
207
|
+
return
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const targetBase = path.resolve(targetDir, targetSubDir)
|
|
211
|
+
const targetPath = targetBase
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
fs.mkdirSync(targetPath, {recursive: true})
|
|
215
|
+
|
|
216
|
+
if (copySkillsOnly) {
|
|
217
|
+
const skillsSource = path.join(aiSourceDir, 'skills')
|
|
218
|
+
fs.cpSync(skillsSource, targetPath, {recursive: true})
|
|
219
|
+
} else {
|
|
220
|
+
fs.cpSync(aiSourceDir, targetPath, {recursive: true})
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
console.log(`\n✨ AI skills successfully synced to: \x1b[32m${targetSubDir}\x1b[0m`)
|
|
224
|
+
console.log('Your AI Agent now has full knowledge of the ODAC Framework. 🚀')
|
|
225
|
+
} catch (err) {
|
|
226
|
+
console.error('❌ Failed to sync AI skills:', err.message)
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
76
230
|
async function run() {
|
|
77
231
|
if (command === 'init') {
|
|
78
232
|
const projectName = args[0] || '.'
|
|
@@ -110,10 +264,21 @@ async function run() {
|
|
|
110
264
|
}
|
|
111
265
|
|
|
112
266
|
console.log('\n✨ Project initialized successfully!')
|
|
267
|
+
|
|
268
|
+
// Interactive AI Skills setup
|
|
269
|
+
if (process.stdout.isTTY) {
|
|
270
|
+
const setupAIIndex = await select('\n🤖 Should we setup AI Agent skills for your IDE?', ['Yes', 'No'])
|
|
271
|
+
if (setupAIIndex === 0) {
|
|
272
|
+
await manageSkills(targetDir)
|
|
273
|
+
} else {
|
|
274
|
+
console.log('\n💡 \x1b[33mTip:\x1b[0m You can always run \x1b[36mnpx odac skills\x1b[0m later.')
|
|
275
|
+
}
|
|
276
|
+
}
|
|
113
277
|
} catch (error) {
|
|
114
278
|
console.error('❌ Error initializing project:', error.message)
|
|
115
279
|
}
|
|
116
280
|
} else if (command === 'dev') {
|
|
281
|
+
// ... existing dev logic ...
|
|
117
282
|
if (cluster.isPrimary) {
|
|
118
283
|
const configs = getTailwindConfigs()
|
|
119
284
|
const tails = []
|
|
@@ -134,20 +299,16 @@ async function run() {
|
|
|
134
299
|
|
|
135
300
|
tailwindProcess = spawn(cmd, args, {
|
|
136
301
|
stdio: ['pipe', 'ignore', 'pipe'],
|
|
137
|
-
shell: !useLocal,
|
|
302
|
+
shell: !useLocal,
|
|
138
303
|
cwd: process.cwd()
|
|
139
304
|
})
|
|
140
305
|
|
|
141
|
-
// Filter stderr: suppress status noise, forward only real errors
|
|
142
306
|
tailwindProcess.stderr.on('data', chunk => {
|
|
143
307
|
const raw = chunk.toString()
|
|
144
308
|
const lines = raw.split('\n')
|
|
145
309
|
for (const line of lines) {
|
|
146
|
-
// Strip ANSI escape codes to ensure reliable filtering
|
|
147
310
|
const clean = line.replace(/\x1B\[[0-9;]*[JKmsu]/g, '').trim()
|
|
148
|
-
|
|
149
311
|
if (!clean || clean.startsWith('Done in') || clean.startsWith('≈')) continue
|
|
150
|
-
|
|
151
312
|
process.stderr.write(`\x1b[31m[ODAC Style Error]\x1b[0m ${line}\n`)
|
|
152
313
|
}
|
|
153
314
|
})
|
|
@@ -166,7 +327,6 @@ async function run() {
|
|
|
166
327
|
|
|
167
328
|
startWatcher()
|
|
168
329
|
|
|
169
|
-
// Push a wrapper compatible with the cleanup function
|
|
170
330
|
tails.push({
|
|
171
331
|
kill: () => {
|
|
172
332
|
if (tailwindProcess) tailwindProcess.kill()
|
|
@@ -214,6 +374,8 @@ async function run() {
|
|
|
214
374
|
} else if (command === 'start') {
|
|
215
375
|
process.env.NODE_ENV = 'production'
|
|
216
376
|
require('../index.js')
|
|
377
|
+
} else if (command === 'skills') {
|
|
378
|
+
await manageSkills()
|
|
217
379
|
} else {
|
|
218
380
|
console.log('Usage:')
|
|
219
381
|
console.log(' npx odac init (Interactive mode)')
|
|
@@ -221,6 +383,7 @@ async function run() {
|
|
|
221
383
|
console.log(' npx odac dev (Development mode)')
|
|
222
384
|
console.log(' npx odac build (Production build)')
|
|
223
385
|
console.log(' npx odac start (Start server)')
|
|
386
|
+
console.log(' npx odac skills (Sync AI Agent skills)')
|
|
224
387
|
}
|
|
225
388
|
|
|
226
389
|
rl.close()
|
package/client/odac.js
CHANGED
|
@@ -142,13 +142,18 @@ if (typeof window !== 'undefined') {
|
|
|
142
142
|
xhr.onload = () => {
|
|
143
143
|
if (xhr.status >= 200 && xhr.status < 300) {
|
|
144
144
|
let responseData = xhr.responseText
|
|
145
|
-
|
|
145
|
+
const contentTypeHeader = xhr.getResponseHeader('Content-Type')
|
|
146
|
+
const isJson = dataType === 'json' || (contentTypeHeader && contentTypeHeader.includes('application/json'))
|
|
147
|
+
|
|
148
|
+
if (isJson) {
|
|
146
149
|
try {
|
|
147
150
|
responseData = JSON.parse(responseData)
|
|
148
151
|
} catch (e) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
+
if (dataType === 'json') {
|
|
153
|
+
console.error('JSON parse error:', e)
|
|
154
|
+
error(xhr, 'parseerror', e)
|
|
155
|
+
return
|
|
156
|
+
}
|
|
152
157
|
}
|
|
153
158
|
}
|
|
154
159
|
|
|
@@ -596,7 +601,7 @@ if (typeof window !== 'undefined') {
|
|
|
596
601
|
}
|
|
597
602
|
},
|
|
598
603
|
xhr: () => {
|
|
599
|
-
|
|
604
|
+
const xhr = new window.XMLHttpRequest()
|
|
600
605
|
xhr.upload.addEventListener(
|
|
601
606
|
'progress',
|
|
602
607
|
evt => {
|
|
@@ -661,24 +666,23 @@ if (typeof window !== 'undefined') {
|
|
|
661
666
|
this.#token.listener = true
|
|
662
667
|
}
|
|
663
668
|
if (!this.#token.hash.length) {
|
|
664
|
-
|
|
669
|
+
const req = new XMLHttpRequest()
|
|
665
670
|
req.open('GET', '/', false)
|
|
666
671
|
req.setRequestHeader('X-Odac', 'token')
|
|
667
672
|
req.setRequestHeader('X-Odac-Client', this.client())
|
|
668
673
|
req.send(null)
|
|
669
|
-
|
|
674
|
+
const req_data = JSON.parse(req.response)
|
|
670
675
|
if (req_data.token) this.#token.hash.push(req_data.token)
|
|
671
676
|
}
|
|
672
|
-
this.#token.hash.filter(n => n)
|
|
673
|
-
|
|
677
|
+
this.#token.hash = this.#token.hash.filter(n => n)
|
|
678
|
+
const return_token = this.#token.hash.shift()
|
|
674
679
|
if (!this.#token.hash.length)
|
|
675
680
|
this.#ajax({
|
|
676
681
|
url: '/',
|
|
677
682
|
type: 'GET',
|
|
678
683
|
headers: {'X-Odac': 'token', 'X-Odac-Client': this.client()},
|
|
679
684
|
success: data => {
|
|
680
|
-
|
|
681
|
-
if (result.token) this.#token.hash.push(result.token)
|
|
685
|
+
if (data.token) this.#token.hash.push(data.token)
|
|
682
686
|
}
|
|
683
687
|
})
|
|
684
688
|
return return_token
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# ODAC AI Agent Master Guide
|
|
2
|
+
|
|
3
|
+
Welcome! If you are reading this document, you are an AI Agent tasked with developing a project using the ODAC (Open Distributed Application Container) Framework.
|
|
4
|
+
|
|
5
|
+
ODAC is a high-performance, enterprise-grade, and zero-configuration Node.js framework. Your mission is to produce secure, scalable code while strictly adhering to the framework's principles.
|
|
6
|
+
|
|
7
|
+
## Core Mandate
|
|
8
|
+
When developing with ODAC, you must not only write code but also leverage the framework's optimized structures (Route, View, Database, Hub, etc.) in the most efficient way possible.
|
|
9
|
+
|
|
10
|
+
## Agent Principles
|
|
11
|
+
1. **Performance Focused**: Always aim for O(1) or O(n log n) complexity. Avoid inefficient queries like `SELECT *`.
|
|
12
|
+
2. **Security-by-Default**: Validate all input data using the `Validator`. Trust the auto-sanitizing View engine but maintain manual control in critical areas.
|
|
13
|
+
3. **Error Management**: Never swallow errors. Use framework-specific methods like `Odac.Request.error()` to return structured error messages.
|
|
14
|
+
4. **Clean Code**: Keep functions small (Single Responsibility) and adhere to ESM (ES Modules) standards.
|
|
15
|
+
|
|
16
|
+
## How to Use This Guide
|
|
17
|
+
The `skills/` directory contains a master `SKILL.md` that indexes specialized rules for each module. When adding a feature, you MUST refer to these rules and base your implementation on the "Reference Patterns".
|
|
18
|
+
|
|
19
|
+
## Core Rules
|
|
20
|
+
Detailed instructions are organized into Backend and Frontend categories:
|
|
21
|
+
|
|
22
|
+
### Backend
|
|
23
|
+
- `backend/authentication.md`: Sessions, Auth, and Realtime Hubs.
|
|
24
|
+
- `backend/config.md`: Configuration management and environment variables.
|
|
25
|
+
- `backend/controllers.md`: Request handling and Class-Based Controllers.
|
|
26
|
+
- `backend/cron.md`: Scheduled background tasks and automation.
|
|
27
|
+
- `backend/database.md`: High-performance query builder usage.
|
|
28
|
+
- `backend/forms.md`: Form processing and validation logic.
|
|
29
|
+
- `backend/ipc.md`: Inter-Process Communication and state sharing.
|
|
30
|
+
- `backend/mail.md`: Transactional email sending.
|
|
31
|
+
- `backend/request_response.md`: Handling Odac.Request and Odac.Response.
|
|
32
|
+
- `backend/routing.md`: Route definitions, Middlewares, and Error Pages.
|
|
33
|
+
- `backend/storage.md`: Persistent key-value storage (LMDB).
|
|
34
|
+
- `backend/streaming.md`: Server-Sent Events (SSE) and Streaming API.
|
|
35
|
+
- `backend/structure.md`: Project organization and Service Classes.
|
|
36
|
+
- `backend/translations.md`: Multi-language support (i18n).
|
|
37
|
+
- `backend/utilities.md`: Odac.Var (String Manipulation) and Flow Control.
|
|
38
|
+
- `backend/validation.md`: Input sanitization and security checks.
|
|
39
|
+
- `backend/views.md`: Template syntax, skeletons, and server-side JS.
|
|
40
|
+
|
|
41
|
+
### Frontend
|
|
42
|
+
- `frontend/core.md`: Framework lifecycle, page scoping, and storage.
|
|
43
|
+
- `frontend/forms.md`: AJAX form handling and API requests.
|
|
44
|
+
- `frontend/navigation.md`: AJAX Navigation (SPA) behavior.
|
|
45
|
+
- `frontend/realtime.md`: WebSocket Hubs and EventSource usage.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
**Remember:** You are an ODAC expert. Every line of code you write should reflect the "Enterprise" spirit of the framework.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: odac-framework
|
|
3
|
+
description: Comprehensive AI developer skills for the ODAC Framework (Backend & Frontend).
|
|
4
|
+
metadata:
|
|
5
|
+
tags: nodejs, framework, backend, frontend, architecture, security
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## When to use
|
|
9
|
+
|
|
10
|
+
Use this skill whenever you are developing an application with the ODAC Framework. It provides domain-specific knowledge for both server-side logic and client-side interactions.
|
|
11
|
+
|
|
12
|
+
## How to use
|
|
13
|
+
|
|
14
|
+
Read the specific rule files based on whether you are working on the Backend or Frontend:
|
|
15
|
+
|
|
16
|
+
### Backend Rules
|
|
17
|
+
- [backend/authentication.md](backend/authentication.md) - Sessions, Auth, and Realtime Hubs
|
|
18
|
+
- [backend/config.md](backend/config.md) - Configuration management and environment variables
|
|
19
|
+
- [backend/controllers.md](backend/controllers.md) - Request handling and Class-Based Controllers
|
|
20
|
+
- [backend/cron.md](backend/cron.md) - Scheduled background tasks and automation
|
|
21
|
+
- [backend/database.md](backend/database.md) - Query Builder and DB best practices
|
|
22
|
+
- [backend/forms.md](backend/forms.md) - Form processing and Validation logic
|
|
23
|
+
- [backend/ipc.md](backend/ipc.md) - Inter-Process Communication and state sharing
|
|
24
|
+
- [backend/mail.md](backend/mail.md) - Transactional email sending
|
|
25
|
+
- [backend/request_response.md](backend/request_response.md) - Handling Odac.Request and Odac.Response
|
|
26
|
+
- [backend/routing.md](backend/routing.md) - Route definitions, Middlewares, and Error Pages
|
|
27
|
+
- [backend/storage.md](backend/storage.md) - Persistent key-value storage (LMDB)
|
|
28
|
+
- [backend/streaming.md](backend/streaming.md) - Server-Sent Events (SSE) and Streaming API
|
|
29
|
+
- [backend/structure.md](backend/structure.md) - Project organization and Service Classes
|
|
30
|
+
- [backend/translations.md](backend/translations.md) - Multi-language support (i18n)
|
|
31
|
+
- [backend/utilities.md](backend/utilities.md) - Odac.Var (String Manipulation) and Flow Control
|
|
32
|
+
- [backend/validation.md](backend/validation.md) - Input sanitization and security checks
|
|
33
|
+
- [backend/views.md](backend/views.md) - Template syntax, skeletons, and server-side JS
|
|
34
|
+
|
|
35
|
+
### Frontend Rules
|
|
36
|
+
- [frontend/core.md](frontend/core.md) - Framework lifecycle, page scoping, and storage
|
|
37
|
+
- [frontend/forms.md](frontend/forms.md) - AJAX form handling and API requests
|
|
38
|
+
- [frontend/navigation.md](frontend/navigation.md) - AJAX Navigation (SPA) behavior
|
|
39
|
+
- [frontend/realtime.md](frontend/realtime.md) - WebSocket Hubs and EventSource usage
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Backend Authentication & Realtime Skill
|
|
2
|
+
|
|
3
|
+
Secure user authentication, session management, and bidirectional communication.
|
|
4
|
+
|
|
5
|
+
## Architectural Approach
|
|
6
|
+
ODAC provides built-in drivers for session handling (Memory/Redis) and multiple authentication flows including standard password-based login and passwordless Magic Links.
|
|
7
|
+
|
|
8
|
+
## Core Rules
|
|
9
|
+
1. **Auth Guard**: Protect routes using `Odac.Route.auth`.
|
|
10
|
+
2. **Session Integrity**: `Odac.Request.setSession()` MUST be called before accessing `Odac.Request.session()`.
|
|
11
|
+
3. **Password Security**: The framework handles BCrypt hashing automatically via `Odac.Auth`.
|
|
12
|
+
4. **Magic Links**: Use `Odac.Auth.magic(email)` for passwordless flows.
|
|
13
|
+
5. **Token Rotation**: Enterprise-grade rotation is enabled by default. It uses a 60s grace period to prevent race conditions in SPAs.
|
|
14
|
+
6. **Persistence**: Cookies use the configured `maxAge` to persist beyond browser closure.
|
|
15
|
+
## Reference Patterns
|
|
16
|
+
|
|
17
|
+
### 1. Standard Login & Check
|
|
18
|
+
```javascript
|
|
19
|
+
// Check if user is logged in
|
|
20
|
+
if (Odac.Auth.check()) {
|
|
21
|
+
const user = Odac.Auth.user();
|
|
22
|
+
const userId = Odac.Auth.id();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Log in a user manually
|
|
26
|
+
await Odac.Auth.login(userId);
|
|
27
|
+
|
|
28
|
+
// Log out
|
|
29
|
+
await Odac.Auth.logout();
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### 2. Magic Links (Passwordless)
|
|
33
|
+
```javascript
|
|
34
|
+
// Generate and send a magic link
|
|
35
|
+
const result = await Odac.Auth.magic('user@example.com', {
|
|
36
|
+
redirect: '/dashboard',
|
|
37
|
+
subject: 'Login to MyApp'
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (result.success) {
|
|
41
|
+
return { message: 'Link sent!' };
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 3. Session Management
|
|
46
|
+
```javascript
|
|
47
|
+
// Get/Set session data
|
|
48
|
+
Odac.session('key', 'value');
|
|
49
|
+
const val = Odac.session('key');
|
|
50
|
+
|
|
51
|
+
// Destroy session
|
|
52
|
+
Odac.session('key', null);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 4. Realtime Hubs (WebSockets)
|
|
56
|
+
```javascript
|
|
57
|
+
// Broadcast to everyone in a room
|
|
58
|
+
Odac.Hub.to('lobby').send('chat_message', { text: 'Hello!' });
|
|
59
|
+
|
|
60
|
+
// Targeted user broadcast
|
|
61
|
+
Odac.Hub.user(userId).send('notification', { text: 'New follower' });
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Security Best Practices
|
|
65
|
+
- **CSRF Protection**: Native `Odac.form` and `Odac.post` handle CSRF tokens automatically using `{{ TOKEN }}` in views.
|
|
66
|
+
- **Brute Force**: Always use `validator.brute()` on authentication endpoints.
|
|
67
|
+
- **Passwordless Preference**: Consider using Magic Links for enhanced security and better user experience.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Backend Configuration Skill
|
|
2
|
+
|
|
3
|
+
Managing application settings using `odac.json` and environment variables.
|
|
4
|
+
|
|
5
|
+
## Architectural Approach
|
|
6
|
+
ODAC uses `odac.json` for structure and `.env` for secrets. Values can be accessed via `Odac.Config` or the `Odac.env()` helper.
|
|
7
|
+
|
|
8
|
+
## Core Rules
|
|
9
|
+
1. **Direct Access**: Use `Odac.Config.key` for settings in `odac.json`.
|
|
10
|
+
2. **Environment Variables**: Use `${VAR_NAME}` in `odac.json` to map to `.env` values.
|
|
11
|
+
3. **Encapsulation**: Never hardcode credentials. Always use `.env`.
|
|
12
|
+
|
|
13
|
+
## Reference Patterns
|
|
14
|
+
### 1. Accessing Config
|
|
15
|
+
```javascript
|
|
16
|
+
// From odac.json: { "app": { "name": "My App" } }
|
|
17
|
+
const appName = Odac.Config.app.name;
|
|
18
|
+
|
|
19
|
+
// From .env: API_KEY=secret
|
|
20
|
+
const apiKey = Odac.env('API_KEY');
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### 2. odac.json Structure
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"mysql": {
|
|
27
|
+
"host": "${DB_HOST}",
|
|
28
|
+
"password": "${DB_PASSWORD}"
|
|
29
|
+
},
|
|
30
|
+
"debug": true
|
|
31
|
+
}
|
|
32
|
+
```
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Backend Controllers Skill
|
|
2
|
+
|
|
3
|
+
Controllers are the bridge between routes and views/services. This skill covers how to write clean, professional controllers in ODAC.
|
|
4
|
+
|
|
5
|
+
## Architectural Approach
|
|
6
|
+
ODAC supports both simple function-based controllers and class-based controllers. For enterprise-grade applications, class-based controllers are strongly recommended for better organization.
|
|
7
|
+
|
|
8
|
+
## Core Rules
|
|
9
|
+
1. **Class-Based Controllers**: Use classes to group related logic. Each method handles a specific route.
|
|
10
|
+
2. **Naming Convention**: Controllers are usually PascalCase (e.g., `UserController.js`).
|
|
11
|
+
3. **Route Mapping**: Use the `ControllerName@MethodName` syntax in routes.
|
|
12
|
+
4. **Automatic Injection**: Methods receive the `Odac` instance as the first argument automatically.
|
|
13
|
+
|
|
14
|
+
## Reference Patterns
|
|
15
|
+
|
|
16
|
+
### 1. Class-Based Controller (Recommended)
|
|
17
|
+
```javascript
|
|
18
|
+
// controller/User.js
|
|
19
|
+
class User {
|
|
20
|
+
// GET /users
|
|
21
|
+
async index(Odac) {
|
|
22
|
+
const users = await Odac.Service.get('User').all();
|
|
23
|
+
return Odac.View.make('user.list', { users });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// GET /users/{id}
|
|
27
|
+
async show(Odac) {
|
|
28
|
+
const id = Odac.Request.input('id');
|
|
29
|
+
const user = await Odac.Service.get('User').find(id);
|
|
30
|
+
return Odac.return(user);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// POST /users
|
|
34
|
+
async store(Odac) {
|
|
35
|
+
const data = Odac.Request.post();
|
|
36
|
+
// Logic to save user...
|
|
37
|
+
return { success: true };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports = User;
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 2. Simple Function-Based Controller
|
|
45
|
+
```javascript
|
|
46
|
+
// controller/Home.js
|
|
47
|
+
module.exports = function(Odac) {
|
|
48
|
+
return "Welcome to ODAC!";
|
|
49
|
+
};
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 3. Usage in Routes
|
|
53
|
+
```javascript
|
|
54
|
+
// route/web.js
|
|
55
|
+
Odac.Route.get('/users', 'User@index');
|
|
56
|
+
Odac.Route.get('/home', 'Home'); // Auto-calls the exported function
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Best Practices
|
|
60
|
+
- **Keep it Thin**: Controllers should only handle request parsing and response formatting. Core logic belongs in **Services**.
|
|
61
|
+
- **Asynchronous Handling**: Always use `async/await` for database or network operations.
|
|
62
|
+
- **Structured Returns**: Use `Odac.return()` for JSON or `Odac.View.make()` for rendering.
|