odac 1.4.7 → 1.4.9

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.
Files changed (54) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/client/odac.js +1 -1
  3. package/docs/ai/README.md +3 -2
  4. package/docs/ai/skills/SKILL.md +3 -2
  5. package/docs/ai/skills/backend/authentication.md +12 -6
  6. package/docs/ai/skills/backend/database.md +183 -12
  7. package/docs/ai/skills/backend/ipc.md +71 -12
  8. package/docs/ai/skills/backend/migrations.md +23 -0
  9. package/docs/ai/skills/backend/odac-var.md +155 -0
  10. package/docs/ai/skills/backend/utilities.md +1 -1
  11. package/docs/ai/skills/frontend/forms.md +23 -1
  12. package/docs/backend/04-routing/09-websocket-quick-reference.md +21 -1
  13. package/docs/backend/04-routing/09-websocket.md +22 -1
  14. package/docs/backend/08-database/05-write-behind-cache.md +230 -0
  15. package/docs/backend/08-database/06-read-through-cache.md +206 -0
  16. package/docs/backend/10-authentication/01-authentication-basics.md +53 -0
  17. package/docs/backend/10-authentication/05-session-management.md +12 -3
  18. package/docs/backend/13-utilities/01-odac-var.md +13 -19
  19. package/docs/backend/13-utilities/02-ipc.md +117 -0
  20. package/docs/frontend/03-forms/01-form-handling.md +15 -2
  21. package/docs/index.json +1 -1
  22. package/package.json +1 -1
  23. package/src/Auth.js +17 -0
  24. package/src/Database/Migration.js +219 -3
  25. package/src/Database/ReadCache.js +174 -0
  26. package/src/Database/WriteBuffer.js +605 -0
  27. package/src/Database.js +95 -1
  28. package/src/Ipc.js +343 -81
  29. package/src/Odac.js +2 -1
  30. package/src/Storage.js +4 -2
  31. package/src/Validator.js +1 -1
  32. package/src/Var.js +1 -0
  33. package/src/WebSocket.js +80 -23
  34. package/test/Database/Migration/migrate_column.test.js +168 -0
  35. package/test/Database/ReadCache/crossTable.test.js +179 -0
  36. package/test/Database/ReadCache/get.test.js +128 -0
  37. package/test/Database/ReadCache/invalidate.test.js +103 -0
  38. package/test/Database/ReadCache/proxy.test.js +184 -0
  39. package/test/Database/WriteBuffer/_recoverFromCheckpoint.test.js +207 -0
  40. package/test/Database/WriteBuffer/buffer.test.js +143 -0
  41. package/test/Database/WriteBuffer/flush.test.js +192 -0
  42. package/test/Database/WriteBuffer/get.test.js +72 -0
  43. package/test/Database/WriteBuffer/increment.test.js +118 -0
  44. package/test/Database/WriteBuffer/update.test.js +178 -0
  45. package/test/Database/insert.test.js +98 -0
  46. package/test/Ipc/hset.test.js +59 -0
  47. package/test/Ipc/incrBy.test.js +65 -0
  48. package/test/Ipc/lock.test.js +62 -0
  49. package/test/Ipc/rpush.test.js +68 -0
  50. package/test/Ipc/sadd.test.js +68 -0
  51. package/test/WebSocket/Client/fragmentation.test.js +130 -0
  52. package/test/WebSocket/Client/limits.test.js +10 -4
  53. package/test/WebSocket/Client/readyState.test.js +154 -0
  54. package/docs/backend/10-authentication/01-user-logins-with-authjs.md +0 -55
package/CHANGELOG.md CHANGED
@@ -1,5 +1,50 @@
1
1
  ### ⚙️ Engine Tuning
2
2
 
3
+ - migrate hashing from BCrypt to scrypt and update Odac.Var documentation and validation logic
4
+
5
+ ### ✨ What's New
6
+
7
+ - **auth:** add token tracking and retrieval method
8
+ - **database:** add read-through cache API for SELECT query results
9
+ - **migration:** add cross-driver default value comparison
10
+ - **migration:** add foreign key introspection and synchronization
11
+ - **websocket:** add readyState tracking and message fragmentation support
12
+
13
+ ### 📚 Documentation
14
+
15
+ - **forms:** add options to control message display for success and error handling
16
+ - **migrations:** add foreign keys and referential actions documentation
17
+
18
+ ### 🛠️ Fixes & Improvements
19
+
20
+ - **database:** add catch method to write operation thenable for Promise compatibility
21
+ - **pr-review:** apply ReadCache null sentinel, fix invalidation test, correct auth docs login example
22
+
23
+
24
+
25
+ ---
26
+
27
+ Powered by [⚡ ODAC](https://odac.run)
28
+
29
+ ### ✨ What's New
30
+
31
+ - **database:** add Write-Behind Cache with counter, update, and batch insert buffering
32
+ - **ipc,database:** extend Ipc with atomic ops and delegate WriteBuffer state to Ipc layer
33
+
34
+ ### 🛠️ Fixes & Improvements
35
+
36
+ - atomic queue drain, transaction-safe flush, hgetall clone & unhandled rejection
37
+ - **odac:** ensure proper token query parameter handling in get method
38
+ - **storage:** ensure synchronous operations for put and remove methods
39
+
40
+
41
+
42
+ ---
43
+
44
+ Powered by [⚡ ODAC](https://odac.run)
45
+
46
+ ### ⚙️ Engine Tuning
47
+
3
48
  - **test:** remove unused fsPromises and standardize async I/O in View tests
4
49
 
5
50
  ### 📚 Documentation
package/client/odac.js CHANGED
@@ -679,7 +679,7 @@ if (typeof window !== 'undefined') {
679
679
  }
680
680
 
681
681
  get(url, callback) {
682
- url = url + '?_token=' + this.token()
682
+ url = url + (url.includes('?') ? '&' : '?') + '_token=' + this.token()
683
683
  this.#ajax({url: url, success: callback})
684
684
  }
685
685
 
package/docs/ai/README.md CHANGED
@@ -24,7 +24,7 @@ Detailed instructions are organized into Backend and Frontend categories:
24
24
  - `backend/config.md`: Configuration management and environment variables.
25
25
  - `backend/controllers.md`: Request handling and Class-Based Controllers.
26
26
  - `backend/cron.md`: Scheduled background tasks and automation.
27
- - `backend/database.md`: High-performance query builder usage.
27
+ - `backend/database.md`: High-performance query builder usage, Write-Behind Cache (buffered counters, last-write-wins updates, batch inserts).
28
28
  - `backend/forms.md`: Form processing and validation logic.
29
29
  - `backend/ipc.md`: Inter-Process Communication and state sharing.
30
30
  - `backend/mail.md`: Transactional email sending.
@@ -34,7 +34,8 @@ Detailed instructions are organized into Backend and Frontend categories:
34
34
  - `backend/streaming.md`: Server-Sent Events (SSE) and Streaming API.
35
35
  - `backend/structure.md`: Project organization and Service Classes.
36
36
  - `backend/translations.md`: Multi-language support (i18n).
37
- - `backend/utilities.md`: Odac.Var (String Manipulation) and Flow Control.
37
+ - `backend/odac-var.md`: Odac.Var (Fluent String Manipulation, Hashing, Encryption).
38
+ - `backend/utilities.md`: Flow Control (abort, direct, session).
38
39
  - `backend/validation.md`: Input sanitization and security checks.
39
40
  - `backend/views.md`: Template syntax, skeletons, and server-side JS.
40
41
 
@@ -18,7 +18,7 @@ Read the specific rule files based on whether you are working on the Backend or
18
18
  - [backend/config.md](backend/config.md) - Configuration management and environment variables
19
19
  - [backend/controllers.md](backend/controllers.md) - Request handling and Class-Based Controllers
20
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
21
+ - [backend/database.md](backend/database.md) - Query Builder, Write-Behind Cache, and DB best practices
22
22
  - [backend/forms.md](backend/forms.md) - Form processing and Validation logic
23
23
  - [backend/ipc.md](backend/ipc.md) - Inter-Process Communication and state sharing
24
24
  - [backend/mail.md](backend/mail.md) - Transactional email sending
@@ -29,7 +29,8 @@ Read the specific rule files based on whether you are working on the Backend or
29
29
  - [backend/streaming.md](backend/streaming.md) - Server-Sent Events (SSE) and Streaming API
30
30
  - [backend/structure.md](backend/structure.md) - Project organization and Service Classes
31
31
  - [backend/translations.md](backend/translations.md) - Multi-language support (i18n)
32
- - [backend/utilities.md](backend/utilities.md) - Odac.Var (String Manipulation) and Flow Control
32
+ - [backend/odac-var.md](backend/odac-var.md) - Odac.Var (Fluent String Manipulation, Hashing, Encryption)
33
+ - [backend/utilities.md](backend/utilities.md) - Flow Control (abort, direct, session)
33
34
  - [backend/validation.md](backend/validation.md) - Input sanitization and security checks
34
35
  - [backend/views.md](backend/views.md) - Template syntax, skeletons, and server-side JS
35
36
 
@@ -19,21 +19,27 @@ ODAC provides built-in drivers for session handling (Memory/Redis) and multiple
19
19
  4. **Magic Links**: Use `Odac.Auth.magic(email)` for passwordless flows.
20
20
  5. **Token Rotation**: Enterprise-grade rotation is enabled by default. It uses a 60s grace period to prevent race conditions in SPAs.
21
21
  6. **Persistence**: Cookies use the configured `maxAge` to persist beyond browser closure.
22
+ 7. **Token Accessor**: Use `Odac.Auth.token()` to access the active auth session record (e.g., auth ID, IP, timestamps). Returns `false` if no active session.
23
+
22
24
  ## Reference Patterns
23
25
 
24
26
  ### 1. Standard Login & Check
25
27
  ```javascript
26
28
  // Check if user is logged in
27
- if (Odac.Auth.check()) {
28
- const user = Odac.Auth.user();
29
- const userId = Odac.Auth.id();
29
+ if (await Odac.Auth.check()) {
30
+ const user = Odac.Auth.user() // Full user object
31
+ const userId = Odac.Auth.user('id') // Specific user field
32
+
33
+ const authRecord = Odac.Auth.token() // Full auth token record
34
+ const authId = Odac.Auth.token('id') // Auth session ID from token table
35
+ const authIp = Odac.Auth.token('ip') // IP address of the session
30
36
  }
31
37
 
32
- // Log in a user manually
33
- await Odac.Auth.login(userId);
38
+ // Log in a user
39
+ await Odac.Auth.login({ email, password })
34
40
 
35
41
  // Log out
36
- await Odac.Auth.logout();
42
+ await Odac.Auth.logout()
37
43
  ```
38
44
 
39
45
  ### 2. Magic Links (Passwordless)
@@ -1,30 +1,112 @@
1
1
  ---
2
2
  name: backend-database-skill
3
- description: High-performance ODAC database querying patterns using the built-in query builder with secure and efficient data access.
3
+ description: High-performance ODAC database querying patterns including the Read-Through Cache for SELECT results and the Write-Behind Cache for counters, last-write-wins updates, and buffered batch inserts.
4
4
  metadata:
5
- tags: backend, database, query-builder, sql, indexing, performance, security
5
+ tags: backend, database, query-builder, sql, read-through-cache, write-behind-cache, performance, security
6
6
  ---
7
7
 
8
8
  # Backend Database Skill
9
9
 
10
- High-performance database operations using the ODAC Query Builder.
10
+ High-performance database operations using the ODAC Query Builder, Read-Through Cache, and Write-Behind Cache.
11
11
 
12
12
  ## Principles
13
13
  1. **Directness**: Avoid ORM overhead. Use fluent Query Builder.
14
14
  2. **Safety**: Always use parameterized queries (built-in).
15
15
  3. **Efficiency**: Index foreign keys. No `SELECT *`.
16
+ 4. **Read Caching**: Use `cache()` for frequently-read, rarely-changed data.
17
+ 5. **Write Coalescing**: Use `buffer` for high-frequency writes to avoid DB saturation.
16
18
 
17
- ## Patterns
19
+ ## Query Builder Patterns
18
20
  ```javascript
19
- const user = await Odac.DB.table('users')
21
+ const user = await Odac.DB.users
20
22
  .select('id', 'name', 'email')
21
23
  .where('status', 'active')
22
- .first();
24
+ .first()
23
25
 
24
- await Odac.DB.table('posts').insert({
25
- title: 'Hello',
26
- user_id: 1
27
- });
26
+ await Odac.DB.posts.insert({title: 'Hello', user_id: 1})
27
+
28
+ await Odac.DB.users.where('id', 1).update({last_login: new Date()})
29
+
30
+ const count = await Odac.DB.users.where('active', true).count() // → Number
31
+ ```
32
+
33
+ ## Read-Through Cache (`cache`)
34
+
35
+ The `cache()` API stores SELECT results in `Odac.Ipc` and serves subsequent requests from memory. Cache keys are SHA-256 hashes of the compiled SQL + bindings — identical queries always hit the same key.
36
+
37
+ ### When to suggest `cache`
38
+
39
+ Propose the Read-Through Cache when the data meets **all three** of these criteria:
40
+ 1. **High read frequency** — the same query runs on many requests.
41
+ 2. **Low write frequency** — the data changes infrequently (minutes to hours between updates).
42
+ 3. **Not user-specific** — the same result is safe to serve to all users (no per-user filtering that could leak data).
43
+
44
+ **Typical candidates:**
45
+ - Blog posts, articles, product listings
46
+ - Navigation menus, category trees, tag lists
47
+ - Site settings, feature flags, configuration values
48
+ - Public API responses that don't vary by user
49
+
50
+ **Do NOT suggest `cache` for:**
51
+ - Queries with user-specific WHERE clauses (data leakage risk)
52
+ - Account balances, inventory counts, or anything requiring real-time accuracy
53
+ - Queries inside transactions where consistency is critical
54
+
55
+ ### API
56
+
57
+ ```javascript
58
+ // Cache with explicit TTL (seconds)
59
+ const posts = await Odac.DB.posts.cache(60).where('active', true).select('id', 'title')
60
+
61
+ // Cache with default TTL (from config, default 300s)
62
+ const post = await Odac.DB.posts.cache().where({id: 5}).first()
63
+
64
+ // Named connection
65
+ const stats = await Odac.DB.analytics.events.cache(120).where('date', today).select()
66
+
67
+ // Manual invalidation — table-level
68
+ await Odac.DB.posts.cache.clear()
69
+
70
+ // Global invalidation
71
+ await Odac.DB.cache.clear('default', 'posts')
72
+ ```
73
+
74
+ ### Automatic Invalidation
75
+
76
+ `insert()`, `update()`, `delete()`, `del()`, and `truncate()` automatically purge all cached queries for that table. No manual `.cache.clear()` needed after writes.
77
+
78
+ ```javascript
79
+ // This update automatically clears all cached queries on 'posts'
80
+ await Odac.DB.posts.where({id: 5}).update({title: 'New Title'})
81
+ ```
82
+
83
+ **Cross-table (JOIN) invalidation:** When a cached query includes `JOIN` clauses, the cache key is registered in all joined tables' indexes. A write to any table involved in the query triggers invalidation automatically.
84
+
85
+ ```javascript
86
+ // Cached in both 'posts' and 'users' indexes
87
+ await Odac.DB.posts.cache(60).join('users', 'posts.user_id', '=', 'users.id').select(...)
88
+
89
+ // Writing to 'users' invalidates the cached join query above
90
+ await Odac.DB.users.where({id: 1}).update({name: 'New Name'})
91
+ ```
92
+
93
+ > **`buffer` + `cache` interaction:** `buffer` writes are deferred and do NOT trigger automatic invalidation. Call `Odac.DB.posts.cache.clear()` after a `buffer.flush()` if the cache must reflect the latest state immediately.
94
+
95
+ ### Read-Through Cache — Key Rules
96
+ 1. **Ipc-Backed**: Cache state goes through `Odac.Ipc`. Memory driver = Primary holds state; Redis driver = Redis holds state.
97
+ 2. **Horizontally Scalable**: With Redis driver, all servers share the same cache. Invalidation on one server is immediately visible to all others.
98
+ 3. **TTL is the safety net**: Even without explicit invalidation, cached data expires after TTL seconds.
99
+ 4. **maxKeys guard**: Once `maxKeys` is reached for a table, new entries are skipped — existing cache still served. Prevents unbounded memory growth.
100
+ 5. **Ipc-safe**: If `Odac.Ipc` is not initialized (e.g., in isolated test environments), invalidation is a no-op — never throws.
101
+
102
+ ## Configuration (`odac.json`)
103
+ ```json
104
+ {
105
+ "cache": {
106
+ "ttl": 300,
107
+ "maxKeys": 10000
108
+ }
109
+ }
28
110
  ```
29
111
 
30
112
  ## ID Strategy (NanoID)
@@ -43,7 +125,96 @@ module.exports = {
43
125
  }
44
126
 
45
127
  // ID is generated automatically
46
- await Odac.DB.table('posts').insert({title: 'Hello'});
128
+ await Odac.DB.posts.insert({title: 'Hello'})
129
+ ```
130
+
131
+ ## Write-Behind Cache (`buffer`)
132
+
133
+ The `buffer` API routes all writes through `Odac.Ipc`. The active IPC driver determines the scaling model — no code changes required when switching drivers.
134
+
135
+ - **`memory` driver** (default): Primary process holds all state; workers communicate via cluster IPC.
136
+ - **`redis` driver**: State lives in Redis — works transparently across multiple servers.
137
+
138
+ ### When to suggest `buffer`
139
+
140
+ Propose the Write-Behind Cache when the user's write operation meets **all three** of these criteria:
141
+ 1. **High frequency** — the same column or row is written on many requests (counters, timestamps, last-seen values).
142
+ 2. **Eventual consistency is acceptable** — a few seconds' delay before the value reaches the DB is fine.
143
+ 3. **Not safety-critical** — losing the buffered value in a crash has no financial, legal, or user-facing consequence.
144
+
145
+ **Typical candidates:**
146
+ - Page / post / download view counters (`increment`)
147
+ - Like, upvote, share counts (`increment`)
148
+ - User last-active timestamp, last IP (`update`)
149
+ - Analytics events, audit logs, activity streams (`insert`)
150
+ - Score accumulators, play counts, rating tallies (`increment`)
151
+
152
+ **Do NOT suggest `buffer` for:**
153
+ - Payments, charges, refunds, or any monetary operation
154
+ - Order creation, inventory decrements
155
+ - Anything the same request needs to read back immediately
156
+ - Inserts that return a generated ID the caller uses
157
+
158
+ When in doubt, use a direct DB call.
159
+
160
+ **Three operation types:**
161
+
162
+ ### 1. Counter Increment (Write Coalescing)
163
+ Accumulates deltas — multiple increments merge into one `UPDATE col = col + delta`.
164
+ `get()` returns `base + pending delta` (always current, no DB read needed).
165
+ ```javascript
166
+ // Increment — returns current total (DB base + buffered delta)
167
+ const views = await Odac.DB.posts.buffer.where(postId).increment('views')
168
+ const likes = await Odac.DB.posts.buffer.where(postId).increment('likes', 5)
169
+
170
+ // Read buffered counter (no extra DB round-trip)
171
+ const current = await Odac.DB.posts.buffer.where(postId).get('views')
172
+
173
+ // Composite key
174
+ await Odac.DB.post_stats.buffer.where({post_id: 1, date: '2026-04-01'}).increment('views')
175
+ ```
176
+
177
+ ### 2. Last-Write-Wins Update (Field Coalescing)
178
+ Multiple updates to the same row merge column maps — 50 requests = 1 `UPDATE` at flush.
179
+ ```javascript
180
+ // Columns are merged per row: first update + second update = single UPDATE at flush
181
+ await Odac.DB.users.buffer.where(userId).update({active_date: new Date()})
182
+ await Odac.DB.users.buffer.where(userId).update({last_ip: req.ip})
183
+ // → UPDATE users SET active_date = ?, last_ip = ? WHERE id = ? (one query)
184
+ ```
185
+
186
+ ### 3. Batch Insert (Queue)
187
+ Rows accumulate in memory; flushed in chunks of 1000. Auto-flushes when `maxQueueSize` is reached.
188
+ ```javascript
189
+ await Odac.DB.activity_log.buffer.insert({user_id: userId, action: 'page_view', meta: url})
190
+ ```
191
+
192
+ ### Manual Flush
193
+ ```javascript
194
+ await Odac.DB.posts.buffer.flush() // flush this table only
195
+ await Odac.DB.buffer.flush() // flush everything
196
+ ```
197
+
198
+ ## Write-Behind Cache — Key Rules
199
+ 1. **Ipc-Backed**: All buffer state goes through `Odac.Ipc`. Memory driver = Primary holds state; Redis driver = Redis holds state.
200
+ 2. **Horizontally Scalable**: With Redis driver, multiple servers share the same buffer state. Distributed lock (`Ipc.lock`) prevents duplicate flushes.
201
+ 3. **Crash-Safe (memory)**: LMDB checkpoint written every 30s. On restart, pending data is recovered and flushed before serving traffic.
202
+ 4. **Crash-Safe (redis)**: Redis persistence provides durability. LMDB checkpoints are skipped.
203
+ 5. **get() is authoritative**: Always returns `DB base + buffered delta`. Never stale.
204
+ 6. **Flush on shutdown**: `Database.close()` triggers a final flush automatically — no data loss on graceful shutdown.
205
+ 7. **Error resilience**: If a flush fails, data is retained in Ipc for the next cycle. Never lost silently.
206
+ 8. **NEVER use buffer for safety-critical writes**: Payment records, order confirmations, balance changes, inventory decrements — anything where data loss has real-world consequences MUST use direct DB transactions. The buffer does not guarantee delivery before a crash.
207
+
208
+ ## Configuration (`odac.json`)
209
+ ```json
210
+ {
211
+ "buffer": {
212
+ "flushInterval": 5000,
213
+ "checkpointInterval": 30000,
214
+ "maxQueueSize": 10000,
215
+ "primaryKey": "id"
216
+ }
217
+ }
47
218
  ```
48
219
 
49
220
  ## Migration Awareness
@@ -53,4 +224,4 @@ await Odac.DB.table('posts').insert({title: 'Hello'});
53
224
  4. **Indexes**: Keep index definitions in schema so add/drop is managed automatically.
54
225
  5. **Data Changes**: Use `migration/*.js` only for one-time data transformation.
55
226
 
56
- See: [migrations.md](./migrations.md)
227
+ See: [migrations.md](./migrations.md) | [write-behind-cache user docs](../../../backend/08-database/05-write-behind-cache.md) | [read-through-cache user docs](../../../backend/08-database/06-read-through-cache.md)
@@ -24,25 +24,81 @@ IPC abstracts the underlying driver, allowing seamless transition between `memor
24
24
  ### 1. Key-Value Storage
25
25
  ```javascript
26
26
  // Set value with optional TTL (seconds)
27
- await Odac.Ipc.set('maintenance_mode', true);
28
- await Odac.Ipc.set('cache_key', { data: 123 }, 60);
27
+ await Odac.Ipc.set('maintenance_mode', true)
28
+ await Odac.Ipc.set('cache_key', {data: 123}, 60)
29
29
 
30
30
  // Get value
31
- const status = await Odac.Ipc.get('maintenance_mode');
31
+ const status = await Odac.Ipc.get('maintenance_mode')
32
32
 
33
33
  // Delete value
34
- await Odac.Ipc.del('maintenance_mode');
34
+ await Odac.Ipc.del('maintenance_mode')
35
35
  ```
36
36
 
37
- ### 2. Pub/Sub Messaging
37
+ ### 2. Atomic Counters
38
38
  ```javascript
39
- // Subscribe to a channel (usually in a service or app start)
39
+ // Increment / decrement a numeric key (returns new value)
40
+ await Odac.Ipc.incrBy('page:views', 1) // → 1
41
+ await Odac.Ipc.incrBy('page:views', 5) // → 6
42
+ await Odac.Ipc.decrBy('page:views', 2) // → 4
43
+
44
+ // Read current value
45
+ const views = await Odac.Ipc.get('page:views') // → 4
46
+ ```
47
+
48
+ ### 3. Hash Maps
49
+ ```javascript
50
+ // Set / merge fields (last-write-wins per field)
51
+ await Odac.Ipc.hset('user:1', {active_date: new Date(), last_ip: '1.2.3.4'})
52
+ await Odac.Ipc.hset('user:1', {score: 42})
53
+
54
+ // Get all fields
55
+ const fields = await Odac.Ipc.hgetall('user:1')
56
+ // → {active_date: ..., last_ip: '1.2.3.4', score: 42}
57
+ ```
58
+
59
+ ### 4. Lists
60
+ ```javascript
61
+ // Append items to the right (returns new length)
62
+ await Odac.Ipc.rpush('events', {type: 'click'}, {type: 'view'}) // → 2
63
+
64
+ // Read range (inclusive, -1 = last item)
65
+ const items = await Odac.Ipc.lrange('events', 0, -1)
66
+ ```
67
+
68
+ ### 5. Sets
69
+ ```javascript
70
+ // Add members
71
+ await Odac.Ipc.sadd('online_users', 'user:1', 'user:2')
72
+
73
+ // Get all members
74
+ const users = await Odac.Ipc.smembers('online_users') // → ['user:1', 'user:2']
75
+
76
+ // Remove members (returns removed count)
77
+ await Odac.Ipc.srem('online_users', 'user:1')
78
+ ```
79
+
80
+ ### 6. Distributed Locks
81
+ ```javascript
82
+ // Acquire lock with TTL in seconds (returns true if acquired, false if already held)
83
+ const acquired = await Odac.Ipc.lock('flush:lock', 10)
84
+ if (!acquired) return // another process is holding the lock
85
+
86
+ try {
87
+ // ... critical section ...
88
+ } finally {
89
+ await Odac.Ipc.unlock('flush:lock')
90
+ }
91
+ ```
92
+
93
+ ### 7. Pub/Sub Messaging
94
+ ```javascript
95
+ // Subscribe to a channel
40
96
  await Odac.Ipc.subscribe('notifications', (msg) => {
41
- console.log('Received:', msg);
42
- });
97
+ console.log('Received:', msg)
98
+ })
43
99
 
44
100
  // Publish a message from any worker
45
- await Odac.Ipc.publish('notifications', { type: 'alert', text: 'System update' });
101
+ await Odac.Ipc.publish('notifications', {type: 'alert', text: 'System update'})
46
102
  ```
47
103
 
48
104
  ## Configuration
@@ -57,6 +113,9 @@ In `odac.json` or a config provider:
57
113
  ```
58
114
 
59
115
  ## Best Practices
60
- - **Async First**: All IPC operations are asynchronous.
61
- - **TTL Usage**: Always set a TTL for temporary cache data to prevent memory bloat.
62
- - **Scaling**: Switch to `redis` driver when deploying across multiple containers or servers.
116
+ - **Async First**: All IPC operations are asynchronous.
117
+ - **TTL Usage**: Always set a TTL for temporary cache data to prevent memory bloat.
118
+ - **Scaling**: Switch to `redis` driver when deploying across multiple containers or servers. No application code changes required.
119
+ - **Locks**: Always release locks in a `finally` block to prevent deadlocks.
120
+ - **Sets for indexes**: Use `sadd/smembers/srem` to track active keys — avoids expensive SCAN operations.
121
+ - **Atomic counters over get+set**: Use `incrBy`/`decrBy` instead of `get` → compute → `set` to prevent race conditions.
@@ -45,6 +45,29 @@ module.exports = {
45
45
  }
46
46
  ```
47
47
 
48
+ ### 2. Foreign Keys & Referential Actions
49
+ ```javascript
50
+ // schema/posts.js
51
+ module.exports = {
52
+ columns: {
53
+ id: {type: 'nanoid', primary: true},
54
+ user_id: {
55
+ type: 'integer',
56
+ unsigned: true,
57
+ nullable: false,
58
+ references: {table: 'users', column: 'id'},
59
+ onDelete: 'CASCADE', // 'CASCADE' | 'SET NULL' | 'RESTRICT' | 'NO ACTION' | 'SET DEFAULT'
60
+ onUpdate: 'CASCADE'
61
+ }
62
+ }
63
+ }
64
+ ```
65
+
66
+ **Rules:**
67
+ - `references` + `onDelete`/`onUpdate` are supported in table creation, column addition, and schema-diff updates to existing columns.
68
+ - Schema diff supports adding, dropping, and replacing an existing column's foreign key constraint. Use an imperative `migration/` file only for cases that require custom data movement or engine-specific manual SQL.
69
+ - Always index foreign key columns for query performance.
70
+
48
71
  ### NanoID Notes
49
72
  - `length` can be customized: `{type: 'nanoid', length: 12, primary: true}`.
50
73
  - If seed rows omit the nanoid field, ODAC fills it automatically.
@@ -0,0 +1,155 @@
1
+ ---
2
+ name: backend-odac-var-skill
3
+ description: Comprehensive API reference and practical patterns for the Odac.Var utility class. Includes every single method available in src/Var.js.
4
+ metadata:
5
+ tags: backend, utilities, strings, hashing, encryption, validation, mapping
6
+ ---
7
+
8
+ # Backend Odac.Var API Reference
9
+
10
+ `Odac.Var` is a high-performance utility class for string manipulation, security, and validation.
11
+
12
+ ## Core Principles
13
+ - **Direct Output**: Manipulation methods return raw values (string/object). Chaining is NOT supported.
14
+ - **Type Safety**: Methods handle various input types (strings, arrays, objects) gracefully.
15
+ - **Enterprise Security**: Uses scrypt for hashing and AES-256-CBC for encryption.
16
+
17
+ ---
18
+
19
+ ## 🛠 Full Method List & API Reference
20
+
21
+ ### 1. `.clear(...args)`
22
+ Removes specified strings from the value using global regex.
23
+ ```javascript
24
+ Odac.Var('a-b-c').clear('-'); // 'abc'
25
+ Odac.Var('hello123world').clear('1', '2', '3'); // 'helloworld'
26
+ ```
27
+
28
+ ### 2. `.contains(...args)`
29
+ Checks if the string contains **all** of the specified values (AND logic).
30
+ ```javascript
31
+ Odac.Var('hello world').contains('hello', 'world'); // true
32
+ ```
33
+
34
+ ### 3. `.containsAny(...args)`
35
+ Checks if the string contains **any** of the specified values (OR logic).
36
+ ```javascript
37
+ Odac.Var('hello world').containsAny('foo', 'world'); // true
38
+ ```
39
+
40
+ ### 4. `.date(format)`
41
+ Formats a date string/timestamp. Default: `Y-m-d H:i:s`.
42
+ - Tokens: `Y` (2024), `y` (24), `m` (01-12), `d` (01-31), `H` (00-23), `i` (00-59), `s` (00-59).
43
+ ```javascript
44
+ Odac.Var('2024-04-10').date('d/m/Y'); // '10/04/2024'
45
+ ```
46
+
47
+ ### 5. `.encrypt(key?)`
48
+ Encrypts the value using AES-256-CBC. Uses `Odac.Config.encrypt.key` by default. Returns Base64.
49
+ ```javascript
50
+ const secret = Odac.Var('data').encrypt();
51
+ ```
52
+
53
+ ### 6. `.decrypt(key?)`
54
+ Decrypts an AES-256-CBC Base64 string.
55
+ ```javascript
56
+ const data = Odac.Var(secret).decrypt();
57
+ ```
58
+
59
+ ### 7. `.hash()`
60
+ Generates a secure **scrypt** hash. Returns string prefixed with `$scrypt$`.
61
+ ```javascript
62
+ const hash = Odac.Var('password').hash();
63
+ ```
64
+
65
+ ### 8. `.hashCheck(check)`
66
+ Verifies a plain text string against an existing scrypt hash. Uses `timingSafeEqual`.
67
+ ```javascript
68
+ Odac.Var(hash).hashCheck('password'); // true
69
+ ```
70
+
71
+ ### 9. `.html()`
72
+ Escapes HTML special characters (`&`, `<`, `>`, `"`, `'`).
73
+ ```javascript
74
+ Odac.Var('<div>').html(); // '&lt;div&gt;'
75
+ ```
76
+
77
+ ### 10. `.is(...args)`
78
+ Validates string against **all** specified rules (AND logic).
79
+ ```javascript
80
+ Odac.Var('test@test.com').is('email'); // true
81
+ ```
82
+
83
+ ### 11. `.isAny(...args)`
84
+ Validates string against **any** specified rule (OR logic).
85
+ ```javascript
86
+ Odac.Var('user123').isAny('email', 'username'); // true
87
+ ```
88
+
89
+ ### 12. `.isBegin(...args)`
90
+ Checks if string starts with any of the specified values.
91
+ ```javascript
92
+ Odac.Var('https://odac.run').isBegin('http', 'https'); // true
93
+ ```
94
+
95
+ ### 13. `.isEnd(...args)`
96
+ Checks if string ends with any of the specified values.
97
+ ```javascript
98
+ Odac.Var('image.png').isEnd('.png', '.jpg'); // true
99
+ ```
100
+
101
+ ### 14. `.md5()`
102
+ Generates an MD5 hash of the value.
103
+ ```javascript
104
+ Odac.Var('hello').md5(); // '5d41402...'
105
+ ```
106
+
107
+ ### 15. `.replace(...args)`
108
+ Replaces values in the string. If the Var value is an object/array, it applies replacement recursively.
109
+ ```javascript
110
+ // String replacement
111
+ Odac.Var('Hello {{n}}').replace('{{n}}', 'Emre');
112
+
113
+ // Bulk replacement
114
+ Odac.Var('{{a}} {{b}}').replace({ '{{a}}': '1', '{{b}}': '2' });
115
+
116
+ // Recursive object replacement
117
+ Odac.Var({ key: '{{v}}' }).replace('{{v}}', 'data'); // { key: 'data' }
118
+ ```
119
+
120
+ ### 16. `.save(path)`
121
+ Saves the value to a file. **Auto-creates directories** recursively if they don't exist.
122
+ ```javascript
123
+ Odac.Var('content').save('./storage/logs/app.log');
124
+ ```
125
+
126
+ ### 17. `.slug(separator = '-')`
127
+ Generates a URL-friendly slug. Replaces non-alphanumeric chars with separator.
128
+ ```javascript
129
+ Odac.Var('Hello World!').slug(); // 'hello-world'
130
+ ```
131
+
132
+ ### 18. `.format(format)`
133
+ Formats a string based on a pattern.
134
+ - `?`: Placeholder for a single character from the input.
135
+ - `*`: Placeholder for the remaining part of the input.
136
+ ```javascript
137
+ Odac.Var('12345').format('??-???'); // '12-345'
138
+ Odac.Var('TR12345').format('?? *'); // 'TR 12345'
139
+ ```
140
+
141
+ ---
142
+
143
+ ## 🔍 Validation Rule Catalog (for `.is()` and `.isAny()`)
144
+ - `alpha`, `alphaspace`: `A-Z, a-z` (+ spaces).
145
+ - `alphanumeric`, `alphanumericspace`: `A-Z, a-z, 0-9` (+ spaces).
146
+ - `username`: `A-Z, a-z, 0-9` (Strictly alphanumeric).
147
+ - `numeric`, `float`: Numbers and decimals.
148
+ - `email`, `domain`, `url`: Standard web identifiers.
149
+ - `ip`, `host`, `mac`: Networking addresses.
150
+ - `json`: Check if value is valid JSON.
151
+ - `date`: Check if value is a valid date string.
152
+ - `hash`: Check if value follows `$scrypt$` hash format.
153
+ - `xss`: Check if value is free of HTML tags.
154
+ - `emoji`: Detect presence of emojis.
155
+ - `md5`: Check for 32-char hex string.
@@ -21,7 +21,7 @@ String manipulation, hashing, and flow control.
21
21
  ```javascript
22
22
  const slug = Odac.Var('My Post Title').slug();
23
23
  const isValid = Odac.Var('test@test.com').is('email');
24
- const password = Odac.Var('secret').hash(); // BCrypt
24
+ const password = Odac.Var('secret').hash(); // scrypt
25
25
  ```
26
26
 
27
27
  ### 2. Redirect and Abort