@live-change/frontend-template 0.9.203 → 0.9.205

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 (30) hide show
  1. package/.claude/rules/live-change-backend-actions-views-triggers.md +49 -1
  2. package/.claude/rules/live-change-backend-models-and-relations.md +24 -6
  3. package/.claude/rules/live-change-service-structure.md +2 -2
  4. package/.claude/settings.json +3 -1
  5. package/.claude/skills/live-change-backend-change-triggers/SKILL.md +15 -0
  6. package/.claude/skills/live-change-dao-protocol/SKILL.md +46 -0
  7. package/.claude/skills/live-change-design-actions-views-triggers/SKILL.md +110 -0
  8. package/.claude/skills/live-change-design-models-relations/SKILL.md +63 -5
  9. package/.claude/skills/live-change-frontend-data-views/SKILL.md +73 -4
  10. package/.claude/skills/live-change-frontend-range-list/SKILL.md +90 -0
  11. package/.claude/skills/live-change-frontend-synchronized/SKILL.md +101 -0
  12. package/.cursor/rules/live-change-backend-actions-views-triggers.mdc +23 -4
  13. package/.cursor/rules/live-change-backend-architecture.mdc +1 -1
  14. package/.cursor/rules/live-change-backend-event-sourcing.mdc +1 -1
  15. package/.cursor/rules/live-change-backend-models-and-relations.mdc +36 -7
  16. package/.cursor/rules/live-change-backend-views-vs-triggers-for-reads-writes.mdc +28 -0
  17. package/.cursor/rules/live-change-dao-protocol.mdc +47 -0
  18. package/.cursor/rules/live-change-frontend-views-not-commands-for-reads.mdc +30 -0
  19. package/.cursor/rules/live-change-frontend-vue-primevue.mdc +70 -4
  20. package/.cursor/rules/live-change-service-structure.mdc +1 -1
  21. package/.cursor/skills/live-change-backend-change-triggers.md +15 -0
  22. package/.cursor/skills/live-change-design-actions-views-triggers.md +51 -0
  23. package/.cursor/skills/live-change-design-models-relations.md +23 -5
  24. package/.cursor/skills/live-change-frontend-data-views.md +15 -0
  25. package/.cursor/skills/live-change-frontend-range-list.md +21 -0
  26. package/.cursor/skills/live-change-frontend-synchronized.md +101 -0
  27. package/.node-version +1 -1
  28. package/.nvmrc +1 -1
  29. package/front/src/pages/index.vue +1 -1
  30. package/package.json +55 -55
@@ -27,6 +27,19 @@ function articlesPathRange(range) {
27
27
  }
28
28
  ```
29
29
 
30
+ ## Step 1a – Hard rules for index-backed ranges
31
+
32
+ For lists loaded with `RangeViewer` / `rangeBuckets`:
33
+
34
+ - backend views should use `sortedIndexRangePath`, not `indexRangePath`,
35
+ - keep `range.gt/gte/lt/lte` for pagination cursor only,
36
+ - never override `gt/lt` in frontend `pathFunction` with ad-hoc filters.
37
+
38
+ Why:
39
+
40
+ - RangeViewer computes next buckets from previous cursor boundaries,
41
+ - replacing cursor fields causes repeated slices and broken infinite loading.
42
+
30
43
  ## Step 2 – Attach related objects with `.with()`
31
44
 
32
45
  Chain `.with()` calls to load related data for each item:
@@ -151,3 +164,11 @@ Why:
151
164
  </template>
152
165
  </ReactiveRangeViewer>
153
166
  ```
167
+
168
+ ## Checklist – range pagination safety
169
+
170
+ - [ ] backend index view is based on `sortedIndexRangePath`
171
+ - [ ] frontend `pathFunction` forwards `range` unchanged (`...range` or `...reverseRange(range)`)
172
+ - [ ] domain filters (`month`, `year`, `status`) are separate view params
173
+ - [ ] no manual cursor overrides (`gt/gte/lt/lte`) in frontend code
174
+ - [ ] if narrowing is needed, backend uses index prefix design first, `prefixRange` only as fallback
@@ -0,0 +1,101 @@
1
+ ---
2
+ name: live-change-frontend-synchronized
3
+ description: Use synchronized and synchronizedList for autosave editing in LiveChange Vue frontends, including object vs list decision flow, identifiers mapping, and save/delete patterns
4
+ ---
5
+
6
+ # Skill: live-change-frontend-synchronized (Cursor)
7
+
8
+ Use this skill when implementing or refactoring frontend editing flows that should keep local form state synchronized with backend data.
9
+
10
+ ## When to use
11
+
12
+ - Editing one object loaded from `live(...)` with autosave or manual save.
13
+ - Editing list items inline (for example access roles) with per-row identifiers.
14
+ - Replacing custom `watch + api.command` autosave logic with a standard helper.
15
+ - Choosing between `synchronized`, `synchronizedList`, and `editorData`.
16
+
17
+ ## Decision flow
18
+
19
+ 1. **One editable object** (`profile`, `note`, `settings`) -> use `synchronized`.
20
+ 2. **Editable list rows** (`accesses`, `invitations`, `requests`) -> use `synchronizedList`.
21
+ 3. **Definition-driven CRUD form with validation UI** -> use `editorData` from auto-form stack.
22
+
23
+ ## What counts as "editable list rows"
24
+
25
+ Treat the feature as a list flow (`synchronizedList`) when most of these are true:
26
+
27
+ - The UI renders rows with `v-for` and each row has editable fields.
28
+ - Users can edit many rows inline in one screen (table/config/admin list).
29
+ - Autosave should happen per row while the list stays reactive.
30
+ - Each row needs its own action payload keys in addition to shared list context.
31
+
32
+ In this case, wire one `synchronizedList(...)` and edit fields directly on `syncList.value`.
33
+ Do not build a parallel `id -> synchronized(...)` map for rows.
34
+
35
+ ## `synchronized` pattern (object)
36
+
37
+ ```javascript
38
+ import { synchronized } from '@live-change/vue3-components'
39
+
40
+ const sync = synchronized({
41
+ source: sourceRef,
42
+ update: actions.service.updateThing,
43
+ identifiers: computed(() => ({ thing: thingId.value })),
44
+ recursive: true,
45
+ autoSave: true,
46
+ debounce: 600
47
+ })
48
+
49
+ const { value: editable, changed, saving, save } = sync
50
+ ```
51
+
52
+ ### Rules
53
+
54
+ - `source` should come from `live(...)` or a computed source.
55
+ - Keep identifiers stable and pass them through `identifiers` (plain object or `computed`).
56
+ - Use `updateDataProperty: 'data'` for draft-like payloads where backend expects nested data.
57
+ - Use `autoSave: false` when save must happen only after explicit confirmation.
58
+
59
+ ## `synchronizedList` pattern (list)
60
+
61
+ ```javascript
62
+ import { synchronizedList } from '@live-change/vue3-components'
63
+
64
+ const syncList = synchronizedList({
65
+ source: accesses,
66
+ update: actions.accessControl.updateSessionOrUserAndObjectOwnedAccess,
67
+ delete: actions.accessControl.resetSessionOrUserAndObjectOwnedAccess,
68
+ identifiers: { object, objectType },
69
+ objectIdentifiers: ({ to, sessionOrUser, sessionOrUserType }) => ({
70
+ access: to, sessionOrUser, sessionOrUserType, object, objectType
71
+ }),
72
+ recursive: true
73
+ })
74
+
75
+ const editableRows = syncList.value
76
+ await syncList.delete(editableRows.value[0])
77
+ ```
78
+
79
+ ### Rules
80
+
81
+ - `source` should be a list from `live(...)`.
82
+ - Every item should have stable `id`.
83
+ - Put shared context in `identifiers`, and row-specific keys in `objectIdentifiers`.
84
+ - Edit rows through `syncList.value`; call `syncList.delete(...)` / `syncList.insert(...)` on the helper object.
85
+ - Prefer this pattern for configuration lists, permission tables, dictionaries, and other multi-row settings editors.
86
+
87
+ ## Project examples to follow
88
+
89
+ - `speed-dating/front/src/components/notes/NoteEditor.vue` (`synchronized` with autosave)
90
+ - `speed-dating/front/src/components/profile/ProfileSettings.vue` (`synchronized` + `updateDataProperty: 'data'`)
91
+ - `family-tree/front/src/components/AgreementDialog.vue` (`synchronized` with manual save)
92
+ - `rcstreamer/front/src/configuration/AccessRequests.vue` (`synchronizedList`)
93
+ - `rcstreamer/front/src/configuration/AccessInvitations.vue` (`synchronizedList`)
94
+ - `rcstreamer/front/src/configuration/AccessList.vue` (`synchronizedList`)
95
+
96
+ ## Common mistakes
97
+
98
+ - Using `synchronized` for a list of rows instead of `synchronizedList`.
99
+ - Forgetting `objectIdentifiers` when backend update/delete actions need per-row keys.
100
+ - Mutating raw `live(...)` list data instead of `synchronizedList(...).value`.
101
+ - Mixing autosave helper patterns with unrelated one-shot action form patterns.
package/.node-version CHANGED
@@ -1 +1 @@
1
- 20.19.2
1
+ 20.20.2
package/.nvmrc CHANGED
@@ -1 +1 @@
1
- 20.19.2
1
+ 20.20.2
@@ -250,7 +250,7 @@ await api.create('user', {
250
250
  </div>
251
251
  <h4 class="font-semibold text-surface-900 dark:text-surface-100 mb-2">Install Framework</h4>
252
252
  <p class="text-surface-600 dark:text-surface-300 text-sm">
253
- npm install @live-change/framework
253
+ npm install &#x40;live-change/framework
254
254
  </p>
255
255
  </div>
256
256
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@live-change/frontend-template",
3
- "version": "0.9.203",
3
+ "version": "0.9.205",
4
4
  "scripts": {
5
5
  "memDev": "tsx --inspect --expose-gc server/start.js memDev --enableSessions --initScript ./init.js --dbAccess",
6
6
  "localDevInit": "tsx server/start.js localDev --enableSessions --initScript ./init.js --dbAccess",
@@ -44,59 +44,59 @@
44
44
  },
45
45
  "type": "module",
46
46
  "dependencies": {
47
- "@codemirror/language": "6.10.1",
47
+ "@codemirror/language": "6.12.3",
48
48
  "@dotenvx/dotenvx": "0.27.0",
49
49
  "@fortawesome/fontawesome-free": "^6.7.2",
50
- "@live-change/access-control-frontend": "^0.9.203",
51
- "@live-change/access-control-service": "^0.9.203",
52
- "@live-change/agreement-service": "^0.9.203",
53
- "@live-change/backup-service": "^0.9.203",
54
- "@live-change/blog-frontend": "^0.9.203",
55
- "@live-change/blog-service": "^0.9.203",
56
- "@live-change/cli": "^0.9.203",
57
- "@live-change/content-frontend": "^0.9.203",
58
- "@live-change/content-service": "^0.9.203",
59
- "@live-change/cron-service": "^0.9.203",
60
- "@live-change/dao": "^0.9.203",
61
- "@live-change/dao-vue3": "^0.9.203",
62
- "@live-change/dao-websocket": "^0.9.203",
63
- "@live-change/db-client": "^0.9.203",
64
- "@live-change/draft-service": "^0.9.203",
65
- "@live-change/email-service": "^0.9.203",
66
- "@live-change/framework": "^0.9.203",
67
- "@live-change/frontend-auto-form": "^0.9.203",
68
- "@live-change/frontend-base": "^0.9.203",
69
- "@live-change/geoip-service": "^0.9.203",
70
- "@live-change/google-authentication-service": "^0.9.203",
71
- "@live-change/image-frontend": "^0.9.203",
72
- "@live-change/linkedin-authentication-service": "^0.9.203",
73
- "@live-change/locale-settings-service": "^0.9.203",
74
- "@live-change/notification-service": "^0.9.203",
75
- "@live-change/password-authentication-service": "^0.9.203",
76
- "@live-change/peer-connection-frontend": "^0.9.203",
77
- "@live-change/peer-connection-service": "^0.9.203",
78
- "@live-change/prosemirror-service": "^0.9.203",
79
- "@live-change/scope-service": "^0.9.203",
80
- "@live-change/secret-code-service": "^0.9.203",
81
- "@live-change/secret-link-service": "^0.9.203",
82
- "@live-change/security-service": "^0.9.203",
83
- "@live-change/session-service": "^0.9.203",
84
- "@live-change/task-frontend": "^0.9.203",
85
- "@live-change/task-service": "^0.9.203",
86
- "@live-change/timer-service": "^0.9.203",
87
- "@live-change/upload-frontend": "^0.9.203",
88
- "@live-change/upload-service": "^0.9.203",
89
- "@live-change/url-frontend": "^0.9.203",
90
- "@live-change/url-service": "^0.9.203",
91
- "@live-change/user-frontend": "^0.9.203",
92
- "@live-change/user-identification-service": "^0.9.203",
93
- "@live-change/user-service": "^0.9.203",
94
- "@live-change/video-call-frontend": "^0.9.203",
95
- "@live-change/video-call-service": "^0.9.203",
96
- "@live-change/vote-service": "^0.9.203",
97
- "@live-change/vue3-components": "^0.9.203",
98
- "@live-change/vue3-ssr": "^0.9.203",
99
- "@live-change/wysiwyg-frontend": "^0.9.203",
50
+ "@live-change/access-control-frontend": "^0.9.205",
51
+ "@live-change/access-control-service": "^0.9.205",
52
+ "@live-change/agreement-service": "^0.9.205",
53
+ "@live-change/backup-service": "^0.9.205",
54
+ "@live-change/blog-frontend": "^0.9.205",
55
+ "@live-change/blog-service": "^0.9.205",
56
+ "@live-change/cli": "^0.9.205",
57
+ "@live-change/content-frontend": "^0.9.205",
58
+ "@live-change/content-service": "^0.9.205",
59
+ "@live-change/cron-service": "^0.9.205",
60
+ "@live-change/dao": "^0.9.205",
61
+ "@live-change/dao-vue3": "^0.9.205",
62
+ "@live-change/dao-websocket": "^0.9.205",
63
+ "@live-change/db-client": "^0.9.205",
64
+ "@live-change/draft-service": "^0.9.205",
65
+ "@live-change/email-service": "^0.9.205",
66
+ "@live-change/framework": "^0.9.205",
67
+ "@live-change/frontend-auto-form": "^0.9.205",
68
+ "@live-change/frontend-base": "^0.9.205",
69
+ "@live-change/geoip-service": "^0.9.205",
70
+ "@live-change/google-authentication-service": "^0.9.205",
71
+ "@live-change/image-frontend": "^0.9.205",
72
+ "@live-change/linkedin-authentication-service": "^0.9.205",
73
+ "@live-change/locale-settings-service": "^0.9.205",
74
+ "@live-change/notification-service": "^0.9.205",
75
+ "@live-change/password-authentication-service": "^0.9.205",
76
+ "@live-change/peer-connection-frontend": "^0.9.205",
77
+ "@live-change/peer-connection-service": "^0.9.205",
78
+ "@live-change/prosemirror-service": "^0.9.205",
79
+ "@live-change/scope-service": "^0.9.205",
80
+ "@live-change/secret-code-service": "^0.9.205",
81
+ "@live-change/secret-link-service": "^0.9.205",
82
+ "@live-change/security-service": "^0.9.205",
83
+ "@live-change/session-service": "^0.9.205",
84
+ "@live-change/task-frontend": "^0.9.205",
85
+ "@live-change/task-service": "^0.9.205",
86
+ "@live-change/timer-service": "^0.9.205",
87
+ "@live-change/upload-frontend": "^0.9.205",
88
+ "@live-change/upload-service": "^0.9.205",
89
+ "@live-change/url-frontend": "^0.9.205",
90
+ "@live-change/url-service": "^0.9.205",
91
+ "@live-change/user-frontend": "^0.9.205",
92
+ "@live-change/user-identification-service": "^0.9.205",
93
+ "@live-change/user-service": "^0.9.205",
94
+ "@live-change/video-call-frontend": "^0.9.205",
95
+ "@live-change/video-call-service": "^0.9.205",
96
+ "@live-change/vote-service": "^0.9.205",
97
+ "@live-change/vue3-components": "^0.9.205",
98
+ "@live-change/vue3-ssr": "^0.9.205",
99
+ "@live-change/wysiwyg-frontend": "^0.9.205",
100
100
  "@vueuse/core": "^12.3.0",
101
101
  "codeceptjs-assert": "^0.0.5",
102
102
  "compression": "^1.7.5",
@@ -119,8 +119,8 @@
119
119
  },
120
120
  "devDependencies": {
121
121
  "copyfiles": "^2.4.1",
122
- "generate-password": "1.7.1",
123
- "playwright": "1.49.1",
122
+ "generate-password": "^1.7.1",
123
+ "playwright": "^1.49.1",
124
124
  "random-profile-generator": "^2.3.0",
125
125
  "tsx": "^4.21.0",
126
126
  "txtgen": "^3.0.7",
@@ -129,5 +129,5 @@
129
129
  "author": "Michał Łaszczewski <michal@laszczewski.pl>",
130
130
  "license": "ISC",
131
131
  "description": "",
132
- "gitHead": "4d4ce413fe747da2c398eb4ad378e37cc81874b3"
132
+ "gitHead": "ef195e51ea283e56d891b11da5d5f586691507db"
133
133
  }