interaqt 2.0.0 → 2.0.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/README.md +1 -0
- package/agent/agentspace/knowledge/generator/api-reference.md +28 -5
- package/agent/agentspace/knowledge/generator/computation-implementation.md +61 -14
- package/agent/agentspace/knowledge/usage/00-mindset-shift.md +2 -0
- package/agent/agentspace/knowledge/usage/04-reactive-computations.md +158 -101
- package/agent/agentspace/knowledge/usage/05-interactions.md +67 -22
- package/agent/agentspace/knowledge/usage/08-activities.md +10 -0
- package/agent/agentspace/knowledge/usage/09-filtered-entities.md +1 -0
- package/agent/agentspace/knowledge/usage/13-testing.md +48 -97
- package/agent/agentspace/knowledge/usage/14-api-reference.md +22 -5
- package/agent/agentspace/knowledge/usage/15-entity-crud-patterns.md +89 -28
- package/agent/agentspace/knowledge/usage/19-common-anti-patterns.md +11 -2
- package/dist/builtins/interaction/Action.d.ts.map +1 -1
- package/dist/builtins/interaction/Activity.d.ts.map +1 -1
- package/dist/builtins/interaction/Attributive.d.ts.map +1 -1
- package/dist/builtins/interaction/Condition.d.ts.map +1 -1
- package/dist/builtins/interaction/Conditions.d.ts.map +1 -1
- package/dist/builtins/interaction/Data.d.ts.map +1 -1
- package/dist/builtins/interaction/Event.d.ts.map +1 -1
- package/dist/builtins/interaction/Gateway.d.ts.map +1 -1
- package/dist/builtins/interaction/Interaction.d.ts.map +1 -1
- package/dist/builtins/interaction/Payload.d.ts.map +1 -1
- package/dist/builtins/interaction/PayloadItem.d.ts.map +1 -1
- package/dist/builtins/interaction/activity/ActivityCall.d.ts.map +1 -1
- package/dist/core/EventSource.d.ts +37 -0
- package/dist/core/EventSource.d.ts.map +1 -1
- package/dist/core/init.d.ts.map +1 -1
- package/dist/core/utils.d.ts.map +1 -1
- package/dist/drivers/Mysql.d.ts +4 -3
- package/dist/drivers/Mysql.d.ts.map +1 -1
- package/dist/drivers/PGLite.d.ts +2 -2
- package/dist/drivers/PGLite.d.ts.map +1 -1
- package/dist/drivers/PostgreSQL.d.ts +2 -2
- package/dist/drivers/PostgreSQL.d.ts.map +1 -1
- package/dist/drivers/SQLite.d.ts +3 -3
- package/dist/drivers/SQLite.d.ts.map +1 -1
- package/dist/drivers.d.ts +2 -0
- package/dist/drivers.js +963 -0
- package/dist/drivers.js.map +1 -0
- package/dist/index.js +4220 -4090
- package/dist/index.js.map +1 -1
- package/dist/runtime/ComputationSourceMap.d.ts +11 -0
- package/dist/runtime/ComputationSourceMap.d.ts.map +1 -1
- package/dist/runtime/Controller.d.ts.map +1 -1
- package/dist/runtime/Scheduler.d.ts +21 -3
- 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/Any.d.ts +17 -52
- package/dist/runtime/computations/Any.d.ts.map +1 -1
- package/dist/runtime/computations/Average.d.ts +15 -49
- package/dist/runtime/computations/Average.d.ts.map +1 -1
- package/dist/runtime/computations/Computation.d.ts +8 -0
- package/dist/runtime/computations/Computation.d.ts.map +1 -1
- package/dist/runtime/computations/Count.d.ts +21 -64
- package/dist/runtime/computations/Count.d.ts.map +1 -1
- package/dist/runtime/computations/Custom.d.ts.map +1 -1
- package/dist/runtime/computations/Every.d.ts +18 -52
- package/dist/runtime/computations/Every.d.ts.map +1 -1
- package/dist/runtime/computations/StateMachine.d.ts +2 -2
- package/dist/runtime/computations/StateMachine.d.ts.map +1 -1
- package/dist/runtime/computations/Summation.d.ts +15 -49
- package/dist/runtime/computations/Summation.d.ts.map +1 -1
- package/dist/runtime/computations/Transform.d.ts.map +1 -1
- package/dist/runtime/computations/WeightedSummation.d.ts +18 -60
- package/dist/runtime/computations/WeightedSummation.d.ts.map +1 -1
- package/dist/runtime/computations/aggregationTemplate.d.ts +163 -0
- package/dist/runtime/computations/aggregationTemplate.d.ts.map +1 -0
- package/dist/runtime/migration.d.ts.map +1 -1
- package/dist/storage/erstorage/AttributeQuery.d.ts.map +1 -1
- package/dist/storage/erstorage/CreationExecutor.d.ts.map +1 -1
- package/dist/storage/erstorage/DeletionExecutor.d.ts.map +1 -1
- package/dist/storage/erstorage/EntityQueryHandle.d.ts +6 -0
- package/dist/storage/erstorage/EntityQueryHandle.d.ts.map +1 -1
- package/dist/storage/erstorage/EntityToTableMap.d.ts +1 -0
- package/dist/storage/erstorage/EntityToTableMap.d.ts.map +1 -1
- package/dist/storage/erstorage/LinkInfo.d.ts +1 -0
- package/dist/storage/erstorage/LinkInfo.d.ts.map +1 -1
- package/dist/storage/erstorage/MatchExp.d.ts +3 -2
- package/dist/storage/erstorage/MatchExp.d.ts.map +1 -1
- package/dist/storage/erstorage/MergedItemProcessor.d.ts +5 -0
- package/dist/storage/erstorage/MergedItemProcessor.d.ts.map +1 -1
- package/dist/storage/erstorage/NewRecordData.d.ts +1 -0
- package/dist/storage/erstorage/NewRecordData.d.ts.map +1 -1
- package/dist/storage/erstorage/QueryExecutor.d.ts.map +1 -1
- package/dist/storage/erstorage/RecordQueryAgent.d.ts +11 -0
- package/dist/storage/erstorage/RecordQueryAgent.d.ts.map +1 -1
- package/dist/storage/erstorage/Setup.d.ts +24 -0
- package/dist/storage/erstorage/Setup.d.ts.map +1 -1
- package/dist/storage/erstorage/UpdateExecutor.d.ts.map +1 -1
- package/package.json +5 -1
|
@@ -629,8 +629,12 @@ const User = Entity.create({
|
|
|
629
629
|
StateTransfer.create({
|
|
630
630
|
current: NameUpdatedState,
|
|
631
631
|
next: NameUpdatedState,
|
|
632
|
-
trigger:
|
|
633
|
-
|
|
632
|
+
trigger: {
|
|
633
|
+
recordName: InteractionEventEntity.name,
|
|
634
|
+
type: 'create',
|
|
635
|
+
record: { interactionName: UpdateUserProfile.name }
|
|
636
|
+
},
|
|
637
|
+
computeTarget: (event) => ({ id: event.record.payload.userId })
|
|
634
638
|
})
|
|
635
639
|
],
|
|
636
640
|
initialState: NameUpdatedState
|
|
@@ -645,8 +649,12 @@ const User = Entity.create({
|
|
|
645
649
|
StateTransfer.create({
|
|
646
650
|
current: BioUpdatedState,
|
|
647
651
|
next: BioUpdatedState,
|
|
648
|
-
trigger:
|
|
649
|
-
|
|
652
|
+
trigger: {
|
|
653
|
+
recordName: InteractionEventEntity.name,
|
|
654
|
+
type: 'create',
|
|
655
|
+
record: { interactionName: UpdateUserProfile.name }
|
|
656
|
+
},
|
|
657
|
+
computeTarget: (event) => ({ id: event.record.payload.userId })
|
|
650
658
|
})
|
|
651
659
|
],
|
|
652
660
|
initialState: BioUpdatedState
|
|
@@ -704,20 +712,32 @@ const Post = Entity.create({
|
|
|
704
712
|
StateTransfer.create({
|
|
705
713
|
current: draftState,
|
|
706
714
|
next: publishedState,
|
|
707
|
-
trigger:
|
|
708
|
-
|
|
715
|
+
trigger: {
|
|
716
|
+
recordName: InteractionEventEntity.name,
|
|
717
|
+
type: 'create',
|
|
718
|
+
record: { interactionName: PublishPost.name }
|
|
719
|
+
},
|
|
720
|
+
computeTarget: (event) => ({ id: event.record.payload.postId })
|
|
709
721
|
}),
|
|
710
722
|
StateTransfer.create({
|
|
711
723
|
current: publishedState,
|
|
712
724
|
next: deletedState,
|
|
713
|
-
trigger:
|
|
714
|
-
|
|
725
|
+
trigger: {
|
|
726
|
+
recordName: InteractionEventEntity.name,
|
|
727
|
+
type: 'create',
|
|
728
|
+
record: { interactionName: DeletePost.name }
|
|
729
|
+
},
|
|
730
|
+
computeTarget: (event) => ({ id: event.record.payload.postId })
|
|
715
731
|
}),
|
|
716
732
|
StateTransfer.create({
|
|
717
733
|
current: draftState,
|
|
718
734
|
next: deletedState,
|
|
719
|
-
trigger:
|
|
720
|
-
|
|
735
|
+
trigger: {
|
|
736
|
+
recordName: InteractionEventEntity.name,
|
|
737
|
+
type: 'create',
|
|
738
|
+
record: { interactionName: DeletePost.name }
|
|
739
|
+
},
|
|
740
|
+
computeTarget: (event) => ({ id: event.record.payload.postId })
|
|
721
741
|
})
|
|
722
742
|
]
|
|
723
743
|
})
|
|
@@ -821,26 +841,42 @@ const Order = Entity.create({
|
|
|
821
841
|
StateTransfer.create({
|
|
822
842
|
current: pendingState,
|
|
823
843
|
next: confirmedState,
|
|
824
|
-
trigger:
|
|
825
|
-
|
|
844
|
+
trigger: {
|
|
845
|
+
recordName: InteractionEventEntity.name,
|
|
846
|
+
type: 'create',
|
|
847
|
+
record: { interactionName: ConfirmPayment.name }
|
|
848
|
+
},
|
|
849
|
+
computeTarget: (event) => ({ id: event.record.payload.orderId })
|
|
826
850
|
}),
|
|
827
851
|
StateTransfer.create({
|
|
828
852
|
current: confirmedState,
|
|
829
853
|
next: shippedState,
|
|
830
|
-
trigger:
|
|
831
|
-
|
|
854
|
+
trigger: {
|
|
855
|
+
recordName: InteractionEventEntity.name,
|
|
856
|
+
type: 'create',
|
|
857
|
+
record: { interactionName: ShipOrder.name }
|
|
858
|
+
},
|
|
859
|
+
computeTarget: (event) => ({ id: event.record.payload.orderId })
|
|
832
860
|
}),
|
|
833
861
|
StateTransfer.create({
|
|
834
862
|
current: shippedState,
|
|
835
863
|
next: deliveredState,
|
|
836
|
-
trigger:
|
|
837
|
-
|
|
864
|
+
trigger: {
|
|
865
|
+
recordName: InteractionEventEntity.name,
|
|
866
|
+
type: 'create',
|
|
867
|
+
record: { interactionName: ConfirmDelivery.name }
|
|
868
|
+
},
|
|
869
|
+
computeTarget: (event) => ({ id: event.record.payload.orderId })
|
|
838
870
|
}),
|
|
839
871
|
StateTransfer.create({
|
|
840
872
|
current: pendingState,
|
|
841
873
|
next: cancelledState,
|
|
842
|
-
trigger:
|
|
843
|
-
|
|
874
|
+
trigger: {
|
|
875
|
+
recordName: InteractionEventEntity.name,
|
|
876
|
+
type: 'create',
|
|
877
|
+
record: { interactionName: CancelOrder.name }
|
|
878
|
+
},
|
|
879
|
+
computeTarget: (event) => ({ id: event.record.payload.orderId })
|
|
844
880
|
})
|
|
845
881
|
]
|
|
846
882
|
})
|
|
@@ -1130,14 +1166,22 @@ const OrderStateMachine = StateMachine.create({
|
|
|
1130
1166
|
StateTransfer.create({
|
|
1131
1167
|
current: PendingState,
|
|
1132
1168
|
next: PaidState,
|
|
1133
|
-
trigger:
|
|
1134
|
-
|
|
1169
|
+
trigger: {
|
|
1170
|
+
recordName: InteractionEventEntity.name,
|
|
1171
|
+
type: 'create',
|
|
1172
|
+
record: { interactionName: PayOrder.name }
|
|
1173
|
+
},
|
|
1174
|
+
computeTarget: (event) => ({ id: event.record.payload.orderId })
|
|
1135
1175
|
}),
|
|
1136
1176
|
StateTransfer.create({
|
|
1137
1177
|
current: PaidState,
|
|
1138
1178
|
next: ShippedState,
|
|
1139
|
-
trigger:
|
|
1140
|
-
|
|
1179
|
+
trigger: {
|
|
1180
|
+
recordName: InteractionEventEntity.name,
|
|
1181
|
+
type: 'create',
|
|
1182
|
+
record: { interactionName: ShipOrder.name }
|
|
1183
|
+
},
|
|
1184
|
+
computeTarget: (event) => ({ id: event.record.payload.orderId })
|
|
1141
1185
|
})
|
|
1142
1186
|
]
|
|
1143
1187
|
});
|
|
@@ -1300,6 +1344,7 @@ const Order = Entity.create({
|
|
|
1300
1344
|
computation: Every.create({
|
|
1301
1345
|
record: OrderItemRelation,
|
|
1302
1346
|
relationDirection: 'source',
|
|
1347
|
+
attributeQuery: ['quantity', ['product', { attributeQuery: ['stock'] }]], // Required when callback reads fields
|
|
1303
1348
|
callback: function(orderItem) {
|
|
1304
1349
|
// Check if each order item has sufficient product inventory
|
|
1305
1350
|
return orderItem.product.stock >= orderItem.quantity;
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# How to Use Activities for Process Management
|
|
2
2
|
|
|
3
|
+
> **⚠️ Maintenance note (2026-07-09)**: parts of this guide are outdated. The REAL Activity API is:
|
|
4
|
+
> `Activity.create({ name, interactions, transfers, groups? })` with `Transfer.create({ name, source, target })`,
|
|
5
|
+
> where `source`/`target` are Interaction / ActivityGroup instances. **There is no string-based
|
|
6
|
+
> `Transfer.create({ from, to, interaction, condition })` form** — examples below using that shape are
|
|
7
|
+
> historical sketches and do not run. Activities are dispatched through `ActivityManager`:
|
|
8
|
+
> compile with `new ActivityManager(activities).getOutput()`, pass the output entities/relations/eventSources
|
|
9
|
+
> to the Controller, then `controller.dispatch(controller.findEventSourceByName('ActivityName:interactionName'), { user, activityId? })`.
|
|
10
|
+
> For a runnable end-to-end example see `agent/agentspace/knowledge/usage/13-testing.md` §12.2.2
|
|
11
|
+
> and `tests/runtime/activity.spec.ts`. A full rewrite of this guide is tracked as follow-up work.
|
|
12
|
+
|
|
3
13
|
Activities are the core mechanism in interaqt for managing complex business processes. They allow you to define multi-step, multi-role business processes and control the execution order and conditions of processes through a state machine model.
|
|
4
14
|
|
|
5
15
|
## Understanding Activity Concepts
|
|
@@ -373,6 +373,7 @@ const User = Entity.create({
|
|
|
373
373
|
defaultValue: () => 0,
|
|
374
374
|
computation: WeightedSummation.create({
|
|
375
375
|
record: UserPublishedPosts,
|
|
376
|
+
attributeQuery: [['target', { attributeQuery: ['viewCount'] }]], // Required when callback reads fields
|
|
376
377
|
callback: (relation) => ({
|
|
377
378
|
weight: 1,
|
|
378
379
|
value: relation.target.viewCount
|
|
@@ -574,121 +574,72 @@ describe('User Interactions', () => {
|
|
|
574
574
|
|
|
575
575
|
### 12.2.2 Activity Testing
|
|
576
576
|
|
|
577
|
+
An Activity is an ordered composition of Interactions (`Activity.create({ name, interactions, transfers })`, with `Transfer.create({ name, source, target })` connecting them — NOT StateNode/StateTransfer, which belong to StateMachine). Use `ActivityManager` to compile activities into dispatchable event sources named `activityName:interactionName`, and drive them through `controller.dispatch`:
|
|
578
|
+
|
|
577
579
|
```typescript
|
|
578
580
|
// tests/activities/approvalProcess.spec.ts
|
|
581
|
+
import {
|
|
582
|
+
Activity, ActivityManager, Transfer, Interaction, Action,
|
|
583
|
+
Controller, MonoSystem, KlassByName
|
|
584
|
+
} from 'interaqt';
|
|
585
|
+
import { PGLiteDB } from '@drivers';
|
|
586
|
+
|
|
579
587
|
describe('Approval Process Activity', () => {
|
|
580
588
|
test('should handle complete approval workflow', async () => {
|
|
581
589
|
const system = new MonoSystem(new PGLiteDB());
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
properties: [
|
|
587
|
-
Property.create({ name: 'title', type: 'string' }),
|
|
588
|
-
Property.create({ name: 'status', type: 'string' }),
|
|
589
|
-
Property.create({ name: 'submitterId', type: 'string' })
|
|
590
|
-
]
|
|
591
|
-
});
|
|
592
|
-
|
|
593
|
-
// Create activity states
|
|
594
|
-
const submittedState = StateNode.create({ name: 'submitted' });
|
|
595
|
-
const reviewingState = StateNode.create({ name: 'reviewing' });
|
|
596
|
-
const approvedState = StateNode.create({ name: 'approved' });
|
|
597
|
-
const rejectedState = StateNode.create({ name: 'rejected' });
|
|
598
|
-
|
|
599
|
-
// Create interactions
|
|
600
|
-
const submitInteraction = Interaction.create({
|
|
590
|
+
system.conceptClass = KlassByName;
|
|
591
|
+
|
|
592
|
+
// 1. Declare the interactions of the workflow
|
|
593
|
+
const submit = Interaction.create({
|
|
601
594
|
name: 'submit',
|
|
602
595
|
action: Action.create({ name: 'submit' })
|
|
603
596
|
});
|
|
604
|
-
|
|
605
|
-
const approveInteraction = Interaction.create({
|
|
597
|
+
const approve = Interaction.create({
|
|
606
598
|
name: 'approve',
|
|
607
599
|
action: Action.create({ name: 'approve' })
|
|
608
600
|
});
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
name: 'reject',
|
|
612
|
-
action: Action.create({ name: 'reject' })
|
|
613
|
-
});
|
|
614
|
-
|
|
615
|
-
// Create state transfers
|
|
616
|
-
const submitTransfer = StateTransfer.create({
|
|
617
|
-
trigger: submitInteraction,
|
|
618
|
-
current: submittedState,
|
|
619
|
-
next: reviewingState
|
|
620
|
-
});
|
|
621
|
-
|
|
622
|
-
const approveTransfer = StateTransfer.create({
|
|
623
|
-
trigger: approveInteraction,
|
|
624
|
-
current: reviewingState,
|
|
625
|
-
next: approvedState
|
|
626
|
-
});
|
|
627
|
-
|
|
628
|
-
const rejectTransfer = StateTransfer.create({
|
|
629
|
-
trigger: rejectInteraction,
|
|
630
|
-
current: reviewingState,
|
|
631
|
-
next: rejectedState
|
|
632
|
-
});
|
|
633
|
-
|
|
634
|
-
// Create activity
|
|
601
|
+
|
|
602
|
+
// 2. Compose them into an activity: submit → approve
|
|
635
603
|
const approvalActivity = Activity.create({
|
|
636
604
|
name: 'ApprovalProcess',
|
|
637
|
-
|
|
638
|
-
transfers: [
|
|
639
|
-
|
|
605
|
+
interactions: [submit, approve],
|
|
606
|
+
transfers: [
|
|
607
|
+
Transfer.create({ name: 'submitToApprove', source: submit, target: approve })
|
|
608
|
+
]
|
|
640
609
|
});
|
|
641
|
-
|
|
610
|
+
|
|
611
|
+
// 3. ActivityManager compiles the activity into event sources / records
|
|
612
|
+
const activityManager = new ActivityManager([approvalActivity]);
|
|
613
|
+
const activityOutput = activityManager.getOutput();
|
|
614
|
+
|
|
642
615
|
const controller = new Controller({
|
|
643
616
|
system,
|
|
644
|
-
entities: [
|
|
645
|
-
relations: [],
|
|
646
|
-
|
|
647
|
-
interactions: [submitInteraction, approveInteraction, rejectInteraction], // interactions
|
|
648
|
-
dict: []
|
|
617
|
+
entities: [...activityOutput.entities],
|
|
618
|
+
relations: [...activityOutput.relations],
|
|
619
|
+
eventSources: [...activityOutput.eventSources]
|
|
649
620
|
});
|
|
650
621
|
await controller.setup(true);
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
const
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
});
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
['id', 'title', 'status', 'submitterId'] // Specify fields
|
|
673
|
-
);
|
|
674
|
-
expect(updatedRequest.status).toBe('reviewing');
|
|
675
|
-
|
|
676
|
-
// Execute approve interaction
|
|
677
|
-
await controller.callInteraction(approveInteraction.name, {
|
|
678
|
-
user: { id: 'admin-user' }, // Add user object
|
|
679
|
-
payload: {
|
|
680
|
-
requestId: request.id
|
|
681
|
-
}
|
|
682
|
-
});
|
|
683
|
-
|
|
684
|
-
// Verify final state
|
|
685
|
-
updatedRequest = await system.storage.findOne(
|
|
686
|
-
'Request',
|
|
687
|
-
MatchExp.atom({ key: 'id', value: ['=', request.id] }),
|
|
688
|
-
undefined,
|
|
689
|
-
['id', 'title', 'status', 'submitterId'] // Specify fields
|
|
690
|
-
);
|
|
691
|
-
expect(updatedRequest.status).toBe('approved');
|
|
622
|
+
|
|
623
|
+
const submitES = controller.findEventSourceByName('ApprovalProcess:submit')!;
|
|
624
|
+
const approveES = controller.findEventSourceByName('ApprovalProcess:approve')!;
|
|
625
|
+
|
|
626
|
+
// 4. The head interaction creates the activity instance
|
|
627
|
+
const res1 = await controller.dispatch(submitES, { user: { id: 'user123' } });
|
|
628
|
+
expect(res1.error).toBeUndefined();
|
|
629
|
+
const activityId = res1.context!.activityId as string;
|
|
630
|
+
|
|
631
|
+
// 5. Subsequent interactions must carry the activityId
|
|
632
|
+
const res2 = await controller.dispatch(approveES, { user: { id: 'admin-user' }, activityId });
|
|
633
|
+
expect(res2.error).toBeUndefined();
|
|
634
|
+
|
|
635
|
+
// 6. The activity is complete: no further interaction is available
|
|
636
|
+
const res3 = await controller.dispatch(approveES, { user: { id: 'admin-user' }, activityId });
|
|
637
|
+
expect(res3.error).toBeDefined();
|
|
638
|
+
|
|
639
|
+
// 7. Verify the activity state directly
|
|
640
|
+
const activityCall = activityManager.getActivityCallByName('ApprovalProcess')!;
|
|
641
|
+
const state = await activityCall.getState(controller, activityId);
|
|
642
|
+
expect(state.current).toBeUndefined(); // undefined = completed
|
|
692
643
|
});
|
|
693
644
|
});
|
|
694
645
|
```
|
|
@@ -299,6 +299,7 @@ const userTotalScore = Property.create({
|
|
|
299
299
|
defaultValue: () => 0, // Must provide default value
|
|
300
300
|
computation: WeightedSummation.create({
|
|
301
301
|
record: UserScoreRelation,
|
|
302
|
+
attributeQuery: ['multiplier', 'points'], // Required when callback reads fields
|
|
302
303
|
callback: function(scoreRecord) {
|
|
303
304
|
return {
|
|
304
305
|
weight: scoreRecord.multiplier || 1,
|
|
@@ -311,6 +312,7 @@ const userTotalScore = Property.create({
|
|
|
311
312
|
// Global weighted summation
|
|
312
313
|
const globalWeightedScore = WeightedSummation.create({
|
|
313
314
|
record: ScoreRecord,
|
|
315
|
+
attributeQuery: ['difficulty', 'score'], // Required when callback reads fields
|
|
314
316
|
callback: function(record) {
|
|
315
317
|
return {
|
|
316
318
|
weight: record.difficulty,
|
|
@@ -460,6 +462,7 @@ const completedAllRequired = Property.create({
|
|
|
460
462
|
defaultValue: () => false, // Must provide default value
|
|
461
463
|
computation: Every.create({
|
|
462
464
|
record: UserCourseRelation,
|
|
465
|
+
attributeQuery: ['status'], // Required when callback reads fields
|
|
463
466
|
callback: function(courseRelation) {
|
|
464
467
|
return courseRelation.status === 'completed'
|
|
465
468
|
},
|
|
@@ -491,6 +494,7 @@ const hasPendingTasks = Property.create({
|
|
|
491
494
|
defaultValue: () => false, // Must provide default value
|
|
492
495
|
computation: Any.create({
|
|
493
496
|
record: UserTaskRelation,
|
|
497
|
+
attributeQuery: ['status'], // Required when callback reads fields
|
|
494
498
|
callback: function(taskRelation) {
|
|
495
499
|
return taskRelation.status === 'pending'
|
|
496
500
|
}
|
|
@@ -530,6 +534,7 @@ Custom callbacks may be replayed after retryable transaction failures. Do not pe
|
|
|
530
534
|
|
|
531
535
|
**Incremental planning**
|
|
532
536
|
- If `incrementalCompute` or `incrementalPatchCompute` is provided, the computation must also provide `incrementalDataDeps` or `planIncremental`.
|
|
537
|
+
- Mutation event snapshot shapes differ by type: `create` events carry the written fields in `record` (`oldRecord` is `undefined`); `update` events carry **only the changed fields** (+ `id`) in `record` and the full pre-update snapshot in `oldRecord`; `delete` events carry the full deleted snapshot in `record` (`oldRecord` is `undefined`). For updates, reconstruct the full new state with `{ ...mutationEvent.oldRecord, ...mutationEvent.record }` or re-fetch the record — reading unchanged fields off `mutationEvent.record` yields `undefined`.
|
|
533
538
|
- `incrementalDataDeps: string[]` lists exactly which `dataDeps` keys should be resolved for the incremental callback; use `[]` when none are needed.
|
|
534
539
|
- `planIncremental(event, record, context)` is the advanced form. Return `{ type: 'incremental', dataDepKeys, needsLastValue? }`, `{ type: 'fullRecompute', reason }`, or `{ type: 'skip', reason }`.
|
|
535
540
|
- Incremental callbacks receive only planned partial `dataDeps`; the runtime no longer resolves all `dataDeps` before incremental execution.
|
|
@@ -621,7 +626,11 @@ const OrderStateMachine = StateMachine.create({
|
|
|
621
626
|
StateTransfer.create({
|
|
622
627
|
current: pendingState,
|
|
623
628
|
next: confirmedState,
|
|
624
|
-
trigger:
|
|
629
|
+
trigger: {
|
|
630
|
+
recordName: InteractionEventEntity.name,
|
|
631
|
+
type: 'create',
|
|
632
|
+
record: { interactionName: ConfirmOrderInteraction.name }
|
|
633
|
+
}
|
|
625
634
|
})
|
|
626
635
|
],
|
|
627
636
|
initialState: pendingState
|
|
@@ -927,23 +936,31 @@ StateTransfer.create(config: StateTransferConfig): KlassInstance<typeof StateTra
|
|
|
927
936
|
```
|
|
928
937
|
|
|
929
938
|
**Parameters**
|
|
930
|
-
- `config.trigger` (
|
|
939
|
+
- `config.trigger` (RecordMutationEventPattern, required): Pattern matched against record mutation events: `{ recordName, type, keys?, record?, oldRecord? }`. For interaction-triggered transfers use `{ recordName: InteractionEventEntity.name, type: 'create', record: { interactionName: SomeInteraction.name } }`. `keys` uses subset semantics (fires when the update touched all listed fields)
|
|
931
940
|
- `config.current` (StateNode, required): Current state node
|
|
932
941
|
- `config.next` (StateNode, required): Next state node
|
|
933
|
-
- `config.computeTarget` (function, optional):
|
|
942
|
+
- `config.computeTarget` (function, optional for global-level, **required for property-level** state machines): Maps a matched mutation event to the record(s) whose property should transition, e.g. `(event) => ({ id: event.record.payload.id })`
|
|
934
943
|
|
|
935
944
|
**Examples**
|
|
936
945
|
```typescript
|
|
937
946
|
// Simple state transfer
|
|
938
947
|
const approveTransfer = StateTransfer.create({
|
|
939
|
-
trigger:
|
|
948
|
+
trigger: {
|
|
949
|
+
recordName: InteractionEventEntity.name,
|
|
950
|
+
type: 'create',
|
|
951
|
+
record: { interactionName: ApproveInteraction.name }
|
|
952
|
+
},
|
|
940
953
|
current: pendingState,
|
|
941
954
|
next: approvedState
|
|
942
955
|
});
|
|
943
956
|
|
|
944
957
|
// State transfer with dynamic target computation
|
|
945
958
|
const conditionalTransfer = StateTransfer.create({
|
|
946
|
-
trigger:
|
|
959
|
+
trigger: {
|
|
960
|
+
recordName: InteractionEventEntity.name,
|
|
961
|
+
type: 'create',
|
|
962
|
+
record: { interactionName: ProcessInteraction.name }
|
|
963
|
+
},
|
|
947
964
|
current: pendingState,
|
|
948
965
|
next: approvedState, // Default next state
|
|
949
966
|
computeTarget: (context) => {
|
|
@@ -363,14 +363,22 @@ const ArticleStatusStateMachine = StateMachine.create({
|
|
|
363
363
|
StateTransfer.create({
|
|
364
364
|
current: ActiveState,
|
|
365
365
|
next: DeletedState,
|
|
366
|
-
trigger:
|
|
367
|
-
|
|
366
|
+
trigger: {
|
|
367
|
+
recordName: InteractionEventEntity.name,
|
|
368
|
+
type: 'create',
|
|
369
|
+
record: { interactionName: DeleteArticle.name }
|
|
370
|
+
},
|
|
371
|
+
computeTarget: (event) => ({ id: event.record.payload.articleId })
|
|
368
372
|
}),
|
|
369
373
|
StateTransfer.create({
|
|
370
374
|
current: DeletedState,
|
|
371
375
|
next: ActiveState,
|
|
372
|
-
trigger:
|
|
373
|
-
|
|
376
|
+
trigger: {
|
|
377
|
+
recordName: InteractionEventEntity.name,
|
|
378
|
+
type: 'create',
|
|
379
|
+
record: { interactionName: RestoreArticle.name }
|
|
380
|
+
},
|
|
381
|
+
computeTarget: (event) => ({ id: event.record.payload.articleId })
|
|
374
382
|
})
|
|
375
383
|
]
|
|
376
384
|
});
|
|
@@ -418,14 +426,22 @@ const Article = Entity.create({
|
|
|
418
426
|
StateTransfer.create({
|
|
419
427
|
current: activeState,
|
|
420
428
|
next: deletedState,
|
|
421
|
-
trigger:
|
|
422
|
-
|
|
429
|
+
trigger: {
|
|
430
|
+
recordName: InteractionEventEntity.name,
|
|
431
|
+
type: 'create',
|
|
432
|
+
record: { interactionName: DeleteArticle.name }
|
|
433
|
+
},
|
|
434
|
+
computeTarget: (event) => ({ id: event.record.payload.articleId })
|
|
423
435
|
}),
|
|
424
436
|
StateTransfer.create({
|
|
425
437
|
current: deletedState,
|
|
426
438
|
next: activeState,
|
|
427
|
-
trigger:
|
|
428
|
-
|
|
439
|
+
trigger: {
|
|
440
|
+
recordName: InteractionEventEntity.name,
|
|
441
|
+
type: 'create',
|
|
442
|
+
record: { interactionName: RestoreArticle.name }
|
|
443
|
+
},
|
|
444
|
+
computeTarget: (event) => ({ id: event.record.payload.articleId })
|
|
429
445
|
})
|
|
430
446
|
],
|
|
431
447
|
initialState: activeState
|
|
@@ -476,11 +492,15 @@ deletionProperty.computation = StateMachine.create({
|
|
|
476
492
|
initialState: NON_DELETED_STATE,
|
|
477
493
|
transfers: [
|
|
478
494
|
StateTransfer.create({
|
|
479
|
-
trigger:
|
|
495
|
+
trigger: {
|
|
496
|
+
recordName: InteractionEventEntity.name,
|
|
497
|
+
type: 'create',
|
|
498
|
+
record: { interactionName: DeleteArticle.name }
|
|
499
|
+
},
|
|
480
500
|
current: NON_DELETED_STATE,
|
|
481
501
|
next: DELETED_STATE,
|
|
482
502
|
computeTarget: function(event) {
|
|
483
|
-
return { id: event.payload.articleId };
|
|
503
|
+
return { id: event.record.payload.articleId };
|
|
484
504
|
}
|
|
485
505
|
})
|
|
486
506
|
]
|
|
@@ -611,14 +631,22 @@ const ArticlePublishStateMachine = StateMachine.create({
|
|
|
611
631
|
StateTransfer.create({
|
|
612
632
|
current: DraftState,
|
|
613
633
|
next: PublishedState,
|
|
614
|
-
trigger:
|
|
615
|
-
|
|
634
|
+
trigger: {
|
|
635
|
+
recordName: InteractionEventEntity.name,
|
|
636
|
+
type: 'create',
|
|
637
|
+
record: { interactionName: PublishArticle.name }
|
|
638
|
+
},
|
|
639
|
+
computeTarget: (event) => ({ id: event.record.payload.articleId })
|
|
616
640
|
}),
|
|
617
641
|
StateTransfer.create({
|
|
618
642
|
current: PublishedState,
|
|
619
643
|
next: DraftState,
|
|
620
|
-
trigger:
|
|
621
|
-
|
|
644
|
+
trigger: {
|
|
645
|
+
recordName: InteractionEventEntity.name,
|
|
646
|
+
type: 'create',
|
|
647
|
+
record: { interactionName: UnpublishArticle.name }
|
|
648
|
+
},
|
|
649
|
+
computeTarget: (event) => ({ id: event.record.payload.articleId })
|
|
622
650
|
})
|
|
623
651
|
]
|
|
624
652
|
});
|
|
@@ -722,8 +750,12 @@ const TimestampStateMachine = StateMachine.create({
|
|
|
722
750
|
StateTransfer.create({
|
|
723
751
|
current: TimestampState,
|
|
724
752
|
next: TimestampState,
|
|
725
|
-
trigger:
|
|
726
|
-
|
|
753
|
+
trigger: {
|
|
754
|
+
recordName: InteractionEventEntity.name,
|
|
755
|
+
type: 'create',
|
|
756
|
+
record: { interactionName: RecordActivity.name }
|
|
757
|
+
},
|
|
758
|
+
computeTarget: (event) => ({ id: event.record.payload.entityId })
|
|
727
759
|
})
|
|
728
760
|
]
|
|
729
761
|
});
|
|
@@ -767,7 +799,11 @@ const User = Entity.create({
|
|
|
767
799
|
StateTransfer.create({
|
|
768
800
|
current: activeState,
|
|
769
801
|
next: activeState,
|
|
770
|
-
trigger:
|
|
802
|
+
trigger: {
|
|
803
|
+
recordName: InteractionEventEntity.name,
|
|
804
|
+
type: 'create',
|
|
805
|
+
record: { interactionName: UserActivityInteraction.name }
|
|
806
|
+
},
|
|
771
807
|
computeTarget: (event) => ({ id: event.user.id })
|
|
772
808
|
})
|
|
773
809
|
],
|
|
@@ -801,8 +837,12 @@ const Article = Entity.create({
|
|
|
801
837
|
StateTransfer.create({
|
|
802
838
|
current: modifiedState,
|
|
803
839
|
next: modifiedState,
|
|
804
|
-
trigger:
|
|
805
|
-
|
|
840
|
+
trigger: {
|
|
841
|
+
recordName: InteractionEventEntity.name,
|
|
842
|
+
type: 'create',
|
|
843
|
+
record: { interactionName: UpdateArticleInteraction.name }
|
|
844
|
+
},
|
|
845
|
+
computeTarget: (event) => ({ id: event.record.payload.articleId })
|
|
806
846
|
})
|
|
807
847
|
],
|
|
808
848
|
initialState: modifiedState
|
|
@@ -834,8 +874,12 @@ const Sensor = Entity.create({
|
|
|
834
874
|
StateTransfer.create({
|
|
835
875
|
current: triggeredState,
|
|
836
876
|
next: triggeredState,
|
|
837
|
-
trigger:
|
|
838
|
-
|
|
877
|
+
trigger: {
|
|
878
|
+
recordName: InteractionEventEntity.name,
|
|
879
|
+
type: 'create',
|
|
880
|
+
record: { interactionName: SensorTriggerInteraction.name }
|
|
881
|
+
},
|
|
882
|
+
computeTarget: (event) => ({ id: event.record.payload.sensorId })
|
|
839
883
|
})
|
|
840
884
|
],
|
|
841
885
|
initialState: triggeredState
|
|
@@ -896,7 +940,11 @@ Property.create({
|
|
|
896
940
|
StateTransfer.create({
|
|
897
941
|
current: activeState,
|
|
898
942
|
next: activeState,
|
|
899
|
-
trigger:
|
|
943
|
+
trigger: {
|
|
944
|
+
recordName: InteractionEventEntity.name,
|
|
945
|
+
type: 'create',
|
|
946
|
+
record: { interactionName: UserActivityInteraction.name }
|
|
947
|
+
},
|
|
900
948
|
computeTarget: (event) => ({ id: event.user.id })
|
|
901
949
|
})
|
|
902
950
|
]
|
|
@@ -925,6 +973,7 @@ const User = Entity.create({
|
|
|
925
973
|
computation: Count.create({
|
|
926
974
|
record: UserArticleRelation,
|
|
927
975
|
direction: 'target',
|
|
976
|
+
attributeQuery: [['source', { attributeQuery: ['status'] }]], // Required when callback reads fields
|
|
928
977
|
callback: (relation) => relation.source.status !== 'deleted'
|
|
929
978
|
})
|
|
930
979
|
})
|
|
@@ -1033,20 +1082,32 @@ const ArticleLifecycleStateMachine = StateMachine.create({
|
|
|
1033
1082
|
StateTransfer.create({
|
|
1034
1083
|
current: DraftState,
|
|
1035
1084
|
next: PublishedState,
|
|
1036
|
-
trigger:
|
|
1037
|
-
|
|
1085
|
+
trigger: {
|
|
1086
|
+
recordName: InteractionEventEntity.name,
|
|
1087
|
+
type: 'create',
|
|
1088
|
+
record: { interactionName: PublishArticle.name }
|
|
1089
|
+
},
|
|
1090
|
+
computeTarget: (event) => ({ id: event.record.payload.articleId })
|
|
1038
1091
|
}),
|
|
1039
1092
|
StateTransfer.create({
|
|
1040
1093
|
current: PublishedState,
|
|
1041
1094
|
next: DeletedState,
|
|
1042
|
-
trigger:
|
|
1043
|
-
|
|
1095
|
+
trigger: {
|
|
1096
|
+
recordName: InteractionEventEntity.name,
|
|
1097
|
+
type: 'create',
|
|
1098
|
+
record: { interactionName: DeleteArticle.name }
|
|
1099
|
+
},
|
|
1100
|
+
computeTarget: (event) => ({ id: event.record.payload.articleId })
|
|
1044
1101
|
}),
|
|
1045
1102
|
StateTransfer.create({
|
|
1046
1103
|
current: DraftState,
|
|
1047
1104
|
next: DeletedState,
|
|
1048
|
-
trigger:
|
|
1049
|
-
|
|
1105
|
+
trigger: {
|
|
1106
|
+
recordName: InteractionEventEntity.name,
|
|
1107
|
+
type: 'create',
|
|
1108
|
+
record: { interactionName: DeleteArticle.name }
|
|
1109
|
+
},
|
|
1110
|
+
computeTarget: (event) => ({ id: event.record.payload.articleId })
|
|
1050
1111
|
})
|
|
1051
1112
|
]
|
|
1052
1113
|
});
|