interaqt 0.3.0 → 0.3.1
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/agent/.claude/agents/code-generation-handler.md +2 -0
- package/agent/.claude/agents/computation-generation-handler.md +1 -0
- package/agent/.claude/agents/implement-design-handler.md +4 -13
- package/agent/.claude/agents/requirements-analysis-handler.md +46 -14
- package/agent/agentspace/knowledge/generator/api-reference.md +3378 -0
- package/agent/agentspace/knowledge/generator/basic-interaction-generation.md +377 -0
- package/agent/agentspace/knowledge/generator/computation-analysis.md +307 -0
- package/agent/agentspace/knowledge/generator/computation-implementation.md +959 -0
- package/agent/agentspace/knowledge/generator/data-analysis.md +463 -0
- package/agent/agentspace/knowledge/generator/entity-relation-generation.md +395 -0
- package/agent/agentspace/knowledge/generator/permission-implementation.md +460 -0
- package/agent/agentspace/knowledge/generator/permission-test-implementation.md +870 -0
- package/agent/agentspace/knowledge/generator/test-implementation.md +674 -0
- package/agent/agentspace/knowledge/usage/00-mindset-shift.md +322 -0
- package/agent/agentspace/knowledge/usage/01-core-concepts.md +131 -0
- package/agent/agentspace/knowledge/usage/02-define-entities-properties.md +407 -0
- package/agent/agentspace/knowledge/usage/03-entity-relations.md +599 -0
- package/agent/agentspace/knowledge/usage/04-reactive-computations.md +2186 -0
- package/agent/agentspace/knowledge/usage/05-interactions.md +1411 -0
- package/agent/agentspace/knowledge/usage/06-attributive-permissions.md +10 -0
- package/agent/agentspace/knowledge/usage/07-payload-parameters.md +593 -0
- package/agent/agentspace/knowledge/usage/08-activities.md +863 -0
- package/agent/agentspace/knowledge/usage/09-filtered-entities.md +784 -0
- package/agent/agentspace/knowledge/usage/10-async-computations.md +734 -0
- package/agent/agentspace/knowledge/usage/11-global-dictionaries.md +942 -0
- package/agent/agentspace/knowledge/usage/12-data-querying.md +1033 -0
- package/agent/agentspace/knowledge/usage/13-testing.md +1201 -0
- package/agent/agentspace/knowledge/usage/14-api-reference.md +1606 -0
- package/agent/agentspace/knowledge/usage/15-entity-crud-patterns.md +1122 -0
- package/agent/agentspace/knowledge/usage/16-frontend-page-design-guide.md +485 -0
- package/agent/agentspace/knowledge/usage/17-performance-optimization.md +283 -0
- package/agent/agentspace/knowledge/usage/18-api-exports-reference.md +176 -0
- package/agent/agentspace/knowledge/usage/19-common-anti-patterns.md +563 -0
- package/agent/agentspace/knowledge/usage/README.md +148 -0
- package/package.json +1 -1
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
# Entity and Relation Generation Guide
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
This guide explains how to generate entities and relations from use cases in the interaqt framework.
|
|
5
|
+
|
|
6
|
+
## 🔴 CRITICAL: Common Mistakes to Avoid
|
|
7
|
+
|
|
8
|
+
### Entity Mistakes
|
|
9
|
+
```typescript
|
|
10
|
+
// ❌ WRONG: Importing User from interaqt
|
|
11
|
+
import { User, Entity } from 'interaqt';
|
|
12
|
+
|
|
13
|
+
// ✅ CORRECT: Define your own entities
|
|
14
|
+
import { Entity, Property } from 'interaqt';
|
|
15
|
+
const User = Entity.create({ name: 'User', properties: [...] });
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Relation Mistakes
|
|
19
|
+
```typescript
|
|
20
|
+
// ❌ WRONG: Specifying relation name
|
|
21
|
+
const UserPostRelation = Relation.create({
|
|
22
|
+
name: 'UserPost', // DON'T do this!
|
|
23
|
+
source: User,
|
|
24
|
+
target: Post,
|
|
25
|
+
type: 'n:1'
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// ✅ CORRECT: Name is auto-generated
|
|
29
|
+
const UserPostRelation = Relation.create({
|
|
30
|
+
source: User,
|
|
31
|
+
target: Post,
|
|
32
|
+
type: 'n:1' // Valid types: '1:1', '1:n', 'n:1', 'n:n'
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Key Principles
|
|
37
|
+
|
|
38
|
+
### 1. Entity Generation
|
|
39
|
+
- Identify core business objects from requirements
|
|
40
|
+
- Each entity should represent a distinct concept
|
|
41
|
+
- Properties should be atomic and well-typed
|
|
42
|
+
|
|
43
|
+
### 2. Relation Generation
|
|
44
|
+
- Relations connect entities meaningfully
|
|
45
|
+
- Consider cardinality (1:1, 1:n, n:n)
|
|
46
|
+
- Relations can have their own properties
|
|
47
|
+
|
|
48
|
+
## Step-by-Step Process
|
|
49
|
+
|
|
50
|
+
### Step 1: Entity Definition
|
|
51
|
+
|
|
52
|
+
#### Basic Entity Structure
|
|
53
|
+
```typescript
|
|
54
|
+
import { Entity, Property } from 'interaqt';
|
|
55
|
+
|
|
56
|
+
const User = Entity.create({
|
|
57
|
+
name: 'User', // PascalCase, singular
|
|
58
|
+
properties: [
|
|
59
|
+
Property.create({ name: 'email', type: 'string' }),
|
|
60
|
+
Property.create({ name: 'name', type: 'string' }),
|
|
61
|
+
Property.create({ name: 'role', type: 'string' })
|
|
62
|
+
]
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
#### Property Types
|
|
67
|
+
- **Basic types**: `'string'`, `'number'`, `'boolean'`
|
|
68
|
+
- **Complex types**: `'object'` (for JSON data)
|
|
69
|
+
- **Collections**: Set `collection: true` for arrays
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
Property.create({
|
|
73
|
+
name: 'tags',
|
|
74
|
+
type: 'string',
|
|
75
|
+
collection: true // Array of strings
|
|
76
|
+
})
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
#### Default Values (MUST be functions)
|
|
80
|
+
```typescript
|
|
81
|
+
// ❌ WRONG
|
|
82
|
+
Property.create({
|
|
83
|
+
name: 'status',
|
|
84
|
+
type: 'string',
|
|
85
|
+
defaultValue: 'active' // Must be function!
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// ✅ CORRECT
|
|
89
|
+
Property.create({
|
|
90
|
+
name: 'status',
|
|
91
|
+
type: 'string',
|
|
92
|
+
defaultValue: () => 'active'
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Dynamic defaults
|
|
96
|
+
Property.create({
|
|
97
|
+
name: 'createdAt',
|
|
98
|
+
type: 'number',
|
|
99
|
+
defaultValue: () => Math.floor(Date.now()/1000)
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Step 2: Relation Definition
|
|
104
|
+
|
|
105
|
+
#### Relation Types
|
|
106
|
+
- **1:1** - One-to-one (User ↔ Profile)
|
|
107
|
+
- **n:1** - Many-to-one (Posts → User)
|
|
108
|
+
- **1:n** - One-to-many (User → Posts)
|
|
109
|
+
- **n:n** - Many-to-many (Users ↔ Tags)
|
|
110
|
+
|
|
111
|
+
#### Basic Relation
|
|
112
|
+
```typescript
|
|
113
|
+
// One user has many styles
|
|
114
|
+
const UserStyleRelation = Relation.create({
|
|
115
|
+
source: User,
|
|
116
|
+
target: Style,
|
|
117
|
+
type: 'n:1' // Many styles to one user
|
|
118
|
+
// NO name property - it's auto-generated!
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
#### Relation with Properties
|
|
123
|
+
```typescript
|
|
124
|
+
// User creates Style with metadata
|
|
125
|
+
const UserStyleRelation = Relation.create({
|
|
126
|
+
source: User,
|
|
127
|
+
target: Style,
|
|
128
|
+
type: 'n:1',
|
|
129
|
+
properties: [
|
|
130
|
+
Property.create({
|
|
131
|
+
name: 'createdAt',
|
|
132
|
+
type: 'number',
|
|
133
|
+
defaultValue: () => Math.floor(Date.now()/1000)
|
|
134
|
+
})
|
|
135
|
+
]
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Step 3: Analysis Process
|
|
140
|
+
|
|
141
|
+
#### From Use Cases to Entities
|
|
142
|
+
1. **Extract nouns** as potential entities:
|
|
143
|
+
- "User creates style" → User, Style
|
|
144
|
+
- "Admin publishes version" → User (admin), Version
|
|
145
|
+
- "Style has versions" → Style, Version
|
|
146
|
+
|
|
147
|
+
2. **Identify properties** from data requirements:
|
|
148
|
+
- Style: label, slug, description, type, status, priority
|
|
149
|
+
- Version: data, isActive, publishedAt
|
|
150
|
+
- User: name, email, role
|
|
151
|
+
|
|
152
|
+
3. **Determine relations** from interactions:
|
|
153
|
+
- User creates Style → UserStyleRelation (n:1)
|
|
154
|
+
- Style has Versions → StyleVersionRelation (n:n)
|
|
155
|
+
- User publishes Version → UserVersionRelation (n:1)
|
|
156
|
+
|
|
157
|
+
## Complete Example
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
import { Entity, Property, Relation } from 'interaqt';
|
|
161
|
+
|
|
162
|
+
// Entities
|
|
163
|
+
export const User = Entity.create({
|
|
164
|
+
name: 'User',
|
|
165
|
+
properties: [
|
|
166
|
+
Property.create({ name: 'name', type: 'string' }),
|
|
167
|
+
Property.create({ name: 'email', type: 'string' }),
|
|
168
|
+
Property.create({ name: 'role', type: 'string' })
|
|
169
|
+
]
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
export const Style = Entity.create({
|
|
173
|
+
name: 'Style',
|
|
174
|
+
properties: [
|
|
175
|
+
Property.create({ name: 'label', type: 'string' }),
|
|
176
|
+
Property.create({ name: 'slug', type: 'string' }),
|
|
177
|
+
Property.create({ name: 'description', type: 'string' }),
|
|
178
|
+
Property.create({ name: 'type', type: 'string' }),
|
|
179
|
+
Property.create({ name: 'thumbKey', type: 'string' }),
|
|
180
|
+
Property.create({ name: 'priority', type: 'number', defaultValue: () => 0 }),
|
|
181
|
+
Property.create({ name: 'status', type: 'string', defaultValue: () => 'draft' }),
|
|
182
|
+
Property.create({ name: 'createdAt', type: 'number', defaultValue: () => Math.floor(Date.now()/1000) }),
|
|
183
|
+
Property.create({ name: 'updatedAt', type: 'number', defaultValue: () => Math.floor(Date.now()/1000) })
|
|
184
|
+
]
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
export const Version = Entity.create({
|
|
188
|
+
name: 'Version',
|
|
189
|
+
properties: [
|
|
190
|
+
Property.create({ name: 'data', type: 'object' }), // Style snapshot
|
|
191
|
+
Property.create({ name: 'isActive', type: 'boolean', defaultValue: () => false }),
|
|
192
|
+
Property.create({ name: 'publishedAt', type: 'bigint', defaultValue: () => Date.now() })
|
|
193
|
+
]
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Relations
|
|
197
|
+
export const UserStyleRelation = Relation.create({
|
|
198
|
+
source: User,
|
|
199
|
+
target: Style,
|
|
200
|
+
type: 'n:1' // User's lastModifiedBy
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
export const StyleVersionRelation = Relation.create({
|
|
204
|
+
source: Style,
|
|
205
|
+
target: Version,
|
|
206
|
+
type: 'n:n', // Style has many versions
|
|
207
|
+
properties: [
|
|
208
|
+
Property.create({
|
|
209
|
+
name: 'createdAt',
|
|
210
|
+
type: 'number',
|
|
211
|
+
defaultValue: () => Math.floor(Date.now()/1000)
|
|
212
|
+
})
|
|
213
|
+
]
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
export const UserVersionRelation = Relation.create({
|
|
217
|
+
source: User,
|
|
218
|
+
target: Version,
|
|
219
|
+
type: 'n:1' // Version's publishedBy
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Common Patterns
|
|
224
|
+
- User-Content relations (author, owner)
|
|
225
|
+
- Status tracking (draft, published, archived)
|
|
226
|
+
- Timestamp properties (createdAt, updatedAt)
|
|
227
|
+
- Soft delete patterns (isDeleted, deletedAt)
|
|
228
|
+
|
|
229
|
+
## Filtered Entities
|
|
230
|
+
|
|
231
|
+
Filtered entities are derived entities that automatically filter records from a source entity based on specific conditions. They are useful for creating logical subsets of data without duplicating storage.
|
|
232
|
+
|
|
233
|
+
### Creating Filtered Entities
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
const PublishedStyle = Entity.create({
|
|
237
|
+
name: 'PublishedStyle',
|
|
238
|
+
baseEntity: Style, // The entity to filter from
|
|
239
|
+
matchExpression: MatchExp.atom({
|
|
240
|
+
key: 'status',
|
|
241
|
+
value: ['=', 'published']
|
|
242
|
+
})
|
|
243
|
+
});
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### MatchExpression Property Type
|
|
247
|
+
|
|
248
|
+
The `matchExpression` uses `MatchExp` expressions to define filtering criteria:
|
|
249
|
+
|
|
250
|
+
#### Basic Conditions
|
|
251
|
+
```typescript
|
|
252
|
+
// Single condition
|
|
253
|
+
matchExpression: MatchExp.atom({
|
|
254
|
+
key: 'status',
|
|
255
|
+
value: ['=', 'active']
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
// Numeric comparison
|
|
259
|
+
matchExpression: MatchExp.atom({
|
|
260
|
+
key: 'priority',
|
|
261
|
+
value: ['>', 5]
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
// Pattern matching
|
|
265
|
+
matchExpression: MatchExp.atom({
|
|
266
|
+
key: 'email',
|
|
267
|
+
value: ['like', '%@admin.com']
|
|
268
|
+
})
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
#### Complex Conditions
|
|
272
|
+
```typescript
|
|
273
|
+
// AND conditions
|
|
274
|
+
matchExpression: MatchExp.atom({
|
|
275
|
+
key: 'status',
|
|
276
|
+
value: ['=', 'published']
|
|
277
|
+
}).and({
|
|
278
|
+
key: 'priority',
|
|
279
|
+
value: ['>=', 10]
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
// OR conditions
|
|
283
|
+
matchExpression: MatchExp.atom({
|
|
284
|
+
key: 'type',
|
|
285
|
+
value: ['=', 'premium']
|
|
286
|
+
}).or({
|
|
287
|
+
key: 'featured',
|
|
288
|
+
value: ['=', true]
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
// Combined AND/OR
|
|
292
|
+
matchExpression: MatchExp.atom({
|
|
293
|
+
key: 'status',
|
|
294
|
+
value: ['=', 'active']
|
|
295
|
+
}).and({
|
|
296
|
+
key: 'createdAt',
|
|
297
|
+
value: ['>', Math.floor(Date.now()/1000) - 86400] // Last 24 hours in seconds
|
|
298
|
+
}).or({
|
|
299
|
+
key: 'isPinned',
|
|
300
|
+
value: ['=', true]
|
|
301
|
+
})
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
#### Available Operators
|
|
305
|
+
- `['=', value]` - Equals
|
|
306
|
+
- `['!=', value]` - Not equals
|
|
307
|
+
- `['>', value]` - Greater than
|
|
308
|
+
- `['<', value]` - Less than
|
|
309
|
+
- `['>=', value]` - Greater than or equal
|
|
310
|
+
- `['<=', value]` - Less than or equal
|
|
311
|
+
- `['like', pattern]` - Pattern matching (% for wildcard)
|
|
312
|
+
- `['in', array]` - Value in array
|
|
313
|
+
- `['between', [min, max]]` - Value in range
|
|
314
|
+
- `['not', null]` - Not null check
|
|
315
|
+
|
|
316
|
+
### Practical Examples
|
|
317
|
+
|
|
318
|
+
#### Active Users
|
|
319
|
+
```typescript
|
|
320
|
+
const ActiveUser = Entity.create({
|
|
321
|
+
name: 'ActiveUser',
|
|
322
|
+
baseEntity: User,
|
|
323
|
+
matchExpression: MatchExp.atom({
|
|
324
|
+
key: 'status',
|
|
325
|
+
value: ['=', 'active']
|
|
326
|
+
}).and({
|
|
327
|
+
key: 'lastLoginDate',
|
|
328
|
+
value: ['>', Date.now() - 7 * 24 * 60 * 60 * 1000] // Last 7 days
|
|
329
|
+
})
|
|
330
|
+
});
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
#### High Priority Styles
|
|
334
|
+
```typescript
|
|
335
|
+
const HighPriorityStyle = Entity.create({
|
|
336
|
+
name: 'HighPriorityStyle',
|
|
337
|
+
baseEntity: Style,
|
|
338
|
+
matchExpression: MatchExp.atom({
|
|
339
|
+
key: 'priority',
|
|
340
|
+
value: ['>=', 8]
|
|
341
|
+
}).and({
|
|
342
|
+
key: 'status',
|
|
343
|
+
value: ['!=', 'archived']
|
|
344
|
+
})
|
|
345
|
+
});
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
#### Premium or Featured Content
|
|
349
|
+
```typescript
|
|
350
|
+
const FeaturedContent = Entity.create({
|
|
351
|
+
name: 'FeaturedContent',
|
|
352
|
+
baseEntity: Article,
|
|
353
|
+
matchExpression: MatchExp.atom({
|
|
354
|
+
key: 'type',
|
|
355
|
+
value: ['=', 'premium']
|
|
356
|
+
}).or({
|
|
357
|
+
key: 'featured',
|
|
358
|
+
value: ['=', true]
|
|
359
|
+
}).or({
|
|
360
|
+
key: 'editorPick',
|
|
361
|
+
value: ['=', true]
|
|
362
|
+
})
|
|
363
|
+
});
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Filtering from Relations
|
|
367
|
+
You can also create filtered entities from relations:
|
|
368
|
+
|
|
369
|
+
```typescript
|
|
370
|
+
const RecentUserPost = Entity.create({
|
|
371
|
+
name: 'RecentUserPost',
|
|
372
|
+
baseEntity: UserPostRelation,
|
|
373
|
+
matchExpression: MatchExp.atom({
|
|
374
|
+
key: 'createdAt',
|
|
375
|
+
value: ['>', Math.floor(Date.now()/1000) - 30 * 24 * 60 * 60] // Last 30 days in seconds
|
|
376
|
+
})
|
|
377
|
+
});
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### Important Notes
|
|
381
|
+
- Filtered entities are read-only views - you cannot create records directly in them
|
|
382
|
+
- They automatically update when the source entity changes
|
|
383
|
+
- Use them for queries and computations, not for direct data manipulation
|
|
384
|
+
- They share the same storage as the source entity (no data duplication)
|
|
385
|
+
- Properties are inherited from the source entity
|
|
386
|
+
|
|
387
|
+
## Validation Checklist
|
|
388
|
+
- [ ] All entity names are PascalCase and singular
|
|
389
|
+
- [ ] All properties have correct types
|
|
390
|
+
- [ ] All defaultValues are functions, not static values
|
|
391
|
+
- [ ] No relation has a name property (auto-generated)
|
|
392
|
+
- [ ] Relation types use correct format ('1:1', 'n:1', etc.)
|
|
393
|
+
- [ ] No entities are imported from interaqt package
|
|
394
|
+
- [ ] Filtered entities have valid baseEntity and matchExpression
|
|
395
|
+
- [ ] TypeScript compilation passes
|