odac 1.4.6 → 1.4.8

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 (34) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/client/odac.js +1 -1
  3. package/docs/ai/README.md +1 -1
  4. package/docs/ai/skills/SKILL.md +1 -1
  5. package/docs/ai/skills/backend/database.md +103 -12
  6. package/docs/ai/skills/backend/ipc.md +71 -12
  7. package/docs/ai/skills/backend/views.md +6 -1
  8. package/docs/backend/00-getting-started/01-quick-start.md +77 -0
  9. package/docs/backend/07-views/03-template-syntax.md +5 -0
  10. package/docs/backend/07-views/04-request-data.md +13 -0
  11. package/docs/backend/08-database/05-write-behind-cache.md +230 -0
  12. package/docs/backend/13-utilities/02-ipc.md +117 -0
  13. package/docs/index.json +10 -0
  14. package/package.json +1 -1
  15. package/src/Database/WriteBuffer.js +605 -0
  16. package/src/Database.js +32 -1
  17. package/src/Ipc.js +343 -81
  18. package/src/Odac.js +2 -1
  19. package/src/Storage.js +4 -2
  20. package/src/View.js +33 -18
  21. package/test/Database/WriteBuffer/_recoverFromCheckpoint.test.js +207 -0
  22. package/test/Database/WriteBuffer/buffer.test.js +143 -0
  23. package/test/Database/WriteBuffer/flush.test.js +192 -0
  24. package/test/Database/WriteBuffer/get.test.js +72 -0
  25. package/test/Database/WriteBuffer/increment.test.js +118 -0
  26. package/test/Database/WriteBuffer/update.test.js +178 -0
  27. package/test/Ipc/hset.test.js +59 -0
  28. package/test/Ipc/incrBy.test.js +65 -0
  29. package/test/Ipc/lock.test.js +62 -0
  30. package/test/Ipc/rpush.test.js +68 -0
  31. package/test/Ipc/sadd.test.js +68 -0
  32. package/test/View/addNavigateAttribute.test.js +53 -0
  33. package/test/View/print.test.js +45 -1
  34. package/test/View/tags.test.js +132 -0
package/CHANGELOG.md CHANGED
@@ -1,3 +1,40 @@
1
+ ### ✨ What's New
2
+
3
+ - **database:** add Write-Behind Cache with counter, update, and batch insert buffering
4
+ - **ipc,database:** extend Ipc with atomic ops and delegate WriteBuffer state to Ipc layer
5
+
6
+ ### 🛠️ Fixes & Improvements
7
+
8
+ - atomic queue drain, transaction-safe flush, hgetall clone & unhandled rejection
9
+ - **odac:** ensure proper token query parameter handling in get method
10
+ - **storage:** ensure synchronous operations for put and remove methods
11
+
12
+
13
+
14
+ ---
15
+
16
+ Powered by [⚡ ODAC](https://odac.run)
17
+
18
+ ### ⚙️ Engine Tuning
19
+
20
+ - **test:** remove unused fsPromises and standardize async I/O in View tests
21
+
22
+ ### 📚 Documentation
23
+
24
+ - add quick start guide and register in documentation index
25
+
26
+ ### 🛠️ Fixes & Improvements
27
+
28
+ - add raw attribute support to `<odac get>` tag for unescaped HTML output
29
+ - improve title extraction logic and optimize data-odac-navigate attribute injection in View rendering
30
+ - prevent data-odac-navigate injection into closing HTML tags
31
+
32
+
33
+
34
+ ---
35
+
36
+ Powered by [⚡ ODAC](https://odac.run)
37
+
1
38
  ### ⚙️ Engine Tuning
2
39
 
3
40
  - replace global Odac reference with private instance and update dependency overrides for security hardening
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.
@@ -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
@@ -1,30 +1,32 @@
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 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, 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 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. **Write Coalescing**: Use `buffer` for high-frequency writes to avoid DB saturation.
16
17
 
17
- ## Patterns
18
+ ## Query Builder Patterns
18
19
  ```javascript
19
- const user = await Odac.DB.table('users')
20
+ const user = await Odac.DB.users
20
21
  .select('id', 'name', 'email')
21
22
  .where('status', 'active')
22
- .first();
23
+ .first()
23
24
 
24
- await Odac.DB.table('posts').insert({
25
- title: 'Hello',
26
- user_id: 1
27
- });
25
+ await Odac.DB.posts.insert({title: 'Hello', user_id: 1})
26
+
27
+ await Odac.DB.users.where('id', 1).update({last_login: new Date()})
28
+
29
+ const count = await Odac.DB.users.where('active', true).count() // → Number
28
30
  ```
29
31
 
30
32
  ## ID Strategy (NanoID)
@@ -43,7 +45,96 @@ module.exports = {
43
45
  }
44
46
 
45
47
  // ID is generated automatically
46
- await Odac.DB.table('posts').insert({title: 'Hello'});
48
+ await Odac.DB.posts.insert({title: 'Hello'})
49
+ ```
50
+
51
+ ## Write-Behind Cache (`buffer`)
52
+
53
+ The `buffer` API routes all writes through `Odac.Ipc`. The active IPC driver determines the scaling model — no code changes required when switching drivers.
54
+
55
+ - **`memory` driver** (default): Primary process holds all state; workers communicate via cluster IPC.
56
+ - **`redis` driver**: State lives in Redis — works transparently across multiple servers.
57
+
58
+ ### When to suggest `buffer`
59
+
60
+ Propose the Write-Behind Cache when the user's write operation meets **all three** of these criteria:
61
+ 1. **High frequency** — the same column or row is written on many requests (counters, timestamps, last-seen values).
62
+ 2. **Eventual consistency is acceptable** — a few seconds' delay before the value reaches the DB is fine.
63
+ 3. **Not safety-critical** — losing the buffered value in a crash has no financial, legal, or user-facing consequence.
64
+
65
+ **Typical candidates:**
66
+ - Page / post / download view counters (`increment`)
67
+ - Like, upvote, share counts (`increment`)
68
+ - User last-active timestamp, last IP (`update`)
69
+ - Analytics events, audit logs, activity streams (`insert`)
70
+ - Score accumulators, play counts, rating tallies (`increment`)
71
+
72
+ **Do NOT suggest `buffer` for:**
73
+ - Payments, charges, refunds, or any monetary operation
74
+ - Order creation, inventory decrements
75
+ - Anything the same request needs to read back immediately
76
+ - Inserts that return a generated ID the caller uses
77
+
78
+ When in doubt, use a direct DB call.
79
+
80
+ **Three operation types:**
81
+
82
+ ### 1. Counter Increment (Write Coalescing)
83
+ Accumulates deltas — multiple increments merge into one `UPDATE col = col + delta`.
84
+ `get()` returns `base + pending delta` (always current, no DB read needed).
85
+ ```javascript
86
+ // Increment — returns current total (DB base + buffered delta)
87
+ const views = await Odac.DB.posts.buffer.where(postId).increment('views')
88
+ const likes = await Odac.DB.posts.buffer.where(postId).increment('likes', 5)
89
+
90
+ // Read buffered counter (no extra DB round-trip)
91
+ const current = await Odac.DB.posts.buffer.where(postId).get('views')
92
+
93
+ // Composite key
94
+ await Odac.DB.post_stats.buffer.where({post_id: 1, date: '2026-04-01'}).increment('views')
95
+ ```
96
+
97
+ ### 2. Last-Write-Wins Update (Field Coalescing)
98
+ Multiple updates to the same row merge column maps — 50 requests = 1 `UPDATE` at flush.
99
+ ```javascript
100
+ // Columns are merged per row: first update + second update = single UPDATE at flush
101
+ await Odac.DB.users.buffer.where(userId).update({active_date: new Date()})
102
+ await Odac.DB.users.buffer.where(userId).update({last_ip: req.ip})
103
+ // → UPDATE users SET active_date = ?, last_ip = ? WHERE id = ? (one query)
104
+ ```
105
+
106
+ ### 3. Batch Insert (Queue)
107
+ Rows accumulate in memory; flushed in chunks of 1000. Auto-flushes when `maxQueueSize` is reached.
108
+ ```javascript
109
+ await Odac.DB.activity_log.buffer.insert({user_id: userId, action: 'page_view', meta: url})
110
+ ```
111
+
112
+ ### Manual Flush
113
+ ```javascript
114
+ await Odac.DB.posts.buffer.flush() // flush this table only
115
+ await Odac.DB.buffer.flush() // flush everything
116
+ ```
117
+
118
+ ## Write-Behind Cache — Key Rules
119
+ 1. **Ipc-Backed**: All buffer state goes through `Odac.Ipc`. Memory driver = Primary holds state; Redis driver = Redis holds state.
120
+ 2. **Horizontally Scalable**: With Redis driver, multiple servers share the same buffer state. Distributed lock (`Ipc.lock`) prevents duplicate flushes.
121
+ 3. **Crash-Safe (memory)**: LMDB checkpoint written every 30s. On restart, pending data is recovered and flushed before serving traffic.
122
+ 4. **Crash-Safe (redis)**: Redis persistence provides durability. LMDB checkpoints are skipped.
123
+ 5. **get() is authoritative**: Always returns `DB base + buffered delta`. Never stale.
124
+ 6. **Flush on shutdown**: `Database.close()` triggers a final flush automatically — no data loss on graceful shutdown.
125
+ 7. **Error resilience**: If a flush fails, data is retained in Ipc for the next cycle. Never lost silently.
126
+ 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.
127
+
128
+ ## Configuration (`odac.json`)
129
+ ```json
130
+ {
131
+ "buffer": {
132
+ "flushInterval": 5000,
133
+ "checkpointInterval": 30000,
134
+ "maxQueueSize": 10000,
135
+ "primaryKey": "id"
136
+ }
137
+ }
47
138
  ```
48
139
 
49
140
  ## Migration Awareness
@@ -53,4 +144,4 @@ await Odac.DB.table('posts').insert({title: 'Hello'});
53
144
  4. **Indexes**: Keep index definitions in schema so add/drop is managed automatically.
54
145
  5. **Data Changes**: Use `migration/*.js` only for one-time data transformation.
55
146
 
56
- See: [migrations.md](./migrations.md)
147
+ See: [migrations.md](./migrations.md) | [write-behind-cache user docs](../../../backend/08-database/05-write-behind-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.
@@ -18,7 +18,8 @@ Views in ODAC are logic-light but powerful. They support automatic XSS protectio
18
18
  3. **Data Binding — Two Equivalent Syntaxes**:
19
19
  - `<odac var="key" />`: Tag-based output (HTML-escaped, XSS-safe).
20
20
  - `{{ key }}`: Inline/interpolation output (HTML-escaped, XSS-safe). Identical behavior to `<odac var>`.
21
- - `<odac var="key" raw />` or `{!! key !!}`: Raw output (use with extreme caution).
21
+ - `<odac get="key" />`: Query Parameter output (HTML-escaped, XSS-safe).
22
+ - `<odac var="key" raw />`, `<odac get="key" raw />` or `{!! key !!}`: Raw output (use with extreme caution).
22
23
  4. **Choosing the Right Syntax**:
23
24
  - **Inside HTML attributes** (`src`, `alt`, `href`, `class`, `value`, etc.) → Always prefer `{{ }}`.
24
25
  - **Inline within text or mixed HTML** → Prefer `{{ }}` for short interpolations.
@@ -112,6 +113,10 @@ module.exports = function (Odac) {
112
113
  <!-- Inline text interpolation -->
113
114
  <p>Welcome, {{ user.name }}. You have {{ notifications }} new messages.</p>
114
115
 
116
+ <!-- Query parameter from URL (/page?q=search) -->
117
+ <p>Search Results for: <odac get="q" /></p>
118
+ <div class="raw-content"><odac get="htmlContent" raw /></div>
119
+
115
120
  <!-- Conditional -->
116
121
  <odac:if condition="stats.users > 100">
117
122
  <span class="badge">Popular!</span>
@@ -0,0 +1,77 @@
1
+ ## 🚀 Quick Start
2
+
3
+ ODAC is built for speed and zero-configuration. You can bootstrap a production-ready, high-performance web application in seconds.
4
+
5
+ ### 1. Requirements
6
+
7
+ - **Node.js:** 18.0.0 or higher.
8
+ - **npm:** 8.0.0 or higher.
9
+
10
+ ---
11
+
12
+ ### 2. Initialize Your Project
13
+
14
+ The standard way to start an ODAC project is using the interactive **init** command via `npx`. Run this in your terminal:
15
+
16
+ ```bash
17
+ npx odac init my-app
18
+ ```
19
+
20
+ **What this does:**
21
+ - Creates a new folder named `my-app`.
22
+ - Copies the ODAC enterprise skeleton (controllers, routes, views, etc.).
23
+ - Initializes `package.json` with the latest ODAC framework dependency.
24
+ - Runs `npm install` automatically.
25
+
26
+ ---
27
+
28
+ ### 3. Launch Development Mode
29
+
30
+ Navigate into your project directory and start the smart development server:
31
+
32
+ ```bash
33
+ cd my-app
34
+ npm run dev
35
+ ```
36
+
37
+ Your app is now live at `http://localhost:1071` (default port).
38
+
39
+ **Features enabled in Dev Mode:**
40
+ - **Hot-reloading:** The server and cluster workers restart instantly on backend changes.
41
+ - **Zero-Config Tailwind CSS v4:** Automatically watches and compiles your styles.
42
+ - **Detailed Stack Traces:** Helps you debug errors quickly in the browser and console.
43
+
44
+ ---
45
+
46
+ ### 4. Setup AI Agent Skills
47
+
48
+ ODAC is designed to be **AI-First**. It provides pre-built "skills" (knowledge files) that teach your AI coding assistant (like Antigravity, Claude, Cursor, or Windsurf) exactly how to write ODAC-compatible code.
49
+
50
+ Run this command inside your project root:
51
+
52
+ ```bash
53
+ npx odac skills
54
+ ```
55
+
56
+ Follow the interactive prompt to sync the documentation and patterns directly into your IDE's agent configuration folder.
57
+
58
+ ---
59
+
60
+ ### 5. Essential Commands
61
+
62
+ | Command | Description |
63
+ | :--- | :--- |
64
+ | `npm run dev` | Start development server with hot-reload & styles. |
65
+ | `npm run build` | Compile and minify styles for production. |
66
+ | `npm start` | Run the application in high-performance production mode. |
67
+ | `npx odac migrate` | Run pending database migrations. |
68
+ | `npx odac skills` | Sync/Update AI Agent knowledge files. |
69
+
70
+ ---
71
+
72
+ ### 6. Next Steps
73
+
74
+ - **Define Routes:** Open `route/web.js` to see how URLs are mapped to views.
75
+ - **Project Structure:** Learn how to organize your [file/folder layout](../02-structure/01-typical-project-layout.md).
76
+ - **Build Views:** Check the `view/` directory for your HTML templates.
77
+ - **Manage Database:** Update `odac.json` to connect to PostgreSQL, MySQL, or SQLite.
@@ -50,6 +50,10 @@ Access URL query parameters directly:
50
50
  <!-- URL: /search?q=laptop -->
51
51
  <odac get="q" />
52
52
  <!-- Output: laptop -->
53
+
54
+ <!-- Get raw query parameter (unescaped) -->
55
+ <odac get="htmlContent" raw />
56
+ <!-- Output: <b>Trusted HTML</b> -->
53
57
  ```
54
58
 
55
59
  **Note:** `<odac get>` is for URL parameters. For controller data, use `<odac var>`.
@@ -185,6 +189,7 @@ Full access to the Odac object in templates:
185
189
  | Raw HTML (inline) | `{!! x !!}` | [Variables](./03-variables.md) |
186
190
  | String | `<odac>text</odac>` | [Variables](./03-variables.md) |
187
191
  | Query Parameter | `<odac get="key" />` | [Request Data](./04-request-data.md) |
192
+ | Query Parameter Raw | `<odac get="key" raw />` | [Request Data](./04-request-data.md) |
188
193
  | Translation | `<odac translate>key</odac>` | [Translations](./07-translations.md) |
189
194
  | Translation Raw | `<odac translate raw>key</odac>` | [Translations](./07-translations.md) |
190
195
  | If | `<odac:if condition="x">` | [Conditionals](./05-conditionals.md) |
@@ -33,6 +33,19 @@ If a parameter doesn't exist, it safely returns an empty string:
33
33
 
34
34
  This prevents errors when parameters are optional.
35
35
 
36
+ ### Raw HTML Output
37
+
38
+ By default, `<odac get>` automatically escapes HTML special characters to prevent XSS attacks. If you need to output raw HTML from a query parameter (only from trusted sources), use the `raw` attribute:
39
+
40
+ ```html
41
+ <!-- URL: /page?content=%3Cb%3EHello%3C/b%3E -->
42
+
43
+ <odac get="content" raw />
44
+ <!-- Output: <b>Hello</b> -->
45
+ ```
46
+
47
+ **Security Warning:** Never use `raw` with query parameters if they can contain user-generated content. Query parameters are easily manipulated by users. Only use `raw` if you are certain the value is safe (e.g., predefined tokens).
48
+
36
49
  ### Difference: get vs var
37
50
 
38
51
  **`<odac get>` - Query Parameters (from URL):**