@live-change/frontend-template 0.9.199 → 0.9.201
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/.claude/rules/live-change-backend-actions-views-triggers.md +62 -0
- package/.claude/rules/live-change-backend-event-sourcing.md +186 -0
- package/.claude/rules/live-change-backend-models-and-relations.md +72 -0
- package/.claude/rules/live-change-frontend-vue-primevue.md +26 -0
- package/.claude/settings.json +32 -0
- package/.claude/skills/create-skills-and-rules/SKILL.md +248 -0
- package/.claude/skills/live-change-backend-change-triggers/SKILL.md +186 -0
- package/.claude/skills/live-change-design-actions-views-triggers/SKILL.md +462 -0
- package/.claude/skills/live-change-design-models-relations/SKILL.md +230 -0
- package/.claude/skills/live-change-design-service/SKILL.md +133 -0
- package/.claude/skills/live-change-frontend-accessible-objects/SKILL.md +384 -0
- package/.claude/skills/live-change-frontend-accessible-objects.md +383 -0
- package/.claude/skills/live-change-frontend-action-buttons/SKILL.md +129 -0
- package/.claude/skills/live-change-frontend-action-form/SKILL.md +149 -0
- package/.claude/skills/live-change-frontend-analytics/SKILL.md +147 -0
- package/.claude/skills/live-change-frontend-command-forms/SKILL.md +216 -0
- package/.claude/skills/live-change-frontend-data-views/SKILL.md +183 -0
- package/.claude/skills/live-change-frontend-editor-form/SKILL.md +240 -0
- package/.claude/skills/live-change-frontend-locale-time/SKILL.md +172 -0
- package/.claude/skills/live-change-frontend-page-list-detail/SKILL.md +201 -0
- package/.claude/skills/live-change-frontend-range-list/SKILL.md +129 -0
- package/.claude/skills/live-change-frontend-ssr-setup/SKILL.md +119 -0
- package/.cursor/rules/live-change-backend-actions-views-triggers.mdc +88 -0
- package/.cursor/rules/live-change-backend-event-sourcing.mdc +185 -0
- package/.cursor/rules/live-change-backend-models-and-relations.mdc +62 -0
- package/.cursor/skills/create-skills-and-rules.md +248 -0
- package/.cursor/skills/live-change-backend-change-triggers.md +186 -0
- package/.cursor/skills/live-change-design-actions-views-triggers.md +178 -79
- package/.cursor/skills/live-change-design-models-relations.md +112 -50
- package/.cursor/skills/live-change-design-service.md +1 -0
- package/.cursor/skills/live-change-frontend-accessible-objects.md +384 -0
- package/.cursor/skills/live-change-frontend-action-buttons.md +1 -0
- package/.cursor/skills/live-change-frontend-action-form.md +9 -3
- package/.cursor/skills/live-change-frontend-analytics.md +1 -0
- package/.cursor/skills/live-change-frontend-command-forms.md +1 -0
- package/.cursor/skills/live-change-frontend-data-views.md +1 -0
- package/.cursor/skills/live-change-frontend-editor-form.md +135 -72
- package/.cursor/skills/live-change-frontend-locale-time.md +1 -0
- package/.cursor/skills/live-change-frontend-page-list-detail.md +1 -0
- package/.cursor/skills/live-change-frontend-range-list.md +1 -0
- package/.cursor/skills/live-change-frontend-ssr-setup.md +1 -0
- package/front/src/router.js +2 -1
- package/opencode.json +10 -0
- package/package.json +52 -50
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: live-change-design-models-relations
|
|
3
|
+
description: Design models with userItem, itemOf, propertyOf relations and access control
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: live-change-design-models-relations (Claude Code)
|
|
7
|
+
|
|
8
|
+
Use this skill when you design or refactor **models and relations** in a LiveChange service.
|
|
9
|
+
|
|
10
|
+
## When to use
|
|
11
|
+
|
|
12
|
+
- You are adding a new model to a service.
|
|
13
|
+
- You want to switch from manual CRUD/views to proper relations.
|
|
14
|
+
- You need consistent access control and index usage.
|
|
15
|
+
|
|
16
|
+
## Step 1 – Decide the relation type
|
|
17
|
+
|
|
18
|
+
For each new model, decide how it relates to the rest of the domain:
|
|
19
|
+
|
|
20
|
+
- **`userItem`** – the object belongs to the signed-in user (e.g. user’s device).
|
|
21
|
+
- **`itemOf`** – a list of children belonging to a parent model (e.g. device connections).
|
|
22
|
+
- **`propertyOf`** – a single state object with the same id as the parent (e.g. cursor state).
|
|
23
|
+
- **no relation** – for global data or other special cases.
|
|
24
|
+
|
|
25
|
+
Choose one main relation; other associations can be plain fields + indexes.
|
|
26
|
+
|
|
27
|
+
## Step 2 – Define `properties` clearly
|
|
28
|
+
|
|
29
|
+
1. Use a **multi-line** style for properties, with clear `type`, `default`, `validation`, etc.
|
|
30
|
+
2. Avoid unreadable one-liners combining everything.
|
|
31
|
+
3. **Do NOT re-declare fields that are auto-added by relations.** Each relation automatically adds identifier fields and indexes:
|
|
32
|
+
|
|
33
|
+
| Relation | Auto-added field(s) | Auto-added index(es) |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| `itemOf: { what: Device }` | `device` | `byDevice` |
|
|
36
|
+
| `propertyOf: { what: Device }` | `device` | `byDevice` |
|
|
37
|
+
| `userItem` | `user` | `byUser` |
|
|
38
|
+
| `sessionOrUserProperty` | `sessionOrUserType`, `sessionOrUser` | `bySessionOrUser` |
|
|
39
|
+
| `propertyOfAny: { to: ['owner'] }` | `ownerType`, `owner` | `byOwner` |
|
|
40
|
+
|
|
41
|
+
Naming convention: parent model name with first letter lowercased (`Device` → `device`, `CostInvoice` → `costInvoice`). Polymorphic relations add a `Type` + value pair (e.g. `ownerType` + `owner`).
|
|
42
|
+
|
|
43
|
+
Example:
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
// ✅ Only define YOUR fields — 'device' is auto-added by itemOf
|
|
47
|
+
properties: {
|
|
48
|
+
name: {
|
|
49
|
+
type: String,
|
|
50
|
+
validation: ['nonEmpty']
|
|
51
|
+
},
|
|
52
|
+
status: {
|
|
53
|
+
type: String,
|
|
54
|
+
default: 'offline'
|
|
55
|
+
},
|
|
56
|
+
capabilities: {
|
|
57
|
+
type: Array,
|
|
58
|
+
of: {
|
|
59
|
+
type: String
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Step 3 – Configure the relation
|
|
66
|
+
|
|
67
|
+
### `userItem`
|
|
68
|
+
|
|
69
|
+
1. Add a `userItem` block inside the model definition.
|
|
70
|
+
2. Set roles for read/write and list which fields can be written.
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
userItem: {
|
|
74
|
+
readAccessControl: { roles: ['owner', 'admin'] },
|
|
75
|
+
writeAccessControl: { roles: ['owner', 'admin'] },
|
|
76
|
+
writeableProperties: ['name']
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### `itemOf`
|
|
81
|
+
|
|
82
|
+
1. Decide the parent model.
|
|
83
|
+
2. If the parent is in another service, declare it via `foreignModel` (see next step).
|
|
84
|
+
|
|
85
|
+
```js
|
|
86
|
+
itemOf: {
|
|
87
|
+
what: Device,
|
|
88
|
+
readAccessControl: { roles: ['owner', 'admin'] },
|
|
89
|
+
writeAccessControl: { roles: ['owner', 'admin'] }
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### `propertyOf`
|
|
94
|
+
|
|
95
|
+
1. Use when the child should share the same id as the parent.
|
|
96
|
+
2. This simplifies lookups and avoids extra indexes.
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
propertyOf: {
|
|
100
|
+
what: Device,
|
|
101
|
+
readAccessControl: { roles: ['owner', 'admin'] },
|
|
102
|
+
writeAccessControl: { roles: ['owner', 'admin'] }
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### `propertyOf` with multiple parents (1:1 link to each)
|
|
107
|
+
|
|
108
|
+
Use this when a model should act as a dedicated 1:1 link between multiple entities (e.g. invoice ↔ contractor role links),
|
|
109
|
+
so the relations/CRUD generator can treat it as a relation rather than a plain `someId` property.
|
|
110
|
+
|
|
111
|
+
Notes:
|
|
112
|
+
|
|
113
|
+
- Usually you’ll have 1–2 parents, but the `propertyOf` list may contain **any number** of parent models (including 3+).
|
|
114
|
+
- If the entity is a relation, avoid adding manual `...Id` fields in `properties` just to represent the link — CRUD generators won’t treat it as a relation.
|
|
115
|
+
|
|
116
|
+
Example:
|
|
117
|
+
|
|
118
|
+
```js
|
|
119
|
+
const CostInvoice = definition.foreignModel('invoice', 'CostInvoice')
|
|
120
|
+
const Contractor = definition.foreignModel('company', 'Contractor')
|
|
121
|
+
|
|
122
|
+
definition.model({
|
|
123
|
+
name: 'Supplier',
|
|
124
|
+
properties: {
|
|
125
|
+
// optional extra fields
|
|
126
|
+
},
|
|
127
|
+
propertyOf: [
|
|
128
|
+
{ what: CostInvoice },
|
|
129
|
+
{ what: Contractor }
|
|
130
|
+
]
|
|
131
|
+
})
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Step 4 – Use `foreignModel` for cross-service relations
|
|
135
|
+
|
|
136
|
+
1. At the top of the domain file, declare:
|
|
137
|
+
|
|
138
|
+
```js
|
|
139
|
+
const Device = definition.foreignModel('deviceManager', 'Device')
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
2. Then use `Device` in `itemOf` or `propertyOf`:
|
|
143
|
+
|
|
144
|
+
```js
|
|
145
|
+
itemOf: {
|
|
146
|
+
what: Device,
|
|
147
|
+
readAccessControl: { roles: ['owner', 'admin'] }
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Step 5 – Add indexes
|
|
152
|
+
|
|
153
|
+
1. Identify frequent queries:
|
|
154
|
+
- by a single field (e.g. `sessionKey`),
|
|
155
|
+
- by combinations (e.g. `(device, status)`).
|
|
156
|
+
2. Declare indexes in the model:
|
|
157
|
+
|
|
158
|
+
```js
|
|
159
|
+
indexes: {
|
|
160
|
+
bySessionKey: {
|
|
161
|
+
property: ['sessionKey']
|
|
162
|
+
},
|
|
163
|
+
byDeviceAndStatus: {
|
|
164
|
+
property: ['device', 'status']
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
3. Use these indexes in views/actions, via `indexObjectGet` / `indexRangeGet`.
|
|
170
|
+
|
|
171
|
+
## Step 6 – Set access control on relations
|
|
172
|
+
|
|
173
|
+
1. For `userItem`, `itemOf`, and `propertyOf`, always define:
|
|
174
|
+
- `readAccessControl`,
|
|
175
|
+
- `writeAccessControl`.
|
|
176
|
+
2. Don’t rely on unspecified defaults; access rules should be explicit in the model.
|
|
177
|
+
|
|
178
|
+
## Step 7 – Grant access on `entity` model creation
|
|
179
|
+
|
|
180
|
+
Models with `entity` and `writeAccessControl` / `readAccessControl` check roles on every CRUD operation, but do **not** auto-grant roles to the creator. Without granting roles, the creator cannot even read their own object.
|
|
181
|
+
|
|
182
|
+
Add a change trigger that grants `'owner'` on creation:
|
|
183
|
+
|
|
184
|
+
```js
|
|
185
|
+
definition.trigger({
|
|
186
|
+
name: 'changeMyService_MyModel',
|
|
187
|
+
properties: {
|
|
188
|
+
object: { type: MyModel, validation: ['nonEmpty'] },
|
|
189
|
+
data: { type: Object },
|
|
190
|
+
oldData: { type: Object }
|
|
191
|
+
},
|
|
192
|
+
async execute({ object, data, oldData }, { client, triggerService }) {
|
|
193
|
+
if (!data || oldData) return // only on create
|
|
194
|
+
if (!client?.user) return
|
|
195
|
+
|
|
196
|
+
await triggerService({ service: 'accessControl', type: 'accessControl_setAccess' }, {
|
|
197
|
+
objectType: 'myService_MyModel', // format: serviceName_ModelName
|
|
198
|
+
object,
|
|
199
|
+
roles: ['owner'],
|
|
200
|
+
sessionOrUserType: 'user_User',
|
|
201
|
+
sessionOrUser: client.user,
|
|
202
|
+
lastUpdate: new Date()
|
|
203
|
+
})
|
|
204
|
+
}
|
|
205
|
+
})
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
For objects that should also be publicly readable, add `accessControl_setPublicAccess`:
|
|
209
|
+
|
|
210
|
+
```js
|
|
211
|
+
await triggerService({ service: 'accessControl', type: 'accessControl_setPublicAccess' }, {
|
|
212
|
+
objectType: 'myService_MyModel',
|
|
213
|
+
object,
|
|
214
|
+
userRoles: ['reader'], // roles for all logged-in users
|
|
215
|
+
sessionRoles: ['reader'], // roles for all sessions (including anonymous)
|
|
216
|
+
lastUpdate: new Date()
|
|
217
|
+
})
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
**Note:** `userItem` and `itemOf`/`propertyOf` relations automatically handle access through the parent — you typically don't need manual `triggerService` calls for those. This step applies primarily to `entity` models.
|
|
221
|
+
|
|
222
|
+
## Step 8 – Check auto-generated views/actions
|
|
223
|
+
|
|
224
|
+
1. After adding relations, review the auto-generated views/actions:
|
|
225
|
+
- “my X” views and CRUD for `userItem`,
|
|
226
|
+
- parent-scoped lists for `itemOf`/`propertyOf`.
|
|
227
|
+
2. Only add custom views/actions when:
|
|
228
|
+
- you need special filters,
|
|
229
|
+
- or custom logic not covered by the generated ones.
|
|
230
|
+
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: live-change-design-service
|
|
3
|
+
description: Create or restructure a LiveChange backend service with proper directory layout
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: live-change-design-service (Claude Code)
|
|
7
|
+
|
|
8
|
+
Use this skill when you need to **create or restructure a LiveChange service** in this project or any other live-change-stack project.
|
|
9
|
+
|
|
10
|
+
## When to use
|
|
11
|
+
|
|
12
|
+
- You are adding a **new domain service** (payments, devices, notifications, etc.).
|
|
13
|
+
- You are splitting logic out of an existing service.
|
|
14
|
+
- You want to make sure the service structure follows the project conventions.
|
|
15
|
+
|
|
16
|
+
## Step 1 – Choose the service name
|
|
17
|
+
|
|
18
|
+
1. Pick a short, domain-oriented name, e.g. `deviceManager`, `payments`, `notifications`.
|
|
19
|
+
2. This name will be used:
|
|
20
|
+
- as `name` in `app.createServiceDefinition({ name })`,
|
|
21
|
+
- as the `name` entry in `app.config.js` (`services: [{ name: '...' }]`),
|
|
22
|
+
- in `services.list.js` as the property key.
|
|
23
|
+
|
|
24
|
+
## Step 2 – Create the service directory
|
|
25
|
+
|
|
26
|
+
1. Create `server/services/<serviceName>/`.
|
|
27
|
+
2. Inside, create at least:
|
|
28
|
+
- `definition.js`
|
|
29
|
+
- `index.js`
|
|
30
|
+
3. Optionally also:
|
|
31
|
+
- `config.js` – for resolving `definition.config`,
|
|
32
|
+
- domain files like `models.js`, `authenticator.js`, `actions.js`, or more fine-grained files.
|
|
33
|
+
|
|
34
|
+
## Step 3 – Implement `definition.js`
|
|
35
|
+
|
|
36
|
+
1. Import `app` from `@live-change/framework`.
|
|
37
|
+
2. If the service uses relations or access control:
|
|
38
|
+
- import `relationsPlugin` from `@live-change/relations-plugin`,
|
|
39
|
+
- import `accessControlService` from `@live-change/access-control-service`.
|
|
40
|
+
3. Call `app.createServiceDefinition({ name: '...', use: [...] })`.
|
|
41
|
+
4. **Do not register models, actions or views** in this file – only the definition.
|
|
42
|
+
|
|
43
|
+
Example:
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
import { app } from '@live-change/framework'
|
|
47
|
+
import relationsPlugin from '@live-change/relations-plugin'
|
|
48
|
+
import accessControlService from '@live-change/access-control-service'
|
|
49
|
+
|
|
50
|
+
const definition = app.createServiceDefinition({
|
|
51
|
+
name: 'myService',
|
|
52
|
+
use: [relationsPlugin, accessControlService]
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
export default definition
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Step 4 – Implement `index.js`
|
|
59
|
+
|
|
60
|
+
1. Import `definition` from `./definition.js`.
|
|
61
|
+
2. Import all domain files (models, views, actions, triggers, authenticators) as side-effect imports.
|
|
62
|
+
3. Export `definition` as the default export.
|
|
63
|
+
4. Do **not** put heavy logic into `index.js`.
|
|
64
|
+
|
|
65
|
+
Example:
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
import definition from './definition.js'
|
|
69
|
+
|
|
70
|
+
import './models.js'
|
|
71
|
+
import './authenticator.js'
|
|
72
|
+
import './actions.js'
|
|
73
|
+
|
|
74
|
+
export default definition
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Step 5 – (Optional) Implement `config.js`
|
|
78
|
+
|
|
79
|
+
1. Import `definition`.
|
|
80
|
+
2. Read `definition.config` and apply default values.
|
|
81
|
+
3. Export a plain object.
|
|
82
|
+
|
|
83
|
+
Example:
|
|
84
|
+
|
|
85
|
+
```js
|
|
86
|
+
import definition from './definition.js'
|
|
87
|
+
|
|
88
|
+
const {
|
|
89
|
+
someOption = 'default'
|
|
90
|
+
} = definition.config
|
|
91
|
+
|
|
92
|
+
export default { someOption }
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Step 6 – Register the service in `services.list.js`
|
|
96
|
+
|
|
97
|
+
1. Import from the service directory `index.js`:
|
|
98
|
+
|
|
99
|
+
```js
|
|
100
|
+
import myService from './services/myService/index.js'
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
2. Add the service to the exported object:
|
|
104
|
+
|
|
105
|
+
```js
|
|
106
|
+
export default {
|
|
107
|
+
// ...
|
|
108
|
+
myService
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Step 7 – Register the service in `app.config.js`
|
|
113
|
+
|
|
114
|
+
1. Ensure there is an entry in `services`:
|
|
115
|
+
|
|
116
|
+
```js
|
|
117
|
+
services: [
|
|
118
|
+
// ...
|
|
119
|
+
{ name: 'myService' }
|
|
120
|
+
]
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
2. Keep a sensible order:
|
|
124
|
+
- core/common services and plugins (user, session, accessControl, etc.) first,
|
|
125
|
+
- domain-specific application services later.
|
|
126
|
+
|
|
127
|
+
## Step 8 – Handle dependencies on other services
|
|
128
|
+
|
|
129
|
+
1. If the service needs to reference models from other services:
|
|
130
|
+
- use `definition.foreignModel('otherService', 'ModelName')` in domain files,
|
|
131
|
+
- do **not** import their model files directly.
|
|
132
|
+
2. Make sure the other services are listed **before** this one in `app.config.js`.
|
|
133
|
+
|