dzql 0.5.32 → 0.6.0

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 (150) hide show
  1. package/.env.sample +28 -0
  2. package/compose.yml +28 -0
  3. package/dist/client/index.ts +1 -0
  4. package/dist/client/stores/useMyProfileStore.ts +114 -0
  5. package/dist/client/stores/useOrgDashboardStore.ts +131 -0
  6. package/dist/client/stores/useVenueDetailStore.ts +117 -0
  7. package/dist/client/ws.ts +716 -0
  8. package/dist/db/migrations/000_core.sql +92 -0
  9. package/dist/db/migrations/20251229T212912022Z_schema.sql +3020 -0
  10. package/dist/db/migrations/20251229T212912022Z_subscribables.sql +371 -0
  11. package/dist/runtime/manifest.json +1562 -0
  12. package/docs/README.md +293 -36
  13. package/docs/feature-requests/applyPatch-bug-report.md +85 -0
  14. package/docs/feature-requests/connection-ready-profile.md +57 -0
  15. package/docs/feature-requests/hidden-bug-report.md +111 -0
  16. package/docs/feature-requests/hidden-fields-subscribables.md +34 -0
  17. package/docs/feature-requests/subscribable-param-key-bug.md +38 -0
  18. package/docs/feature-requests/todo.md +146 -0
  19. package/docs/for_ai.md +641 -0
  20. package/docs/project-setup.md +432 -0
  21. package/examples/blog.ts +50 -0
  22. package/examples/invalid.ts +18 -0
  23. package/examples/venues.js +485 -0
  24. package/package.json +23 -60
  25. package/src/cli/codegen/client.ts +99 -0
  26. package/src/cli/codegen/manifest.ts +95 -0
  27. package/src/cli/codegen/pinia.ts +174 -0
  28. package/src/cli/codegen/realtime.ts +58 -0
  29. package/src/cli/codegen/sql.ts +698 -0
  30. package/src/cli/codegen/subscribable_sql.ts +547 -0
  31. package/src/cli/codegen/subscribable_store.ts +184 -0
  32. package/src/cli/codegen/types.ts +142 -0
  33. package/src/cli/compiler/analyzer.ts +52 -0
  34. package/src/cli/compiler/graph_rules.ts +251 -0
  35. package/src/cli/compiler/ir.ts +233 -0
  36. package/src/cli/compiler/loader.ts +132 -0
  37. package/src/cli/compiler/permissions.ts +227 -0
  38. package/src/cli/index.ts +164 -0
  39. package/src/client/index.ts +1 -0
  40. package/src/client/ws.ts +286 -0
  41. package/src/create/.env.example +8 -0
  42. package/src/create/README.md +101 -0
  43. package/src/create/compose.yml +14 -0
  44. package/src/create/domain.ts +153 -0
  45. package/src/create/package.json +24 -0
  46. package/src/create/server.ts +18 -0
  47. package/src/create/setup.sh +11 -0
  48. package/src/create/tsconfig.json +15 -0
  49. package/src/runtime/auth.ts +39 -0
  50. package/src/runtime/db.ts +33 -0
  51. package/src/runtime/errors.ts +51 -0
  52. package/src/runtime/index.ts +98 -0
  53. package/src/runtime/js_functions.ts +63 -0
  54. package/src/runtime/manifest_loader.ts +29 -0
  55. package/src/runtime/namespace.ts +483 -0
  56. package/src/runtime/server.ts +87 -0
  57. package/src/runtime/ws.ts +197 -0
  58. package/src/shared/ir.ts +197 -0
  59. package/tests/client.test.ts +38 -0
  60. package/tests/codegen.test.ts +71 -0
  61. package/tests/compiler.test.ts +45 -0
  62. package/tests/graph_rules.test.ts +173 -0
  63. package/tests/integration/db.test.ts +174 -0
  64. package/tests/integration/e2e.test.ts +65 -0
  65. package/tests/integration/features.test.ts +922 -0
  66. package/tests/integration/full_stack.test.ts +262 -0
  67. package/tests/integration/setup.ts +45 -0
  68. package/tests/ir.test.ts +32 -0
  69. package/tests/namespace.test.ts +395 -0
  70. package/tests/permissions.test.ts +55 -0
  71. package/tests/pinia.test.ts +48 -0
  72. package/tests/realtime.test.ts +22 -0
  73. package/tests/runtime.test.ts +80 -0
  74. package/tests/subscribable_gen.test.ts +72 -0
  75. package/tests/subscribable_reactivity.test.ts +258 -0
  76. package/tests/venues_gen.test.ts +25 -0
  77. package/tsconfig.json +20 -0
  78. package/tsconfig.tsbuildinfo +1 -0
  79. package/README.md +0 -90
  80. package/bin/cli.js +0 -727
  81. package/docs/compiler/ADVANCED_FILTERS.md +0 -183
  82. package/docs/compiler/CODING_STANDARDS.md +0 -415
  83. package/docs/compiler/COMPARISON.md +0 -673
  84. package/docs/compiler/QUICKSTART.md +0 -326
  85. package/docs/compiler/README.md +0 -134
  86. package/docs/examples/README.md +0 -38
  87. package/docs/examples/blog.sql +0 -160
  88. package/docs/examples/venue-detail-simple.sql +0 -8
  89. package/docs/examples/venue-detail-subscribable.sql +0 -45
  90. package/docs/for-ai/claude-guide.md +0 -1210
  91. package/docs/getting-started/quickstart.md +0 -125
  92. package/docs/getting-started/subscriptions-quick-start.md +0 -203
  93. package/docs/getting-started/tutorial.md +0 -1104
  94. package/docs/guides/atomic-updates.md +0 -299
  95. package/docs/guides/client-stores.md +0 -730
  96. package/docs/guides/composite-primary-keys.md +0 -158
  97. package/docs/guides/custom-functions.md +0 -362
  98. package/docs/guides/drop-semantics.md +0 -554
  99. package/docs/guides/field-defaults.md +0 -240
  100. package/docs/guides/interpreter-vs-compiler.md +0 -237
  101. package/docs/guides/many-to-many.md +0 -929
  102. package/docs/guides/subscriptions.md +0 -537
  103. package/docs/reference/api.md +0 -1373
  104. package/docs/reference/client.md +0 -224
  105. package/src/client/stores/index.js +0 -8
  106. package/src/client/stores/useAppStore.js +0 -285
  107. package/src/client/stores/useWsStore.js +0 -289
  108. package/src/client/ws.js +0 -762
  109. package/src/compiler/cli/compile-example.js +0 -33
  110. package/src/compiler/cli/compile-subscribable.js +0 -43
  111. package/src/compiler/cli/debug-compile.js +0 -44
  112. package/src/compiler/cli/debug-parse.js +0 -26
  113. package/src/compiler/cli/debug-path-parser.js +0 -18
  114. package/src/compiler/cli/debug-subscribable-parser.js +0 -21
  115. package/src/compiler/cli/index.js +0 -174
  116. package/src/compiler/codegen/auth-codegen.js +0 -153
  117. package/src/compiler/codegen/drop-semantics-codegen.js +0 -553
  118. package/src/compiler/codegen/graph-rules-codegen.js +0 -450
  119. package/src/compiler/codegen/notification-codegen.js +0 -232
  120. package/src/compiler/codegen/operation-codegen.js +0 -1382
  121. package/src/compiler/codegen/permission-codegen.js +0 -318
  122. package/src/compiler/codegen/subscribable-codegen.js +0 -827
  123. package/src/compiler/compiler.js +0 -371
  124. package/src/compiler/index.js +0 -11
  125. package/src/compiler/parser/entity-parser.js +0 -440
  126. package/src/compiler/parser/path-parser.js +0 -290
  127. package/src/compiler/parser/subscribable-parser.js +0 -244
  128. package/src/database/dzql-core.sql +0 -161
  129. package/src/database/migrations/001_schema.sql +0 -60
  130. package/src/database/migrations/002_functions.sql +0 -890
  131. package/src/database/migrations/003_operations.sql +0 -1135
  132. package/src/database/migrations/004_search.sql +0 -581
  133. package/src/database/migrations/005_entities.sql +0 -730
  134. package/src/database/migrations/006_auth.sql +0 -94
  135. package/src/database/migrations/007_events.sql +0 -133
  136. package/src/database/migrations/008_hello.sql +0 -18
  137. package/src/database/migrations/008a_meta.sql +0 -172
  138. package/src/database/migrations/009_subscriptions.sql +0 -240
  139. package/src/database/migrations/010_atomic_updates.sql +0 -157
  140. package/src/database/migrations/010_fix_m2m_events.sql +0 -94
  141. package/src/index.js +0 -40
  142. package/src/server/api.js +0 -9
  143. package/src/server/db.js +0 -442
  144. package/src/server/index.js +0 -317
  145. package/src/server/logger.js +0 -259
  146. package/src/server/mcp.js +0 -594
  147. package/src/server/meta-route.js +0 -251
  148. package/src/server/namespace.js +0 -292
  149. package/src/server/subscriptions.js +0 -351
  150. package/src/server/ws.js +0 -573
@@ -1,554 +0,0 @@
1
- # Drop Semantics
2
-
3
- Compile-time manifest describing valid drag-and-drop interactions for canvas UIs.
4
-
5
- ## Overview
6
-
7
- When you compile entity definitions, DZQL generates a `drop-semantics.json` file that describes all valid drag-and-drop relationships between entities. This allows canvas UIs to:
8
-
9
- - Know which entities can be dropped onto which targets
10
- - Display appropriate visual feedback (containment, frames, edges, badges)
11
- - Execute the correct database operation for each drop
12
- - Provide unlink/remove functionality
13
-
14
- **Key benefit:** The canvas never interprets SQL - it reads a static manifest and knows exactly what connections are valid.
15
-
16
- ## Quick Start
17
-
18
- ```bash
19
- dzql compile entities/domain.sql -o compiled/
20
- # Outputs:
21
- # compiled/entities.sql
22
- # compiled/drop-semantics.json ← Canvas consumes this
23
- # compiled/checksums.json
24
- ```
25
-
26
- ## Output Format
27
-
28
- ```json
29
- {
30
- "entities": {
31
- "tasks": {
32
- "droppable_on": {
33
- "task_groups": [{
34
- "relation": "group_id",
35
- "type": "fk",
36
- "action": "move",
37
- "visual": "containment",
38
- "label": "Move to group",
39
- "operation": {
40
- "method": "save",
41
- "entity": "tasks",
42
- "params": { "id": "@source.id", "group_id": "@target.id" }
43
- },
44
- "removable": true,
45
- "remove_operation": {
46
- "method": "save",
47
- "entity": "tasks",
48
- "params": { "id": "@source.id", "group_id": null }
49
- }
50
- }],
51
- "users": [{
52
- "relation": "assigned_to_user_id",
53
- "type": "fk",
54
- "action": "move",
55
- "visual": "badge",
56
- "label": "Move to user",
57
- "primary_direction": "accepts",
58
- "operation": { ... }
59
- }]
60
- },
61
- "accepts": {
62
- "users": [{
63
- "relation": "assigned_to_user_id",
64
- "type": "fk",
65
- "action": "assign",
66
- "visual": "badge",
67
- "label": "Assign user",
68
- "operation": {
69
- "method": "save",
70
- "entity": "tasks",
71
- "params": { "id": "@target.id", "assigned_to_user_id": "@source.id" }
72
- },
73
- "removable": true,
74
- "remove_operation": {
75
- "method": "save",
76
- "entity": "tasks",
77
- "params": { "id": "@target.id", "assigned_to_user_id": null }
78
- }
79
- }]
80
- }
81
- }
82
- }
83
- }
84
- ```
85
-
86
- ### Primary Direction Hint
87
-
88
- Some relationships have a natural gesture direction. For example, you typically drop a *user* onto a *task* to assign them, not the other way around. When `primary_direction: "accepts"` is present, the canvas should prioritize the `accepts` entry for UI affordances (drop zones, visual hints).
89
-
90
- ```json
91
- {
92
- "relation": "assigned_to_user_id",
93
- "primary_direction": "accepts"
94
- }
95
- ```
96
-
97
- The compiler infers this from naming patterns like `assigned_to_*`, `created_by_*`, `author`, `owner`, etc.
98
-
99
- ## Terminology
100
-
101
- - **source** - The entity being dragged
102
- - **target** - The entity being dropped onto
103
- - **droppable_on** - What THIS entity can be dropped onto
104
- - **accepts** - What can be dropped onto THIS entity
105
-
106
- ## Derivation Rules
107
-
108
- The compiler derives drop semantics from your schema relationships:
109
-
110
- ### 1. Foreign Key Relationships
111
-
112
- ```sql
113
- -- tasks.group_id REFERENCES task_groups
114
- ```
115
-
116
- Generates:
117
-
118
- | Perspective | Entry | Meaning |
119
- |-------------|-------|---------|
120
- | `tasks.droppable_on.task_groups` | action: "move" | Drag task onto group → update task.group_id |
121
- | `tasks.accepts.task_groups` | action: "assign" | Drag group onto task → update task.group_id |
122
-
123
- ### 2. Many-to-Many (Junction Tables)
124
-
125
- ```sql
126
- -- post_tags(post_id, tag_id)
127
- ```
128
-
129
- Generates:
130
-
131
- | Perspective | Entry | Meaning |
132
- |-------------|-------|---------|
133
- | `posts.droppable_on.tags` | action: "link" | Drag post onto tag → insert junction |
134
- | `posts.accepts.tags` | action: "link" | Drag tag onto post → insert junction |
135
-
136
- ### 3. Self-Referential FK
137
-
138
- ```sql
139
- -- categories.parent_id REFERENCES categories
140
- ```
141
-
142
- Generates:
143
-
144
- | Perspective | Entry | Meaning |
145
- |-------------|-------|---------|
146
- | `categories.droppable_on.categories` | action: "reparent" | Drag category onto another → set parent |
147
-
148
- ### 4. Self-Referential Junction (Dependencies)
149
-
150
- ```sql
151
- -- task_dependencies(task_id, depends_on_task_id)
152
- ```
153
-
154
- Generates:
155
-
156
- | Perspective | Entry | Meaning |
157
- |-------------|-------|---------|
158
- | `tasks.droppable_on.tasks` | action: "link", visual: "edge" | Drag task onto task → create dependency edge |
159
-
160
- ## Visual Types
161
-
162
- The `visual` field tells the canvas how to render each relationship:
163
-
164
- | Visual | Meaning | When Used |
165
- |--------|---------|-----------|
166
- | `containment` | Node moves inside container | Tree structures (folders, groups) |
167
- | `frame` | Visual bounding box around members | Sets, collections |
168
- | `edge` | Arrow drawn between nodes | Dependencies, relationships |
169
- | `badge` | Tag/chip displayed on node | Assignments, references |
170
-
171
- ### Automatic Visual Inference
172
-
173
- The compiler infers visual type using these rules (in order):
174
-
175
- 1. **Self-referential junction** → `edge`
176
- 2. **Self-referential FK** → `containment`
177
- 3. **Target has self-referential FK** (is a tree) → `containment`
178
- 4. **Name ends with `_groups`, `_folders`, `_categories`** → `containment`
179
- 5. **Name ends with `_sets`, `_collections`, `_lists`** → `frame`
180
- 6. **Default** → `badge`
181
-
182
- ### Edge Direction
183
-
184
- For `edge` visuals (self-referential junctions), the output includes direction:
185
-
186
- ```json
187
- {
188
- "visual": "edge",
189
- "direction": "source_to_target",
190
- "self_referential": true
191
- }
192
- ```
193
-
194
- The canvas can use this to draw arrows in the correct direction.
195
-
196
- ## Remove Operations
197
-
198
- Every relationship includes remove semantics:
199
-
200
- ### FK Relationships
201
-
202
- ```json
203
- {
204
- "removable": true,
205
- "remove_operation": {
206
- "method": "save",
207
- "entity": "tasks",
208
- "params": { "id": "@source.id", "group_id": null }
209
- }
210
- }
211
- ```
212
-
213
- Setting the FK to `null` unlinks the relationship.
214
-
215
- ### Junction Relationships
216
-
217
- ```json
218
- {
219
- "removable": true,
220
- "remove_operation": {
221
- "method": "delete",
222
- "entity": "post_tags",
223
- "params": { "post_id": "@source.id", "tag_id": "@target.id" }
224
- }
225
- }
226
- ```
227
-
228
- Deleting the junction record removes the link.
229
-
230
- ## Composite Primary Keys
231
-
232
- Entities with composite primary keys include all key fields in params:
233
-
234
- ```json
235
- {
236
- "operation": {
237
- "method": "save",
238
- "entity": "org_items",
239
- "params": {
240
- "org_id": "@source.org_id",
241
- "item_code": "@source.item_code",
242
- "category_id": "@target.id"
243
- }
244
- }
245
- }
246
- ```
247
-
248
- ## Canvas Integration
249
-
250
- ### Checking Valid Drops
251
-
252
- ```javascript
253
- function canDrop(sourceEntity, sourceId, targetEntity, targetId) {
254
- const semantics = dropSemantics.entities[sourceEntity];
255
- if (!semantics) return false;
256
-
257
- return semantics.droppable_on[targetEntity]?.length > 0;
258
- }
259
- ```
260
-
261
- ### Executing Drop
262
-
263
- ```javascript
264
- async function executeDrop(ws, sourceEntity, sourceData, targetEntity, targetData, relationIndex = 0) {
265
- const action = dropSemantics.entities[sourceEntity].droppable_on[targetEntity][relationIndex];
266
-
267
- const params = resolveParams(action.operation.params, sourceData, targetData);
268
-
269
- if (action.operation.method === 'save') {
270
- await ws.api.save[action.operation.entity](params);
271
- } else if (action.operation.method === 'delete') {
272
- await ws.api.delete[action.operation.entity](params);
273
- }
274
- }
275
-
276
- function resolveParams(template, sourceData, targetData) {
277
- const params = {};
278
- for (const [key, value] of Object.entries(template)) {
279
- if (typeof value === 'string' && value.startsWith('@source.')) {
280
- params[key] = sourceData[value.replace('@source.', '')];
281
- } else if (typeof value === 'string' && value.startsWith('@target.')) {
282
- params[key] = targetData[value.replace('@target.', '')];
283
- } else {
284
- params[key] = value;
285
- }
286
- }
287
- return params;
288
- }
289
- ```
290
-
291
- ### Getting Visual Hint
292
-
293
- ```javascript
294
- function getDropVisual(sourceEntity, targetEntity) {
295
- const action = dropSemantics.entities[sourceEntity]?.droppable_on[targetEntity]?.[0];
296
- return action?.visual || null;
297
- }
298
- ```
299
-
300
- ### Multiple Relations Picker
301
-
302
- When multiple relations exist between the same entities (e.g., task→task could be "depends on" or "blocks"), show a picker:
303
-
304
- ```javascript
305
- function getDropOptions(sourceEntity, targetEntity) {
306
- const actions = dropSemantics.entities[sourceEntity]?.droppable_on[targetEntity] || [];
307
- return actions.map((action, index) => ({
308
- index,
309
- label: action.label,
310
- visual: action.visual,
311
- relation: action.relation
312
- }));
313
- }
314
-
315
- // In Vue component
316
- <template>
317
- <div v-if="dropOptions.length > 1" class="relation-picker">
318
- <button
319
- v-for="option in dropOptions"
320
- :key="option.index"
321
- @click="executeDrop(option.index)"
322
- >
323
- {{ option.label }}
324
- </button>
325
- </div>
326
- </template>
327
- ```
328
-
329
- ## Example: Complete Task Management
330
-
331
- ### Schema
332
-
333
- ```sql
334
- -- Groups with hierarchy
335
- CREATE TABLE task_groups (
336
- id SERIAL PRIMARY KEY,
337
- name TEXT NOT NULL,
338
- parent_id INT REFERENCES task_groups(id)
339
- );
340
-
341
- -- Tasks
342
- CREATE TABLE tasks (
343
- id SERIAL PRIMARY KEY,
344
- title TEXT NOT NULL,
345
- group_id INT REFERENCES task_groups(id),
346
- assigned_to_user_id INT REFERENCES users(id)
347
- );
348
-
349
- -- Task sets (for batch operations)
350
- CREATE TABLE task_sets (
351
- id SERIAL PRIMARY KEY,
352
- name TEXT NOT NULL
353
- );
354
-
355
- CREATE TABLE task_set_members (
356
- task_id INT REFERENCES tasks(id) ON DELETE CASCADE,
357
- set_id INT REFERENCES task_sets(id) ON DELETE CASCADE,
358
- PRIMARY KEY (task_id, set_id)
359
- );
360
-
361
- -- Task dependencies
362
- CREATE TABLE task_dependencies (
363
- task_id INT REFERENCES tasks(id) ON DELETE CASCADE,
364
- depends_on_task_id INT REFERENCES tasks(id) ON DELETE CASCADE,
365
- PRIMARY KEY (task_id, depends_on_task_id)
366
- );
367
-
368
- -- Entity registrations
369
- SELECT dzql.register_entity('task_groups', 'name', ARRAY['name'],
370
- jsonb_build_object('parent', 'task_groups'),
371
- false, '{}', '{}',
372
- jsonb_build_object('view', ARRAY[]::text[], 'create', ARRAY[]::text[],
373
- 'update', ARRAY[]::text[], 'delete', ARRAY[]::text[])
374
- );
375
-
376
- SELECT dzql.register_entity('tasks', 'title', ARRAY['title'],
377
- jsonb_build_object('group', 'task_groups', 'assigned_to_user', 'users'),
378
- false, '{}', '{}',
379
- jsonb_build_object('view', ARRAY[]::text[], 'create', ARRAY[]::text[],
380
- 'update', ARRAY[]::text[], 'delete', ARRAY[]::text[]),
381
- jsonb_build_object(
382
- 'many_to_many', jsonb_build_object(
383
- 'sets', jsonb_build_object(
384
- 'junction_table', 'task_set_members',
385
- 'local_key', 'task_id',
386
- 'foreign_key', 'set_id',
387
- 'target_entity', 'task_sets',
388
- 'id_field', 'set_ids'
389
- ),
390
- 'dependencies', jsonb_build_object(
391
- 'junction_table', 'task_dependencies',
392
- 'local_key', 'task_id',
393
- 'foreign_key', 'depends_on_task_id',
394
- 'target_entity', 'tasks',
395
- 'id_field', 'dependency_ids'
396
- )
397
- )
398
- )
399
- );
400
- ```
401
-
402
- ### Generated Drop Semantics
403
-
404
- ```json
405
- {
406
- "entities": {
407
- "task_groups": {
408
- "droppable_on": {
409
- "task_groups": [{
410
- "relation": "parent_id",
411
- "type": "fk",
412
- "action": "reparent",
413
- "visual": "containment",
414
- "label": "Set parent",
415
- "operation": { ... },
416
- "removable": true,
417
- "remove_operation": { ... }
418
- }]
419
- },
420
- "accepts": {
421
- "tasks": [{ ... }]
422
- }
423
- },
424
- "tasks": {
425
- "droppable_on": {
426
- "task_groups": [{
427
- "relation": "group_id",
428
- "type": "fk",
429
- "action": "move",
430
- "visual": "containment",
431
- "label": "Move to group",
432
- "operation": { ... }
433
- }],
434
- "users": [{
435
- "relation": "assigned_to_user_id",
436
- "type": "fk",
437
- "action": "move",
438
- "visual": "badge",
439
- "label": "Move to assigned to user",
440
- "operation": { ... }
441
- }],
442
- "task_sets": [{
443
- "relation": "task_set_members",
444
- "type": "junction",
445
- "action": "link",
446
- "visual": "frame",
447
- "label": "Add task set member",
448
- "operation": { ... }
449
- }],
450
- "tasks": [{
451
- "relation": "task_dependencies",
452
- "type": "junction",
453
- "action": "link",
454
- "visual": "edge",
455
- "direction": "source_to_target",
456
- "label": "Add task dependency",
457
- "operation": { ... },
458
- "self_referential": true
459
- }]
460
- },
461
- "accepts": {
462
- "users": [{
463
- "relation": "assigned_to_user_id",
464
- "type": "fk",
465
- "action": "assign",
466
- "visual": "badge",
467
- "label": "Assign assigned to user",
468
- "operation": { ... }
469
- }],
470
- "task_sets": [{ ... }],
471
- "tasks": [{
472
- "visual": "edge",
473
- "direction": "target_to_source"
474
- }]
475
- }
476
- }
477
- }
478
- }
479
- ```
480
-
481
- ### Canvas Interpretation
482
-
483
- | Drop | Visual | Result |
484
- |------|--------|--------|
485
- | Task → Group | containment | Task node moves inside group container |
486
- | Task → Task | edge | Arrow drawn from source to target |
487
- | User → Task | badge | User chip appears on task node |
488
- | Task → Set | frame | Task included in set's visual boundary |
489
- | Group → Group | containment | Group nests inside another group |
490
-
491
- ## Relationship Types Summary
492
-
493
- | Schema Pattern | Type | Action | Default Visual |
494
- |----------------|------|--------|----------------|
495
- | `A.fk_id REFERENCES B` | `fk` | `move` | `badge` or `containment`* |
496
- | `A.fk_id REFERENCES A` | `fk` | `reparent` | `containment` |
497
- | Junction(A, B) | `junction` | `link` | `badge` or `frame`* |
498
- | Junction(A, A) | `junction` | `link` | `edge` |
499
-
500
- *Visual depends on target entity name and structure
501
-
502
- ## Canvas Position Storage
503
-
504
- The drop-semantics manifest covers relationships but not node positions. For canvas x/y coordinates, consider:
505
-
506
- ### Option A: JSON Column on Entity
507
-
508
- Simple approach for single-user or shared layouts:
509
-
510
- ```sql
511
- ALTER TABLE tasks ADD COLUMN canvas JSONB DEFAULT '{}';
512
-
513
- -- Store position
514
- UPDATE tasks SET canvas = jsonb_build_object('x', 100, 'y', 200) WHERE id = 1;
515
-
516
- -- Or include in entity definition for automatic handling
517
- ```
518
-
519
- ### Option B: Separate Positions Table
520
-
521
- For multi-user layouts or per-project views:
522
-
523
- ```sql
524
- CREATE TABLE canvas_positions (
525
- entity TEXT NOT NULL,
526
- record_id INT NOT NULL,
527
- user_id INT REFERENCES users(id),
528
- project_id INT, -- Optional: per-project layouts
529
- x FLOAT NOT NULL,
530
- y FLOAT NOT NULL,
531
- updated_at TIMESTAMPTZ DEFAULT NOW(),
532
- PRIMARY KEY (entity, record_id, COALESCE(user_id, 0), COALESCE(project_id, 0))
533
- );
534
-
535
- CREATE INDEX idx_canvas_positions_lookup
536
- ON canvas_positions(entity, user_id, project_id);
537
- ```
538
-
539
- ### Option C: Client-Side Storage
540
-
541
- For personal layouts that don't need server persistence:
542
-
543
- ```javascript
544
- // localStorage per user
545
- const positions = JSON.parse(localStorage.getItem('canvas_positions') || '{}');
546
- positions[`${entity}:${id}`] = { x, y };
547
- localStorage.setItem('canvas_positions', JSON.stringify(positions));
548
- ```
549
-
550
- ## See Also
551
-
552
- - [Many-to-Many](./many-to-many.md) - Junction table configuration
553
- - [Compiler Guide](../compiler/README.md) - Full compilation workflow
554
- - [Custom Functions](./custom-functions.md) - Extending with business logic