dzql 0.4.3 โ 0.4.4
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/README.md +9 -6
- package/docs/README.md +5 -23
- package/docs/for-ai/claude-guide.md +32 -0
- package/docs/guides/interpreter-vs-compiler.md +237 -0
- package/docs/guides/many-to-many.md +17 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ PostgreSQL-powered framework with automatic CRUD operations, live query subscrip
|
|
|
7
7
|
- **[Documentation Hub](docs/)** - Complete documentation index
|
|
8
8
|
- **[Getting Started Tutorial](docs/getting-started/tutorial.md)** - Complete tutorial with working todo app
|
|
9
9
|
- **[API Reference](docs/reference/api.md)** - Complete API documentation
|
|
10
|
-
- **[Live Query Subscriptions](docs/getting-started/subscriptions-quick-start.md)** - Real-time denormalized documents
|
|
10
|
+
- **[Live Query Subscriptions](docs/getting-started/subscriptions-quick-start.md)** - Real-time denormalized documents
|
|
11
11
|
- **[Compiler Documentation](docs/compiler/)** - Entity compilation guide and coding standards
|
|
12
12
|
- **[Claude Guide](docs/for-ai/claude-guide.md)** - Development guide for AI assistants
|
|
13
13
|
- **[Venues Example](../venues/)** - Full working application
|
|
@@ -32,7 +32,7 @@ await ws.connect();
|
|
|
32
32
|
const user = await ws.api.save.users({ name: 'Alice' });
|
|
33
33
|
const results = await ws.api.search.users({ filters: { name: 'alice' } });
|
|
34
34
|
|
|
35
|
-
//
|
|
35
|
+
// Live query subscriptions
|
|
36
36
|
const { data, unsubscribe } = await ws.api.subscribe_venue_detail(
|
|
37
37
|
{ venue_id: 123 },
|
|
38
38
|
(updated) => console.log('Venue changed!', updated)
|
|
@@ -59,17 +59,20 @@ See **[Compiler Documentation](docs/compiler/)** for complete usage guide, codin
|
|
|
59
59
|
## Testing
|
|
60
60
|
|
|
61
61
|
```bash
|
|
62
|
-
#
|
|
63
|
-
|
|
62
|
+
# From repository root - start test database
|
|
63
|
+
docker compose up -d
|
|
64
|
+
|
|
65
|
+
# Initialize test database
|
|
66
|
+
bun run test:init
|
|
64
67
|
|
|
65
68
|
# Run tests
|
|
66
69
|
bun test
|
|
67
70
|
|
|
68
71
|
# Stop database
|
|
69
|
-
|
|
72
|
+
docker compose down
|
|
70
73
|
```
|
|
71
74
|
|
|
72
|
-
All tests use `bun:test` framework
|
|
75
|
+
All tests use `bun:test` framework. See **[tests/README.md](../../tests/README.md)** for details.
|
|
73
76
|
|
|
74
77
|
## License
|
|
75
78
|
|
package/docs/README.md
CHANGED
|
@@ -7,6 +7,7 @@ Complete documentation for the DZQL PostgreSQL-powered framework.
|
|
|
7
7
|
New to DZQL? Start here:
|
|
8
8
|
|
|
9
9
|
- **[Tutorial](getting-started/tutorial.md)** - Complete step-by-step guide with a working todo app
|
|
10
|
+
- **[Interpreter vs Compiler](guides/interpreter-vs-compiler.md)** - Understand the two execution modes
|
|
10
11
|
- **[Subscriptions Quick Start](getting-started/subscriptions-quick-start.md)** - Get real-time subscriptions working in 5 minutes
|
|
11
12
|
|
|
12
13
|
## ๐ Guides
|
|
@@ -14,6 +15,9 @@ New to DZQL? Start here:
|
|
|
14
15
|
Feature-specific guides and how-tos:
|
|
15
16
|
|
|
16
17
|
- **[Live Query Subscriptions](guides/subscriptions.md)** - Real-time denormalized documents
|
|
18
|
+
- **[Many-to-Many Relationships](guides/many-to-many.md)** - Junction table management
|
|
19
|
+
- **[Field Defaults](guides/field-defaults.md)** - Auto-populate fields on create
|
|
20
|
+
- **[Custom Functions](guides/custom-functions.md)** - Extend with PostgreSQL or Bun functions
|
|
17
21
|
- **[Client Stores](guides/client-stores.md)** - Pinia store patterns for Vue.js
|
|
18
22
|
|
|
19
23
|
## ๐ Reference
|
|
@@ -29,7 +33,7 @@ Complete API documentation:
|
|
|
29
33
|
- [Quickstart](compiler/QUICKSTART.md) - Get started with the DZQL compiler
|
|
30
34
|
- [Advanced Filters](compiler/ADVANCED_FILTERS.md) - Complex search operators
|
|
31
35
|
- [Coding Standards](compiler/CODING_STANDARDS.md) - Best practices for DZQL code
|
|
32
|
-
- [Comparison](compiler/COMPARISON.md) -
|
|
36
|
+
- [Comparison](compiler/COMPARISON.md) - Runtime vs compiled side-by-side
|
|
33
37
|
|
|
34
38
|
## ๐ค For AI Assistants
|
|
35
39
|
|
|
@@ -40,28 +44,6 @@ Complete API documentation:
|
|
|
40
44
|
- [npm Package](https://www.npmjs.com/package/dzql)
|
|
41
45
|
- [GitHub Repository](https://github.com/blueshed/dzql)
|
|
42
46
|
- [Issue Tracker](https://github.com/blueshed/dzql/issues)
|
|
43
|
-
- [Changelog](../../../CHANGELOG.md)
|
|
44
|
-
- [Contributing](../../../CONTRIBUTING.md)
|
|
45
|
-
|
|
46
|
-
## ๐๏ธ Architecture
|
|
47
|
-
|
|
48
|
-
Looking for architecture and design docs? See the [repository docs](../../../docs/):
|
|
49
|
-
|
|
50
|
-
- [Permissions System](../../../docs/architecture/PERMISSIONS.md)
|
|
51
|
-
- [Project Roadmap](../../../docs/architecture/ROADMAP.md)
|
|
52
|
-
- [Subscription Architecture](../../../docs/architecture/SUBSCRIPTIONS_STRATEGY.md)
|
|
53
|
-
|
|
54
|
-
## ๐งช Development
|
|
55
|
-
|
|
56
|
-
Contributing to DZQL? See development documentation:
|
|
57
|
-
|
|
58
|
-
- [TDD Workflow](../../../docs/development/TDD_WORKFLOW.md)
|
|
59
|
-
- [WebSocket Testing](../../../docs/development/WEBSOCKET_TESTING.md)
|
|
60
|
-
- [Claude Web Setup](../../../docs/development/CLAUDE-WEB.md)
|
|
61
|
-
|
|
62
|
-
## ๐ฆ Package Contents
|
|
63
|
-
|
|
64
|
-
This documentation is published with the npm package. For repository-wide documentation (contributors, development workflow, architecture), see [`/docs/`](../../../docs/) in the repository root.
|
|
65
47
|
|
|
66
48
|
## Need Help?
|
|
67
49
|
|
|
@@ -2,6 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
4
|
|
|
5
|
+
## Quick Reference Card
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
DZQL QUICK REFERENCE
|
|
9
|
+
====================
|
|
10
|
+
|
|
11
|
+
5 Operations: get, save, delete, lookup, search
|
|
12
|
+
2 Modes: Interpreter (runtime) | Compiler (static SQL)
|
|
13
|
+
Client API: ws.api.{operation}.{entity}(params)
|
|
14
|
+
Server API: db.api.{operation}.{entity}(params, userId)
|
|
15
|
+
|
|
16
|
+
Entity Registration:
|
|
17
|
+
dzql.register_entity(
|
|
18
|
+
table_name, -- 'todos'
|
|
19
|
+
label_field, -- 'title' (for lookups)
|
|
20
|
+
searchable_fields, -- ARRAY['title', 'description']
|
|
21
|
+
fk_includes, -- '{"org": "organisations"}'
|
|
22
|
+
soft_delete, -- false
|
|
23
|
+
temporal_fields, -- '{}'
|
|
24
|
+
notification_paths, -- '{"ownership": ["@org_id->acts_for..."]}'
|
|
25
|
+
permission_paths, -- '{"view": [], "create": [...]}'
|
|
26
|
+
graph_rules, -- '{"on_create": {...}, "many_to_many": {...}}'
|
|
27
|
+
field_defaults -- '{"owner_id": "@user_id"}'
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
M2M id_field naming: tag_ids (singular + _ids), NOT tags_ids
|
|
31
|
+
Permission [] = public, omitted = denied
|
|
32
|
+
Path syntax: @field->table[filter]{temporal}.target_field
|
|
33
|
+
|
|
34
|
+
Compile: dzql compile entities.sql -o compiled/
|
|
35
|
+
```
|
|
36
|
+
|
|
5
37
|
## Project Overview
|
|
6
38
|
|
|
7
39
|
DZQL is a PostgreSQL-powered framework that eliminates CRUD boilerplate by providing automatic database operations, real-time WebSocket synchronization, and graph-based relationship management. The core concept: register an entity in PostgreSQL and instantly get 5 standard operations (get, save, delete, lookup, search) plus real-time notifications with zero code.
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# Interpreter vs Compiler Mode
|
|
2
|
+
|
|
3
|
+
DZQL offers two execution modes for your entities. Understanding when to use each is fundamental to getting the best out of the framework.
|
|
4
|
+
|
|
5
|
+
## Quick Summary
|
|
6
|
+
|
|
7
|
+
| Aspect | Interpreter | Compiler |
|
|
8
|
+
|--------|-------------|----------|
|
|
9
|
+
| **Setup** | Register entity, use immediately | Register entity, compile, deploy SQL |
|
|
10
|
+
| **Performance** | ~8-12ms per operation | ~2-4ms per operation |
|
|
11
|
+
| **Debugging** | Opaque (dynamic SQL) | Transparent (static SQL) |
|
|
12
|
+
| **Best For** | Development, prototyping | Production, performance-critical |
|
|
13
|
+
|
|
14
|
+
## How It Works
|
|
15
|
+
|
|
16
|
+
### Interpreter Mode (Runtime)
|
|
17
|
+
|
|
18
|
+
Entity configuration is stored as JSON in `dzql.entities` table and parsed at runtime:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
Client Request โ generic_exec() โ Parse JSON config โ Build SQL โ Execute
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Characteristics:**
|
|
25
|
+
- Zero build step - changes take effect immediately
|
|
26
|
+
- JSON config parsed on every request
|
|
27
|
+
- Dynamic SQL generated at runtime
|
|
28
|
+
- Generic query plans (harder to optimize)
|
|
29
|
+
|
|
30
|
+
**Usage:**
|
|
31
|
+
```sql
|
|
32
|
+
-- Register entity
|
|
33
|
+
SELECT dzql.register_entity('todos', 'title', ARRAY['title'], ...);
|
|
34
|
+
|
|
35
|
+
-- Use immediately via generic executor
|
|
36
|
+
SELECT dzql.generic_exec('save', 'todos', '{"title": "Buy milk"}'::jsonb, 1);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Compiler Mode (Static)
|
|
40
|
+
|
|
41
|
+
Entity configuration is compiled into dedicated PostgreSQL functions:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
Entity Definition โ dzql compile โ Static SQL Functions โ Deploy โ Execute
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Characteristics:**
|
|
48
|
+
- Build step required
|
|
49
|
+
- No JSON parsing at runtime
|
|
50
|
+
- Static SQL with specific query plans
|
|
51
|
+
- PostgreSQL can optimize and cache plans
|
|
52
|
+
|
|
53
|
+
**Usage:**
|
|
54
|
+
```bash
|
|
55
|
+
# Compile entities to SQL
|
|
56
|
+
dzql compile entities.sql -o compiled/
|
|
57
|
+
|
|
58
|
+
# Deploy to database
|
|
59
|
+
psql < compiled/entities.sql
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```sql
|
|
63
|
+
-- Use compiled functions directly
|
|
64
|
+
SELECT save_todos('{"title": "Buy milk"}'::jsonb, 1);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## The Server Automatically Chooses
|
|
68
|
+
|
|
69
|
+
The DZQL server (`db.js`) automatically tries compiled functions first:
|
|
70
|
+
|
|
71
|
+
```javascript
|
|
72
|
+
// In callDZQLOperation()
|
|
73
|
+
try {
|
|
74
|
+
// Try compiled function: save_todos()
|
|
75
|
+
const result = await sql.unsafe(`SELECT save_todos($1, $2)`, [data, userId]);
|
|
76
|
+
return result[0].result;
|
|
77
|
+
} catch (error) {
|
|
78
|
+
// If compiled function doesn't exist, fall back to interpreter
|
|
79
|
+
if (error.message.includes('save_todos') && error.code === '42883') {
|
|
80
|
+
return await sql`SELECT dzql.generic_exec('save', 'todos', ${data}, ${userId})`;
|
|
81
|
+
}
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
This means you can:
|
|
87
|
+
1. Start with interpreter mode during development
|
|
88
|
+
2. Compile and deploy when ready for production
|
|
89
|
+
3. Mix and match - some entities compiled, others interpreted
|
|
90
|
+
|
|
91
|
+
## Performance Comparison
|
|
92
|
+
|
|
93
|
+
### Interpreter (Runtime Parsing)
|
|
94
|
+
|
|
95
|
+
```sql
|
|
96
|
+
SELECT dzql.generic_exec('save', 'venues', '{"name": "MSG"}'::jsonb, 42);
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Execution steps:**
|
|
100
|
+
1. Fetch entity config from `dzql.entities` (table lookup)
|
|
101
|
+
2. Parse `permission_paths` JSONB
|
|
102
|
+
3. Build permission query dynamically
|
|
103
|
+
4. Parse `graph_rules` JSONB
|
|
104
|
+
5. Execute rules via dynamic SQL
|
|
105
|
+
6. Parse `notification_paths` JSONB
|
|
106
|
+
7. Resolve paths dynamically
|
|
107
|
+
8. Execute the actual save
|
|
108
|
+
|
|
109
|
+
**Cost:** ~8-12ms, 3-5 JSONB parses, unpredictable query plans
|
|
110
|
+
|
|
111
|
+
### Compiler (Pre-built Functions)
|
|
112
|
+
|
|
113
|
+
```sql
|
|
114
|
+
SELECT save_venues('{"name": "MSG"}'::jsonb, 42);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Execution steps:**
|
|
118
|
+
1. Call `can_update_venues()` - pre-compiled permission check
|
|
119
|
+
2. Execute INSERT/UPDATE - direct SQL
|
|
120
|
+
3. Call `graph_venues_on_create()` - pre-compiled graph rules
|
|
121
|
+
4. Call `resolve_notification_paths_venues()` - pre-compiled
|
|
122
|
+
5. Done
|
|
123
|
+
|
|
124
|
+
**Cost:** ~2-4ms, 0 JSONB parses, optimized query plans
|
|
125
|
+
|
|
126
|
+
## When to Use Each
|
|
127
|
+
|
|
128
|
+
### Use Interpreter When:
|
|
129
|
+
- Rapid prototyping and development
|
|
130
|
+
- Schema changes frequently
|
|
131
|
+
- Learning DZQL concepts
|
|
132
|
+
- Small applications with low traffic
|
|
133
|
+
- Need maximum flexibility
|
|
134
|
+
|
|
135
|
+
### Use Compiler When:
|
|
136
|
+
- Production deployments
|
|
137
|
+
- Performance is critical
|
|
138
|
+
- Need predictable query performance
|
|
139
|
+
- Want reviewable/auditable SQL
|
|
140
|
+
- Large teams (generated SQL is easy to review)
|
|
141
|
+
- Complex permission or graph rules
|
|
142
|
+
|
|
143
|
+
### Recommended Workflow:
|
|
144
|
+
1. **Development:** Use interpreter for fast iteration
|
|
145
|
+
2. **Staging:** Compile and test performance
|
|
146
|
+
3. **Production:** Deploy compiled functions
|
|
147
|
+
|
|
148
|
+
## Compiling Entities
|
|
149
|
+
|
|
150
|
+
### Via CLI
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
# Single file
|
|
154
|
+
dzql compile database/entities.sql -o compiled/
|
|
155
|
+
|
|
156
|
+
# Multiple files
|
|
157
|
+
dzql compile database/*.sql -o compiled/
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Programmatically
|
|
161
|
+
|
|
162
|
+
```javascript
|
|
163
|
+
import { DZQLCompiler } from 'dzql/compiler';
|
|
164
|
+
|
|
165
|
+
const compiler = new DZQLCompiler();
|
|
166
|
+
const result = compiler.compileFromSQL(sqlContent);
|
|
167
|
+
|
|
168
|
+
console.log(result.sql); // Generated PostgreSQL functions
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### What Gets Generated
|
|
172
|
+
|
|
173
|
+
For each entity, the compiler generates:
|
|
174
|
+
|
|
175
|
+
| Function | Purpose |
|
|
176
|
+
|----------|---------|
|
|
177
|
+
| `get_{entity}(id, user_id)` | Retrieve single record |
|
|
178
|
+
| `save_{entity}(data, user_id)` | Create or update |
|
|
179
|
+
| `delete_{entity}(id, user_id)` | Delete record |
|
|
180
|
+
| `lookup_{entity}(term, user_id)` | Autocomplete search |
|
|
181
|
+
| `search_{entity}(filters, user_id)` | Paginated search |
|
|
182
|
+
| `can_view_{entity}(user_id, record)` | Permission check |
|
|
183
|
+
| `can_create_{entity}(user_id, record)` | Permission check |
|
|
184
|
+
| `can_update_{entity}(user_id, record)` | Permission check |
|
|
185
|
+
| `can_delete_{entity}(user_id, record)` | Permission check |
|
|
186
|
+
|
|
187
|
+
## Debugging
|
|
188
|
+
|
|
189
|
+
### Interpreter Mode
|
|
190
|
+
|
|
191
|
+
Debugging is harder because SQL is generated dynamically:
|
|
192
|
+
|
|
193
|
+
```sql
|
|
194
|
+
-- You see this
|
|
195
|
+
EXPLAIN ANALYZE SELECT dzql.generic_exec('save', 'venues', '...');
|
|
196
|
+
|
|
197
|
+
-- But the actual query is hidden inside
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Compiler Mode
|
|
201
|
+
|
|
202
|
+
Standard PostgreSQL tools work:
|
|
203
|
+
|
|
204
|
+
```sql
|
|
205
|
+
-- See the actual function
|
|
206
|
+
\sf save_venues
|
|
207
|
+
|
|
208
|
+
-- Analyze performance
|
|
209
|
+
EXPLAIN ANALYZE SELECT save_venues('{"name": "MSG"}'::jsonb, 42);
|
|
210
|
+
|
|
211
|
+
-- Check slow queries
|
|
212
|
+
SELECT * FROM pg_stat_statements WHERE query LIKE '%save_venues%';
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Feature Parity
|
|
216
|
+
|
|
217
|
+
Both modes support the same features:
|
|
218
|
+
|
|
219
|
+
| Feature | Interpreter | Compiler |
|
|
220
|
+
|---------|-------------|----------|
|
|
221
|
+
| CRUD operations | โ
| โ
|
|
|
222
|
+
| Permission paths | โ
| โ
|
|
|
223
|
+
| Graph rules | โ
| โ
|
|
|
224
|
+
| Notification paths | โ
| โ
|
|
|
225
|
+
| FK includes | โ
| โ
|
|
|
226
|
+
| Many-to-many | โ
| โ
|
|
|
227
|
+
| Field defaults | โ
| โ
|
|
|
228
|
+
| Soft delete | โ
| โ
|
|
|
229
|
+
| Temporal fields | โ
| โ
|
|
|
230
|
+
|
|
231
|
+
The difference is purely in execution speed and debuggability, not functionality.
|
|
232
|
+
|
|
233
|
+
## See Also
|
|
234
|
+
|
|
235
|
+
- [Compiler Quickstart](../compiler/QUICKSTART.md) - Get started with compilation
|
|
236
|
+
- [Compiler Comparison](../compiler/COMPARISON.md) - Detailed side-by-side analysis
|
|
237
|
+
- [API Reference](../reference/api.md) - The 5 operations
|
|
@@ -161,6 +161,23 @@ SELECT dzql.register_entity(
|
|
|
161
161
|
| `id_field` | Yes | Field name for ID array in API | `"tag_ids"` |
|
|
162
162
|
| `expand` | No | Include full objects (default: false) | `false` or `true` |
|
|
163
163
|
|
|
164
|
+
### Naming Convention for `id_field`
|
|
165
|
+
|
|
166
|
+
**Important:** The `id_field` should use the **singular** form of the target entity, not plural:
|
|
167
|
+
|
|
168
|
+
| Target Entity | Correct `id_field` | Wrong |
|
|
169
|
+
|---------------|-------------------|-------|
|
|
170
|
+
| `tags` | `tag_ids` | `tags_ids` |
|
|
171
|
+
| `roles` | `role_ids` | `roles_ids` |
|
|
172
|
+
| `categories` | `category_ids` | `categories_ids` |
|
|
173
|
+
| `users` | `user_ids` | `users_ids` |
|
|
174
|
+
|
|
175
|
+
This convention matches common ORM patterns and is more readable:
|
|
176
|
+
- `tag_ids: [1, 2, 3]` reads as "tag IDs"
|
|
177
|
+
- `tags_ids: [1, 2, 3]` reads awkwardly as "tags IDs"
|
|
178
|
+
|
|
179
|
+
Using the wrong naming will cause the M2M sync to fail because the generated code looks for a field name that doesn't match what clients send.
|
|
180
|
+
|
|
164
181
|
### The `expand` Flag
|
|
165
182
|
|
|
166
183
|
Controls whether full related objects are included in responses:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dzql",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "PostgreSQL-powered framework with zero boilerplate CRUD operations and real-time WebSocket synchronization",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/server/index.js",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
],
|
|
25
25
|
"scripts": {
|
|
26
26
|
"test": "bun test ../../tests/core/*.test.js",
|
|
27
|
-
"prepublishOnly": "echo 'โ
Publishing DZQL
|
|
27
|
+
"prepublishOnly": "echo 'โ
Publishing DZQL...' "
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"jose": "^6.1.0",
|