interaqt 2.0.4 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/agent/agentspace/knowledge/generator/api-reference.md +2 -9
- package/agent/agentspace/knowledge/generator/permission-test-implementation.md +18 -0
- package/agent/agentspace/knowledge/generator/test-implementation.md +6 -5
- package/agent/agentspace/knowledge/usage/02-define-entities-properties.md +11 -6
- package/agent/agentspace/knowledge/usage/04-reactive-computations.md +6 -1
- package/agent/agentspace/knowledge/usage/05-interactions.md +51 -60
- package/agent/agentspace/knowledge/usage/06-attributive-permissions.md +54 -7
- package/agent/agentspace/knowledge/usage/07-payload-parameters.md +95 -72
- package/agent/agentspace/knowledge/usage/11-global-dictionaries.md +13 -14
- package/agent/agentspace/knowledge/usage/13-testing.md +2 -2
- package/agent/agentspace/knowledge/usage/14-api-reference.md +24 -66
- package/agent/agentspace/knowledge/usage/16-frontend-page-design-guide.md +3 -3
- package/agent/agentspace/knowledge/usage/18-api-exports-reference.md +2 -5
- package/agent/agentspace/knowledge/usage/README.md +2 -2
- package/agent/skill/interaqt-reference.md +6 -18
- package/dist/builtins/interaction/Interaction.d.ts +1 -22
- package/dist/builtins/interaction/Interaction.d.ts.map +1 -1
- package/dist/builtins/interaction/PayloadItem.d.ts +5 -14
- package/dist/builtins/interaction/PayloadItem.d.ts.map +1 -1
- package/dist/builtins/interaction/activity/ActivityCall.d.ts +2 -5
- package/dist/builtins/interaction/activity/ActivityCall.d.ts.map +1 -1
- package/dist/builtins/interaction/activity/ActivityManager.d.ts.map +1 -1
- package/dist/builtins/interaction/index.d.ts +0 -2
- package/dist/builtins/interaction/index.d.ts.map +1 -1
- package/dist/core/interfaces.d.ts +0 -12
- package/dist/core/interfaces.d.ts.map +1 -1
- package/dist/drivers/Mysql.d.ts +1 -0
- package/dist/drivers/Mysql.d.ts.map +1 -1
- package/dist/drivers/PGLite.d.ts +1 -0
- package/dist/drivers/PGLite.d.ts.map +1 -1
- package/dist/drivers/PostgreSQL.d.ts +1 -0
- package/dist/drivers/PostgreSQL.d.ts.map +1 -1
- package/dist/drivers/SQLite.d.ts +1 -0
- package/dist/drivers/SQLite.d.ts.map +1 -1
- package/dist/drivers.js +15 -5
- package/dist/drivers.js.map +1 -1
- package/dist/index.js +2262 -2451
- package/dist/index.js.map +1 -1
- package/dist/runtime/Controller.d.ts +11 -0
- package/dist/runtime/Controller.d.ts.map +1 -1
- package/dist/runtime/MonoSystem.d.ts.map +1 -1
- package/dist/runtime/Scheduler.d.ts +7 -0
- package/dist/runtime/Scheduler.d.ts.map +1 -1
- package/dist/runtime/System.d.ts +1 -0
- package/dist/runtime/System.d.ts.map +1 -1
- package/dist/runtime/computations/Custom.d.ts.map +1 -1
- package/dist/runtime/computations/Transform.d.ts.map +1 -1
- package/dist/runtime/computations/TransitionFinder.d.ts.map +1 -1
- package/dist/runtime/computations/aggregationTemplate.d.ts.map +1 -1
- package/dist/runtime/errors/ConditionErrors.d.ts +2 -5
- package/dist/runtime/errors/ConditionErrors.d.ts.map +1 -1
- package/dist/runtime/transaction.d.ts.map +1 -1
- package/dist/storage/erstorage/EntityToTableMap.d.ts.map +1 -1
- package/dist/storage/erstorage/MatchExp.d.ts.map +1 -1
- package/dist/storage/erstorage/Modifier.d.ts.map +1 -1
- package/dist/storage/erstorage/SchemaDialect.d.ts +1 -0
- package/dist/storage/erstorage/SchemaDialect.d.ts.map +1 -1
- package/dist/storage/erstorage/Setup.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/builtins/interaction/Attributive.d.ts +0 -91
- package/dist/builtins/interaction/Attributive.d.ts.map +0 -1
- package/dist/builtins/interaction/User.d.ts +0 -10
- package/dist/builtins/interaction/User.d.ts.map +0 -1
|
@@ -27,15 +27,16 @@ const CreatePost = Interaction.create({
|
|
|
27
27
|
```javascript
|
|
28
28
|
PayloadItem.create({
|
|
29
29
|
name: 'itemName', // Parameter name (required)
|
|
30
|
+
type: 'string', // Primitive type check (optional)
|
|
30
31
|
base: Entity, // Reference to Entity/Relation (optional)
|
|
31
32
|
isRef: true, // Whether it's a reference with id (default: false)
|
|
32
33
|
required: true, // Whether this parameter is required (default: false)
|
|
33
|
-
isCollection: true
|
|
34
|
-
attributives: Attributive, // Validation rules (optional)
|
|
35
|
-
itemRef: Entity // Reference to other concepts (optional)
|
|
34
|
+
isCollection: true // Whether it's an array (default: false)
|
|
36
35
|
})
|
|
37
36
|
```
|
|
38
37
|
|
|
38
|
+
To validate payload **contents** (e.g. "only published posts can be shared"), write the check as a `Condition` on the Interaction — see [Payload Validation with Conditions](#payload-validation-with-conditions) below.
|
|
39
|
+
|
|
39
40
|
## Basic Payload Usage
|
|
40
41
|
|
|
41
42
|
### Simple Parameters Without Entity Reference
|
|
@@ -82,7 +83,7 @@ await controller.callInteraction('SendMessage', {
|
|
|
82
83
|
**Important**: When `base` is not specified, the framework only checks:
|
|
83
84
|
- Whether required parameters are present
|
|
84
85
|
- Whether collection parameters are arrays
|
|
85
|
-
- No concept validation
|
|
86
|
+
- No concept validation is performed
|
|
86
87
|
|
|
87
88
|
### Parameters with Entity Reference
|
|
88
89
|
|
|
@@ -242,18 +243,23 @@ await controller.callInteraction('TagPosts', {
|
|
|
242
243
|
});
|
|
243
244
|
```
|
|
244
245
|
|
|
245
|
-
## Payload Validation with
|
|
246
|
+
## Payload Validation with Conditions
|
|
246
247
|
|
|
247
|
-
|
|
248
|
+
Payload content checks are written as `Condition`s on the Interaction. A condition callback receives the full event args (`user`, `payload`, `query`, `activityId`) with the Controller as `this`, so it can read the payload and query storage. It must return a strict boolean — any non-boolean result fails the check (fail-closed).
|
|
248
249
|
|
|
249
|
-
|
|
250
|
+
### Basic Condition Validation
|
|
250
251
|
|
|
251
252
|
```javascript
|
|
252
|
-
// Define validation
|
|
253
|
-
const
|
|
254
|
-
name: '
|
|
255
|
-
content: function(
|
|
256
|
-
|
|
253
|
+
// Define validation condition
|
|
254
|
+
const PublishedPostOnly = Condition.create({
|
|
255
|
+
name: 'PublishedPostOnly',
|
|
256
|
+
content: async function(event) {
|
|
257
|
+
const post = await this.system.storage.findOne('Post',
|
|
258
|
+
MatchExp.atom({ key: 'id', value: ['=', event.payload.post.id] }),
|
|
259
|
+
undefined,
|
|
260
|
+
['status']
|
|
261
|
+
);
|
|
262
|
+
return post?.status === 'published';
|
|
257
263
|
}
|
|
258
264
|
});
|
|
259
265
|
|
|
@@ -266,11 +272,11 @@ const SharePost = Interaction.create({
|
|
|
266
272
|
name: 'post',
|
|
267
273
|
base: Post,
|
|
268
274
|
isRef: true,
|
|
269
|
-
required: true
|
|
270
|
-
attributives: PublishedPost // Only published posts can be shared
|
|
275
|
+
required: true
|
|
271
276
|
})
|
|
272
277
|
]
|
|
273
|
-
})
|
|
278
|
+
}),
|
|
279
|
+
conditions: PublishedPostOnly // Only published posts can be shared
|
|
274
280
|
});
|
|
275
281
|
|
|
276
282
|
// This will succeed:
|
|
@@ -292,20 +298,30 @@ await controller.callInteraction('SharePost', {
|
|
|
292
298
|
|
|
293
299
|
### Complex Validation with BoolExp
|
|
294
300
|
|
|
295
|
-
You can combine multiple
|
|
301
|
+
You can combine multiple conditions using `Conditions.create` and BoolExp:
|
|
296
302
|
|
|
297
303
|
```javascript
|
|
298
|
-
const
|
|
299
|
-
name: '
|
|
300
|
-
content: function(
|
|
301
|
-
|
|
304
|
+
const TagsAreActive = Condition.create({
|
|
305
|
+
name: 'TagsAreActive',
|
|
306
|
+
content: async function(event) {
|
|
307
|
+
const tags = await this.system.storage.find('Tag',
|
|
308
|
+
MatchExp.atom({ key: 'id', value: ['in', event.payload.tags.map(tag => tag.id)] }),
|
|
309
|
+
undefined,
|
|
310
|
+
['isActive']
|
|
311
|
+
);
|
|
312
|
+
return tags.every(tag => tag.isActive === true);
|
|
302
313
|
}
|
|
303
314
|
});
|
|
304
315
|
|
|
305
|
-
const
|
|
306
|
-
name: '
|
|
307
|
-
content: function(
|
|
308
|
-
|
|
316
|
+
const TagsArePopular = Condition.create({
|
|
317
|
+
name: 'TagsArePopular',
|
|
318
|
+
content: async function(event) {
|
|
319
|
+
const tags = await this.system.storage.find('Tag',
|
|
320
|
+
MatchExp.atom({ key: 'id', value: ['in', event.payload.tags.map(tag => tag.id)] }),
|
|
321
|
+
undefined,
|
|
322
|
+
['usageCount']
|
|
323
|
+
);
|
|
324
|
+
return tags.every(tag => tag.usageCount > 100);
|
|
309
325
|
}
|
|
310
326
|
});
|
|
311
327
|
|
|
@@ -318,26 +334,32 @@ const AddTags = Interaction.create({
|
|
|
318
334
|
name: 'tags',
|
|
319
335
|
base: Tag,
|
|
320
336
|
isRef: true,
|
|
321
|
-
isCollection: true
|
|
322
|
-
// Tags must be both active AND popular
|
|
323
|
-
attributives: Attributives.create({
|
|
324
|
-
content: BoolExp.atom(ActiveTag).and(BoolExp.atom(PopularTag))
|
|
325
|
-
})
|
|
337
|
+
isCollection: true
|
|
326
338
|
})
|
|
327
339
|
]
|
|
340
|
+
}),
|
|
341
|
+
// Tags must be both active AND popular
|
|
342
|
+
conditions: Conditions.create({
|
|
343
|
+
content: BoolExp.atom(TagsAreActive).and(TagsArePopular)
|
|
328
344
|
})
|
|
329
345
|
});
|
|
330
346
|
```
|
|
331
347
|
|
|
332
348
|
### Validation for Collections
|
|
333
349
|
|
|
334
|
-
When
|
|
350
|
+
When a payload item is a collection, write the condition to check **every item** in the array:
|
|
335
351
|
|
|
336
352
|
```javascript
|
|
337
|
-
const
|
|
338
|
-
name: '
|
|
339
|
-
content: function(
|
|
340
|
-
|
|
353
|
+
const AllUsersVerified = Condition.create({
|
|
354
|
+
name: 'AllUsersVerified',
|
|
355
|
+
content: async function(event) {
|
|
356
|
+
const users = await this.system.storage.find('User',
|
|
357
|
+
MatchExp.atom({ key: 'id', value: ['in', event.payload.users.map(user => user.id)] }),
|
|
358
|
+
undefined,
|
|
359
|
+
['isVerified']
|
|
360
|
+
);
|
|
361
|
+
return users.length === event.payload.users.length &&
|
|
362
|
+
users.every(user => user.isVerified === true);
|
|
341
363
|
}
|
|
342
364
|
});
|
|
343
365
|
|
|
@@ -350,11 +372,11 @@ const InviteUsers = Interaction.create({
|
|
|
350
372
|
name: 'users',
|
|
351
373
|
base: User,
|
|
352
374
|
isRef: true,
|
|
353
|
-
isCollection: true
|
|
354
|
-
attributives: VerifiedUser // ALL users must be verified
|
|
375
|
+
isCollection: true
|
|
355
376
|
})
|
|
356
377
|
]
|
|
357
|
-
})
|
|
378
|
+
}),
|
|
379
|
+
conditions: AllUsersVerified // ALL users must be verified
|
|
358
380
|
});
|
|
359
381
|
|
|
360
382
|
// This will fail if ANY user in the array is not verified
|
|
@@ -386,9 +408,8 @@ Framework behavior:
|
|
|
386
408
|
- ✅ Checks if required parameter is present
|
|
387
409
|
- ✅ Checks if collection parameter is an array (when isCollection: true)
|
|
388
410
|
- ❌ Does NOT perform concept validation
|
|
389
|
-
- ❌ Does NOT check attributives
|
|
390
411
|
- ❌ Does NOT validate data structure
|
|
391
|
-
- **You must handle validation in your own logic**
|
|
412
|
+
- **You must handle validation in your own logic (e.g. through conditions)**
|
|
392
413
|
|
|
393
414
|
### When base IS specified
|
|
394
415
|
|
|
@@ -396,8 +417,7 @@ Framework behavior:
|
|
|
396
417
|
PayloadItem.create({
|
|
397
418
|
name: 'post',
|
|
398
419
|
base: Post, // Entity reference
|
|
399
|
-
isRef: true
|
|
400
|
-
attributives: PublishedPost
|
|
420
|
+
isRef: true
|
|
401
421
|
})
|
|
402
422
|
```
|
|
403
423
|
|
|
@@ -405,8 +425,8 @@ Framework behavior:
|
|
|
405
425
|
- ✅ Checks if required parameter is present
|
|
406
426
|
- ✅ Validates the data matches the concept (Entity/Relation)
|
|
407
427
|
- ✅ If isRef: true, verifies id exists and fetches full record
|
|
408
|
-
- ✅
|
|
409
|
-
-
|
|
428
|
+
- ✅ For collections, validates every item against the concept
|
|
429
|
+
- Content-level rules (e.g. "post must be published") are expressed as conditions on the Interaction
|
|
410
430
|
|
|
411
431
|
### Payload Storage Behavior
|
|
412
432
|
|
|
@@ -436,11 +456,11 @@ const CreateArticle = Interaction.create({
|
|
|
436
456
|
|
|
437
457
|
```javascript
|
|
438
458
|
// ✅ Good: Use base for entity-related data
|
|
459
|
+
// (content rules like "user must be active" go into the interaction's conditions)
|
|
439
460
|
PayloadItem.create({
|
|
440
461
|
name: 'targetUser',
|
|
441
462
|
base: User,
|
|
442
|
-
isRef: true
|
|
443
|
-
attributives: ActiveUser
|
|
463
|
+
isRef: true
|
|
444
464
|
})
|
|
445
465
|
|
|
446
466
|
// ✅ Good: Simple parameters without base
|
|
@@ -490,12 +510,17 @@ const UpdateUserProfile = Interaction.create({
|
|
|
490
510
|
### 3. Consistent Validation Patterns
|
|
491
511
|
|
|
492
512
|
```javascript
|
|
493
|
-
// Define reusable
|
|
494
|
-
const
|
|
495
|
-
name:
|
|
496
|
-
content: function(
|
|
497
|
-
|
|
498
|
-
|
|
513
|
+
// Define a reusable condition factory
|
|
514
|
+
const canBeEdited = (entityName, payloadKey) => Condition.create({
|
|
515
|
+
name: `${entityName}CanBeEdited`,
|
|
516
|
+
content: async function(event) {
|
|
517
|
+
const item = await this.system.storage.findOne(entityName,
|
|
518
|
+
MatchExp.atom({ key: 'id', value: ['=', event.payload[payloadKey].id] }),
|
|
519
|
+
undefined,
|
|
520
|
+
['status', 'createdBy']
|
|
521
|
+
);
|
|
522
|
+
return !!item && item.status !== 'locked' &&
|
|
523
|
+
item.createdBy === event.user.id;
|
|
499
524
|
}
|
|
500
525
|
});
|
|
501
526
|
|
|
@@ -506,11 +531,11 @@ const EditPost = Interaction.create({
|
|
|
506
531
|
PayloadItem.create({
|
|
507
532
|
name: 'post',
|
|
508
533
|
base: Post,
|
|
509
|
-
isRef: true
|
|
510
|
-
attributives: CanBeEdited
|
|
534
|
+
isRef: true
|
|
511
535
|
})
|
|
512
536
|
]
|
|
513
|
-
})
|
|
537
|
+
}),
|
|
538
|
+
conditions: canBeEdited('Post', 'post')
|
|
514
539
|
});
|
|
515
540
|
|
|
516
541
|
const EditComment = Interaction.create({
|
|
@@ -519,11 +544,11 @@ const EditComment = Interaction.create({
|
|
|
519
544
|
PayloadItem.create({
|
|
520
545
|
name: 'comment',
|
|
521
546
|
base: Comment,
|
|
522
|
-
isRef: true
|
|
523
|
-
attributives: CanBeEdited
|
|
547
|
+
isRef: true
|
|
524
548
|
})
|
|
525
549
|
]
|
|
526
|
-
})
|
|
550
|
+
}),
|
|
551
|
+
conditions: canBeEdited('Comment', 'comment')
|
|
527
552
|
});
|
|
528
553
|
```
|
|
529
554
|
|
|
@@ -531,17 +556,15 @@ const EditComment = Interaction.create({
|
|
|
531
556
|
|
|
532
557
|
```javascript
|
|
533
558
|
// In your application code
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
if (error
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
console.error('Cannot share: Post is not published');
|
|
544
|
-
}
|
|
559
|
+
const result = await controller.callInteraction('SharePost', {
|
|
560
|
+
user: currentUser,
|
|
561
|
+
payload: { post: { id: postId } }
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
if (result.error) {
|
|
565
|
+
if (result.error.type === 'condition check failed') {
|
|
566
|
+
// Handle validation failure; result.error.error.data.name is the failing condition
|
|
567
|
+
console.error('Cannot share: Post is not published');
|
|
545
568
|
}
|
|
546
569
|
}
|
|
547
570
|
```
|
|
@@ -579,15 +602,15 @@ NOT setting base property maintains the same functionality but avoids the resolu
|
|
|
579
602
|
|
|
580
603
|
The Payload system in interaqt provides a powerful way to:
|
|
581
604
|
- Define structured interaction parameters
|
|
582
|
-
- Enforce validation rules through
|
|
605
|
+
- Enforce content validation rules through conditions
|
|
583
606
|
- Ensure type safety with entity references
|
|
584
607
|
- Handle both simple data and complex entity relationships
|
|
585
608
|
|
|
586
609
|
Key points to remember:
|
|
587
|
-
- **With base**: Framework handles validation and
|
|
610
|
+
- **With base**: Framework handles concept validation and reference resolution
|
|
588
611
|
- **Without base**: You handle validation in your own logic
|
|
589
612
|
- **isRef**: Distinguishes between references and new data
|
|
590
|
-
- **
|
|
591
|
-
- **isCollection**: Validates every item in arrays
|
|
613
|
+
- **conditions**: Payload content rules live on the Interaction, not on PayloadItem
|
|
614
|
+
- **isCollection**: Validates every item in arrays against the concept
|
|
592
615
|
|
|
593
616
|
By properly using Payload and PayloadItem, you can build robust, type-safe interactions that validate data at the framework level, reducing the need for manual validation code and improving system reliability.
|
|
@@ -470,6 +470,16 @@ async function setDefaultConfig() {
|
|
|
470
470
|
### 10.4.3 Conditional Logic Based on Dictionaries
|
|
471
471
|
|
|
472
472
|
```typescript
|
|
473
|
+
// Use Condition to control permissions based on dictionary values
|
|
474
|
+
const MaxUsersNotReached = Condition.create({
|
|
475
|
+
name: 'MaxUsersNotReached',
|
|
476
|
+
content: async function(this: Controller, event) {
|
|
477
|
+
const currentUserCount = await this.system.storage.get('state', 'totalUsers');
|
|
478
|
+
const maxUsers = await this.system.storage.get('state', 'maxUsers');
|
|
479
|
+
return currentUserCount < maxUsers;
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
|
|
473
483
|
// Using dictionary values for conditional logic in interactions
|
|
474
484
|
const createUserInteraction = Interaction.create({
|
|
475
485
|
name: 'createUser',
|
|
@@ -481,21 +491,10 @@ const createUserInteraction = Interaction.create({
|
|
|
481
491
|
base: userEntity
|
|
482
492
|
})
|
|
483
493
|
]
|
|
484
|
-
})
|
|
494
|
+
}),
|
|
495
|
+
// Dictionary-based permission control
|
|
496
|
+
conditions: MaxUsersNotReached
|
|
485
497
|
});
|
|
486
|
-
|
|
487
|
-
// Use Attributive to control permissions based on dictionary values
|
|
488
|
-
const MaxUsersReachedAttributive = Attributive.create({
|
|
489
|
-
name: 'MaxUsersNotReached',
|
|
490
|
-
content: async function(context: any) {
|
|
491
|
-
const currentUserCount = await context.system.storage.get('state', 'totalUsers');
|
|
492
|
-
const maxUsers = await context.system.storage.get('state', 'maxUsers');
|
|
493
|
-
return currentUserCount < maxUsers;
|
|
494
|
-
}
|
|
495
|
-
});
|
|
496
|
-
|
|
497
|
-
// Add dictionary-based permission control to interaction
|
|
498
|
-
createUserInteraction.attributives = [MaxUsersReachedAttributive];
|
|
499
498
|
```
|
|
500
499
|
|
|
501
500
|
### 10.4.4 Combining Dictionaries with Computations
|
|
@@ -645,11 +645,11 @@ describe('Approval Process Activity', () => {
|
|
|
645
645
|
});
|
|
646
646
|
```
|
|
647
647
|
|
|
648
|
-
## 12.3 Testing Permissions
|
|
648
|
+
## 12.3 Testing Permissions (Conditions)
|
|
649
649
|
|
|
650
650
|
> **Important: Correct Error Handling Approach**
|
|
651
651
|
>
|
|
652
|
-
> The interaqt framework automatically catches all errors (including
|
|
652
|
+
> The interaqt framework automatically catches all errors (including condition check failures, insufficient permissions, etc.) and returns error information through the `error` field in the return value. The framework **does not throw uncaught exceptions**.
|
|
653
653
|
>
|
|
654
654
|
> Therefore, when writing tests:
|
|
655
655
|
> - ✅ **Correct approach**: Check the `error` field in the return value
|
|
@@ -1124,17 +1124,18 @@ PayloadItem.create(config: PayloadItemConfig): KlassInstance<typeof PayloadItem>
|
|
|
1124
1124
|
|
|
1125
1125
|
**Parameters**
|
|
1126
1126
|
- `config.name` (string, required): Parameter name
|
|
1127
|
-
- `config.
|
|
1127
|
+
- `config.type` (string, optional): Primitive type check ('string', 'number', 'boolean', 'object')
|
|
1128
|
+
- `config.base` (Entity|Relation, optional): Parameter entity type, only needed when isRef is true
|
|
1128
1129
|
- `config.isRef` (boolean, optional): Whether it's a reference type, defaults to false
|
|
1129
1130
|
- `config.required` (boolean, optional): Whether it's required, defaults to false
|
|
1130
1131
|
- `config.isCollection` (boolean, optional): Whether it's a collection type, defaults to false
|
|
1131
|
-
|
|
1132
|
-
|
|
1132
|
+
|
|
1133
|
+
Payload **content** validation (e.g. "post must have title and content") is expressed as `conditions` on the Interaction — see Condition.create().
|
|
1133
1134
|
|
|
1134
1135
|
**Examples**
|
|
1135
1136
|
```typescript
|
|
1136
1137
|
// Reference existing user
|
|
1137
|
-
const
|
|
1138
|
+
const userItem = PayloadItem.create({
|
|
1138
1139
|
name: 'user',
|
|
1139
1140
|
base: User,
|
|
1140
1141
|
isRef: true,
|
|
@@ -1145,13 +1146,15 @@ const userRef = PayloadItem.create({
|
|
|
1145
1146
|
const postData = PayloadItem.create({
|
|
1146
1147
|
name: 'postData',
|
|
1147
1148
|
base: Post,
|
|
1148
|
-
required: true
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1149
|
+
required: true
|
|
1150
|
+
})
|
|
1151
|
+
|
|
1152
|
+
// Content validation goes on the Interaction's conditions
|
|
1153
|
+
const ValidPost = Condition.create({
|
|
1154
|
+
name: 'ValidPost',
|
|
1155
|
+
content: async function(event) {
|
|
1156
|
+
return !!(event.payload.postData.title && event.payload.postData.content)
|
|
1157
|
+
}
|
|
1155
1158
|
})
|
|
1156
1159
|
|
|
1157
1160
|
// Collection type reference
|
|
@@ -1159,19 +1162,7 @@ const reviewersItem = PayloadItem.create({
|
|
|
1159
1162
|
name: 'reviewers',
|
|
1160
1163
|
base: User,
|
|
1161
1164
|
isRef: true,
|
|
1162
|
-
isCollection: true
|
|
1163
|
-
attributives: Attributives.create({
|
|
1164
|
-
content: BoolAtomData.create({data: ReviewerAttr, type: 'atom'})
|
|
1165
|
-
})
|
|
1166
|
-
})
|
|
1167
|
-
|
|
1168
|
-
// Activity item reference
|
|
1169
|
-
const activityItem = PayloadItem.create({
|
|
1170
|
-
name: 'to',
|
|
1171
|
-
base: User,
|
|
1172
|
-
isRef: true,
|
|
1173
|
-
attributives: boolExpToAttributives(BoolExp.atom(OtherAttr)),
|
|
1174
|
-
itemRef: userRefB
|
|
1165
|
+
isCollection: true
|
|
1175
1166
|
})
|
|
1176
1167
|
```
|
|
1177
1168
|
|
|
@@ -1244,7 +1235,7 @@ const ApprovalTransfer = Transfer.create({
|
|
|
1244
1235
|
|
|
1245
1236
|
### Condition.create()
|
|
1246
1237
|
|
|
1247
|
-
Create
|
|
1238
|
+
Create an execution condition — the single guard concept for interactions (permission checks, payload content checks, activity user binding).
|
|
1248
1239
|
|
|
1249
1240
|
**Syntax**
|
|
1250
1241
|
```typescript
|
|
@@ -1253,16 +1244,21 @@ Condition.create(config: ConditionConfig): KlassInstance<typeof Condition>
|
|
|
1253
1244
|
|
|
1254
1245
|
**Parameters**
|
|
1255
1246
|
- `config.name` (string, required): Condition name
|
|
1256
|
-
- `config.content` (function, required): Condition judgment function
|
|
1247
|
+
- `config.content` (function, required): Condition judgment function. Called with the Controller as `this` and the full event args (`user`, `payload`, `query`, `activityId`). Must return a strict boolean — any non-boolean result fails the check (fail-closed).
|
|
1257
1248
|
|
|
1258
1249
|
**Examples**
|
|
1259
1250
|
```typescript
|
|
1260
1251
|
const OrderValueCondition = Condition.create({
|
|
1261
1252
|
name: 'highValueOrder',
|
|
1262
|
-
content: function(
|
|
1263
|
-
return order.totalAmount > 1000
|
|
1253
|
+
content: async function(event) {
|
|
1254
|
+
return event.payload.order.totalAmount > 1000
|
|
1264
1255
|
}
|
|
1265
1256
|
})
|
|
1257
|
+
|
|
1258
|
+
// Combine multiple conditions with Conditions.create + BoolExp
|
|
1259
|
+
const AdminHighValueOrder = Conditions.create({
|
|
1260
|
+
content: BoolExp.atom(AdminOnly).and(OrderValueCondition)
|
|
1261
|
+
})
|
|
1266
1262
|
```
|
|
1267
1263
|
|
|
1268
1264
|
## 13.5 System-Related APIs
|
|
@@ -1476,44 +1472,6 @@ const condition = MatchExp.fromObject({
|
|
|
1476
1472
|
// Equivalent to: status='active' AND age=25 AND city='Beijing'
|
|
1477
1473
|
```
|
|
1478
1474
|
|
|
1479
|
-
### Attributive.create()
|
|
1480
|
-
|
|
1481
|
-
Create permission attributive for access control.
|
|
1482
|
-
|
|
1483
|
-
**Syntax**
|
|
1484
|
-
```typescript
|
|
1485
|
-
Attributive.create(config: AttributiveConfig): KlassInstance<typeof Attributive>
|
|
1486
|
-
```
|
|
1487
|
-
|
|
1488
|
-
**Parameters**
|
|
1489
|
-
- `config.name` (string, optional): Attributive name
|
|
1490
|
-
- `config.content` (function, required): Permission judgment function
|
|
1491
|
-
- `config.isRef` (boolean, optional): Whether it's a reference
|
|
1492
|
-
|
|
1493
|
-
**Examples**
|
|
1494
|
-
```typescript
|
|
1495
|
-
// Admin permission
|
|
1496
|
-
const AdminAttributive = Attributive.create({
|
|
1497
|
-
name: 'Admin',
|
|
1498
|
-
content: function(target, { user }) {
|
|
1499
|
-
return user.role === 'admin'
|
|
1500
|
-
}
|
|
1501
|
-
})
|
|
1502
|
-
|
|
1503
|
-
// Resource owner permission
|
|
1504
|
-
const OwnerAttributive = Attributive.create({
|
|
1505
|
-
name: 'Owner',
|
|
1506
|
-
content: function(target, { user }) {
|
|
1507
|
-
return target.userId === user.id
|
|
1508
|
-
}
|
|
1509
|
-
})
|
|
1510
|
-
|
|
1511
|
-
// Combined permissions (using BoolExp)
|
|
1512
|
-
const AdminOrOwnerAttributives = boolExpToAttributives(
|
|
1513
|
-
BoolExp.atom(AdminAttributive).or(OwnerAttributive)
|
|
1514
|
-
)
|
|
1515
|
-
```
|
|
1516
|
-
|
|
1517
1475
|
### BoolExp
|
|
1518
1476
|
|
|
1519
1477
|
Boolean expression builder for constructing complex logical expressions.
|
|
@@ -14,7 +14,7 @@ This document guides how to systematically design frontend page architecture and
|
|
|
14
14
|
### 2. Reactive Data Display
|
|
15
15
|
- **Reactive Computations**: All properties with `computation` should be displayed in real-time in the interface
|
|
16
16
|
- **State-Aware**: Dynamically display different operation options based on entity state
|
|
17
|
-
- **Permission Control**: Control interface element visibility based on
|
|
17
|
+
- **Permission Control**: Control interface element visibility based on Condition definitions
|
|
18
18
|
|
|
19
19
|
### 3. User Experience Consistency
|
|
20
20
|
- **Operation Feedback**: Every interaction operation should have clear feedback
|
|
@@ -191,7 +191,7 @@ Operation History Area (Optional)
|
|
|
191
191
|
```
|
|
192
192
|
|
|
193
193
|
#### 2.2 Design Points
|
|
194
|
-
- **Permission Control**: Dynamically display action buttons based on
|
|
194
|
+
- **Permission Control**: Dynamically display action buttons based on Condition
|
|
195
195
|
- **State Awareness**: Display different operation options based on entity state
|
|
196
196
|
- **Association Display**: Reasonably display associated entities, avoid information overload
|
|
197
197
|
- **Operation Feedback**: Clear operation result notifications
|
|
@@ -340,7 +340,7 @@ For properties containing `computation`:
|
|
|
340
340
|
- **Transition Hints**: Display possible state transition operations
|
|
341
341
|
|
|
342
342
|
#### 2.2 Conditional Operation Display
|
|
343
|
-
Based on permission control defined by
|
|
343
|
+
Based on permission control defined by Condition:
|
|
344
344
|
- **Dynamic Buttons**: Dynamically display action buttons based on permissions
|
|
345
345
|
- **Disabled States**: Show disabled operations when conditions aren't met
|
|
346
346
|
- **Permission Hints**: Explain why certain operations are unavailable
|
|
@@ -40,9 +40,7 @@ import {
|
|
|
40
40
|
RealTime,
|
|
41
41
|
MathResolver,
|
|
42
42
|
|
|
43
|
-
//
|
|
44
|
-
Attributive,
|
|
45
|
-
Attributives,
|
|
43
|
+
// Conditions
|
|
46
44
|
Condition,
|
|
47
45
|
Conditions,
|
|
48
46
|
|
|
@@ -95,8 +93,7 @@ import {
|
|
|
95
93
|
RelationBasedEvery, // Only 'Every' exists
|
|
96
94
|
InteractionEvent, // Correct name is 'InteractionEventEntity'
|
|
97
95
|
FilteredEntity, // Created via Entity.create with baseEntity
|
|
98
|
-
SideEffect
|
|
99
|
-
DataAttributive // Use Attributive for all purposes
|
|
96
|
+
SideEffect // Not a direct export
|
|
100
97
|
} from 'interaqt';
|
|
101
98
|
```
|
|
102
99
|
|
|
@@ -22,7 +22,7 @@ Master declarative data computation.
|
|
|
22
22
|
### 6. 🎮 [Interactions](./05-interactions.md)
|
|
23
23
|
Learn how to define user interactions (Remember: Action is just an identifier!).
|
|
24
24
|
|
|
25
|
-
### 7. 🔐 [
|
|
25
|
+
### 7. 🔐 [Permission Control (Conditions)](./06-attributive-permissions.md)
|
|
26
26
|
Understand how to control access permissions.
|
|
27
27
|
|
|
28
28
|
### 8. 📦 [Payload Parameters](./07-payload-parameters.md)
|
|
@@ -124,7 +124,7 @@ Remember: **Stop thinking "how to do", start thinking "what is"**!
|
|
|
124
124
|
### Reactive System
|
|
125
125
|
5. [Reactive Computations](./04-reactive-computations.md) - Declarative data computation
|
|
126
126
|
6. [Interactions](./05-interactions.md) - User interaction definition
|
|
127
|
-
7. [
|
|
127
|
+
7. [Permission Control (Conditions)](./06-attributive-permissions.md) - Access control
|
|
128
128
|
8. [Payload Parameters](./07-payload-parameters.md) - Interaction parameter validation
|
|
129
129
|
9. [Activities](./08-activities.md) - Complex business processes
|
|
130
130
|
|
|
@@ -125,14 +125,15 @@ Payload.create(args: {
|
|
|
125
125
|
PayloadItem.create(args: {
|
|
126
126
|
name: string // Parameter name
|
|
127
127
|
type: string // Required: data type
|
|
128
|
-
base?: EntityInstance
|
|
128
|
+
base?: EntityInstance | RelationInstance // Entity/Relation reference for validation
|
|
129
129
|
isRef?: boolean // true = reference by ID to existing entity
|
|
130
130
|
required?: boolean // true = mandatory parameter
|
|
131
131
|
isCollection?: boolean // true = array of items
|
|
132
|
-
itemRef?: AttributiveInstance | EntityInstance // Reference to entities defined in other interactions (for Activity)
|
|
133
132
|
}): PayloadItemInstance
|
|
134
133
|
```
|
|
135
134
|
|
|
135
|
+
Payload content validation is expressed as `conditions` on the Interaction (see Condition.create).
|
|
136
|
+
|
|
136
137
|
---
|
|
137
138
|
|
|
138
139
|
## Count.create
|
|
@@ -530,19 +531,6 @@ MatchExp.atom({ key: 'author.name', value: ['=', 'Alice'] })
|
|
|
530
531
|
|
|
531
532
|
---
|
|
532
533
|
|
|
533
|
-
## Attributive.create
|
|
534
|
-
|
|
535
|
-
```typescript
|
|
536
|
-
Attributive.create(args: {
|
|
537
|
-
name?: string
|
|
538
|
-
content: (record: any, eventArgs: any) => boolean
|
|
539
|
-
}): AttributiveInstance
|
|
540
|
-
```
|
|
541
|
-
|
|
542
|
-
Used on Interaction `userAttributives` to validate user context.
|
|
543
|
-
|
|
544
|
-
---
|
|
545
|
-
|
|
546
534
|
## BoolExp
|
|
547
535
|
|
|
548
536
|
```typescript
|
|
@@ -551,7 +539,7 @@ boolExp.and(other: BoolExp | T): BoolExp
|
|
|
551
539
|
boolExp.or(other: BoolExp | T): BoolExp
|
|
552
540
|
```
|
|
553
541
|
|
|
554
|
-
Combines multiple
|
|
542
|
+
Combines multiple Conditions, match atoms, or other expressions for complex logic.
|
|
555
543
|
|
|
556
544
|
---
|
|
557
545
|
|
|
@@ -568,8 +556,9 @@ Conditions.create(args: {
|
|
|
568
556
|
}): ConditionsInstance
|
|
569
557
|
```
|
|
570
558
|
|
|
571
|
-
- `content` returns `true` to allow, `false` to reject
|
|
559
|
+
- `content` returns `true` to allow, `false` to reject; any non-boolean result fails the check (fail-closed)
|
|
572
560
|
- `this` is bound to Controller — can access `this.system.storage`
|
|
561
|
+
- `event` contains `user`, `payload`, `query`, `activityId` — use it for user checks, payload content checks, and activity user binding
|
|
573
562
|
- Failed conditions return `{ error: { type: 'condition check failed' } }`
|
|
574
563
|
|
|
575
564
|
---
|
|
@@ -652,7 +641,6 @@ import {
|
|
|
652
641
|
Expression, Inequality, Equation, MathResolver,
|
|
653
642
|
|
|
654
643
|
// Validation & conditions
|
|
655
|
-
Attributive, Attributives,
|
|
656
644
|
Condition, Conditions,
|
|
657
645
|
|
|
658
646
|
// Data policy
|