odac 1.4.8 → 1.4.10

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 (37) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/docs/ai/README.md +2 -1
  3. package/docs/ai/skills/SKILL.md +2 -1
  4. package/docs/ai/skills/backend/authentication.md +12 -6
  5. package/docs/ai/skills/backend/database.md +85 -5
  6. package/docs/ai/skills/backend/migrations.md +23 -0
  7. package/docs/ai/skills/backend/odac-var.md +155 -0
  8. package/docs/ai/skills/backend/utilities.md +1 -1
  9. package/docs/ai/skills/frontend/forms.md +23 -1
  10. package/docs/backend/04-routing/09-websocket-quick-reference.md +21 -1
  11. package/docs/backend/04-routing/09-websocket.md +22 -1
  12. package/docs/backend/08-database/06-read-through-cache.md +206 -0
  13. package/docs/backend/10-authentication/01-authentication-basics.md +53 -0
  14. package/docs/backend/10-authentication/05-session-management.md +12 -3
  15. package/docs/backend/13-utilities/01-odac-var.md +13 -19
  16. package/docs/frontend/03-forms/01-form-handling.md +15 -2
  17. package/docs/index.json +1 -1
  18. package/package.json +1 -1
  19. package/src/Auth.js +17 -0
  20. package/src/Database/Migration.js +321 -10
  21. package/src/Database/ReadCache.js +174 -0
  22. package/src/Database/WriteBuffer.js +15 -1
  23. package/src/Database.js +78 -1
  24. package/src/Validator.js +1 -1
  25. package/src/Var.js +1 -0
  26. package/src/WebSocket.js +80 -23
  27. package/test/Database/Migration/migrate_column.test.js +311 -0
  28. package/test/Database/ReadCache/crossTable.test.js +179 -0
  29. package/test/Database/ReadCache/get.test.js +128 -0
  30. package/test/Database/ReadCache/invalidate.test.js +103 -0
  31. package/test/Database/ReadCache/proxy.test.js +184 -0
  32. package/test/Database/WriteBuffer/insert.test.js +118 -0
  33. package/test/Database/insert.test.js +98 -0
  34. package/test/WebSocket/Client/fragmentation.test.js +130 -0
  35. package/test/WebSocket/Client/limits.test.js +10 -4
  36. package/test/WebSocket/Client/readyState.test.js +154 -0
  37. package/docs/backend/10-authentication/01-user-logins-with-authjs.md +0 -55
package/CHANGELOG.md CHANGED
@@ -1,3 +1,46 @@
1
+ ### 🛠️ Fixes & Improvements
2
+
3
+ - **cache:** preserve Knex chain for insert operations during invalidation
4
+ - **database:** resolve duplicate error handler bug in insert cache invalidation
5
+ - **migration:** add column type comparison to detect schema changes
6
+ - **migration:** handle PostgreSQL primary key type changes safely
7
+ - **migration:** preserve column nullable state when altering without explicit schema
8
+ - **write-buffer:** auto-generate nanoid values for buffered inserts
9
+
10
+
11
+
12
+ ---
13
+
14
+ Powered by [⚡ ODAC](https://odac.run)
15
+
16
+ ### ⚙️ Engine Tuning
17
+
18
+ - migrate hashing from BCrypt to scrypt and update Odac.Var documentation and validation logic
19
+
20
+ ### ✨ What's New
21
+
22
+ - **auth:** add token tracking and retrieval method
23
+ - **database:** add read-through cache API for SELECT query results
24
+ - **migration:** add cross-driver default value comparison
25
+ - **migration:** add foreign key introspection and synchronization
26
+ - **websocket:** add readyState tracking and message fragmentation support
27
+
28
+ ### 📚 Documentation
29
+
30
+ - **forms:** add options to control message display for success and error handling
31
+ - **migrations:** add foreign keys and referential actions documentation
32
+
33
+ ### 🛠️ Fixes & Improvements
34
+
35
+ - **database:** add catch method to write operation thenable for Promise compatibility
36
+ - **pr-review:** apply ReadCache null sentinel, fix invalidation test, correct auth docs login example
37
+
38
+
39
+
40
+ ---
41
+
42
+ Powered by [⚡ ODAC](https://odac.run)
43
+
1
44
  ### ✨ What's New
2
45
 
3
46
  - **database:** add Write-Behind Cache with counter, update, and batch insert buffering
package/docs/ai/README.md CHANGED
@@ -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
 
@@ -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,19 +1,20 @@
1
1
  ---
2
2
  name: backend-database-skill
3
- description: High-performance ODAC database querying patterns including the Write-Behind Cache for counters, last-write-wins updates, and buffered batch inserts.
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, write-behind-cache, 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 and Write-Behind Cache.
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. **Write Coalescing**: Use `buffer` for high-frequency writes to avoid DB saturation.
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.
17
18
 
18
19
  ## Query Builder Patterns
19
20
  ```javascript
@@ -29,6 +30,85 @@ 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
30
31
  ```
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
+ }
110
+ ```
111
+
32
112
  ## ID Strategy (NanoID)
33
113
  1. **Schema-Driven**: Define string IDs with `type: 'nanoid'` in `schema/*.js`.
34
114
  2. **Auto Generation**: On `insert()`, ODAC auto-generates missing nanoid fields.
@@ -144,4 +224,4 @@ await Odac.DB.buffer.flush() // flush everything
144
224
  4. **Indexes**: Keep index definitions in schema so add/drop is managed automatically.
145
225
  5. **Data Changes**: Use `migration/*.js` only for one-time data transformation.
146
226
 
147
- See: [migrations.md](./migrations.md) | [write-behind-cache user docs](../../../backend/08-database/05-write-behind-cache.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)
@@ -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
@@ -42,7 +42,29 @@ Odac.form(
42
42
  }
43
43
  )
44
44
 
45
- // 5) Manual GET request helper
45
+ // 5) Disable all automatic messages and handle everything in the callback
46
+ Odac.form(
47
+ {form: 'form[data-odac-form]', messages: false},
48
+ response => {
49
+ if (response?.result?.success) {
50
+ // Custom success: e.g. show a toast, update UI
51
+ } else if (response?.errors) {
52
+ // Custom error: manually map errors to DOM
53
+ }
54
+ }
55
+ )
56
+
57
+ // 6) Only show success messages, handle errors manually
58
+ Odac.form(
59
+ {form: 'form[data-odac-form]', messages: ['success']},
60
+ response => {
61
+ if (!response?.result?.success && response?.errors) {
62
+ // Custom error handling
63
+ }
64
+ }
65
+ )
66
+
67
+ // 7) Manual GET request helper
46
68
  Odac.get('/api/status', data => {
47
69
  const status = document.querySelector('[data-api-status]')
48
70
  if (status) status.textContent = String(data?.status ?? '')
@@ -31,6 +31,7 @@ Odac.Route.ws('/path', Odac => {
31
31
  | Property | Description |
32
32
  |----------|-------------|
33
33
  | `Odac.ws.id` | Unique client ID |
34
+ | `Odac.ws.readyState` | RFC 6455 state: `0` CONNECTING, `1` OPEN, `2` CLOSING, `3` CLOSED |
34
35
  | `Odac.ws.rooms` | Array of joined rooms |
35
36
  | `Odac.ws.data` | Custom data storage |
36
37
 
@@ -169,14 +170,33 @@ const ws = Odac.ws('/chat', {shared: true})
169
170
  |------|-------------|
170
171
  | `1000` | Normal closure |
171
172
  | `1001` | Going away |
172
- | `1002` | Protocol error |
173
+ | `1002` | Protocol error (e.g. unexpected continuation frame) |
173
174
  | `1003` | Unsupported data |
174
175
  | `1006` | Abnormal closure |
176
+ | `1008` | Rate limit exceeded |
177
+ | `1009` | Payload too large |
175
178
  | `4000` | Middleware rejected |
176
179
  | `4001` | Unauthorized |
177
180
  | `4002` | Invalid/missing token |
178
181
  | `4003` | Forbidden (middleware) |
179
182
 
183
+ ## readyState Constants
184
+
185
+ | Value | Constant | Description |
186
+ |-------|----------|-------------|
187
+ | `0` | `CONNECTING` | Handshake done, handler not yet invoked |
188
+ | `1` | `OPEN` | Active — messages can be sent/received |
189
+ | `2` | `CLOSING` | Close frame sent, draining |
190
+ | `3` | `CLOSED` | Fully terminated |
191
+
192
+ ```javascript
193
+ const {READY_STATE} = require('odac')
194
+
195
+ if (Odac.ws.readyState === READY_STATE.OPEN) {
196
+ Odac.ws.send({status: 'alive'})
197
+ }
198
+ ```
199
+
180
200
  ## Best Practices
181
201
 
182
202
  1. **Always handle authentication**
@@ -97,9 +97,30 @@ Odac.ws.on('error', err => {}) // Error occurred
97
97
  ### Connection Management
98
98
 
99
99
  ```javascript
100
- Odac.ws.close() // Close connection
100
+ Odac.ws.close() // Close connection (sends close frame, transitions to CLOSED)
101
101
  Odac.ws.ping() // Send ping frame
102
102
  Odac.ws.id // Unique client ID
103
+ Odac.ws.readyState // Current connection state (0–3)
104
+ ```
105
+
106
+ ### Connection State
107
+
108
+ `Odac.ws.readyState` reflects the RFC 6455 lifecycle. Use the static constants on `WebSocketClient` or the exported `READY_STATE` enum for readable comparisons:
109
+
110
+ | Value | Constant | Description |
111
+ |-------|----------|-------------|
112
+ | `0` | `CONNECTING` | Handshake complete, handler not yet called |
113
+ | `1` | `OPEN` | Connection active, messages can be sent |
114
+ | `2` | `CLOSING` | Close frame sent, waiting for socket drain |
115
+ | `3` | `CLOSED` | Connection fully terminated |
116
+
117
+ ```javascript
118
+ const {READY_STATE} = require('odac')
119
+
120
+ Odac.ws.on('message', data => {
121
+ if (Odac.ws.readyState !== READY_STATE.OPEN) return
122
+ Odac.ws.send({echo: data})
123
+ })
103
124
  ```
104
125
 
105
126
  ## Rooms