interaqt 0.6.1 → 0.6.3

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.
@@ -2799,6 +2799,137 @@ new Controller({
2799
2799
 
2800
2800
  ⚠️ **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.
2801
2801
 
2802
+ ### RecordMutationSideEffect
2803
+
2804
+ RecordMutationSideEffect allows you to execute custom logic when records are created, updated, or deleted within interaction contexts. It's useful for triggering external operations, logging, or custom business logic in response to data changes.
2805
+
2806
+ **Syntax**
2807
+ ```typescript
2808
+ RecordMutationSideEffect.create(config: RecordMutationSideEffectConfig): RecordMutationSideEffectInstance
2809
+ ```
2810
+
2811
+ **Parameters**
2812
+ - `config.name` (string, required): Unique name for the side effect
2813
+ - `config.record` (object, required): Target record configuration
2814
+ - `record.name` (string, required): Entity or relation name to monitor
2815
+ - `config.content` (function, required): Async function executed when mutation occurs
2816
+ ```typescript
2817
+ async (event: RecordMutationEvent) => Promise<any>
2818
+ ```
2819
+
2820
+ **RecordMutationEvent Structure**
2821
+ ```typescript
2822
+ type RecordMutationEvent = {
2823
+ recordName: string // Entity/relation name
2824
+ type: 'create' | 'update' | 'delete' // Mutation type
2825
+ record?: { id: string, ... } // New/current record (for create/update)
2826
+ oldRecord?: { id: string, ... } // Previous record (for update/delete)
2827
+ keys?: string[] // Changed fields (for update)
2828
+ }
2829
+ ```
2830
+
2831
+ **Important Notes**
2832
+ - RecordMutationSideEffect **only** triggers within interaction execution context
2833
+ - Direct storage operations (e.g., `controller.system.storage.create()`) do **NOT** trigger side effects
2834
+ - All side effects registered for an entity are called on any mutation of that entity
2835
+ - Side effects run after the storage operation completes
2836
+ - Side effect errors are captured but don't fail the interaction
2837
+
2838
+ **Examples**
2839
+
2840
+ ```typescript
2841
+ // Log user creation events
2842
+ const userCreatedLogger = RecordMutationSideEffect.create({
2843
+ name: 'userCreatedLogger',
2844
+ record: { name: 'User' },
2845
+ content: async (event) => {
2846
+ if (event.type === 'create') {
2847
+ console.log('New user created:', event.record?.id);
2848
+ // Send welcome email, update analytics, etc.
2849
+ return { logged: true, userId: event.record?.id };
2850
+ }
2851
+ return null;
2852
+ }
2853
+ });
2854
+
2855
+ // Audit trail for data changes
2856
+ const auditLogger = RecordMutationSideEffect.create({
2857
+ name: 'auditLogger',
2858
+ record: { name: 'Order' },
2859
+ content: async (event) => {
2860
+ const audit = {
2861
+ action: event.type,
2862
+ entityId: event.record?.id || event.oldRecord?.id,
2863
+ timestamp: new Date().toISOString(),
2864
+ changes: event.type === 'update' ? {
2865
+ before: event.oldRecord,
2866
+ after: event.record,
2867
+ fields: event.keys
2868
+ } : null
2869
+ };
2870
+
2871
+ // Store audit log (pseudo-code)
2872
+ await auditService.log(audit);
2873
+ return { audited: true };
2874
+ }
2875
+ });
2876
+
2877
+ // Cache invalidation on updates
2878
+ const cacheInvalidator = RecordMutationSideEffect.create({
2879
+ name: 'cacheInvalidator',
2880
+ record: { name: 'Product' },
2881
+ content: async (event) => {
2882
+ if (event.type === 'update' || event.type === 'delete') {
2883
+ const productId = event.record?.id || event.oldRecord?.id;
2884
+ await cacheService.invalidate(`product:${productId}`);
2885
+ return { invalidated: true, productId };
2886
+ }
2887
+ return null;
2888
+ }
2889
+ });
2890
+
2891
+ // Using in Controller
2892
+ const controller = new Controller({
2893
+ system: system,
2894
+ entities: [User, Order, Product],
2895
+ relations: [],
2896
+ interactions: [CreateUserInteraction, UpdateOrderInteraction],
2897
+ recordMutationSideEffects: [
2898
+ userCreatedLogger,
2899
+ auditLogger,
2900
+ cacheInvalidator
2901
+ ]
2902
+ });
2903
+
2904
+ // When interaction is called, side effects are triggered
2905
+ const result = await controller.callInteraction('createUser', {
2906
+ user: { id: 'admin' },
2907
+ payload: { name: 'John', email: 'john@example.com' }
2908
+ });
2909
+
2910
+ // Access side effect results
2911
+ if (result.sideEffects?.userCreatedLogger?.error) {
2912
+ console.warn('Logger failed:', result.sideEffects.userCreatedLogger.error);
2913
+ } else {
2914
+ console.log('Logger result:', result.sideEffects.userCreatedLogger.result);
2915
+ }
2916
+ ```
2917
+
2918
+ **Common Use Cases**
2919
+ 1. **Audit Logging**: Track all changes to sensitive data
2920
+ 2. **External System Sync**: Update external systems when data changes
2921
+ 3. **Cache Management**: Invalidate caches on data mutations
2922
+ 4. **Notifications**: Send notifications when specific events occur
2923
+ 5. **Analytics**: Track user actions and data changes
2924
+ 6. **Cleanup Tasks**: Perform cleanup when records are deleted
2925
+
2926
+ **Best Practices**
2927
+ 1. Filter by event type within the content function if needed
2928
+ 2. Handle errors gracefully - side effect failures shouldn't break the main operation
2929
+ 3. Keep side effects lightweight to avoid impacting performance
2930
+ 4. Use descriptive names for debugging
2931
+ 5. Return meaningful results for monitoring
2932
+
2802
2933
  **Main Methods**
2803
2934
 
2804
2935
  #### setup(install?: boolean)
@@ -2807,11 +2938,17 @@ Initialize system.
2807
2938
  await controller.setup(true) // Create database tables
2808
2939
  ```
2809
2940
 
2810
- #### callInteraction(interactionName: string, args: InteractionEventArgs)
2811
- Call interaction.
2941
+ #### callInteraction(interactionName: string, args: InteractionEventArgs, activityName?: string, activityId?: string)
2942
+ Call interaction or activity interaction.
2812
2943
 
2813
2944
  **Note about ignorePermission**: When `controller.ignorePermission` is set to `true`, this method will bypass all condition checks, user validation, and payload validation defined in the interaction.
2814
2945
 
2946
+ **Parameters**
2947
+ - `interactionName`: The name of the interaction to call
2948
+ - `args`: The interaction event arguments containing user and payload
2949
+ - `activityName` (optional): The name of the activity when calling an activity interaction
2950
+ - `activityId` (optional): The ID of the activity instance when calling an activity interaction
2951
+
2815
2952
  **Return Type**
2816
2953
  ```typescript
2817
2954
  type InteractionCallResponse = {
@@ -2842,7 +2979,7 @@ type InteractionCallResponse = {
2842
2979
  }
2843
2980
  ```
2844
2981
 
2845
- **Example**
2982
+ **Example - Regular Interaction**
2846
2983
  ```typescript
2847
2984
  const result = await controller.callInteraction('createPost', {
2848
2985
  user: { id: 'user1' },
@@ -2864,14 +3001,13 @@ if (result.sideEffects?.emailNotification?.error) {
2864
3001
  }
2865
3002
  ```
2866
3003
 
2867
- #### callActivityInteraction(activityName: string, interactionName: string, activityId: string, args: InteractionEventArgs)
2868
- Call interaction within activity.
3004
+ **Example - Activity Interaction**
2869
3005
  ```typescript
2870
- const result = await controller.callActivityInteraction(
2871
- 'OrderProcess',
3006
+ const result = await controller.callInteraction(
2872
3007
  'confirmOrder',
2873
- 'activity-instance-1',
2874
- { user: { id: 'user1' }, payload: { orderData: {...} } }
3008
+ { user: { id: 'user1' }, payload: { orderData: {...} } },
3009
+ 'OrderProcess',
3010
+ 'activity-instance-1'
2875
3011
  )
2876
3012
  ```
2877
3013
 
@@ -35,7 +35,19 @@ expect(result.error).toBeUndefined()
35
35
 
36
36
  ## callInteraction Return Value
37
37
 
38
- The `controller.callInteraction()` method returns a `InteractionCallResponse` object with the following structure:
38
+ The `controller.callInteraction()` method is used for both regular interactions and activity interactions.
39
+
40
+ **Method Signature:**
41
+ ```typescript
42
+ callInteraction(
43
+ interactionName: string,
44
+ args: InteractionEventArgs,
45
+ activityName?: string, // Optional: for activity interactions
46
+ activityId?: string // Optional: for activity interactions
47
+ ): Promise<InteractionCallResponse>
48
+ ```
49
+
50
+ The method returns a `InteractionCallResponse` object with the following structure:
39
51
 
40
52
  ```typescript
41
53
  type InteractionCallResponse = {
@@ -232,11 +244,11 @@ expect(publishResult.error).toBeUndefined()
232
244
  expect(publishResult.sideEffects?.emailNotification?.result).toBe('sent')
233
245
 
234
246
  // 4. Activity interactions return activityId
235
- const activityResult = await controller.callActivityInteraction(
236
- 'ApprovalWorkflow',
237
- 'StartApproval',
238
- undefined,
239
- {...}
247
+ const activityResult = await controller.callInteraction(
248
+ 'StartApproval',
249
+ {...},
250
+ 'ApprovalWorkflow',
251
+ undefined
240
252
  )
241
253
  const activityId = activityResult.context?.activityId
242
254
  ```
@@ -1205,20 +1205,20 @@ if (createPostInteraction) {
1205
1205
 
1206
1206
  ```javascript
1207
1207
  // Execute interaction as part of an activity
1208
- const result = await controller.callActivityInteraction(
1209
- 'OrderProcess', // activity name
1208
+ const result = await controller.callInteraction(
1210
1209
  'processPayment', // interaction name
1211
- 'activity-instance-id',// activity instance ID
1212
1210
  {
1213
1211
  user: { id: 'user123' },
1214
1212
  payload: { /* ... */ }
1215
- }
1213
+ },
1214
+ 'OrderProcess', // activity name (optional)
1215
+ 'activity-instance-id' // activity instance ID (optional)
1216
1216
  );
1217
1217
  ```
1218
1218
 
1219
1219
  ## Error Handling
1220
1220
 
1221
- > **Important**: The interaqt framework automatically catches and handles all errors, never throwing uncaught exceptions. All errors are returned through the `error` field in the return value of `callInteraction` or `callActivityInteraction`. Therefore, **DO NOT use try-catch to test error cases**, instead check the `error` field in the return value.
1221
+ > **Important**: The interaqt framework automatically catches and handles all errors, never throwing uncaught exceptions. All errors are returned through the `error` field in the return value of `callInteraction`. Therefore, **DO NOT use try-catch to test error cases**, instead check the `error` field in the return value.
1222
1222
 
1223
1223
  ### Parameter Validation Errors
1224
1224
 
@@ -150,12 +150,12 @@ const result = await controller.callInteraction(interactionName: string, args: {
150
150
  payload?: { [key: string]: any } // Optional payload
151
151
  })
152
152
 
153
- // Call activity interaction
154
- const result = await controller.callActivityInteraction(
155
- activityName: string,
153
+ // Call activity interaction (using the same callInteraction method)
154
+ const result = await controller.callInteraction(
156
155
  interactionName: string,
157
- activityId: string,
158
- args: InteractionEventArgs
156
+ args: InteractionEventArgs,
157
+ activityName: string, // Optional: for activity interactions
158
+ activityId: string // Optional: for activity interactions
159
159
  )
160
160
  ```
161
161
 
@@ -1200,8 +1200,16 @@ Initialize system.
1200
1200
  await controller.setup(true) // Create database tables
1201
1201
  ```
1202
1202
 
1203
- #### callInteraction(interactionName: string, args: InteractionEventArgs)
1204
- Call interaction.
1203
+ #### callInteraction(interactionName: string, args: InteractionEventArgs, activityName?: string, activityId?: string)
1204
+ Call interaction or activity interaction.
1205
+
1206
+ **Parameters:**
1207
+ - `interactionName`: The name of the interaction to call
1208
+ - `args`: The interaction event arguments containing user and payload
1209
+ - `activityName` (optional): The name of the activity when calling an activity interaction
1210
+ - `activityId` (optional): The ID of the activity instance when calling an activity interaction
1211
+
1212
+ **Example - Regular Interaction:**
1205
1213
  ```typescript
1206
1214
  const result = await controller.callInteraction('createPost', {
1207
1215
  user: { id: 'user1' },
@@ -1209,17 +1217,17 @@ const result = await controller.callInteraction('createPost', {
1209
1217
  })
1210
1218
  ```
1211
1219
 
1212
- #### callActivityInteraction(activityName: string, interactionName: string, activityId: string, args: InteractionEventArgs)
1213
- Call interaction within activity.
1220
+ **Example - Activity Interaction:**
1214
1221
  ```typescript
1215
- const result = await controller.callActivityInteraction(
1216
- 'OrderProcess',
1222
+ const result = await controller.callInteraction(
1217
1223
  'confirmOrder',
1218
- 'activity-instance-1',
1219
- { user: { id: 'user1' }, payload: { orderData: {...} } }
1224
+ { user: { id: 'user1' }, payload: { orderData: {...} } },
1225
+ 'OrderProcess',
1226
+ 'activity-instance-1'
1220
1227
  )
1221
1228
  ```
1222
1229
 
1230
+
1223
1231
  ### System
1224
1232
 
1225
1233
  System abstract interface that defines basic services like storage and logging.
@@ -0,0 +1,34 @@
1
+ 在需求分析的过程中,我们已经在 `.claude/agents/requirements-analysis-handle.md` 中提出了一种以最终"读需求"为起点,反向衍生出 创建/修改/删除 需求的方法。
2
+ 在实际的探索中发现:用户可能会在输入阶段就按照自己的真实需要描述了一部分流程了,这个流程里面包含了
3
+
4
+ - 进行增删改查交互的步骤,通常是有依赖顺序的
5
+ - 进行交互的角色
6
+ - 交互对数据产生的具体增删改变化
7
+ 例子:在 `requirements/requirements.md` 中,用户直接在描述中叙述如何创建版本 rollback 时数据应该如何变化。这里面就已经包含了:"创建版本-回退版本"的具体流程。
8
+
9
+ 这个流程本身也是用户的一种需求,我们需要严格遵照他的指示实现。
10
+ 现在,你来将 `.claude/agents/requirements-analysis-handle.md` 完全重写。要求:
11
+ 1. 先将 `.claude/agents/requirements-analysis-handle.md` 关于目标补充、数据分析、交互动作定义的步骤、设计的数据结构完全提取出来,这些部分已经很稳定,可以留作复用。
12
+
13
+ 用下面的这些步骤作为重写的分析步骤:
14
+ 1. 对用户的输入进行分析,识别出其中的:
15
+ - 目标。通常是和具体软件功能无关,和现实中真实目标相关的描述。例如“管理图书”,“管理员工”,“和好友实时交流”等。
16
+ - 流程。例如 "发出好友申请-收到申请后同意/收到申请后拒绝"。
17
+ - 数据概念。(使用原文档里数据概念)。
18
+ 2. 进行常见的目标补充(使用原文档里的方法)。
19
+ 3. 进行完整的数据概念设计(复用原文档中的方法)。
20
+ 4. 进行流程和交互动作设计。
21
+ 3.1. 优先整合用户描述中的流程。并且看流程达到了哪些目标。
22
+ 3.2. 为没有达到的目标设计流程。
23
+ 3.2.1. 流程是一系列有顺序的交互的总和,其中可以包含可能的分支或者循环。交互的具体定义仍然采用原文档中的定义。
24
+ 3.2.2. 流程的最后仍然应该是一个"读需求"作为结尾,来真正满足目标。但前面需要补充常见的创建等逻辑。
25
+ 3.3. 注意,流程中的每一步应该都是一个交互动作,交互动作要使用原文档中 interactions-design.json 中的数据结构表示。要包含完整 `data.creates`/`data.updates`/`data.deletes`。从用户的输入中直接提取的出来的流程也要完善这些信息。
26
+ 5. 设计完流程后。从数据的角度来补充用户可能需要的其他增删改查需求:
27
+ 4.1. 为每一个修改和删除的交互考虑用户作为人类,是否需要经过查看/搜索再进行决策的需求。
28
+ 4.2. 为每一个创建的交互动作考虑,数据在现实中是否允许修改/删除。如果允许就应该增加相应的需求。
29
+ 6. 最终产出的 interactions-design.json 仍然使用原文档里的 interaction 数据结构,但以流程为组来组织。
30
+
31
+ 注意,在每一个步骤中,都要给出清晰的数据结构定义,用 json 来写。
32
+ 整体用简洁的英语完成文档重写。注意原本文档中的在关键步骤 update STATUS.json 仍然按照原文档的方式写。
33
+
34
+