dzql 0.6.5 → 0.6.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dzql",
3
- "version": "0.6.5",
3
+ "version": "0.6.7",
4
4
  "description": "Database-first real-time framework with TypeScript support",
5
5
  "repository": {
6
6
  "type": "git",
@@ -18,7 +18,9 @@
18
18
  },
19
19
  "files": [
20
20
  "src",
21
- "docs",
21
+ "docs/README.md",
22
+ "docs/project-setup.md",
23
+ "docs/for_ai.md",
22
24
  "README.md"
23
25
  ],
24
26
  "scripts": {
@@ -1,85 +0,0 @@
1
- # DZQL Bug Report
2
-
3
- ## Generated Store applyPatch Doesn't Match Data Structure
4
-
5
- **Severity:** High (Breaking)
6
-
7
- **Status:** ✅ FIXED
8
-
9
- **Description:**
10
- The generated Pinia store's `applyPatch` function assumes a flat data structure, but the subscribable SQL was returning nested wrapper objects.
11
-
12
- **SQL was returning:**
13
- ```json
14
- {
15
- "venues": {...},
16
- "sites": [
17
- { "sites": { "id": 1, "name": "..." }, "allocations": [...] },
18
- { "sites": { "id": 2, "name": "..." }, "allocations": [...] }
19
- ]
20
- }
21
- ```
22
-
23
- **Fix:**
24
- Updated `subscribable_sql.ts` to use JSONB concatenation (`||`) to merge entity fields with nested includes into a flat structure:
25
-
26
- ```sql
27
- SELECT jsonb_agg(
28
- row_to_json(rel.*) || jsonb_build_object(
29
- 'allocations', COALESCE((
30
- SELECT jsonb_agg(row_to_json(nested.*))
31
- FROM allocations nested
32
- WHERE nested.site_id = rel.id
33
- ), '[]'::jsonb))
34
- )
35
- ```
36
-
37
- **SQL now returns:**
38
- ```json
39
- {
40
- "venues": {...},
41
- "sites": [
42
- { "id": 1, "name": "...", "allocations": [...] },
43
- { "id": 2, "name": "...", "allocations": [...] }
44
- ]
45
- }
46
- ```
47
-
48
- This flat structure matches what `applyPatch` and `handleArrayPatch` expect, so realtime updates now work correctly for nested includes.
49
-
50
- ---
51
-
52
- ## Bug 2: json/jsonb Type Mismatch in Subscribable SQL
53
-
54
- **Severity:** Critical (Blocking)
55
-
56
- **Status:** ✅ FIXED
57
-
58
- **Description:**
59
- The generated subscribable SQL used `row_to_json()` which returns `json` type, but concatenated it with `jsonb_build_object()` which returns `jsonb`. PostgreSQL cannot concatenate these types.
60
-
61
- **Generated SQL (before):**
62
- ```sql
63
- row_to_json(rel.*) || jsonb_build_object(...)
64
- ```
65
-
66
- **Error:**
67
- ```
68
- operator does not exist: json || jsonb
69
- ```
70
-
71
- **Fix:**
72
- Changed to use `to_jsonb()` instead of `row_to_json()` when concatenating with nested includes:
73
-
74
- ```sql
75
- to_jsonb(rel.*) || jsonb_build_object(...)
76
- ```
77
-
78
- ---
79
-
80
- ## Environment
81
-
82
- - dzql version: local development (linked)
83
- - Database: PostgreSQL 17 (Docker)
84
- - Client: Vue 3 + Pinia + TypeScript
85
- - Runtime: Bun
@@ -1,57 +0,0 @@
1
- # Feature Request: Send User Profile on WebSocket Connect
2
-
3
- **Status: IMPLEMENTED**
4
-
5
- ## Summary
6
-
7
- When a client connects to the DZQL WebSocket server, the server should immediately send a connection:ready message containing the authenticated user profile (or null if not authenticated).
8
-
9
- ## Current Behavior
10
-
11
- 1. Client connects with optional ?token=... in URL
12
- 2. Server opens connection but sends nothing
13
- 3. Client must call auth RPC to authenticate, then separately fetch profile
14
- 4. Client has no immediate knowledge of auth state
15
-
16
- ## Proposed Behavior
17
-
18
- 1. Client connects with optional ?token=... in URL
19
- 2. Server validates token (if present) and fetches user profile
20
- 3. Server sends first message:
21
-
22
- {"method": "connection:ready", "params": {"user": {"id": 1, "name": "...", "email": "..."} | null}}
23
-
24
- 4. Client knows auth state immediately, can render accordingly
25
-
26
- ## Why This Matters
27
-
28
- - Single source of truth: WebSocket connection determines auth state, not localStorage
29
- - No race conditions: UI waits for connection:ready before rendering
30
- - Simpler client code: No need for separate auth check after connect
31
- - Better UX: App shows loading state until connection ready, then immediately correct view
32
-
33
- ## Client Usage Pattern
34
-
35
- Template:
36
- div v-if="!ready" - loading spinner
37
- LoginModal v-else-if="!user"
38
- RouterView v-else
39
-
40
- ## Suggested Server Changes
41
-
42
- In src/runtime/ws.ts, modify handleOpen to:
43
- 1. Parse token from URL query params
44
- 2. If valid, verify token and fetch user profile
45
- 3. Send connection:ready message with user (or null)
46
-
47
- ## Suggested Client Changes
48
-
49
- In src/client/ws.ts:
50
- 1. Add user and ready properties to class
51
- 2. Handle connection:ready message in handleMessage
52
- 3. Add onReady callback method
53
-
54
- ## Migration
55
-
56
- - Existing clients that dont handle connection:ready will ignore it (no breaking change)
57
- - New clients can opt-in to the pattern
@@ -1,111 +0,0 @@
1
- # DZQL Bug Report
2
-
3
- Bugs discovered while building the Venues application with DZQL.
4
-
5
- ---
6
-
7
- ## Bug 1: Hidden Fields Exposed in Subscribables
8
-
9
- **Severity:** High (Security)
10
-
11
- **Status:** ✅ FIXED
12
-
13
- **Description:**
14
- Fields marked as `hidden: true` in the domain schema are still exposed when using subscribables. The generated SQL uses `row_to_json(root.*)` which includes all columns regardless of the `hidden` property.
15
-
16
- **Fix:**
17
- Added `buildVisibleRowJson` helper in `subscribable_sql.ts` that generates `jsonb_build_object()` with only visible fields instead of `row_to_json(root.*)`. Also updated all other SQL generators (`sql.ts`) to exclude hidden fields from `get_*`, `search_*`, `save_*`, and `delete_*` functions.
18
-
19
- ---
20
-
21
- ## Bug 2: Generated Pinia Stores Don't Await Async Subscribe
22
-
23
- **Severity:** Medium
24
-
25
- **Status:** ✅ FIXED
26
-
27
- **Description:**
28
- The generated Pinia stores call the async `subscribe` method but don't await it. This means `store.data` is undefined immediately after calling `store.subscribe()` even though the subscription has been initiated.
29
-
30
- **Fix:**
31
- Updated `subscribable_store.ts` to make the `bind` function async. It now:
32
- 1. Creates a `ready` Promise that resolves when first data arrives
33
- 2. Awaits the `ready` Promise before returning
34
- 3. Stores the `ready` Promise so repeat calls can also await it
35
-
36
- **Generated Code (after fix):**
37
- ```typescript
38
- async function bind(params) {
39
- const key = JSON.stringify(params);
40
- if (documents.value[key]) {
41
- const existing = documents.value[key];
42
- if (existing.loading.value) {
43
- await existing.ready;
44
- }
45
- return existing;
46
- }
47
-
48
- const docState = ref(null);
49
- const loading = ref(true);
50
- let resolveReady;
51
- const ready = new Promise((resolve) => { resolveReady = resolve; });
52
-
53
- ws.api.subscribe_venue_detail(params, (initialData) => {
54
- docState.value = initialData;
55
- loading.value = false;
56
- resolveReady();
57
- });
58
-
59
- documents.value[key] = { data: docState, loading, ready };
60
- await ready;
61
- return documents.value[key];
62
- }
63
- ```
64
-
65
- **Usage:**
66
- ```typescript
67
- const store = useMyProfileStore();
68
- const { data, loading } = await store.bind({ user_id: 1 });
69
- // data.value is now populated
70
- ```
71
-
72
- ---
73
-
74
- ## Bug 3: Subscribable Permission Check Uses Param Name Instead of Column Name
75
-
76
- **Severity:** High (Breaking)
77
-
78
- **Status:** ✅ FIXED
79
-
80
- **Description:**
81
- When generating SQL for subscribable permission checks, the compiler uses the parameter name instead of the actual database column name. This causes SQL errors when the param name differs from the column name.
82
-
83
- **Fix:**
84
- Updated `compileSubscribePermission` in `subscribable_sql.ts` to map param names to the root entity's `id` column when they match the rootKey. For example, `@org_id` now correctly resolves to `v_root.id` instead of `v_root.org_id`.
85
-
86
- ---
87
-
88
- ## Summary
89
-
90
- | Bug | Severity | Status | Notes |
91
- |-----|----------|--------|-------|
92
- | Hidden fields exposed | High | ✅ Fixed | Uses `jsonb_build_object` to exclude hidden fields |
93
- | Stores don't await | Medium | ✅ Fixed | `bind()` is now async and awaits first data |
94
- | Permission param/column mismatch | High | ✅ Fixed | Maps param name to `id` column |
95
-
96
- ---
97
-
98
- ## Environment
99
-
100
- - dzql version: (linked local development version)
101
- - Database: PostgreSQL 17 (via Docker)
102
- - Client: Vue 3 + Pinia + TypeScript
103
- - Runtime: Bun
104
-
105
- ---
106
-
107
- ## Related Files
108
-
109
- Detailed bug documents created in dzql docs:
110
- - `/packages/tzql/docs/feature-requests/hidden-fields-in-subscribables.md`
111
- - `/packages/tzql/docs/feature-requests/subscribable-param-key-bug.md`
@@ -1,34 +0,0 @@
1
- # Bug: Hidden fields exposed in subscribables
2
-
3
- **Status: IMPLEMENTED**
4
-
5
- ## Issue
6
-
7
- Entity hidden fields are not excluded from subscribable queries.
8
-
9
- ## Example
10
-
11
- domain.js:
12
- users: {
13
- schema: { ... },
14
- hidden: ["password_hash"], // Should be excluded from all queries
15
- }
16
-
17
- But get_my_profile returns:
18
- {
19
- "users": {
20
- "password_hash": "$2a$06$..." // EXPOSED!
21
- }
22
- }
23
-
24
- ## Root Cause
25
-
26
- The subscribable SQL generator uses row_to_json(root.*) which returns all columns.
27
-
28
- ## Fix
29
-
30
- When generating subscribable SQL, exclude hidden fields by using explicit column list.
31
-
32
- ## Impact
33
-
34
- Security vulnerability - sensitive data like password hashes exposed to clients.
@@ -1,38 +0,0 @@
1
- # Bug: Subscribable permission check uses param name instead of column name
2
-
3
- ## Issue
4
-
5
- When generating subscribable SQL, the permission check incorrectly uses the param name instead of the actual column name on the root entity.
6
-
7
- ## Example
8
-
9
- Domain:
10
- ```javascript
11
- org_dashboard: {
12
- params: { org_id: "int" },
13
- root: { entity: "organisations", key: "org_id" },
14
- canSubscribe: ["@org_id->acts_for[org_id=$]{active}.user_id"]
15
- }
16
- ```
17
-
18
- Generated SQL:
19
- ```sql
20
- -- WRONG: v_root.org_id doesn't exist on organisations table
21
- WHERE acts_for.org_id = v_root.org_id
22
- ```
23
-
24
- Should be:
25
- ```sql
26
- -- CORRECT: organisations.id is the actual column
27
- WHERE acts_for.org_id = v_root.id
28
- ```
29
-
30
- ## Error
31
-
32
- ```
33
- ERROR: record "v_root" has no field "org_id"
34
- ```
35
-
36
- ## Fix
37
-
38
- The compiler should resolve `key: "org_id"` to mean "param org_id maps to the root entity's primary key (id)", not "access v_root.org_id".
@@ -1,146 +0,0 @@
1
- # DZQL v2 Gap Analysis TODO
2
-
3
- Gap analysis comparing v1 tests to v2 functionality. Updated 2024-12-17.
4
-
5
- ---
6
-
7
- ## High Priority - COMPLETED
8
-
9
- ### Soft Delete ✅
10
- - [x] Add `softDelete` handling to `generateDeleteFunction()` in `src/cli/codegen/sql.ts`
11
- - Sets `deleted_at = now()` instead of `DELETE FROM` when entity has `softDelete: true`
12
- - [x] Add `deleted_at IS NULL` filter to `generateSearchFunction()`
13
- - [x] GET still retrieves deleted records (for audit purposes)
14
- - [x] Add soft delete integration tests (3 tests)
15
- - Reference: `tests/integration/features.test.ts` - "Soft Delete" describe block
16
-
17
- ### Field Defaults ✅
18
- - [x] Add `fieldDefaults` handling to `generateSaveFunction()` INSERT branch in `src/cli/codegen/sql.ts`
19
- - `@user_id` → `p_user_id`
20
- - `@now` → `now()`
21
- - `@today` → `current_date`
22
- - Literal values → insert as-is
23
- - [x] Only apply defaults on INSERT, not UPDATE (uses COALESCE)
24
- - [x] Add field defaults integration tests (4 tests)
25
- - Reference: `tests/integration/features.test.ts` - "Field Defaults" describe block
26
-
27
- ### Composite Primary Key Support ✅
28
- - [x] Update `generateSaveFunction()` to handle multi-column PKs
29
- - Dynamic PK null check and EXISTS query
30
- - WHERE clause supports all PK fields
31
- - [x] Update `generateDeleteFunction()` for composite PKs
32
- - [x] Update `generateGetFunction()` for composite PKs
33
- - [x] Events store composite PK as JSONB object
34
- - [x] Proper type casting for date, boolean, numeric columns
35
- - [x] Add composite PK integration tests (4 tests)
36
- - Reference: `tests/integration/features.test.ts` - "Composite Primary Keys" describe block
37
-
38
- ---
39
-
40
- ## Medium Priority - V1 Features Not Ported
41
-
42
- ### Dashboard Collection (filter: TRUE)
43
- - [ ] Add `filter: TRUE` handling to `generateSubscribableGetFunction()` in `src/cli/codegen/subscribable_sql.ts`
44
- - Fetch ALL rows regardless of FK relationship to root
45
- - [ ] Add `filter: TRUE` handling to `affected_keys` function
46
- - Return `'{}'::jsonb` to notify ALL subscribers
47
- - [ ] Add dashboard collection integration test
48
- - Reference: `tests/integration/dashboard-collection.test.js` (v1)
49
-
50
- ### Null-Root Dashboard
51
- - [ ] Support subscribables with no root entity parameter
52
- - `root.key: '@user_id'` or similar built-in
53
- - [ ] Add null-root dashboard test
54
- - Reference: `tests/integration/null-root-dashboard.test.js` (v1)
55
-
56
- ### Delete Subscription Resolution
57
- - [ ] Verify `affected_keys` function receives full record data on DELETE
58
- - [ ] Add integration test for DELETE event subscription routing
59
- - Reference: `tests/integration/delete-subscription-resolution.test.js` (v1)
60
-
61
- ### Atomic Subscription Updates
62
- - [ ] Add integration test for Pinia store `applyPatch` with real WebSocket events
63
- - [ ] Verify schema inclusion in subscribe response
64
- - Reference: `tests/integration/atomic-subscription-updates.test.js` (v1)
65
-
66
- ---
67
-
68
- ## Low Priority - COMPLETED
69
-
70
- ### Custom Functions Pass-through ✅
71
- - [x] Parse `customFunctions` from domain definition in IR generator
72
- - [x] Include custom function SQL in generated migrations
73
- - [x] Add custom functions to manifest allowlist for runtime security
74
- - [x] Add custom functions integration tests (4 tests)
75
- - Reference: `tests/integration/features.test.ts` - "Custom Functions" describe block
76
-
77
- ### JavaScript Custom Functions ✅
78
- - [x] Add JS function handler registry (`src/runtime/js_functions.ts`)
79
- - [x] Update `handleRequest` to check for JS handlers (takes precedence over SQL)
80
- - [x] JS functions receive context with `userId`, `params`, and `db.query()` access
81
- - [x] Export `registerJsFunction` API from runtime
82
- - [x] Add JavaScript custom function integration tests (5 tests)
83
- - Reference: `tests/integration/features.test.ts` - "JavaScript Custom Functions" describe block
84
-
85
- ### Temporal Table Support
86
- - [ ] Implement `temporal.validFrom` / `temporal.validTo` filtering
87
- - [ ] Auto-filter to current validity period
88
- - Schema already defined in `venues.js:acts_for.temporal`
89
-
90
- ### On-Delete Graph Rules
91
- - [ ] Verify `onDelete` graph rules receive correct record data
92
- - [ ] Test cascading deletes
93
- - Reference: `tests/integration/on-delete-graph-rules.test.js` (v1)
94
-
95
- ---
96
-
97
- ## Integration Test Coverage
98
-
99
- ### Missing Integration Tests (port from v1)
100
- - [ ] Security / SQL injection prevention (`security.test.js`)
101
- - [ ] M2M runtime operations (`m2m-runtime.test.js`)
102
- - [ ] Permissions enforcement (`permissions.test.js` - more comprehensive)
103
- - [ ] Event validation (`event-validation.test.js`)
104
- - [ ] Namespace/entity discovery (`namespace-cli.test.js`)
105
- - [ ] Migration runner (`migrations.test.js`)
106
-
107
- ### Current V2 Integration Tests
108
- - `tests/integration/db.test.ts` - CRUD, permissions, events, graph rules ✅
109
- - `tests/integration/e2e.test.ts` - Compiler pipeline ✅
110
- - `tests/integration/full_stack.test.ts` - Runtime + WebSocket + Pinia ✅
111
- - `tests/integration/features.test.ts` - Search filters, deep paths, M2M, soft delete, field defaults, composite PK ✅
112
-
113
- ---
114
-
115
- ## Not Applicable to V2
116
-
117
- ### Interpreted Mode
118
- - V1 had runtime `register_entity` + generic functions
119
- - V2 is compile-only by design - no gap, intentional removal
120
-
121
- ---
122
-
123
- ## Feature Comparison Summary
124
-
125
- | Feature | V1 | V2 | Status |
126
- |---------|----|----|--------|
127
- | Authentication | ✅ | ✅ | Done |
128
- | CRUD Operations | ✅ | ✅ | Done |
129
- | Permissions | ✅ | ✅ | Done |
130
- | Graph Rules | ✅ | ✅ | Done |
131
- | M2M Relationships | ✅ | ✅ | Done |
132
- | Events | ✅ | ✅ | Done |
133
- | Subscribables | ✅ | ✅ | Done |
134
- | Security (allowlist) | ✅ | ✅ | Done |
135
- | Search Operators | ✅ | ✅ | Done |
136
- | Deep Permission Paths | ✅ | ✅ | Done |
137
- | Pinia Stores | ❌ | ✅ | New in V2 |
138
- | .env Support | ❌ | ✅ | New in V2 |
139
- | Soft Delete | ✅ | ✅ | Done |
140
- | Field Defaults | ✅ | ✅ | Done |
141
- | Composite PK | ✅ | ✅ | Done |
142
- | Custom Functions (SQL) | ✅ | ✅ | Done |
143
- | Custom Functions (JS) | ✅ | ✅ | Done |
144
- | Dashboard Collection | ✅ | ❌ | **TODO** |
145
- | Null-Root Dashboard | ✅ | ❌ | **TODO** |
146
- | Interpreted Mode | ✅ | ❌ | Removed |