odac 1.4.0 → 1.4.2
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 +8 -0
- package/.github/workflows/release.yml +1 -1
- package/.releaserc.js +9 -2
- package/CHANGELOG.md +61 -0
- package/README.md +10 -0
- package/bin/odac.js +193 -2
- package/client/odac.js +32 -13
- package/docs/ai/skills/SKILL.md +4 -3
- package/docs/ai/skills/backend/authentication.md +7 -0
- package/docs/ai/skills/backend/config.md +7 -0
- package/docs/ai/skills/backend/controllers.md +7 -0
- package/docs/ai/skills/backend/cron.md +9 -2
- package/docs/ai/skills/backend/database.md +37 -2
- package/docs/ai/skills/backend/forms.md +112 -11
- package/docs/ai/skills/backend/ipc.md +7 -0
- package/docs/ai/skills/backend/mail.md +7 -0
- package/docs/ai/skills/backend/migrations.md +86 -0
- package/docs/ai/skills/backend/request_response.md +7 -0
- package/docs/ai/skills/backend/routing.md +7 -0
- package/docs/ai/skills/backend/storage.md +7 -0
- package/docs/ai/skills/backend/streaming.md +7 -0
- package/docs/ai/skills/backend/structure.md +8 -1
- package/docs/ai/skills/backend/translations.md +7 -0
- package/docs/ai/skills/backend/utilities.md +7 -0
- package/docs/ai/skills/backend/validation.md +138 -31
- package/docs/ai/skills/backend/views.md +7 -0
- package/docs/ai/skills/frontend/core.md +7 -0
- package/docs/ai/skills/frontend/forms.md +48 -13
- package/docs/ai/skills/frontend/navigation.md +7 -0
- package/docs/ai/skills/frontend/realtime.md +7 -0
- package/docs/backend/08-database/02-basics.md +49 -9
- package/docs/backend/08-database/04-migrations.md +259 -37
- package/package.json +1 -1
- package/src/Auth.js +82 -43
- package/src/Config.js +1 -1
- package/src/Database/ConnectionFactory.js +70 -0
- package/src/Database/Migration.js +1228 -0
- package/src/Database/nanoid.js +30 -0
- package/src/Database.js +157 -46
- package/src/Ipc.js +37 -0
- package/src/Odac.js +1 -1
- package/src/Route/Cron.js +11 -0
- package/src/Route.js +8 -0
- package/src/Server.js +77 -23
- package/src/Storage.js +15 -1
- package/src/Validator.js +22 -20
- package/template/schema/users.js +23 -0
- package/test/{Auth.test.js → Auth/check.test.js} +153 -6
- package/test/Client/data.test.js +91 -0
- package/test/Client/get.test.js +90 -0
- package/test/Client/storage.test.js +87 -0
- package/test/Client/token.test.js +82 -0
- package/test/Client/ws.test.js +86 -0
- package/test/Config/deepMerge.test.js +14 -0
- package/test/Config/init.test.js +66 -0
- package/test/Config/interpolate.test.js +35 -0
- package/test/Database/ConnectionFactory/buildConnectionConfig.test.js +13 -0
- package/test/Database/ConnectionFactory/buildConnections.test.js +31 -0
- package/test/Database/ConnectionFactory/resolveClient.test.js +12 -0
- package/test/Database/Migration/migrate_column.test.js +52 -0
- package/test/Database/Migration/migrate_files.test.js +70 -0
- package/test/Database/Migration/migrate_index.test.js +89 -0
- package/test/Database/Migration/migrate_nanoid.test.js +160 -0
- package/test/Database/Migration/migrate_seed.test.js +77 -0
- package/test/Database/Migration/migrate_table.test.js +88 -0
- package/test/Database/Migration/rollback.test.js +61 -0
- package/test/Database/Migration/snapshot.test.js +38 -0
- package/test/Database/Migration/status.test.js +41 -0
- package/test/Database/autoNanoid.test.js +215 -0
- package/test/Database/nanoid.test.js +19 -0
- package/test/Lang/constructor.test.js +25 -0
- package/test/Lang/get.test.js +65 -0
- package/test/Lang/set.test.js +49 -0
- package/test/Odac/init.test.js +42 -0
- package/test/Odac/instance.test.js +58 -0
- package/test/Route/{Middleware.test.js → Middleware/chaining.test.js} +5 -29
- package/test/Route/Middleware/use.test.js +35 -0
- package/test/{Route.test.js → Route/check.test.js} +4 -55
- package/test/Route/set.test.js +52 -0
- package/test/Route/ws.test.js +23 -0
- package/test/View/EarlyHints/cache.test.js +32 -0
- package/test/View/EarlyHints/extractFromHtml.test.js +143 -0
- package/test/View/EarlyHints/formatLinkHeader.test.js +33 -0
- package/test/View/EarlyHints/send.test.js +99 -0
- package/test/View/{Form.test.js → Form/generateFieldHtml.test.js} +2 -2
- package/test/View/constructor.test.js +22 -0
- package/test/View/print.test.js +19 -0
- package/test/WebSocket/Client/limits.test.js +55 -0
- package/test/WebSocket/Server/broadcast.test.js +33 -0
- package/test/WebSocket/Server/route.test.js +37 -0
- package/test/Client.test.js +0 -197
- package/test/Config.test.js +0 -112
- package/test/Lang.test.js +0 -92
- package/test/Odac.test.js +0 -88
- package/test/View/EarlyHints.test.js +0 -282
- package/test/WebSocket.test.js +0 -238
|
@@ -1,19 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: backend-forms-validation-skill
|
|
3
|
+
description: Practical ODAC form usage patterns for register/login, magic-login, custom actions, and automatic database insert.
|
|
4
|
+
metadata:
|
|
5
|
+
tags: backend, forms, validation, register, login, magic-login, request-processing
|
|
6
|
+
---
|
|
7
|
+
|
|
1
8
|
# Backend Forms & Validation Skill
|
|
2
9
|
|
|
3
|
-
|
|
10
|
+
ODAC forms for validation, authentication flows, and safe request handling.
|
|
4
11
|
|
|
5
12
|
## Rules
|
|
6
|
-
1. **
|
|
7
|
-
2. **
|
|
8
|
-
3. **
|
|
13
|
+
1. **Use ODAC form tags**: Prefer `<odac:register>`, `<odac:login>`, `<odac:magic-login>`, `<odac:form>` instead of manual raw form handlers.
|
|
14
|
+
2. **Do not add manual hidden security fields**: Keep forms clean and use ODAC defaults.
|
|
15
|
+
3. **Validation in template**: Define rules with `<odac:validate rule="..." message="..."/>`; they become both frontend HTML constraints and backend validator checks.
|
|
16
|
+
4. **Server-side enrichment**: Use `<odac:set>` for trusted fields (`compute`, `value`, `callback`, `if-empty`) instead of taking these values from user input.
|
|
17
|
+
5. **Action vs table**: In `<odac:form>`, use `table="..."` for automatic insert or `action="Class.method"` for custom business logic.
|
|
18
|
+
|
|
19
|
+
## Form Type Variants
|
|
20
|
+
|
|
21
|
+
### 1) Register Form
|
|
22
|
+
```html
|
|
23
|
+
<odac:register redirect="/dashboard" autologin="true">
|
|
24
|
+
<odac:input name="email" type="email" label="Email">
|
|
25
|
+
<odac:validate rule="required|email" message="Valid email required"/>
|
|
26
|
+
</odac:input>
|
|
27
|
+
|
|
28
|
+
<odac:input name="password" type="password" label="Password">
|
|
29
|
+
<odac:validate rule="required|minlen:8" message="Min 8 chars"/>
|
|
30
|
+
</odac:input>
|
|
31
|
+
|
|
32
|
+
<odac:set name="created_at" compute="now"/>
|
|
33
|
+
<odac:submit text="Register" loading="Processing..."/>
|
|
34
|
+
</odac:register>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### 2) Login Form
|
|
38
|
+
```html
|
|
39
|
+
<odac:login redirect="/panel">
|
|
40
|
+
<odac:input name="email" type="email" label="Email">
|
|
41
|
+
<odac:validate rule="required|email" message="Email required"/>
|
|
42
|
+
</odac:input>
|
|
43
|
+
|
|
44
|
+
<odac:input name="password" type="password" label="Password">
|
|
45
|
+
<odac:validate rule="required" message="Password required"/>
|
|
46
|
+
</odac:input>
|
|
47
|
+
|
|
48
|
+
<odac:submit text="Login" loading="Logging in..."/>
|
|
49
|
+
</odac:login>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 3) Magic Login Form
|
|
53
|
+
```html
|
|
54
|
+
<odac:magic-login redirect="/dashboard" email-label="Work Email" submit-text="Send Link" />
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```html
|
|
58
|
+
<odac:magic-login redirect="/dashboard">
|
|
59
|
+
<odac:input name="email" type="email" label="Email">
|
|
60
|
+
<odac:validate rule="required|email" message="Valid email required"/>
|
|
61
|
+
</odac:input>
|
|
62
|
+
<odac:submit text="Send Magic Link" loading="Sending..."/>
|
|
63
|
+
</odac:magic-login>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 4) Custom Form with Automatic DB Insert
|
|
67
|
+
```html
|
|
68
|
+
<odac:form table="waitlist" redirect="/" success="Thank you!" clear="true">
|
|
69
|
+
<odac:input name="email" type="email" label="Email">
|
|
70
|
+
<odac:validate rule="required|email|unique" message="Email already exists"/>
|
|
71
|
+
</odac:input>
|
|
72
|
+
|
|
73
|
+
<odac:set name="created_at" compute="now"/>
|
|
74
|
+
<odac:set name="ip" compute="ip"/>
|
|
75
|
+
<odac:submit text="Join" loading="Joining..."/>
|
|
76
|
+
</odac:form>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 5) Custom Form with Controller Action
|
|
80
|
+
```html
|
|
81
|
+
<odac:form action="Contact.submit" clear="false">
|
|
82
|
+
<odac:input name="subject" type="text" label="Subject">
|
|
83
|
+
<odac:validate rule="required|minlen:3" message="Subject is too short"/>
|
|
84
|
+
</odac:input>
|
|
85
|
+
<odac:submit text="Send" loading="Sending..."/>
|
|
86
|
+
</odac:form>
|
|
87
|
+
```
|
|
9
88
|
|
|
10
|
-
## Patterns
|
|
11
89
|
```javascript
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
90
|
+
module.exports = class Contact {
|
|
91
|
+
constructor(Odac) {
|
|
92
|
+
this.Odac = Odac
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async submit(form) {
|
|
96
|
+
const data = form.data
|
|
97
|
+
if (!data.subject) return form.error('subject', 'Subject required')
|
|
98
|
+
return form.success('Message sent', '/thank-you')
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
17
102
|
|
|
18
|
-
|
|
103
|
+
## Field-Level Variants
|
|
104
|
+
- **Input types**: `text`, `email`, `password`, `number`, `url`, `textarea`, `checkbox`.
|
|
105
|
+
- **Validation mapping**: `required|minlen|maxlen|min|max|alpha|alphanumeric|numeric|email|url|accepted`.
|
|
106
|
+
- **Pass-through attrs**: Unrecognized `<odac:input ...>` attributes are preserved into generated HTML input/textarea.
|
|
107
|
+
- **Skip persistence**: Use `skip` on `<odac:input>` to validate a field but exclude it from final payload.
|
|
108
|
+
- **Unique shorthand**: `unique` attribute on `<odac:input>` enables auth-register uniqueness list.
|
|
109
|
+
|
|
110
|
+
## Patterns
|
|
111
|
+
```javascript
|
|
112
|
+
// Access validated data in action-driven custom form
|
|
113
|
+
Odac.Route.class.post('/contact', class Contact {
|
|
114
|
+
async submit(form) {
|
|
115
|
+
const {email, message} = form.data
|
|
116
|
+
if (!email || !message) return form.error('_odac_form', 'Missing input')
|
|
117
|
+
return form.success('Saved successfully')
|
|
118
|
+
}
|
|
119
|
+
})
|
|
19
120
|
```
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: backend-ipc-skill
|
|
3
|
+
description: ODAC inter-process communication guidance for memory and Redis drivers, shared state, and distributed coordination.
|
|
4
|
+
metadata:
|
|
5
|
+
tags: backend, ipc, redis, cluster, distributed-state, synchronization
|
|
6
|
+
---
|
|
7
|
+
|
|
1
8
|
# Backend IPC (Inter-Process Communication) Skill
|
|
2
9
|
|
|
3
10
|
ODAC provides a built-in IPC system to share data and sync states across application workers or multiple servers.
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: backend-mail-skill
|
|
3
|
+
description: Transactional email integration patterns in ODAC using fluent mail APIs, templating, and transport configuration.
|
|
4
|
+
metadata:
|
|
5
|
+
tags: backend, mail, smtp, transactional-email, templating, notifications
|
|
6
|
+
---
|
|
7
|
+
|
|
1
8
|
# Backend Mail Skill
|
|
2
9
|
|
|
3
10
|
Sending transactional emails using the fluent `Odac.Mail` service.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: backend-migrations-skill
|
|
3
|
+
description: Schema-first ODAC migration strategy for deterministic database evolution, index sync, and cluster-safe execution.
|
|
4
|
+
metadata:
|
|
5
|
+
tags: backend, migrations, schema, database-evolution, indexes, cluster-safety
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Backend Migrations Skill
|
|
9
|
+
|
|
10
|
+
Schema-first, zero-config migration strategy for ODAC.
|
|
11
|
+
|
|
12
|
+
## Architectural Approach
|
|
13
|
+
ODAC migrations are **declarative**. The `schema/` directory is the single source of truth for final DB state. The migration engine diffs desired schema vs current DB and applies create/alter/drop operations automatically.
|
|
14
|
+
|
|
15
|
+
## Core Rules
|
|
16
|
+
1. **Source of Truth**: Always update `schema/*.js` files, not historical migration chains, for structural changes.
|
|
17
|
+
2. **Auto Execution**: Migrations run automatically during app startup via `Database.init()`.
|
|
18
|
+
3. **Cluster Safety**: Auto-migration runs only on `cluster.isPrimary` to prevent race conditions.
|
|
19
|
+
4. **Index Sync**: Define indexes in schema; engine adds/removes them automatically.
|
|
20
|
+
5. **Drop Behavior**: If a column/index is removed from schema, it is removed from DB on next startup.
|
|
21
|
+
6. **Seeds**: Use `seed` + `seedKey` for idempotent reference data.
|
|
22
|
+
7. **NanoID Columns**: `type: 'nanoid'` maps to string columns and missing values are auto-generated on insert/seed.
|
|
23
|
+
8. **Data Transformations**: Use imperative files under `migration/` only for one-time data migration logic.
|
|
24
|
+
|
|
25
|
+
## Reference Patterns
|
|
26
|
+
### 1. Schema File (Final State)
|
|
27
|
+
```javascript
|
|
28
|
+
// schema/users.js
|
|
29
|
+
'use strict'
|
|
30
|
+
|
|
31
|
+
module.exports = {
|
|
32
|
+
columns: {
|
|
33
|
+
id: {type: 'nanoid', primary: true},
|
|
34
|
+
email: {type: 'string', length: 255, nullable: false},
|
|
35
|
+
role: {type: 'enum', values: ['admin', 'user'], default: 'user'},
|
|
36
|
+
timestamps: {type: 'timestamps'}
|
|
37
|
+
},
|
|
38
|
+
indexes: [
|
|
39
|
+
{columns: ['email'], unique: true}
|
|
40
|
+
],
|
|
41
|
+
seed: [
|
|
42
|
+
{email: 'admin@example.com', role: 'admin'}
|
|
43
|
+
],
|
|
44
|
+
seedKey: 'email'
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### NanoID Notes
|
|
49
|
+
- `length` can be customized: `{type: 'nanoid', length: 12, primary: true}`.
|
|
50
|
+
- If seed rows omit the nanoid field, ODAC fills it automatically.
|
|
51
|
+
- If seed rows provide an explicit nanoid value, ODAC keeps it unchanged.
|
|
52
|
+
|
|
53
|
+
### 2. Multi-Database Layout
|
|
54
|
+
```
|
|
55
|
+
schema/
|
|
56
|
+
users.js # default DB
|
|
57
|
+
analytics/
|
|
58
|
+
events.js # analytics DB
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 3. Imperative Data Migration (One-Time)
|
|
62
|
+
```javascript
|
|
63
|
+
// migration/20260225_001_backfill_roles.js
|
|
64
|
+
module.exports = {
|
|
65
|
+
async up(db) {
|
|
66
|
+
await db('users').whereNull('role').update({role: 'user'})
|
|
67
|
+
},
|
|
68
|
+
async down(db) {
|
|
69
|
+
await db('users').where('role', 'user').update({role: null})
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 4. CLI Operations
|
|
75
|
+
```bash
|
|
76
|
+
npx odac migrate
|
|
77
|
+
npx odac migrate:status
|
|
78
|
+
npx odac migrate:rollback
|
|
79
|
+
npx odac migrate:snapshot
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Performance and Safety Notes
|
|
83
|
+
- Keep schema declarations deterministic and minimal.
|
|
84
|
+
- Prefer additive changes; drops are destructive and should be intentional.
|
|
85
|
+
- Ensure high-cardinality lookup columns are indexed in schema definitions.
|
|
86
|
+
- For very large tables, plan expensive column rewrites as dedicated data migrations.
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: backend-request-response-skill
|
|
3
|
+
description: ODAC request parsing and response composition patterns for consistent, secure, and predictable API behavior.
|
|
4
|
+
metadata:
|
|
5
|
+
tags: backend, request, response, headers, status-codes, json, api
|
|
6
|
+
---
|
|
7
|
+
|
|
1
8
|
# Backend Request & Response Skill
|
|
2
9
|
|
|
3
10
|
Handling incoming data and sending structured responses.
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: backend-routing-middleware-skill
|
|
3
|
+
description: High-performance ODAC routing and middleware orchestration for secure request pipelines and scalable URL mapping.
|
|
4
|
+
metadata:
|
|
5
|
+
tags: backend, routing, middleware, pipeline, auth, performance, url-matching
|
|
6
|
+
---
|
|
7
|
+
|
|
1
8
|
# Backend Routing & Middleware Skill
|
|
2
9
|
|
|
3
10
|
Routing manages the request pipeline, directing URLs to controllers while applying security and business logic via middlewares.
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: backend-persistent-storage-skill
|
|
3
|
+
description: Embedded ODAC storage usage patterns with LMDB for sub-millisecond key-value persistence across workers.
|
|
4
|
+
metadata:
|
|
5
|
+
tags: backend, storage, lmdb, key-value, persistence, high-performance
|
|
6
|
+
---
|
|
7
|
+
|
|
1
8
|
# Backend Persistent Storage Skill
|
|
2
9
|
|
|
3
10
|
ODAC provides a high-performance, embedded key-value store using LMDB, exposed via `Odac.Storage`.
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: backend-streaming-sse-skill
|
|
3
|
+
description: Server-Sent Events streaming patterns in ODAC for realtime one-way updates with safe connection lifecycle management.
|
|
4
|
+
metadata:
|
|
5
|
+
tags: backend, streaming, sse, realtime, event-stream, connection-lifecycle
|
|
6
|
+
---
|
|
7
|
+
|
|
1
8
|
# Backend Streaming API Skill
|
|
2
9
|
|
|
3
10
|
Real-time data streaming using Server-Sent Events (SSE).
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: backend-structure-services-skill
|
|
3
|
+
description: ODAC project organization rules for directory structure, service classes, and request-scoped architecture.
|
|
4
|
+
metadata:
|
|
5
|
+
tags: backend, structure, services, architecture, request-scope, organization
|
|
6
|
+
---
|
|
7
|
+
|
|
1
8
|
# Backend Structure & Services Skill
|
|
2
9
|
|
|
3
10
|
ODAC follows a strictly organized directory structure and focuses on request-scoped architecture. This skill explains how to organize code and use Service Classes.
|
|
@@ -31,7 +38,7 @@ class User {
|
|
|
31
38
|
|
|
32
39
|
async getProfile(id) {
|
|
33
40
|
// Access database or auth via this.Odac
|
|
34
|
-
return await this.Odac.
|
|
41
|
+
return await this.Odac.DB.table('users').where('id', id).first();
|
|
35
42
|
}
|
|
36
43
|
}
|
|
37
44
|
module.exports = User;
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: backend-translations-i18n-skill
|
|
3
|
+
description: Internationalization patterns in ODAC for locale files, placeholders, and multilingual rendering workflows.
|
|
4
|
+
metadata:
|
|
5
|
+
tags: backend, i18n, translations, locale, placeholders, multilingual
|
|
6
|
+
---
|
|
7
|
+
|
|
1
8
|
# Backend Translations (i18n) Skill
|
|
2
9
|
|
|
3
10
|
ODAC provides built-in support for internationalization, allowing for easy multi-language application development.
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: backend-utilities-skill
|
|
3
|
+
description: Practical ODAC utility patterns for string processing, hashing, encryption, and request flow control.
|
|
4
|
+
metadata:
|
|
5
|
+
tags: backend, utilities, strings, hashing, encryption, flow-control
|
|
6
|
+
---
|
|
7
|
+
|
|
1
8
|
# Backend Utilities Skill
|
|
2
9
|
|
|
3
10
|
String manipulation, hashing, and flow control.
|
|
@@ -1,53 +1,160 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
name: backend-validation-skill
|
|
3
|
+
description: Detailed ODAC Validator usage for request validation, security checks, brute-force protection, and consistent API responses.
|
|
4
|
+
metadata:
|
|
5
|
+
tags: backend, validation, fluent-api, input-security, brute-force, error-handling
|
|
6
|
+
---
|
|
2
7
|
|
|
3
|
-
|
|
8
|
+
# Backend Validation Skill
|
|
4
9
|
|
|
5
|
-
|
|
6
|
-
Validation should happen as early as possible in the request lifecycle. The `Validator` service handles automatic error formatting and frontend integration.
|
|
10
|
+
ODAC validation should be centralized with the fluent `Validator` API and returned in framework-standard result format.
|
|
7
11
|
|
|
8
12
|
## Core Rules
|
|
9
|
-
1. **
|
|
10
|
-
2. **
|
|
11
|
-
3. **
|
|
12
|
-
4. **
|
|
13
|
+
1. **Create validator per request**: Use `const validator = Odac.validator()`.
|
|
14
|
+
2. **Fail fast**: Run all checks, then immediately return on `await validator.error()`.
|
|
15
|
+
3. **Field-first messages**: Assign a specific `.message(...)` per check chain.
|
|
16
|
+
4. **Use standard result shape**: Return `await validator.result('Validation failed')` for errors.
|
|
17
|
+
5. **Protect sensitive flows**: Add `await validator.brute(n)` on login/reset/auth endpoints.
|
|
18
|
+
|
|
19
|
+
## Minimal Flow
|
|
20
|
+
```javascript
|
|
21
|
+
module.exports = async Odac => {
|
|
22
|
+
const validator = Odac.validator()
|
|
23
|
+
|
|
24
|
+
validator.post('email').check('required|email').message('Valid email required')
|
|
25
|
+
validator.post('password').check('required|minlen:8').message('Password must be at least 8 characters')
|
|
26
|
+
|
|
27
|
+
if (await validator.error()) {
|
|
28
|
+
await validator.brute(5)
|
|
29
|
+
return await validator.result('Validation failed')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return await validator.success({ok: true})
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## API Surface
|
|
37
|
+
- `post(key)`: Validate POST payload field.
|
|
38
|
+
- `get(key)`: Validate querystring field.
|
|
39
|
+
- `var(name, value)`: Validate computed/custom value.
|
|
40
|
+
- `file(name)`: Validate uploaded file object.
|
|
41
|
+
- `check(rules | boolean)`: Apply pipe rules or direct boolean validation.
|
|
42
|
+
- `message(text)`: Set message for the latest check on current field.
|
|
43
|
+
- `error()`: Runs validation and returns `true` if any error exists.
|
|
44
|
+
- `result(message?, data?)`: Returns ODAC-standard response object.
|
|
45
|
+
- `success(dataOrMessage?)`: Convenience wrapper for success payload.
|
|
46
|
+
- `brute(maxAttempts = 5)`: Tracks failed attempts per hour/page/ip.
|
|
47
|
+
|
|
48
|
+
## Rule Catalog
|
|
49
|
+
|
|
50
|
+
### Type & format rules
|
|
51
|
+
- `required`, `accepted`
|
|
52
|
+
- `numeric`, `float`
|
|
53
|
+
- `alpha`, `alphaspace`, `alphanumeric`, `alphanumericspace`, `username`
|
|
54
|
+
- `email`, `ip`, `mac`, `domain`, `url`
|
|
55
|
+
- `array`, `date`, `xss`
|
|
56
|
+
|
|
57
|
+
### Length & value rules
|
|
58
|
+
- `len:X`, `minlen:X`, `maxlen:X`
|
|
59
|
+
- `min:X`, `max:X`
|
|
60
|
+
- `equal:value`, `not:value`
|
|
61
|
+
- `same:field`, `different:field`
|
|
62
|
+
|
|
63
|
+
### String/date matching rules
|
|
64
|
+
- `in:substring`, `notin:substring`
|
|
65
|
+
- `regex:pattern`
|
|
66
|
+
- `mindate:YYYY-MM-DD`, `maxdate:YYYY-MM-DD`
|
|
67
|
+
|
|
68
|
+
### Auth/security rules
|
|
69
|
+
- `usercheck`: Must be authenticated.
|
|
70
|
+
- `user:field`: Input must match authenticated user field.
|
|
71
|
+
- `disposable`: Email must be disposable.
|
|
72
|
+
- `!disposable`: Email must not be disposable.
|
|
73
|
+
|
|
74
|
+
### Inverse rules
|
|
75
|
+
- Prefix any rule with `!` to invert: `!required`, `!email`, `!equal:admin`.
|
|
13
76
|
|
|
14
77
|
## Reference Patterns
|
|
15
78
|
|
|
16
|
-
### 1
|
|
79
|
+
### 1) Multi-check per field with specific errors
|
|
17
80
|
```javascript
|
|
18
|
-
module.exports = async
|
|
19
|
-
const validator = Odac.
|
|
81
|
+
module.exports = async Odac => {
|
|
82
|
+
const validator = Odac.validator()
|
|
20
83
|
|
|
21
84
|
validator
|
|
22
|
-
.post('
|
|
23
|
-
.
|
|
85
|
+
.post('password')
|
|
86
|
+
.check('required').message('Password is required')
|
|
87
|
+
.check('minlen:8').message('Minimum 8 characters')
|
|
88
|
+
.check('regex:[A-Z]').message('At least one uppercase letter')
|
|
89
|
+
.check('regex:[0-9]').message('At least one number')
|
|
24
90
|
|
|
25
91
|
if (await validator.error()) {
|
|
26
|
-
return validator.result('Please fix input errors')
|
|
92
|
+
return await validator.result('Please fix input errors')
|
|
27
93
|
}
|
|
28
94
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
95
|
+
return await validator.success('Success')
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 2) GET + POST + VAR together
|
|
100
|
+
```javascript
|
|
101
|
+
module.exports = async Odac => {
|
|
102
|
+
const validator = Odac.validator()
|
|
103
|
+
const plan = await Odac.request('plan')
|
|
104
|
+
|
|
105
|
+
validator.get('page').check('numeric|min:1').message('Invalid page')
|
|
106
|
+
validator.post('email').check('required|email|!disposable').message('Corporate email required')
|
|
107
|
+
validator.var('plan', plan).check('in:pro').message('Only pro plan is allowed')
|
|
108
|
+
|
|
109
|
+
if (await validator.error()) return await validator.result('Validation failed')
|
|
110
|
+
return await validator.success({ok: true})
|
|
111
|
+
}
|
|
32
112
|
```
|
|
33
113
|
|
|
34
|
-
###
|
|
114
|
+
### 3) Boolean check for business rules
|
|
35
115
|
```javascript
|
|
36
|
-
|
|
116
|
+
module.exports = async Odac => {
|
|
117
|
+
const validator = Odac.validator()
|
|
118
|
+
const canPublish = await somePermissionCheck(Odac)
|
|
119
|
+
|
|
120
|
+
validator.post('title').check('required').message('Title required')
|
|
121
|
+
validator.var('permission', null).check(canPublish).message('No publish permission')
|
|
122
|
+
|
|
123
|
+
if (await validator.error()) return await validator.result('Validation failed')
|
|
124
|
+
return await validator.success('Published')
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 4) Brute-force on auth endpoint
|
|
129
|
+
```javascript
|
|
130
|
+
module.exports = async Odac => {
|
|
131
|
+
const validator = Odac.validator()
|
|
132
|
+
|
|
133
|
+
validator.post('email').check('required|email').message('Email required')
|
|
134
|
+
validator.post('password').check('required').message('Password required')
|
|
135
|
+
|
|
136
|
+
if (await validator.error()) {
|
|
137
|
+
await validator.brute(5)
|
|
138
|
+
return await validator.result('Login failed')
|
|
139
|
+
}
|
|
37
140
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
validator.var('auth', null).check('usercheck').message('Authentication required');
|
|
141
|
+
return await validator.success('OK')
|
|
142
|
+
}
|
|
41
143
|
```
|
|
42
144
|
|
|
43
|
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
145
|
+
## Response Contract
|
|
146
|
+
- **Success**:
|
|
147
|
+
- `result.success: true`
|
|
148
|
+
- optional `result.message`
|
|
149
|
+
- optional `data`
|
|
150
|
+
- **Failure**:
|
|
151
|
+
- `result.success: false`
|
|
152
|
+
- `errors.{field}` map
|
|
153
|
+
- global errors may use `errors._odac_form`
|
|
49
154
|
|
|
50
155
|
## Best Practices
|
|
51
|
-
-
|
|
52
|
-
-
|
|
53
|
-
-
|
|
156
|
+
- Keep validation at route/controller entry; do not defer to deep service layers.
|
|
157
|
+
- Use separate `check()` calls when you need rule-specific messages.
|
|
158
|
+
- Prefer `var()` for derived values instead of re-reading mutable request state.
|
|
159
|
+
- Use `xss` for text fields that can later be rendered in HTML.
|
|
160
|
+
- Always combine auth endpoints with `brute()` to reduce credential-stuffing risk.
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: backend-views-templates-skill
|
|
3
|
+
description: ODAC server-side rendering guidelines for template performance, skeleton layouts, and safe output rendering.
|
|
4
|
+
metadata:
|
|
5
|
+
tags: backend, views, templates, ssr, xss-protection, skeleton, rendering
|
|
6
|
+
---
|
|
7
|
+
|
|
1
8
|
# Backend Views & Templates Skill
|
|
2
9
|
|
|
3
10
|
High-performance server-side rendering using ODAC's optimized template engine.
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: frontend-core-skill
|
|
3
|
+
description: Core ODAC frontend architecture patterns based on actions, lifecycle hooks, and event-driven UI behavior.
|
|
4
|
+
metadata:
|
|
5
|
+
tags: frontend, odac-js, actions, lifecycle, events, ui-architecture
|
|
6
|
+
---
|
|
7
|
+
|
|
1
8
|
# Frontend Core Skill
|
|
2
9
|
|
|
3
10
|
The foundational principles of the `odac.js` library for building reactive and interactive user interfaces.
|
|
@@ -1,21 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: frontend-forms-api-skill
|
|
3
|
+
description: odac.js form submission patterns for parser-generated ODAC forms and predictable AJAX request handling.
|
|
4
|
+
metadata:
|
|
5
|
+
tags: frontend, forms, ajax, odac-form, register, login, magic-login
|
|
6
|
+
---
|
|
7
|
+
|
|
1
8
|
# Frontend Forms & API Skill
|
|
2
9
|
|
|
3
|
-
Handling AJAX form submissions
|
|
10
|
+
Handling ODAC AJAX form submissions generated from server-side form parsing.
|
|
4
11
|
|
|
5
12
|
## Rules
|
|
6
|
-
1. **
|
|
7
|
-
2. **
|
|
8
|
-
3. **
|
|
13
|
+
1. **Bind by form selector**: Use `Odac.form({ form: 'selector' }, callback)` or short form `Odac.form('selector', callback)`.
|
|
14
|
+
2. **Leverage parser-generated forms**: `odac-register`, `odac-login`, `odac-magic-login`, and `odac-custom-form` are all auto-bound on page load and after every AJAX navigation.
|
|
15
|
+
3. **Expect JSON result shape**: Handle `result.success`, `result.message`, `result.redirect`, and `errors` in callback.
|
|
16
|
+
4. **Message/clear control**: Use `messages` and `clear` options for UX behavior.
|
|
9
17
|
|
|
10
18
|
## Patterns
|
|
11
19
|
```javascript
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
// 1) Bind a parsed custom form
|
|
21
|
+
Odac.form('form[data-odac-form]')
|
|
22
|
+
|
|
23
|
+
// 2) Bind parsed register/login forms explicitly (optional)
|
|
24
|
+
Odac.form('form[data-odac-register]')
|
|
25
|
+
Odac.form('form[data-odac-login]')
|
|
26
|
+
|
|
27
|
+
// 3) Bind parsed magic-login form manually
|
|
28
|
+
Odac.form('form[data-odac-magic-login]', response => {
|
|
29
|
+
if (response?.result?.success && !response.result.redirect) {
|
|
30
|
+
const info = document.querySelector('[data-status]')
|
|
31
|
+
if (info) info.textContent = response.result.message
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
// 4) Advanced options: disable auto clear and hide success messages
|
|
36
|
+
Odac.form(
|
|
37
|
+
{form: 'form[data-odac-form]', clear: false, messages: ['error']},
|
|
38
|
+
response => {
|
|
39
|
+
if (response?.result?.success && response.result.redirect) {
|
|
40
|
+
window.location.href = response.result.redirect
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
// 5) Manual GET request helper
|
|
46
|
+
Odac.get('/api/status', data => {
|
|
47
|
+
const status = document.querySelector('[data-api-status]')
|
|
48
|
+
if (status) status.textContent = String(data?.status ?? '')
|
|
49
|
+
})
|
|
21
50
|
```
|
|
51
|
+
|
|
52
|
+
## Response Handling Contract
|
|
53
|
+
- **Success**: `response.result.success === true`
|
|
54
|
+
- **Redirect**: `response.result.redirect` exists when server wants navigation
|
|
55
|
+
- **Form errors**: `response.errors.{fieldName}` maps to `odac-form-error="fieldName"`
|
|
56
|
+
- **Global form error**: `response.errors._odac_form`
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: frontend-navigation-spa-skill
|
|
3
|
+
description: Single-page navigation patterns in odac.js for smooth transitions, route control, and lifecycle-safe execution.
|
|
4
|
+
metadata:
|
|
5
|
+
tags: frontend, navigation, spa, ajax-navigation, page-lifecycle, transitions
|
|
6
|
+
---
|
|
7
|
+
|
|
1
8
|
# Frontend Navigation & SPA Skill
|
|
2
9
|
|
|
3
10
|
Smooth transitions and single-page application behavior using `odac.js`.
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: frontend-realtime-websocket-skill
|
|
3
|
+
description: Realtime frontend communication patterns in odac.js using shared WebSockets, SSE, and resilient reconnect behavior.
|
|
4
|
+
metadata:
|
|
5
|
+
tags: frontend, realtime, websocket, sse, sharedworker, auto-reconnect
|
|
6
|
+
---
|
|
7
|
+
|
|
1
8
|
# Frontend Realtime & WebSocket Skill
|
|
2
9
|
|
|
3
10
|
Real-time bidirectional communication and server-sent events with high efficiency.
|