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,1606 @@
|
|
|
1
|
+
# Chapter 13: API Reference
|
|
2
|
+
|
|
3
|
+
This chapter provides detailed reference documentation for all core APIs in the interaqt framework, including complete parameter descriptions, type definitions, and usage examples.
|
|
4
|
+
|
|
5
|
+
## 13.1 Entity-Related APIs
|
|
6
|
+
|
|
7
|
+
### Entity.create()
|
|
8
|
+
|
|
9
|
+
Create entity definition. Entities are the basic units of data in the system.
|
|
10
|
+
|
|
11
|
+
**Syntax**
|
|
12
|
+
```typescript
|
|
13
|
+
Entity.create(config: EntityConfig): KlassInstance<typeof Entity>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**Parameters**
|
|
17
|
+
- `config.name` (string, required): Entity name, must match `/^[a-zA-Z0-9_]+$/` format
|
|
18
|
+
- `config.properties` (Property[], required): Entity property list, defaults to empty array
|
|
19
|
+
- `config.computation` (Computation[], optional): Entity-level computed data
|
|
20
|
+
- `config.baseEntity` (Entity|Relation, optional): Base entity for filtered entity (used to create filtered entities)
|
|
21
|
+
- `config.filterCondition` (MatchExp, optional): Filter condition (used to create filtered entities)
|
|
22
|
+
|
|
23
|
+
**Examples**
|
|
24
|
+
```typescript
|
|
25
|
+
// Create basic entity
|
|
26
|
+
const User = Entity.create({
|
|
27
|
+
name: 'User',
|
|
28
|
+
properties: [
|
|
29
|
+
Property.create({ name: 'username', type: 'string' }),
|
|
30
|
+
Property.create({ name: 'email', type: 'string' })
|
|
31
|
+
]
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// Create filtered entity
|
|
35
|
+
const ActiveUser = Entity.create({
|
|
36
|
+
name: 'ActiveUser',
|
|
37
|
+
baseEntity: User,
|
|
38
|
+
filterCondition: MatchExp.atom({
|
|
39
|
+
key: 'status',
|
|
40
|
+
value: ['=', 'active']
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Property.create()
|
|
46
|
+
|
|
47
|
+
Create entity property definition.
|
|
48
|
+
|
|
49
|
+
**Syntax**
|
|
50
|
+
```typescript
|
|
51
|
+
Property.create(config: PropertyConfig): KlassInstance<typeof Property>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Parameters**
|
|
55
|
+
- `config.name` (string, required): Property name, must be 1-5 characters long
|
|
56
|
+
- `config.type` (string, required): Property type, options: 'string' | 'number' | 'boolean'
|
|
57
|
+
- `config.collection` (boolean, optional): Whether it's a collection type
|
|
58
|
+
- `config.defaultValue` (function, optional): Default value function
|
|
59
|
+
- `config.computed` (function, optional): Computed property function
|
|
60
|
+
- `config.computation` (Computation, optional): Property computed data
|
|
61
|
+
|
|
62
|
+
**Examples**
|
|
63
|
+
```typescript
|
|
64
|
+
// Basic property
|
|
65
|
+
const username = Property.create({
|
|
66
|
+
name: 'username',
|
|
67
|
+
type: 'string'
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
// Property with default value
|
|
71
|
+
const createdAt = Property.create({
|
|
72
|
+
name: 'createdAt',
|
|
73
|
+
type: 'string',
|
|
74
|
+
defaultValue: () => new Date().toISOString()
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
// Computed property
|
|
78
|
+
const fullName = Property.create({
|
|
79
|
+
name: 'fullName',
|
|
80
|
+
type: 'string',
|
|
81
|
+
computed: function(user) {
|
|
82
|
+
return `${user.firstName} ${user.lastName}`
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
// Property with reactive computation
|
|
87
|
+
const postCount = Property.create({
|
|
88
|
+
name: 'postCount',
|
|
89
|
+
type: 'number',
|
|
90
|
+
defaultValue: () => 0, // Must provide default value
|
|
91
|
+
computation: Count.create({
|
|
92
|
+
record: UserPostRelation
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Relation.create()
|
|
98
|
+
|
|
99
|
+
Create relationship definition between entities.
|
|
100
|
+
|
|
101
|
+
**Syntax**
|
|
102
|
+
```typescript
|
|
103
|
+
Relation.create(config: RelationConfig): KlassInstance<typeof Relation>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Important: Auto-Generated Relation Names**
|
|
107
|
+
|
|
108
|
+
⚠️ **DO NOT specify a `name` property when creating relations.** The framework automatically generates the relation name based on the source and target entities. For example:
|
|
109
|
+
- A relation between `User` and `Post` → automatically named `UserPost`
|
|
110
|
+
- A relation between `Post` and `Comment` → automatically named `PostComment`
|
|
111
|
+
|
|
112
|
+
**Parameters**
|
|
113
|
+
- `config.source` (Entity|Relation, required): Source entity of the relationship
|
|
114
|
+
- `config.sourceProperty` (string, required): Relationship property name in source entity
|
|
115
|
+
- `config.target` (Entity|Relation, required): Target entity of the relationship
|
|
116
|
+
- `config.targetProperty` (string, required): Relationship property name in target entity
|
|
117
|
+
- `config.type` (string, required): Relationship type, options: '1:1' | '1:n' | 'n:1' | 'n:n'
|
|
118
|
+
- `config.properties` (Property[], optional): Properties of the relationship itself
|
|
119
|
+
- `config.computation` (Computation, optional): Relationship-level computed data
|
|
120
|
+
|
|
121
|
+
**Note on Symmetric Relations**: The system automatically detects symmetric relations when `source === target` AND `sourceProperty === targetProperty`. There is no need to specify a `symmetric` parameter.
|
|
122
|
+
|
|
123
|
+
**Examples**
|
|
124
|
+
```typescript
|
|
125
|
+
// One-to-many relationship
|
|
126
|
+
const UserPostRelation = Relation.create({
|
|
127
|
+
source: User,
|
|
128
|
+
sourceProperty: 'posts',
|
|
129
|
+
target: Post,
|
|
130
|
+
targetProperty: 'author',
|
|
131
|
+
type: '1:n'
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
// Many-to-many relationship
|
|
135
|
+
const UserTagRelation = Relation.create({
|
|
136
|
+
source: User,
|
|
137
|
+
sourceProperty: 'tags',
|
|
138
|
+
target: Tag,
|
|
139
|
+
targetProperty: 'users',
|
|
140
|
+
type: 'n:n'
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
// Symmetric relationship (friendship)
|
|
144
|
+
// Note: System detects symmetric relations automatically when source === target AND sourceProperty === targetProperty
|
|
145
|
+
const FriendRelation = Relation.create({
|
|
146
|
+
source: User,
|
|
147
|
+
sourceProperty: 'friends',
|
|
148
|
+
target: User,
|
|
149
|
+
targetProperty: 'friends', // Same as sourceProperty - automatically symmetric
|
|
150
|
+
type: 'n:n'
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
// Relationship with properties
|
|
154
|
+
const UserRoleRelation = Relation.create({
|
|
155
|
+
source: User,
|
|
156
|
+
sourceProperty: 'roles',
|
|
157
|
+
target: Role,
|
|
158
|
+
targetProperty: 'users',
|
|
159
|
+
type: 'n:n',
|
|
160
|
+
properties: [
|
|
161
|
+
Property.create({ name: 'assignedAt', type: 'string' }),
|
|
162
|
+
Property.create({ name: 'isActive', type: 'boolean' })
|
|
163
|
+
]
|
|
164
|
+
})
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## 13.2 Computation-Related APIs
|
|
168
|
+
|
|
169
|
+
### Count.create()
|
|
170
|
+
|
|
171
|
+
Create count computation for counting records.
|
|
172
|
+
|
|
173
|
+
**Syntax**
|
|
174
|
+
```typescript
|
|
175
|
+
Count.create(config: CountConfig): KlassInstance<typeof Count>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Parameters**
|
|
179
|
+
- `config.record` (Entity|Relation, required): Entity or relation to count
|
|
180
|
+
- `config.direction` (string, optional): Relationship direction, options: 'source' | 'target', only for relation counting
|
|
181
|
+
- `config.callback` (function, optional): Filter callback function, returns boolean to decide if included in count
|
|
182
|
+
- `config.attributeQuery` (AttributeQueryData, optional): Attribute query configuration to optimize data fetching
|
|
183
|
+
- `config.dataDeps` (object, optional): Data dependency configuration, format: `{[key: string]: DataDep}`
|
|
184
|
+
|
|
185
|
+
**Examples**
|
|
186
|
+
```typescript
|
|
187
|
+
// Basic global count
|
|
188
|
+
const totalUsers = Count.create({
|
|
189
|
+
record: User
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
// Basic property count (user's post count)
|
|
193
|
+
const userPostCount = Property.create({
|
|
194
|
+
name: 'postCount',
|
|
195
|
+
type: 'number',
|
|
196
|
+
defaultValue: () => 0, // Must provide default value
|
|
197
|
+
computation: Count.create({
|
|
198
|
+
record: UserPostRelation
|
|
199
|
+
})
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
// Count with filter condition (only count published posts)
|
|
203
|
+
const publishedPostCount = Property.create({
|
|
204
|
+
name: 'publishedPostCount',
|
|
205
|
+
type: 'number',
|
|
206
|
+
defaultValue: () => 0,
|
|
207
|
+
computation: Count.create({
|
|
208
|
+
record: UserPostRelation,
|
|
209
|
+
attributeQuery: [['target', {attributeQuery: ['status']}]],
|
|
210
|
+
callback: function(relation) {
|
|
211
|
+
return relation.target.status === 'published'
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
// Count with data dependencies (filter based on global minimum score setting)
|
|
217
|
+
const minScoreThreshold = Dictionary.create({
|
|
218
|
+
name: 'minScoreThreshold',
|
|
219
|
+
type: 'number',
|
|
220
|
+
collection: false
|
|
221
|
+
})
|
|
222
|
+
const highScorePostCount = Property.create({
|
|
223
|
+
name: 'highScorePostCount',
|
|
224
|
+
type: 'number',
|
|
225
|
+
defaultValue: () => 0,
|
|
226
|
+
computation: Count.create({
|
|
227
|
+
record: UserPostRelation,
|
|
228
|
+
attributeQuery: [['target', {attributeQuery: ['score']}]],
|
|
229
|
+
dataDeps: {
|
|
230
|
+
minScore: {
|
|
231
|
+
type: 'global',
|
|
232
|
+
source: minScoreThreshold
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
callback: function(relation, dataDeps) {
|
|
236
|
+
return relation.target.score >= dataDeps.minScore
|
|
237
|
+
}
|
|
238
|
+
})
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
// Global count with filter and data dependencies
|
|
242
|
+
const userActiveDays = Dictionary.create({
|
|
243
|
+
name: 'userActiveDays',
|
|
244
|
+
type: 'number',
|
|
245
|
+
collection: false
|
|
246
|
+
})
|
|
247
|
+
const activeUsersCount = Dictionary.create({
|
|
248
|
+
name: 'activeUsersCount',
|
|
249
|
+
type: 'number',
|
|
250
|
+
collection: false,
|
|
251
|
+
computation: Count.create({
|
|
252
|
+
record: User,
|
|
253
|
+
attributeQuery: ['lastLoginDate'],
|
|
254
|
+
dataDeps: {
|
|
255
|
+
activeDays: {
|
|
256
|
+
type: 'global',
|
|
257
|
+
source: userActiveDays
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
callback: function(user, dataDeps) {
|
|
261
|
+
const daysSinceLogin = (Date.now() - new Date(user.lastLoginDate).getTime()) / (1000 * 60 * 60 * 24)
|
|
262
|
+
return daysSinceLogin <= dataDeps.activeDays
|
|
263
|
+
}
|
|
264
|
+
})
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
// Relation count with direction parameter
|
|
268
|
+
const authorPostCount = Property.create({
|
|
269
|
+
name: 'authoredPostCount',
|
|
270
|
+
type: 'number',
|
|
271
|
+
defaultValue: () => 0,
|
|
272
|
+
computation: Count.create({
|
|
273
|
+
record: UserPostRelation,
|
|
274
|
+
direction: 'target' // Count related posts from user perspective
|
|
275
|
+
})
|
|
276
|
+
})
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### WeightedSummation.create()
|
|
280
|
+
|
|
281
|
+
Create weighted summation computation.
|
|
282
|
+
|
|
283
|
+
**Syntax**
|
|
284
|
+
```typescript
|
|
285
|
+
WeightedSummation.create(config: WeightedSummationConfig): KlassInstance<typeof WeightedSummation>
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
**Parameters**
|
|
289
|
+
- `config.record` (Entity|Relation, required): Entity or relation to compute
|
|
290
|
+
- `config.callback` (function, required): Callback function to calculate weight and value, returns `{weight: number, value: number}`
|
|
291
|
+
- `config.attributeQuery` (AttributeQueryData, required): Attribute query configuration
|
|
292
|
+
|
|
293
|
+
**Examples**
|
|
294
|
+
```typescript
|
|
295
|
+
// Calculate user total score
|
|
296
|
+
const userTotalScore = Property.create({
|
|
297
|
+
name: 'totalScore',
|
|
298
|
+
type: 'number',
|
|
299
|
+
defaultValue: () => 0, // Must provide default value
|
|
300
|
+
computation: WeightedSummation.create({
|
|
301
|
+
record: UserScoreRelation,
|
|
302
|
+
callback: function(scoreRecord) {
|
|
303
|
+
return {
|
|
304
|
+
weight: scoreRecord.multiplier || 1,
|
|
305
|
+
value: scoreRecord.points
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
})
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
// Global weighted summation
|
|
312
|
+
const globalWeightedScore = WeightedSummation.create({
|
|
313
|
+
record: ScoreRecord,
|
|
314
|
+
callback: function(record) {
|
|
315
|
+
return {
|
|
316
|
+
weight: record.difficulty,
|
|
317
|
+
value: record.score
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
})
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Summation.create()
|
|
324
|
+
|
|
325
|
+
Create summation computation for summing specified fields.
|
|
326
|
+
|
|
327
|
+
**Syntax**
|
|
328
|
+
```typescript
|
|
329
|
+
Summation.create(config: SummationConfig): KlassInstance<typeof Summation>
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
**Parameters**
|
|
333
|
+
- `config.record` (Entity|Relation, required): Entity or relation to compute
|
|
334
|
+
- `config.attributeQuery` (AttributeQueryData, required): Attribute query configuration, specifies field path to sum
|
|
335
|
+
- `config.direction` (string, optional): Relationship direction, options: 'source' | 'target', only for relation summation
|
|
336
|
+
|
|
337
|
+
**How it works**
|
|
338
|
+
|
|
339
|
+
Summation sums the field pointed to by the leftmost path in `attributeQuery`. If any value in the path is `undefined`, `null`, `NaN`, or `Infinity`, that value will be treated as 0.
|
|
340
|
+
|
|
341
|
+
**Examples**
|
|
342
|
+
```typescript
|
|
343
|
+
// Basic global summation (sum all transaction amounts)
|
|
344
|
+
const totalRevenue = Dictionary.create({
|
|
345
|
+
name: 'totalRevenue',
|
|
346
|
+
type: 'number',
|
|
347
|
+
collection: false,
|
|
348
|
+
computation: Summation.create({
|
|
349
|
+
record: Transaction,
|
|
350
|
+
attributeQuery: ['amount']
|
|
351
|
+
})
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
// Property-level summation (calculate user's total order amount)
|
|
355
|
+
const userTotalSpent = Property.create({
|
|
356
|
+
name: 'totalSpent',
|
|
357
|
+
type: 'number',
|
|
358
|
+
defaultValue: () => 0, // Must provide default value
|
|
359
|
+
computation: Summation.create({
|
|
360
|
+
record: UserOrderRelation,
|
|
361
|
+
attributeQuery: [['target', {attributeQuery: ['totalAmount']}]]
|
|
362
|
+
})
|
|
363
|
+
})
|
|
364
|
+
|
|
365
|
+
// Nested path summation (sum nested fields of related entities)
|
|
366
|
+
const departmentBudget = Property.create({
|
|
367
|
+
name: 'totalBudget',
|
|
368
|
+
type: 'number',
|
|
369
|
+
defaultValue: () => 0,
|
|
370
|
+
computation: Summation.create({
|
|
371
|
+
record: DepartmentProjectRelation,
|
|
372
|
+
attributeQuery: [['target', {
|
|
373
|
+
attributeQuery: [['budget', {
|
|
374
|
+
attributeQuery: ['allocatedAmount']
|
|
375
|
+
}]]
|
|
376
|
+
}]]
|
|
377
|
+
})
|
|
378
|
+
})
|
|
379
|
+
|
|
380
|
+
// Direct summation of relation properties
|
|
381
|
+
const totalShippingCost = Property.create({
|
|
382
|
+
name: 'totalShippingCost',
|
|
383
|
+
type: 'number',
|
|
384
|
+
defaultValue: () => 0,
|
|
385
|
+
computation: Summation.create({
|
|
386
|
+
record: OrderShipmentRelation,
|
|
387
|
+
attributeQuery: ['shippingFee'] // Relation's own property
|
|
388
|
+
})
|
|
389
|
+
})
|
|
390
|
+
|
|
391
|
+
// Global summation handling missing values
|
|
392
|
+
const totalBalance = Dictionary.create({
|
|
393
|
+
name: 'totalBalance',
|
|
394
|
+
type: 'number',
|
|
395
|
+
collection: false,
|
|
396
|
+
computation: Summation.create({
|
|
397
|
+
record: Account,
|
|
398
|
+
attributeQuery: ['balance'] // null or undefined values treated as 0
|
|
399
|
+
})
|
|
400
|
+
})
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
**Working with Other Computations**
|
|
404
|
+
|
|
405
|
+
If you need complex summation logic (like conditional filtering, data transformation etc.), you can first use other computations (like Transform) to calculate the needed values on records, then use Summation for simple summing:
|
|
406
|
+
|
|
407
|
+
```typescript
|
|
408
|
+
// First use Transform to calculate discounted price
|
|
409
|
+
const OrderItem = Entity.create({
|
|
410
|
+
name: 'OrderItem',
|
|
411
|
+
properties: [
|
|
412
|
+
Property.create({ name: 'price', type: 'number' }),
|
|
413
|
+
Property.create({ name: 'quantity', type: 'number' }),
|
|
414
|
+
Property.create({ name: 'discountRate', type: 'number' }),
|
|
415
|
+
Property.create({
|
|
416
|
+
name: 'finalPrice',
|
|
417
|
+
type: 'number',
|
|
418
|
+
computed: function(item) {
|
|
419
|
+
const subtotal = (item.price || 0) * (item.quantity || 0);
|
|
420
|
+
const discount = subtotal * (item.discountRate || 0);
|
|
421
|
+
return subtotal - discount;
|
|
422
|
+
}
|
|
423
|
+
})
|
|
424
|
+
]
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
// Then use Summation to sum computed values
|
|
428
|
+
const orderTotal = Property.create({
|
|
429
|
+
name: 'total',
|
|
430
|
+
type: 'number',
|
|
431
|
+
defaultValue: () => 0,
|
|
432
|
+
computation: Summation.create({
|
|
433
|
+
record: OrderItemRelation,
|
|
434
|
+
attributeQuery: [['target', {attributeQuery: ['finalPrice']}]]
|
|
435
|
+
})
|
|
436
|
+
});
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### Every.create()
|
|
440
|
+
|
|
441
|
+
Create boolean computation that checks if all records meet a condition.
|
|
442
|
+
|
|
443
|
+
**Syntax**
|
|
444
|
+
```typescript
|
|
445
|
+
Every.create(config: EveryConfig): KlassInstance<typeof Every>
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
**Parameters**
|
|
449
|
+
- `config.record` (Entity|Relation, required): Entity or relation to check
|
|
450
|
+
- `config.callback` (function, required): Condition check function, returns boolean
|
|
451
|
+
- `config.attributeQuery` (AttributeQueryData, required): Attribute query configuration
|
|
452
|
+
- `config.notEmpty` (boolean, optional): Return value when collection is empty
|
|
453
|
+
|
|
454
|
+
**Examples**
|
|
455
|
+
```typescript
|
|
456
|
+
// Check if user completed all required courses
|
|
457
|
+
const completedAllRequired = Property.create({
|
|
458
|
+
name: 'completedAllRequired',
|
|
459
|
+
type: 'boolean',
|
|
460
|
+
defaultValue: () => false, // Must provide default value
|
|
461
|
+
computation: Every.create({
|
|
462
|
+
record: UserCourseRelation,
|
|
463
|
+
callback: function(courseRelation) {
|
|
464
|
+
return courseRelation.status === 'completed'
|
|
465
|
+
},
|
|
466
|
+
notEmpty: false
|
|
467
|
+
})
|
|
468
|
+
})
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
### Any.create()
|
|
472
|
+
|
|
473
|
+
Create boolean computation that checks if any record meets a condition.
|
|
474
|
+
|
|
475
|
+
**Syntax**
|
|
476
|
+
```typescript
|
|
477
|
+
Any.create(config: AnyConfig): KlassInstance<typeof Any>
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
**Parameters**
|
|
481
|
+
- `config.record` (Entity|Relation, required): Entity or relation to check
|
|
482
|
+
- `config.callback` (function, required): Condition check function, returns boolean
|
|
483
|
+
- `config.attributeQuery` (AttributeQueryData, required): Attribute query configuration
|
|
484
|
+
|
|
485
|
+
**Examples**
|
|
486
|
+
```typescript
|
|
487
|
+
// Check if user has any pending tasks
|
|
488
|
+
const hasPendingTasks = Property.create({
|
|
489
|
+
name: 'hasPendingTasks',
|
|
490
|
+
type: 'boolean',
|
|
491
|
+
defaultValue: () => false, // Must provide default value
|
|
492
|
+
computation: Any.create({
|
|
493
|
+
record: UserTaskRelation,
|
|
494
|
+
callback: function(taskRelation) {
|
|
495
|
+
return taskRelation.status === 'pending'
|
|
496
|
+
}
|
|
497
|
+
})
|
|
498
|
+
})
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
### Transform.create()
|
|
502
|
+
|
|
503
|
+
Create custom transformation computation.
|
|
504
|
+
|
|
505
|
+
Transform is fundamentally about **transforming data from one collection to another collection**. It transforms sets of data (e.g., InteractionEventEntity → Entity/Relation, Entity → different Entity). Transform **cannot** be used for property computations within the same entity - use `getValue` for that purpose.
|
|
506
|
+
|
|
507
|
+
**Syntax**
|
|
508
|
+
```typescript
|
|
509
|
+
Transform.create(config: TransformConfig): KlassInstance<typeof Transform>
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
**Parameters**
|
|
513
|
+
- `config.record` (Entity|Relation, required): Entity or relation to transform from (source collection)
|
|
514
|
+
- `config.callback` (function, required): Transformation function that converts source data to target data
|
|
515
|
+
- `config.attributeQuery` (AttributeQueryData, required): Attribute query configuration
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
### StateMachine.create()
|
|
519
|
+
|
|
520
|
+
Create state machine computation.
|
|
521
|
+
|
|
522
|
+
**Syntax**
|
|
523
|
+
```typescript
|
|
524
|
+
StateMachine.create(config: StateMachineConfig): KlassInstance<typeof StateMachine>
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
**Parameters**
|
|
528
|
+
- `config.states` (StateNode[], required): List of state nodes
|
|
529
|
+
- `config.transfers` (StateTransfer[], required): List of state transfers
|
|
530
|
+
- `config.defaultState` (StateNode, required): Default state
|
|
531
|
+
|
|
532
|
+
**Examples**
|
|
533
|
+
```typescript
|
|
534
|
+
// First declare state nodes
|
|
535
|
+
const pendingState = StateNode.create({ name: 'pending' });
|
|
536
|
+
const confirmedState = StateNode.create({ name: 'confirmed' });
|
|
537
|
+
const shippedState = StateNode.create({ name: 'shipped' });
|
|
538
|
+
const deliveredState = StateNode.create({ name: 'delivered' });
|
|
539
|
+
|
|
540
|
+
// Order state machine
|
|
541
|
+
const OrderStateMachine = StateMachine.create({
|
|
542
|
+
states: [pendingState, confirmedState, shippedState, deliveredState],
|
|
543
|
+
transfers: [
|
|
544
|
+
StateTransfer.create({
|
|
545
|
+
current: pendingState,
|
|
546
|
+
next: confirmedState,
|
|
547
|
+
trigger: ConfirmOrderInteraction
|
|
548
|
+
})
|
|
549
|
+
],
|
|
550
|
+
defaultState: pendingState
|
|
551
|
+
})
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
### RealTime.create()
|
|
555
|
+
|
|
556
|
+
Create real-time computation for handling time-based reactive computations. Real-time computations automatically manage state (lastRecomputeTime and nextRecomputeTime) and adopt different scheduling strategies based on return type.
|
|
557
|
+
|
|
558
|
+
**Syntax**
|
|
559
|
+
```typescript
|
|
560
|
+
RealTime.create(config: RealTimeConfig): KlassInstance<typeof RealTime>
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
**Parameters**
|
|
564
|
+
- `config.callback` (function, required): Real-time computation callback function, accepts `(now: Expression, dataDeps: any) => Expression | Inequality | Equation`
|
|
565
|
+
- `config.nextRecomputeTime` (function, optional): Recomputation interval function, accepts `(now: number, dataDeps: any) => number`, only valid for Expression type
|
|
566
|
+
- `config.dataDeps` (object, optional): Data dependency configuration, format: `{[key: string]: DataDep}`
|
|
567
|
+
- `config.attributeQuery` (AttributeQueryData, optional): Attribute query configuration
|
|
568
|
+
|
|
569
|
+
**Return Types and Scheduling Behavior**
|
|
570
|
+
- **Expression**: Returns numeric computation result, nextRecomputeTime = lastRecomputeTime + nextRecomputeTime function return value
|
|
571
|
+
- **Inequality**: Returns boolean comparison result, nextRecomputeTime = solve() result (critical time point for state change)
|
|
572
|
+
- **Equation**: Returns boolean equation result, nextRecomputeTime = solve() result (critical time point for state change)
|
|
573
|
+
|
|
574
|
+
**State Management**
|
|
575
|
+
|
|
576
|
+
RealTime computations automatically create and manage two state fields:
|
|
577
|
+
- `lastRecomputeTime`: Timestamp of last computation
|
|
578
|
+
- `nextRecomputeTime`: Timestamp of next computation
|
|
579
|
+
|
|
580
|
+
State field naming convention:
|
|
581
|
+
- Global computations: `_global_boundState_{computationName}_{stateName}`
|
|
582
|
+
- Property computations: `_record_boundState_{entityName}_{propertyName}_{stateName}`
|
|
583
|
+
|
|
584
|
+
**Examples**
|
|
585
|
+
|
|
586
|
+
```typescript
|
|
587
|
+
// Expression type: manually specify recomputation interval
|
|
588
|
+
const currentTimestamp = Dictionary.create({
|
|
589
|
+
name: 'currentTimestamp',
|
|
590
|
+
type: 'number',
|
|
591
|
+
computation: RealTime.create({
|
|
592
|
+
nextRecomputeTime: (now: number, dataDeps: any) => 1000, // Update every second
|
|
593
|
+
callback: async (now: Expression, dataDeps: any) => {
|
|
594
|
+
return now.divide(1000); // Convert to seconds
|
|
595
|
+
}
|
|
596
|
+
})
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
// Inequality type: system automatically calculates critical time points
|
|
600
|
+
const isAfterDeadline = Dictionary.create({
|
|
601
|
+
name: 'isAfterDeadline',
|
|
602
|
+
type: 'boolean',
|
|
603
|
+
computation: RealTime.create({
|
|
604
|
+
dataDeps: {
|
|
605
|
+
project: {
|
|
606
|
+
type: 'records',
|
|
607
|
+
source: projectEntity,
|
|
608
|
+
attributeQuery: ['deadline']
|
|
609
|
+
}
|
|
610
|
+
},
|
|
611
|
+
callback: async (now: Expression, dataDeps: any) => {
|
|
612
|
+
const deadline = dataDeps.project?.[0]?.deadline || Date.now() + 86400000;
|
|
613
|
+
// System will automatically recompute at deadline time
|
|
614
|
+
return now.gt(deadline);
|
|
615
|
+
}
|
|
616
|
+
})
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
// Equation type: check time equations
|
|
620
|
+
const isExactHour = Dictionary.create({
|
|
621
|
+
name: 'isExactHour',
|
|
622
|
+
type: 'boolean',
|
|
623
|
+
computation: RealTime.create({
|
|
624
|
+
callback: async (now: Expression, dataDeps: any) => {
|
|
625
|
+
const millisecondsInHour = 3600000;
|
|
626
|
+
// System will automatically recompute at next exact hour
|
|
627
|
+
return now.modulo(millisecondsInHour).eq(0);
|
|
628
|
+
}
|
|
629
|
+
})
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
// Property-level real-time computation
|
|
633
|
+
const userEntity = Entity.create({
|
|
634
|
+
name: 'User',
|
|
635
|
+
properties: [
|
|
636
|
+
Property.create({ name: 'lastLoginAt', type: 'number' }),
|
|
637
|
+
Property.create({
|
|
638
|
+
name: 'isRecentlyActive',
|
|
639
|
+
type: 'boolean',
|
|
640
|
+
computation: RealTime.create({
|
|
641
|
+
dataDeps: {
|
|
642
|
+
_current: {
|
|
643
|
+
type: 'property',
|
|
644
|
+
attributeQuery: ['lastLoginAt']
|
|
645
|
+
}
|
|
646
|
+
},
|
|
647
|
+
callback: async (now: Expression, dataDeps: any) => {
|
|
648
|
+
const lastLogin = dataDeps._current?.lastLoginAt || 0;
|
|
649
|
+
const oneHourAgo = now.subtract(3600000);
|
|
650
|
+
return Expression.number(lastLogin).gt(oneHourAgo);
|
|
651
|
+
}
|
|
652
|
+
})
|
|
653
|
+
})
|
|
654
|
+
]
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
// Complex data dependencies real-time computation
|
|
658
|
+
const businessMetrics = Dictionary.create({
|
|
659
|
+
name: 'businessMetrics',
|
|
660
|
+
type: 'object',
|
|
661
|
+
computation: RealTime.create({
|
|
662
|
+
nextRecomputeTime: (now: number, dataDeps: any) => 300000, // Update every 5 minutes
|
|
663
|
+
dataDeps: {
|
|
664
|
+
config: {
|
|
665
|
+
type: 'records',
|
|
666
|
+
source: configEntity,
|
|
667
|
+
attributeQuery: ['businessHourStart', 'businessHourEnd']
|
|
668
|
+
},
|
|
669
|
+
metrics: {
|
|
670
|
+
type: 'records',
|
|
671
|
+
source: metricsEntity,
|
|
672
|
+
attributeQuery: ['dailyTarget', 'currentValue']
|
|
673
|
+
}
|
|
674
|
+
},
|
|
675
|
+
callback: async (now: Expression, dataDeps: any) => {
|
|
676
|
+
const config = dataDeps.config?.[0] || {};
|
|
677
|
+
const metrics = dataDeps.metrics?.[0] || {};
|
|
678
|
+
|
|
679
|
+
const startHour = config.businessHourStart || 9;
|
|
680
|
+
const endHour = config.businessHourEnd || 17;
|
|
681
|
+
const currentHour = now.divide(3600000).modulo(24);
|
|
682
|
+
|
|
683
|
+
const isBusinessTime = currentHour.gt(startHour).and(currentHour.lt(endHour));
|
|
684
|
+
const progressRate = Expression.number(metrics.currentValue || 0).divide(metrics.dailyTarget || 1);
|
|
685
|
+
|
|
686
|
+
return {
|
|
687
|
+
isBusinessTime: isBusinessTime.evaluate({now: Date.now()}),
|
|
688
|
+
progressRate: progressRate.evaluate({now: Date.now()}),
|
|
689
|
+
timestamp: now.evaluate({now: Date.now()})
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
})
|
|
693
|
+
});
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
**State Access Example**
|
|
697
|
+
|
|
698
|
+
```typescript
|
|
699
|
+
// Get computation instance
|
|
700
|
+
const realTimeComputation = Array.from(controller.scheduler.computations.values()).find(
|
|
701
|
+
computation => computation.dataContext.type === 'global' &&
|
|
702
|
+
computation.dataContext.id === 'currentTimestamp'
|
|
703
|
+
);
|
|
704
|
+
|
|
705
|
+
// Get state key names
|
|
706
|
+
const lastRecomputeTimeKey = controller.scheduler.getBoundStateName(
|
|
707
|
+
realTimeComputation.dataContext,
|
|
708
|
+
'lastRecomputeTime',
|
|
709
|
+
realTimeComputation.state.lastRecomputeTime
|
|
710
|
+
);
|
|
711
|
+
|
|
712
|
+
const nextRecomputeTimeKey = controller.scheduler.getBoundStateName(
|
|
713
|
+
realTimeComputation.dataContext,
|
|
714
|
+
'nextRecomputeTime',
|
|
715
|
+
realTimeComputation.state.nextRecomputeTime
|
|
716
|
+
);
|
|
717
|
+
|
|
718
|
+
// Read state values
|
|
719
|
+
const lastRecomputeTime = await system.storage.get(DICTIONARY_RECORD, lastRecomputeTimeKey);
|
|
720
|
+
const nextRecomputeTime = await system.storage.get(DICTIONARY_RECORD, nextRecomputeTimeKey);
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
### Dictionary.create()
|
|
724
|
+
|
|
725
|
+
Create global dictionary for storing system-wide state and values.
|
|
726
|
+
|
|
727
|
+
**Syntax**
|
|
728
|
+
```typescript
|
|
729
|
+
Dictionary.create(config: DictionaryConfig): KlassInstance<typeof Dictionary>
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
**Parameters**
|
|
733
|
+
- `config.name` (string, required): Dictionary name
|
|
734
|
+
- `config.type` (string, required): Value type, must be one of PropertyTypes (e.g., 'string', 'number', 'boolean', 'object', etc.)
|
|
735
|
+
- `config.collection` (boolean, required): Whether it's a collection type, defaults to false
|
|
736
|
+
- `config.args` (object, optional): Type-specific arguments (e.g., string length, number range)
|
|
737
|
+
- `config.defaultValue` (function, optional): Default value generator function
|
|
738
|
+
- `config.computation` (Computation, optional): Reactive computation for the dictionary value
|
|
739
|
+
|
|
740
|
+
**Examples**
|
|
741
|
+
```typescript
|
|
742
|
+
// Simple global counter
|
|
743
|
+
const userCountDict = Dictionary.create({
|
|
744
|
+
name: 'userCount',
|
|
745
|
+
type: 'number',
|
|
746
|
+
collection: false,
|
|
747
|
+
defaultValue: () => 0,
|
|
748
|
+
computation: Count.create({
|
|
749
|
+
record: User
|
|
750
|
+
})
|
|
751
|
+
})
|
|
752
|
+
|
|
753
|
+
// System configuration
|
|
754
|
+
const systemConfig = Dictionary.create({
|
|
755
|
+
name: 'config',
|
|
756
|
+
type: 'object',
|
|
757
|
+
collection: false,
|
|
758
|
+
defaultValue: () => ({
|
|
759
|
+
maxUsers: 1000,
|
|
760
|
+
maintenanceMode: false
|
|
761
|
+
})
|
|
762
|
+
})
|
|
763
|
+
|
|
764
|
+
// Real-time values
|
|
765
|
+
const currentTime = Dictionary.create({
|
|
766
|
+
name: 'currentTime',
|
|
767
|
+
type: 'number',
|
|
768
|
+
collection: false,
|
|
769
|
+
computation: RealTime.create({
|
|
770
|
+
nextRecomputeTime: () => 1000, // Update every second
|
|
771
|
+
callback: async (now) => {
|
|
772
|
+
return now.divide(1000);
|
|
773
|
+
}
|
|
774
|
+
})
|
|
775
|
+
})
|
|
776
|
+
|
|
777
|
+
// Collection type dictionary
|
|
778
|
+
const activeUsers = Dictionary.create({
|
|
779
|
+
name: 'activeUsers',
|
|
780
|
+
type: 'string',
|
|
781
|
+
collection: true,
|
|
782
|
+
defaultValue: () => [],
|
|
783
|
+
computation: Transform.create({
|
|
784
|
+
record: User,
|
|
785
|
+
attributeQuery: ['id', 'lastLoginTime'],
|
|
786
|
+
callback: (users) => {
|
|
787
|
+
const oneHourAgo = Date.now() - 3600000;
|
|
788
|
+
return users
|
|
789
|
+
.filter(u => u.lastLoginTime > oneHourAgo)
|
|
790
|
+
.map(u => u.id);
|
|
791
|
+
}
|
|
792
|
+
})
|
|
793
|
+
})
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
**Usage in Controller**
|
|
797
|
+
|
|
798
|
+
Dictionaries are passed as the 6th parameter to Controller:
|
|
799
|
+
|
|
800
|
+
```typescript
|
|
801
|
+
const controller = new Controller({
|
|
802
|
+
system: system,
|
|
803
|
+
entities: entities,
|
|
804
|
+
relations: relations,
|
|
805
|
+
activities: activities,
|
|
806
|
+
interactions: interactions,
|
|
807
|
+
dict: [userCountDict, systemConfig, currentTime, activeUsers],, // Dictionaries
|
|
808
|
+
recordMutationSideEffects: []
|
|
809
|
+
});
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
### StateNode.create()
|
|
813
|
+
|
|
814
|
+
Create state node for state machine computation.
|
|
815
|
+
|
|
816
|
+
**Syntax**
|
|
817
|
+
```typescript
|
|
818
|
+
StateNode.create(config: StateNodeConfig): KlassInstance<typeof StateNode>
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
**Parameters**
|
|
822
|
+
- `config.name` (string, required): State name identifier
|
|
823
|
+
- `config.computeValue` (function, optional): Function to compute value for this state
|
|
824
|
+
|
|
825
|
+
**Examples**
|
|
826
|
+
```typescript
|
|
827
|
+
// Simple state node
|
|
828
|
+
const pendingState = StateNode.create({ name: 'pending' });
|
|
829
|
+
|
|
830
|
+
// State node with computed value
|
|
831
|
+
const activeState = StateNode.create({
|
|
832
|
+
name: 'active',
|
|
833
|
+
computeValue: (context) => {
|
|
834
|
+
// Compute state-specific value
|
|
835
|
+
return {
|
|
836
|
+
activatedAt: Date.now(),
|
|
837
|
+
priority: context.priority || 'normal'
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
### StateTransfer.create()
|
|
844
|
+
|
|
845
|
+
Create state transfer for state machine computation.
|
|
846
|
+
|
|
847
|
+
**Syntax**
|
|
848
|
+
```typescript
|
|
849
|
+
StateTransfer.create(config: StateTransferConfig): KlassInstance<typeof StateTransfer>
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
**Parameters**
|
|
853
|
+
- `config.trigger` (any, required): Trigger for the state transfer (usually an Interaction)
|
|
854
|
+
- `config.current` (StateNode, required): Current state node
|
|
855
|
+
- `config.next` (StateNode, required): Next state node
|
|
856
|
+
- `config.computeTarget` (function, optional): Function to dynamically compute the target state
|
|
857
|
+
|
|
858
|
+
**Examples**
|
|
859
|
+
```typescript
|
|
860
|
+
// Simple state transfer
|
|
861
|
+
const approveTransfer = StateTransfer.create({
|
|
862
|
+
trigger: ApproveInteraction,
|
|
863
|
+
current: pendingState,
|
|
864
|
+
next: approvedState
|
|
865
|
+
});
|
|
866
|
+
|
|
867
|
+
// State transfer with dynamic target computation
|
|
868
|
+
const conditionalTransfer = StateTransfer.create({
|
|
869
|
+
trigger: ProcessInteraction,
|
|
870
|
+
current: pendingState,
|
|
871
|
+
next: approvedState, // Default next state
|
|
872
|
+
computeTarget: (context) => {
|
|
873
|
+
// Dynamically determine next state based on context
|
|
874
|
+
if (context.autoApprove) {
|
|
875
|
+
return approvedState;
|
|
876
|
+
} else if (context.requiresReview) {
|
|
877
|
+
return reviewState;
|
|
878
|
+
}
|
|
879
|
+
return rejectedState;
|
|
880
|
+
}
|
|
881
|
+
});
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
## 13.3 Interaction-Related APIs
|
|
885
|
+
|
|
886
|
+
### Interaction.create()
|
|
887
|
+
|
|
888
|
+
Create user interaction definition.
|
|
889
|
+
|
|
890
|
+
**Syntax**
|
|
891
|
+
```typescript
|
|
892
|
+
Interaction.create(config: InteractionConfig): KlassInstance<typeof Interaction>
|
|
893
|
+
```
|
|
894
|
+
|
|
895
|
+
**Parameters**
|
|
896
|
+
- `config.name` (string, required): Interaction name
|
|
897
|
+
- `config.action` (Action, required): Interaction action
|
|
898
|
+
- `config.payload` (Payload, optional): Interaction parameters
|
|
899
|
+
- `config.conditions` (Condition|Conditions, optional): Execution conditions
|
|
900
|
+
- `config.sideEffects` (SideEffect[], optional): Side effect handlers
|
|
901
|
+
- `config.data` (Entity|Relation, optional): Associated data entity
|
|
902
|
+
- `config.query` (Query, optional): Query definition for data fetching
|
|
903
|
+
|
|
904
|
+
**Examples**
|
|
905
|
+
```typescript
|
|
906
|
+
// Create post interaction
|
|
907
|
+
const CreatePostInteraction = Interaction.create({
|
|
908
|
+
name: 'createPost',
|
|
909
|
+
action: Action.create({ name: 'create' }),
|
|
910
|
+
payload: Payload.create({
|
|
911
|
+
items: [
|
|
912
|
+
PayloadItem.create({
|
|
913
|
+
name: 'postData',
|
|
914
|
+
base: Post,
|
|
915
|
+
required: true
|
|
916
|
+
})
|
|
917
|
+
]
|
|
918
|
+
})
|
|
919
|
+
})
|
|
920
|
+
```
|
|
921
|
+
|
|
922
|
+
### Action.create()
|
|
923
|
+
|
|
924
|
+
Create interaction action identifier.
|
|
925
|
+
|
|
926
|
+
⚠️ **Important: Action is not an "operation" but an identifier**
|
|
927
|
+
|
|
928
|
+
Action is just a name for interaction types, like event type labels. It contains no operation logic or execution code.
|
|
929
|
+
|
|
930
|
+
**Syntax**
|
|
931
|
+
```typescript
|
|
932
|
+
Action.create(config: ActionConfig): KlassInstance<typeof Action>
|
|
933
|
+
```
|
|
934
|
+
|
|
935
|
+
**Parameters**
|
|
936
|
+
- `config.name` (string, required): Action type identifier name
|
|
937
|
+
|
|
938
|
+
**Examples**
|
|
939
|
+
```typescript
|
|
940
|
+
// These are just identifiers, containing no operation logic
|
|
941
|
+
const CreateAction = Action.create({ name: 'create' })
|
|
942
|
+
const UpdateAction = Action.create({ name: 'update' })
|
|
943
|
+
const DeleteAction = Action.create({ name: 'delete' })
|
|
944
|
+
const LikeAction = Action.create({ name: 'like' })
|
|
945
|
+
|
|
946
|
+
// ❌ Wrong understanding: thinking Action contains operation logic
|
|
947
|
+
const WrongAction = Action.create({
|
|
948
|
+
name: 'create',
|
|
949
|
+
execute: () => { /* ... */ } // ❌ Action has no execute method!
|
|
950
|
+
})
|
|
951
|
+
|
|
952
|
+
// ✅ Correct understanding: Action is just an identifier
|
|
953
|
+
const CorrectAction = Action.create({
|
|
954
|
+
name: 'create' // That's it!
|
|
955
|
+
})
|
|
956
|
+
```
|
|
957
|
+
|
|
958
|
+
**Where is the operation logic?**
|
|
959
|
+
|
|
960
|
+
All operation logic is implemented through reactive computations (Transform, Count, etc.):
|
|
961
|
+
|
|
962
|
+
```typescript
|
|
963
|
+
// Interaction just declares that users can create posts
|
|
964
|
+
const CreatePost = Interaction.create({
|
|
965
|
+
name: 'CreatePost',
|
|
966
|
+
action: Action.create({ name: 'create' }), // Just an identifier
|
|
967
|
+
payload: Payload.create({ /* ... */ })
|
|
968
|
+
});
|
|
969
|
+
|
|
970
|
+
// The actual "create" logic is in Transform
|
|
971
|
+
const UserPostRelation = Relation.create({
|
|
972
|
+
source: User,
|
|
973
|
+
target: Post,
|
|
974
|
+
computation: Transform.create({
|
|
975
|
+
record: InteractionEventEntity,
|
|
976
|
+
callback: (event) => {
|
|
977
|
+
if (event.interactionName === 'CreatePost') {
|
|
978
|
+
// This is where the actual creation logic is
|
|
979
|
+
return {
|
|
980
|
+
source: event.user.id,
|
|
981
|
+
target: {
|
|
982
|
+
title: event.payload.title,
|
|
983
|
+
content: event.payload.content
|
|
984
|
+
}
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
})
|
|
989
|
+
});
|
|
990
|
+
```
|
|
991
|
+
|
|
992
|
+
### Payload.create()
|
|
993
|
+
|
|
994
|
+
Create interaction parameter definition.
|
|
995
|
+
|
|
996
|
+
**Syntax**
|
|
997
|
+
```typescript
|
|
998
|
+
Payload.create(config: PayloadConfig): KlassInstance<typeof Payload>
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
**Parameters**
|
|
1002
|
+
- `config.items` (PayloadItem[], required): Parameter item list, defaults to empty array
|
|
1003
|
+
|
|
1004
|
+
**Examples**
|
|
1005
|
+
```typescript
|
|
1006
|
+
const CreateUserPayload = Payload.create({
|
|
1007
|
+
items: [
|
|
1008
|
+
PayloadItem.create({
|
|
1009
|
+
name: 'userData',
|
|
1010
|
+
base: User,
|
|
1011
|
+
required: true
|
|
1012
|
+
}),
|
|
1013
|
+
PayloadItem.create({
|
|
1014
|
+
name: 'profileData',
|
|
1015
|
+
base: Profile,
|
|
1016
|
+
required: false
|
|
1017
|
+
})
|
|
1018
|
+
]
|
|
1019
|
+
})
|
|
1020
|
+
```
|
|
1021
|
+
|
|
1022
|
+
### PayloadItem.create()
|
|
1023
|
+
|
|
1024
|
+
Create interaction parameter item.
|
|
1025
|
+
|
|
1026
|
+
**Syntax**
|
|
1027
|
+
```typescript
|
|
1028
|
+
PayloadItem.create(config: PayloadItemConfig): KlassInstance<typeof PayloadItem>
|
|
1029
|
+
```
|
|
1030
|
+
|
|
1031
|
+
**Parameters**
|
|
1032
|
+
- `config.name` (string, required): Parameter name
|
|
1033
|
+
- `config.base` (Entity, optional): Parameter entity type, only needed when isRef is true
|
|
1034
|
+
- `config.isRef` (boolean, optional): Whether it's a reference type, defaults to false
|
|
1035
|
+
- `config.required` (boolean, optional): Whether it's required, defaults to false
|
|
1036
|
+
- `config.isCollection` (boolean, optional): Whether it's a collection type, defaults to false
|
|
1037
|
+
- `config.attributives` (Attributive|Attributives, optional): Parameter permission attributives
|
|
1038
|
+
- `config.itemRef` (Attributive|Entity, optional): Used to reference entities defined in other interactions within Activity
|
|
1039
|
+
|
|
1040
|
+
**Examples**
|
|
1041
|
+
```typescript
|
|
1042
|
+
// Reference existing user
|
|
1043
|
+
const userRef = PayloadItem.create({
|
|
1044
|
+
name: 'user',
|
|
1045
|
+
base: User,
|
|
1046
|
+
isRef: true,
|
|
1047
|
+
required: true
|
|
1048
|
+
})
|
|
1049
|
+
|
|
1050
|
+
// Create new post data
|
|
1051
|
+
const postData = PayloadItem.create({
|
|
1052
|
+
name: 'postData',
|
|
1053
|
+
base: Post,
|
|
1054
|
+
required: true,
|
|
1055
|
+
attributives: Attributive.create({
|
|
1056
|
+
name: 'ValidPost',
|
|
1057
|
+
content: function(post) {
|
|
1058
|
+
return post.title && post.content
|
|
1059
|
+
}
|
|
1060
|
+
})
|
|
1061
|
+
})
|
|
1062
|
+
|
|
1063
|
+
// Collection type reference
|
|
1064
|
+
const reviewersItem = PayloadItem.create({
|
|
1065
|
+
name: 'reviewers',
|
|
1066
|
+
base: User,
|
|
1067
|
+
isRef: true,
|
|
1068
|
+
isCollection: true,
|
|
1069
|
+
attributives: Attributives.create({
|
|
1070
|
+
content: BoolAtomData.create({data: ReviewerAttr, type: 'atom'})
|
|
1071
|
+
})
|
|
1072
|
+
})
|
|
1073
|
+
|
|
1074
|
+
// Activity item reference
|
|
1075
|
+
const activityItem = PayloadItem.create({
|
|
1076
|
+
name: 'to',
|
|
1077
|
+
base: User,
|
|
1078
|
+
isRef: true,
|
|
1079
|
+
attributives: boolExpToAttributives(BoolExp.atom(OtherAttr)),
|
|
1080
|
+
itemRef: userRefB
|
|
1081
|
+
})
|
|
1082
|
+
```
|
|
1083
|
+
|
|
1084
|
+
## 13.4 Activity-Related APIs
|
|
1085
|
+
|
|
1086
|
+
### Activity.create()
|
|
1087
|
+
|
|
1088
|
+
Create business activity definition.
|
|
1089
|
+
|
|
1090
|
+
**Syntax**
|
|
1091
|
+
```typescript
|
|
1092
|
+
Activity.create(config: ActivityConfig): KlassInstance<typeof Activity>
|
|
1093
|
+
```
|
|
1094
|
+
|
|
1095
|
+
**Parameters**
|
|
1096
|
+
- `config.name` (string, required): Activity name
|
|
1097
|
+
- `config.interactions` (Interaction[], optional): List of interactions in the activity
|
|
1098
|
+
- `config.transfers` (Transfer[], optional): List of state transfers
|
|
1099
|
+
- `config.groups` (ActivityGroup[], optional): List of activity groups
|
|
1100
|
+
- `config.gateways` (Gateway[], optional): List of gateways
|
|
1101
|
+
- `config.events` (Event[], optional): List of events
|
|
1102
|
+
|
|
1103
|
+
**Examples**
|
|
1104
|
+
```typescript
|
|
1105
|
+
const OrderProcessActivity = Activity.create({
|
|
1106
|
+
name: 'OrderProcess',
|
|
1107
|
+
interactions: [
|
|
1108
|
+
CreateOrderInteraction,
|
|
1109
|
+
ConfirmOrderInteraction,
|
|
1110
|
+
PayOrderInteraction,
|
|
1111
|
+
ShipOrderInteraction
|
|
1112
|
+
],
|
|
1113
|
+
transfers: [
|
|
1114
|
+
Transfer.create({
|
|
1115
|
+
name: 'createToConfirm',
|
|
1116
|
+
source: CreateOrderInteraction,
|
|
1117
|
+
target: ConfirmOrderInteraction
|
|
1118
|
+
}),
|
|
1119
|
+
Transfer.create({
|
|
1120
|
+
name: 'confirmToPay',
|
|
1121
|
+
source: ConfirmOrderInteraction,
|
|
1122
|
+
target: PayOrderInteraction
|
|
1123
|
+
})
|
|
1124
|
+
]
|
|
1125
|
+
})
|
|
1126
|
+
```
|
|
1127
|
+
|
|
1128
|
+
### Transfer.create()
|
|
1129
|
+
|
|
1130
|
+
Create activity state transfer.
|
|
1131
|
+
|
|
1132
|
+
**Syntax**
|
|
1133
|
+
```typescript
|
|
1134
|
+
Transfer.create(config: TransferConfig): KlassInstance<typeof Transfer>
|
|
1135
|
+
```
|
|
1136
|
+
|
|
1137
|
+
**Parameters**
|
|
1138
|
+
- `config.name` (string, required): Transfer name
|
|
1139
|
+
- `config.source` (Interaction|ActivityGroup|Gateway, required): Source node
|
|
1140
|
+
- `config.target` (Interaction|ActivityGroup|Gateway, required): Target node
|
|
1141
|
+
|
|
1142
|
+
**Examples**
|
|
1143
|
+
```typescript
|
|
1144
|
+
const ApprovalTransfer = Transfer.create({
|
|
1145
|
+
name: 'submitToApprove',
|
|
1146
|
+
source: SubmitApplicationInteraction,
|
|
1147
|
+
target: ApproveApplicationInteraction
|
|
1148
|
+
})
|
|
1149
|
+
```
|
|
1150
|
+
|
|
1151
|
+
### Condition.create()
|
|
1152
|
+
|
|
1153
|
+
Create activity execution condition.
|
|
1154
|
+
|
|
1155
|
+
**Syntax**
|
|
1156
|
+
```typescript
|
|
1157
|
+
Condition.create(config: ConditionConfig): KlassInstance<typeof Condition>
|
|
1158
|
+
```
|
|
1159
|
+
|
|
1160
|
+
**Parameters**
|
|
1161
|
+
- `config.name` (string, required): Condition name
|
|
1162
|
+
- `config.content` (function, required): Condition judgment function
|
|
1163
|
+
|
|
1164
|
+
**Examples**
|
|
1165
|
+
```typescript
|
|
1166
|
+
const OrderValueCondition = Condition.create({
|
|
1167
|
+
name: 'highValueOrder',
|
|
1168
|
+
content: function(order) {
|
|
1169
|
+
return order.totalAmount > 1000
|
|
1170
|
+
}
|
|
1171
|
+
})
|
|
1172
|
+
```
|
|
1173
|
+
|
|
1174
|
+
## 13.5 System-Related APIs
|
|
1175
|
+
|
|
1176
|
+
### Controller
|
|
1177
|
+
|
|
1178
|
+
System controller that coordinates the work of various components.
|
|
1179
|
+
|
|
1180
|
+
**Constructor**
|
|
1181
|
+
```typescript
|
|
1182
|
+
new Controller({
|
|
1183
|
+
system: System,
|
|
1184
|
+
entities: KlassInstance<typeof Entity>[],
|
|
1185
|
+
relations: KlassInstance<typeof Relation>[],
|
|
1186
|
+
activities: KlassInstance<typeof Activity>[],
|
|
1187
|
+
interactions: KlassInstance<typeof Interaction>[],
|
|
1188
|
+
dict?: KlassInstance<typeof Property>[], // Note: This is for global dictionaries, NOT computations
|
|
1189
|
+
recordMutationSideEffects?: RecordMutationSideEffect[]
|
|
1190
|
+
})
|
|
1191
|
+
```
|
|
1192
|
+
|
|
1193
|
+
⚠️ **IMPORTANT**: Controller does NOT accept a computations parameter. All computations should be defined within the `computation` field of Entity/Relation/Property definitions. The 6th parameter `dict` is for global dictionary definitions (Dictionary.create), not for computation definitions.
|
|
1194
|
+
|
|
1195
|
+
**Main Methods**
|
|
1196
|
+
|
|
1197
|
+
#### setup(install?: boolean)
|
|
1198
|
+
Initialize system.
|
|
1199
|
+
```typescript
|
|
1200
|
+
await controller.setup(true) // Create database tables
|
|
1201
|
+
```
|
|
1202
|
+
|
|
1203
|
+
#### callInteraction(interactionName: string, args: InteractionEventArgs)
|
|
1204
|
+
Call interaction.
|
|
1205
|
+
```typescript
|
|
1206
|
+
const result = await controller.callInteraction('createPost', {
|
|
1207
|
+
user: { id: 'user1' },
|
|
1208
|
+
payload: { postData: { title: 'Hello', content: 'World' } }
|
|
1209
|
+
})
|
|
1210
|
+
```
|
|
1211
|
+
|
|
1212
|
+
#### callActivityInteraction(activityName: string, interactionName: string, activityId: string, args: InteractionEventArgs)
|
|
1213
|
+
Call interaction within activity.
|
|
1214
|
+
```typescript
|
|
1215
|
+
const result = await controller.callActivityInteraction(
|
|
1216
|
+
'OrderProcess',
|
|
1217
|
+
'confirmOrder',
|
|
1218
|
+
'activity-instance-1',
|
|
1219
|
+
{ user: { id: 'user1' }, payload: { orderData: {...} } }
|
|
1220
|
+
)
|
|
1221
|
+
```
|
|
1222
|
+
|
|
1223
|
+
### System
|
|
1224
|
+
|
|
1225
|
+
System abstract interface that defines basic services like storage and logging.
|
|
1226
|
+
|
|
1227
|
+
**Interface Definition**
|
|
1228
|
+
```typescript
|
|
1229
|
+
interface System {
|
|
1230
|
+
conceptClass: Map<string, ReturnType<typeof createClass>>
|
|
1231
|
+
storage: Storage
|
|
1232
|
+
logger: SystemLogger
|
|
1233
|
+
setup: (entities: Entity[], relations: Relation[], states: ComputationState[], install?: boolean) => Promise<any>
|
|
1234
|
+
}
|
|
1235
|
+
```
|
|
1236
|
+
|
|
1237
|
+
### Storage
|
|
1238
|
+
|
|
1239
|
+
Storage layer interface providing data persistence functionality.
|
|
1240
|
+
|
|
1241
|
+
**Main Methods**
|
|
1242
|
+
|
|
1243
|
+
#### Entity/Relation Operations
|
|
1244
|
+
|
|
1245
|
+
🔴 **CRITICAL: Always specify attributeQuery parameter!**
|
|
1246
|
+
- Without `attributeQuery`, only the `id` field is returned
|
|
1247
|
+
- This is a common source of bugs in tests and applications
|
|
1248
|
+
- Always explicitly list all fields you need
|
|
1249
|
+
|
|
1250
|
+
```typescript
|
|
1251
|
+
// ❌ WRONG: Only returns { id: '...' }
|
|
1252
|
+
const user = await storage.findOne('User', MatchExp.atom({
|
|
1253
|
+
key: 'email',
|
|
1254
|
+
value: ['=', 'user@example.com']
|
|
1255
|
+
}))
|
|
1256
|
+
console.log(user.name) // undefined!
|
|
1257
|
+
|
|
1258
|
+
// ✅ CORRECT: Returns all specified fields
|
|
1259
|
+
const user = await storage.findOne('User',
|
|
1260
|
+
MatchExp.atom({
|
|
1261
|
+
key: 'email',
|
|
1262
|
+
value: ['=', 'user@example.com']
|
|
1263
|
+
}),
|
|
1264
|
+
undefined, // modifier
|
|
1265
|
+
['id', 'name', 'email', 'role', 'createdAt'] // attributeQuery
|
|
1266
|
+
)
|
|
1267
|
+
console.log(user.name) // 'John Doe' ✓
|
|
1268
|
+
|
|
1269
|
+
// Create record
|
|
1270
|
+
await storage.create('User', { username: 'john', email: 'john@example.com' })
|
|
1271
|
+
|
|
1272
|
+
// Find single record (MUST specify attributeQuery!)
|
|
1273
|
+
const user = await storage.findOne('User', MatchExp.atom({
|
|
1274
|
+
key: 'username',
|
|
1275
|
+
value: ['=', 'john']
|
|
1276
|
+
}), undefined, ['id', 'username', 'email', 'status'])
|
|
1277
|
+
|
|
1278
|
+
// Find multiple records (MUST specify attributeQuery!)
|
|
1279
|
+
const users = await storage.find('User', MatchExp.atom({
|
|
1280
|
+
key: 'status',
|
|
1281
|
+
value: ['=', 'active']
|
|
1282
|
+
}), undefined, ['id', 'username', 'email', 'lastLoginDate'])
|
|
1283
|
+
|
|
1284
|
+
// Update record
|
|
1285
|
+
await storage.update('User', MatchExp.atom({
|
|
1286
|
+
key: 'id',
|
|
1287
|
+
value: ['=', 'user1']
|
|
1288
|
+
}), { status: 'inactive' })
|
|
1289
|
+
|
|
1290
|
+
// Delete record
|
|
1291
|
+
await storage.delete('User', MatchExp.atom({
|
|
1292
|
+
key: 'id',
|
|
1293
|
+
value: ['=', 'user1']
|
|
1294
|
+
}))
|
|
1295
|
+
```
|
|
1296
|
+
|
|
1297
|
+
#### KV Storage Operations
|
|
1298
|
+
```typescript
|
|
1299
|
+
// Set value
|
|
1300
|
+
await storage.set('config', 'maxUsers', 1000)
|
|
1301
|
+
|
|
1302
|
+
// Get value
|
|
1303
|
+
const maxUsers = await storage.get('config', 'maxUsers', 100) // Default value 100
|
|
1304
|
+
```
|
|
1305
|
+
|
|
1306
|
+
## 13.6 Utility Function APIs
|
|
1307
|
+
|
|
1308
|
+
### MatchExp
|
|
1309
|
+
|
|
1310
|
+
Query expression builder for constructing complex query conditions.
|
|
1311
|
+
|
|
1312
|
+
#### MatchExp.atom(condition: MatchAtom)
|
|
1313
|
+
Create atomic query condition.
|
|
1314
|
+
|
|
1315
|
+
**Parameters**
|
|
1316
|
+
- `condition.key` (string): Field name, supports dot notation like 'user.profile.name'
|
|
1317
|
+
- `condition.value` ([string, any]): Array of operator and value
|
|
1318
|
+
- `condition.isReferenceValue` (boolean, optional): Whether it's a reference value
|
|
1319
|
+
|
|
1320
|
+
**Supported Operators**
|
|
1321
|
+
- `['=', value]`: Equals
|
|
1322
|
+
- `['!=', value]`: Not equals
|
|
1323
|
+
- `['>', value]`: Greater than
|
|
1324
|
+
- `['<', value]`: Less than
|
|
1325
|
+
- `['>=', value]`: Greater than or equal
|
|
1326
|
+
- `['<=', value]`: Less than or equal
|
|
1327
|
+
- `['like', pattern]`: Pattern matching
|
|
1328
|
+
- `['in', array]`: In array
|
|
1329
|
+
- `['between', [min, max]]`: In range
|
|
1330
|
+
- `['not', null]`: Not null
|
|
1331
|
+
|
|
1332
|
+
**Examples**
|
|
1333
|
+
```typescript
|
|
1334
|
+
// Basic condition
|
|
1335
|
+
const condition1 = MatchExp.atom({
|
|
1336
|
+
key: 'status',
|
|
1337
|
+
value: ['=', 'active']
|
|
1338
|
+
})
|
|
1339
|
+
|
|
1340
|
+
// Range query
|
|
1341
|
+
const condition2 = MatchExp.atom({
|
|
1342
|
+
key: 'age',
|
|
1343
|
+
value: ['between', [18, 65]]
|
|
1344
|
+
})
|
|
1345
|
+
|
|
1346
|
+
// Relational query
|
|
1347
|
+
const condition3 = MatchExp.atom({
|
|
1348
|
+
key: 'user.profile.city',
|
|
1349
|
+
value: ['=', 'Beijing']
|
|
1350
|
+
})
|
|
1351
|
+
|
|
1352
|
+
// Combined conditions
|
|
1353
|
+
const complexCondition = MatchExp.atom({
|
|
1354
|
+
key: 'status',
|
|
1355
|
+
value: ['=', 'active']
|
|
1356
|
+
}).and({
|
|
1357
|
+
key: 'age',
|
|
1358
|
+
value: ['>', 18]
|
|
1359
|
+
}).or({
|
|
1360
|
+
key: 'vip',
|
|
1361
|
+
value: ['=', true]
|
|
1362
|
+
})
|
|
1363
|
+
```
|
|
1364
|
+
|
|
1365
|
+
#### MatchExp.fromObject(condition: Object)
|
|
1366
|
+
Create query condition from object (all conditions connected with AND).
|
|
1367
|
+
|
|
1368
|
+
```typescript
|
|
1369
|
+
const condition = MatchExp.fromObject({
|
|
1370
|
+
status: 'active',
|
|
1371
|
+
age: 25,
|
|
1372
|
+
city: 'Beijing'
|
|
1373
|
+
})
|
|
1374
|
+
// Equivalent to: status='active' AND age=25 AND city='Beijing'
|
|
1375
|
+
```
|
|
1376
|
+
|
|
1377
|
+
### Attributive.create()
|
|
1378
|
+
|
|
1379
|
+
Create permission attributive for access control.
|
|
1380
|
+
|
|
1381
|
+
**Syntax**
|
|
1382
|
+
```typescript
|
|
1383
|
+
Attributive.create(config: AttributiveConfig): KlassInstance<typeof Attributive>
|
|
1384
|
+
```
|
|
1385
|
+
|
|
1386
|
+
**Parameters**
|
|
1387
|
+
- `config.name` (string, optional): Attributive name
|
|
1388
|
+
- `config.content` (function, required): Permission judgment function
|
|
1389
|
+
- `config.isRef` (boolean, optional): Whether it's a reference
|
|
1390
|
+
|
|
1391
|
+
**Examples**
|
|
1392
|
+
```typescript
|
|
1393
|
+
// Admin permission
|
|
1394
|
+
const AdminAttributive = Attributive.create({
|
|
1395
|
+
name: 'Admin',
|
|
1396
|
+
content: function(target, { user }) {
|
|
1397
|
+
return user.role === 'admin'
|
|
1398
|
+
}
|
|
1399
|
+
})
|
|
1400
|
+
|
|
1401
|
+
// Resource owner permission
|
|
1402
|
+
const OwnerAttributive = Attributive.create({
|
|
1403
|
+
name: 'Owner',
|
|
1404
|
+
content: function(target, { user }) {
|
|
1405
|
+
return target.userId === user.id
|
|
1406
|
+
}
|
|
1407
|
+
})
|
|
1408
|
+
|
|
1409
|
+
// Combined permissions (using BoolExp)
|
|
1410
|
+
const AdminOrOwnerAttributives = boolExpToAttributives(
|
|
1411
|
+
BoolExp.atom(AdminAttributive).or(OwnerAttributive)
|
|
1412
|
+
)
|
|
1413
|
+
```
|
|
1414
|
+
|
|
1415
|
+
### BoolExp
|
|
1416
|
+
|
|
1417
|
+
Boolean expression builder for constructing complex logical expressions.
|
|
1418
|
+
|
|
1419
|
+
#### BoolExp.atom(data: T)
|
|
1420
|
+
Create atomic expression.
|
|
1421
|
+
|
|
1422
|
+
```typescript
|
|
1423
|
+
const expr1 = BoolExp.atom({ condition: 'isActive' })
|
|
1424
|
+
const expr2 = BoolExp.atom({ condition: 'isAdmin' })
|
|
1425
|
+
|
|
1426
|
+
// Combined expression
|
|
1427
|
+
const combined = expr1.and(expr2).or({ condition: 'isOwner' })
|
|
1428
|
+
```
|
|
1429
|
+
|
|
1430
|
+
## Type Definitions
|
|
1431
|
+
|
|
1432
|
+
### Core Types
|
|
1433
|
+
|
|
1434
|
+
```typescript
|
|
1435
|
+
// Entity instance types
|
|
1436
|
+
type EntityInstance = KlassInstance<typeof Entity>
|
|
1437
|
+
type RelationInstance = KlassInstance<typeof Relation>
|
|
1438
|
+
type InteractionInstance = KlassInstance<typeof Interaction>
|
|
1439
|
+
type ActivityInstance = KlassInstance<typeof Activity>
|
|
1440
|
+
|
|
1441
|
+
// Interaction event arguments
|
|
1442
|
+
type InteractionEventArgs = {
|
|
1443
|
+
user: { id: string, [key: string]: any }
|
|
1444
|
+
payload?: { [key: string]: any }
|
|
1445
|
+
[key: string]: any
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
// Record mutation event
|
|
1449
|
+
type RecordMutationEvent = {
|
|
1450
|
+
recordName: string
|
|
1451
|
+
type: 'create' | 'update' | 'delete'
|
|
1452
|
+
record?: EntityIdRef & { [key: string]: any }
|
|
1453
|
+
oldRecord?: EntityIdRef & { [key: string]: any }
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
// Entity reference
|
|
1457
|
+
type EntityIdRef = {
|
|
1458
|
+
id: string
|
|
1459
|
+
_rowId?: string
|
|
1460
|
+
[key: string]: any
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
// Attribute query data
|
|
1464
|
+
type AttributeQueryData = (string | [string, { attributeQuery?: AttributeQueryData }])[]
|
|
1465
|
+
```
|
|
1466
|
+
|
|
1467
|
+
### Computation-Related Types
|
|
1468
|
+
|
|
1469
|
+
```typescript
|
|
1470
|
+
// Computation context
|
|
1471
|
+
type DataContext = {
|
|
1472
|
+
type: 'global' | 'entity' | 'relation' | 'property'
|
|
1473
|
+
id: string | Entity | Relation
|
|
1474
|
+
host?: Entity | Relation
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
// Computation dependency
|
|
1478
|
+
type DataDep = {
|
|
1479
|
+
type: 'records' | 'property'
|
|
1480
|
+
source?: Entity | Relation
|
|
1481
|
+
attributeQuery?: AttributeQueryData
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
// Computation result
|
|
1485
|
+
type ComputationResult = any
|
|
1486
|
+
type ComputationResultPatch = {
|
|
1487
|
+
type: 'insert' | 'update' | 'delete'
|
|
1488
|
+
data?: any
|
|
1489
|
+
affectedId?: string
|
|
1490
|
+
}
|
|
1491
|
+
```
|
|
1492
|
+
|
|
1493
|
+
## Usage Examples
|
|
1494
|
+
|
|
1495
|
+
### Complete Blog System Example
|
|
1496
|
+
|
|
1497
|
+
```typescript
|
|
1498
|
+
import { Entity, Property, Relation, Interaction, Activity, Controller } from 'interaqt'
|
|
1499
|
+
|
|
1500
|
+
// 1. Define entities
|
|
1501
|
+
const User = Entity.create({
|
|
1502
|
+
name: 'User',
|
|
1503
|
+
properties: [
|
|
1504
|
+
Property.create({ name: 'username', type: 'string' }),
|
|
1505
|
+
Property.create({ name: 'email', type: 'string' }),
|
|
1506
|
+
Property.create({
|
|
1507
|
+
name: 'postCount',
|
|
1508
|
+
type: 'number',
|
|
1509
|
+
computation: Count.create({ record: UserPostRelation })
|
|
1510
|
+
})
|
|
1511
|
+
]
|
|
1512
|
+
})
|
|
1513
|
+
|
|
1514
|
+
const Post = Entity.create({
|
|
1515
|
+
name: 'Post',
|
|
1516
|
+
properties: [
|
|
1517
|
+
Property.create({ name: 'title', type: 'string' }),
|
|
1518
|
+
Property.create({ name: 'content', type: 'string' }),
|
|
1519
|
+
Property.create({
|
|
1520
|
+
name: 'likeCount',
|
|
1521
|
+
type: 'number',
|
|
1522
|
+
computation: Count.create({ record: PostLikeRelation })
|
|
1523
|
+
})
|
|
1524
|
+
]
|
|
1525
|
+
})
|
|
1526
|
+
|
|
1527
|
+
// 2. Define relations
|
|
1528
|
+
const UserPostRelation = Relation.create({
|
|
1529
|
+
source: User,
|
|
1530
|
+
sourceProperty: 'posts',
|
|
1531
|
+
target: Post,
|
|
1532
|
+
targetProperty: 'author',
|
|
1533
|
+
type: '1:n'
|
|
1534
|
+
})
|
|
1535
|
+
|
|
1536
|
+
const PostLikeRelation = Relation.create({
|
|
1537
|
+
source: Post,
|
|
1538
|
+
sourceProperty: 'likes',
|
|
1539
|
+
target: User,
|
|
1540
|
+
targetProperty: 'likedPosts',
|
|
1541
|
+
type: 'n:n'
|
|
1542
|
+
})
|
|
1543
|
+
|
|
1544
|
+
// 3. Define interactions
|
|
1545
|
+
const CreatePostInteraction = Interaction.create({
|
|
1546
|
+
name: 'createPost',
|
|
1547
|
+
action: Action.create({ name: 'create' }),
|
|
1548
|
+
payload: Payload.create({
|
|
1549
|
+
items: [
|
|
1550
|
+
PayloadItem.create({
|
|
1551
|
+
name: 'postData',
|
|
1552
|
+
base: Post,
|
|
1553
|
+
required: true
|
|
1554
|
+
})
|
|
1555
|
+
]
|
|
1556
|
+
})
|
|
1557
|
+
})
|
|
1558
|
+
|
|
1559
|
+
const LikePostInteraction = Interaction.create({
|
|
1560
|
+
name: 'likePost',
|
|
1561
|
+
action: Action.create({ name: 'create' }),
|
|
1562
|
+
payload: Payload.create({
|
|
1563
|
+
items: [
|
|
1564
|
+
PayloadItem.create({
|
|
1565
|
+
name: 'post',
|
|
1566
|
+
base: Post,
|
|
1567
|
+
isRef: true,
|
|
1568
|
+
required: true
|
|
1569
|
+
})
|
|
1570
|
+
]
|
|
1571
|
+
})
|
|
1572
|
+
})
|
|
1573
|
+
|
|
1574
|
+
// 4. Create controller and initialize system
|
|
1575
|
+
const controller = new Controller({
|
|
1576
|
+
system, // System implementation
|
|
1577
|
+
entities: [User, Post], // Entities
|
|
1578
|
+
relations: [UserPostRelation, PostLikeRelation], // Relations
|
|
1579
|
+
activities: [], // Activities
|
|
1580
|
+
interactions: [CreatePostInteraction, LikePostInteraction] // Interactions
|
|
1581
|
+
})
|
|
1582
|
+
|
|
1583
|
+
await controller.setup(true)
|
|
1584
|
+
|
|
1585
|
+
// 5. Use APIs
|
|
1586
|
+
// Create post
|
|
1587
|
+
const result = await controller.callInteraction('createPost', {
|
|
1588
|
+
user: { id: 'user1' },
|
|
1589
|
+
payload: {
|
|
1590
|
+
postData: {
|
|
1591
|
+
title: 'Hello World',
|
|
1592
|
+
content: 'This is my first post!'
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
})
|
|
1596
|
+
|
|
1597
|
+
// Like post
|
|
1598
|
+
await controller.callInteraction('likePost', {
|
|
1599
|
+
user: { id: 'user2' },
|
|
1600
|
+
payload: {
|
|
1601
|
+
post: { id: result.recordId }
|
|
1602
|
+
}
|
|
1603
|
+
})
|
|
1604
|
+
```
|
|
1605
|
+
|
|
1606
|
+
This API reference documentation covers all core APIs of the interaqt framework, providing complete parameter descriptions and practical usage examples. Developers can quickly get started and deeply use various framework features based on this documentation.
|