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.
Files changed (35) hide show
  1. package/agent/.claude/agents/code-generation-handler.md +2 -0
  2. package/agent/.claude/agents/computation-generation-handler.md +1 -0
  3. package/agent/.claude/agents/implement-design-handler.md +4 -13
  4. package/agent/.claude/agents/requirements-analysis-handler.md +46 -14
  5. package/agent/agentspace/knowledge/generator/api-reference.md +3378 -0
  6. package/agent/agentspace/knowledge/generator/basic-interaction-generation.md +377 -0
  7. package/agent/agentspace/knowledge/generator/computation-analysis.md +307 -0
  8. package/agent/agentspace/knowledge/generator/computation-implementation.md +959 -0
  9. package/agent/agentspace/knowledge/generator/data-analysis.md +463 -0
  10. package/agent/agentspace/knowledge/generator/entity-relation-generation.md +395 -0
  11. package/agent/agentspace/knowledge/generator/permission-implementation.md +460 -0
  12. package/agent/agentspace/knowledge/generator/permission-test-implementation.md +870 -0
  13. package/agent/agentspace/knowledge/generator/test-implementation.md +674 -0
  14. package/agent/agentspace/knowledge/usage/00-mindset-shift.md +322 -0
  15. package/agent/agentspace/knowledge/usage/01-core-concepts.md +131 -0
  16. package/agent/agentspace/knowledge/usage/02-define-entities-properties.md +407 -0
  17. package/agent/agentspace/knowledge/usage/03-entity-relations.md +599 -0
  18. package/agent/agentspace/knowledge/usage/04-reactive-computations.md +2186 -0
  19. package/agent/agentspace/knowledge/usage/05-interactions.md +1411 -0
  20. package/agent/agentspace/knowledge/usage/06-attributive-permissions.md +10 -0
  21. package/agent/agentspace/knowledge/usage/07-payload-parameters.md +593 -0
  22. package/agent/agentspace/knowledge/usage/08-activities.md +863 -0
  23. package/agent/agentspace/knowledge/usage/09-filtered-entities.md +784 -0
  24. package/agent/agentspace/knowledge/usage/10-async-computations.md +734 -0
  25. package/agent/agentspace/knowledge/usage/11-global-dictionaries.md +942 -0
  26. package/agent/agentspace/knowledge/usage/12-data-querying.md +1033 -0
  27. package/agent/agentspace/knowledge/usage/13-testing.md +1201 -0
  28. package/agent/agentspace/knowledge/usage/14-api-reference.md +1606 -0
  29. package/agent/agentspace/knowledge/usage/15-entity-crud-patterns.md +1122 -0
  30. package/agent/agentspace/knowledge/usage/16-frontend-page-design-guide.md +485 -0
  31. package/agent/agentspace/knowledge/usage/17-performance-optimization.md +283 -0
  32. package/agent/agentspace/knowledge/usage/18-api-exports-reference.md +176 -0
  33. package/agent/agentspace/knowledge/usage/19-common-anti-patterns.md +563 -0
  34. package/agent/agentspace/knowledge/usage/README.md +148 -0
  35. package/package.json +1 -1
@@ -0,0 +1,460 @@
1
+ # Permission Implementation Guide
2
+
3
+ ## Overview
4
+ Permissions in interaqt are implemented through conditions in interactions. These control who can perform actions and under what circumstances.
5
+
6
+ **Important**: Conditions can return:
7
+ - `true` - Permission granted
8
+ - `false` - Permission denied (generic error)
9
+ - A string - Permission denied with specific error message (recommended)
10
+
11
+ ## 🔴 CRITICAL: Common Mistakes
12
+
13
+ ### Don't Check Permissions in Computations
14
+ ```typescript
15
+ // ❌ WRONG: Permission check in Transform
16
+ Transform.create({
17
+ source: CreateArticle,
18
+ computation: async (event) => {
19
+ if (event.user.role !== 'admin') { // Don't do this!
20
+ throw new Error('Not allowed');
21
+ }
22
+ return { ... };
23
+ }
24
+ })
25
+
26
+ // ✅ CORRECT: Use Conditions in Interaction
27
+ const CreateArticle = Interaction.create({
28
+ name: 'CreateArticle',
29
+ conditions: AdminRole // Check here!
30
+ });
31
+ ```
32
+
33
+ ### Use MatchExp in Conditions, Not BoolExp
34
+ ```typescript
35
+ // ❌ WRONG: Using BoolExp for queries
36
+ const CheckMyPost = Condition.create({
37
+ content: async function(this: Controller, event) {
38
+ // BoolExp is for logic, not queries!
39
+ return BoolExp.atom({ author: event.user })
40
+ }
41
+ })
42
+
43
+ // ✅ CORRECT: Use MatchExp for queries
44
+ const CheckMyPost = Condition.create({
45
+ content: async function(this: Controller, event) {
46
+ const post = await this.system.storage.findOne('Post',
47
+ MatchExp.atom({ key: 'author', value: ['=', event.user] })
48
+ )
49
+ return !!post;
50
+ }
51
+ })
52
+ ```
53
+
54
+ ### Use Conditions.create for BoolExp Combinations
55
+ Remember to wrap BoolExp combinations with Conditions.create:
56
+
57
+ ```typescript
58
+ import { Conditions } from 'interaqt';
59
+
60
+ conditions: Conditions.create({
61
+ content: BoolExp.atom(isAdmin).and(isActive)
62
+ })
63
+ ```
64
+
65
+ ## Basic Role-Based Permissions
66
+
67
+ ### 1. Define Role Conditions
68
+ ```typescript
69
+ import { Condition, BoolExp, Conditions, Interaction, Action, Payload, PayloadItem, MatchExp } from 'interaqt';
70
+
71
+ // Define role conditions
72
+ export const AdminRole = Condition.create({
73
+ name: 'AdminRole',
74
+ content: async function(this: Controller, event) {
75
+ return event.user?.role === 'admin';
76
+ }
77
+ });
78
+
79
+ export const OperatorRole = Condition.create({
80
+ name: 'OperatorRole',
81
+ content: async function(this: Controller, event) {
82
+ return event.user?.role === 'operator';
83
+ }
84
+ });
85
+
86
+ export const ViewerRole = Condition.create({
87
+ name: 'ViewerRole',
88
+ content: async function(this: Controller, event) {
89
+ return event.user?.role === 'viewer';
90
+ }
91
+ });
92
+
93
+ // Combined permission
94
+ export const OperatorOrAdmin = Condition.create({
95
+ name: 'OperatorOrAdmin',
96
+ content: async function(this: Controller, event) {
97
+ const role = event.user?.role;
98
+ return role === 'admin' || role === 'operator';
99
+ }
100
+ });
101
+ ```
102
+
103
+ ### 2. Apply to Interactions
104
+ ```typescript
105
+ // Admin-only action
106
+ export const DeleteStyle = Interaction.create({
107
+ name: 'DeleteStyle',
108
+ action: Action.create({ name: 'deleteStyle' }),
109
+ payload: Payload.create({
110
+ items: [
111
+ PayloadItem.create({
112
+ name: 'style',
113
+ base: Style,
114
+ isRef: true
115
+ })
116
+ ]
117
+ }),
118
+ conditions: AdminRole // Direct condition
119
+ });
120
+
121
+ // Multiple roles allowed
122
+ export const UpdateStyle = Interaction.create({
123
+ name: 'UpdateStyle',
124
+ action: Action.create({ name: 'updateStyle' }),
125
+ conditions: OperatorOrAdmin // Combined condition
126
+ });
127
+ ```
128
+
129
+ ## Advanced Permission Patterns
130
+
131
+ ### 1. Resource Ownership
132
+ ```typescript
133
+ // Check if user owns the resource
134
+ export const OwnerOnly = Condition.create({
135
+ name: 'OwnerOnly',
136
+ content: async function(this: Controller, event) {
137
+ const styleId = event.payload?.style?.id;
138
+ if (!styleId) return 'Style ID is required';
139
+
140
+ const style = await this.system.storage.findOne('Style',
141
+ MatchExp.atom({ key: 'id', value: ['=', styleId] }),
142
+ undefined,
143
+ ['creator'] // Must include creator field
144
+ );
145
+
146
+ if (style?.creator?.id !== event.user?.id) {
147
+ return 'You can only modify your own styles';
148
+ }
149
+
150
+ return true;
151
+ }
152
+ });
153
+
154
+ // Combine with role check
155
+ import { BoolExp, Conditions } from 'interaqt';
156
+
157
+ export const DeleteOwnStyle = Interaction.create({
158
+ name: 'DeleteOwnStyle',
159
+ action: Action.create({ name: 'deleteOwnStyle' }),
160
+ payload: Payload.create({
161
+ items: [
162
+ PayloadItem.create({
163
+ name: 'style',
164
+ base: Style,
165
+ isRef: true
166
+ })
167
+ ]
168
+ }),
169
+ conditions: Conditions.create({
170
+ content: BoolExp.atom(OwnerOnly).or(BoolExp.atom(AdminRole))
171
+ })
172
+ });
173
+ ```
174
+
175
+ ### 2. Context-Based Permissions
176
+ ```typescript
177
+ // Check resource state
178
+ export const PublishedOnly = Condition.create({
179
+ name: 'PublishedOnly',
180
+ content: async function(this: Controller, event) {
181
+ const styleId = event.payload?.style?.id;
182
+ if (!styleId) return 'Style ID is required';
183
+
184
+ const style = await this.system.storage.findOne('Style',
185
+ MatchExp.atom({ key: 'id', value: ['=', styleId] }),
186
+ undefined,
187
+ ['status'] // Include status field
188
+ );
189
+
190
+ if (style?.status !== 'published') {
191
+ return 'Style must be published to perform this action';
192
+ }
193
+
194
+ return true;
195
+ }
196
+ });
197
+
198
+ // Apply to interaction
199
+ export const ShareStyle = Interaction.create({
200
+ name: 'ShareStyle',
201
+ action: Action.create({ name: 'shareStyle' }),
202
+ conditions: Conditions.create({
203
+ content: BoolExp.atom(PublishedOnly).and(BoolExp.atom(OperatorOrAdmin))
204
+ })
205
+ });
206
+ ```
207
+
208
+ ### 3. Dynamic Permissions
209
+ ```typescript
210
+ // Permission based on user's department
211
+ export const SameDepartment = Condition.create({
212
+ name: 'SameDepartment',
213
+ content: async function(this: Controller, event) {
214
+ const targetUserId = event.payload?.targetUser?.id;
215
+ if (!targetUserId) return 'Target user ID is required';
216
+ if (!event.user?.department) return 'Your department information is missing';
217
+
218
+ const targetUser = await this.system.storage.findOne('User',
219
+ MatchExp.atom({ key: 'id', value: ['=', targetUserId] }),
220
+ undefined,
221
+ ['department']
222
+ );
223
+
224
+ if (targetUser?.department !== event.user.department) {
225
+ return 'You can only perform this action on users in your department';
226
+ }
227
+
228
+ return true;
229
+ }
230
+ });
231
+ ```
232
+
233
+ ### 4. Payload Validation in Conditions
234
+ ```typescript
235
+ // Validate payload data within conditions
236
+ export const ValidateStyleType = Condition.create({
237
+ name: 'ValidateStyleType',
238
+ content: async function(this: Controller, event) {
239
+ const styleData = event.payload?.styleData;
240
+ if (!styleData) return 'Style data is required';
241
+
242
+ const validTypes = ['theme', 'component', 'template'];
243
+ if (!validTypes.includes(styleData.type)) {
244
+ return `Invalid style type '${styleData.type}'. Must be one of: ${validTypes.join(', ')}`;
245
+ }
246
+
247
+ return true;
248
+ }
249
+ });
250
+
251
+ // Combine with user permission check
252
+ export const CreateStyle = Interaction.create({
253
+ name: 'CreateStyle',
254
+ action: Action.create({ name: 'createStyle' }),
255
+ payload: Payload.create({
256
+ items: [
257
+ PayloadItem.create({
258
+ name: 'styleData',
259
+ base: Style
260
+ })
261
+ ]
262
+ }),
263
+ conditions: Conditions.create({
264
+ content: BoolExp.atom(OperatorOrAdminRole).and(BoolExp.atom(ValidateStyleType))
265
+ })
266
+ });
267
+ ```
268
+
269
+ ## Real-World Example
270
+ ```typescript
271
+ import { Condition, BoolExp, Conditions, Interaction, Action, Payload, PayloadItem, MatchExp } from 'interaqt';
272
+
273
+ // Basic conditions
274
+ export const AuthenticatedUser = Condition.create({
275
+ name: 'AuthenticatedUser',
276
+ content: async function(this: Controller, event) {
277
+ if (!event.user || !event.user.id) {
278
+ return 'Authentication required';
279
+ }
280
+ return true;
281
+ }
282
+ });
283
+
284
+ export const ActiveUser = Condition.create({
285
+ name: 'ActiveUser',
286
+ content: async function(this: Controller, event) {
287
+ if (event.user?.status !== 'active') {
288
+ return 'Your account is not active';
289
+ }
290
+ return true;
291
+ }
292
+ });
293
+
294
+ export const AdminRole = Condition.create({
295
+ name: 'AdminRole',
296
+ content: async function(this: Controller, event) {
297
+ if (event.user?.role !== 'admin') {
298
+ return 'Admin privileges required';
299
+ }
300
+ return true;
301
+ }
302
+ });
303
+
304
+ // Resource conditions
305
+ export const StyleExists = Condition.create({
306
+ name: 'StyleExists',
307
+ content: async function(this: Controller, event) {
308
+ const styleId = event.payload?.style?.id;
309
+ if (!styleId) return 'Style ID is required';
310
+
311
+ const style = await this.system.storage.findOne('Style',
312
+ MatchExp.atom({ key: 'id', value: ['=', styleId] }),
313
+ undefined,
314
+ ['id']
315
+ );
316
+
317
+ if (!style) {
318
+ return 'Style not found';
319
+ }
320
+
321
+ return true;
322
+ }
323
+ });
324
+
325
+ export const StyleNotOffline = Condition.create({
326
+ name: 'StyleNotOffline',
327
+ content: async function(this: Controller, event) {
328
+ const styleId = event.payload?.style?.id;
329
+ if (!styleId) return 'Style ID is required';
330
+
331
+ const style = await this.system.storage.findOne('Style',
332
+ MatchExp.atom({ key: 'id', value: ['=', styleId] }),
333
+ undefined,
334
+ ['status']
335
+ );
336
+
337
+ if (style?.status === 'offline') {
338
+ return 'Cannot perform this action on offline styles';
339
+ }
340
+
341
+ return true;
342
+ }
343
+ });
344
+
345
+ // Complex interaction with multiple conditions
346
+ export const PublishStyle = Interaction.create({
347
+ name: 'PublishStyle',
348
+ action: Action.create({ name: 'publish' }),
349
+ payload: Payload.create({
350
+ items: [
351
+ PayloadItem.create({
352
+ name: 'style',
353
+ base: Style,
354
+ isRef: true
355
+ })
356
+ ]
357
+ }),
358
+ conditions: Conditions.create({
359
+ content: BoolExp.atom(AuthenticatedUser)
360
+ .and(BoolExp.atom(ActiveUser))
361
+ .and(BoolExp.atom(AdminRole))
362
+ .and(BoolExp.atom(StyleExists))
363
+ .and(BoolExp.atom(StyleNotOffline))
364
+ })
365
+ });
366
+
367
+ // Alternative: Define combined condition
368
+ export const CanPublishStyle = Conditions.create({
369
+ content: BoolExp.atom(AuthenticatedUser)
370
+ .and(BoolExp.atom(ActiveUser))
371
+ .and(BoolExp.atom(AdminRole))
372
+ .and(BoolExp.atom(StyleExists))
373
+ .and(BoolExp.atom(StyleNotOffline))
374
+ });
375
+
376
+ export const PublishStyleAlt = Interaction.create({
377
+ name: 'PublishStyle',
378
+ action: Action.create({ name: 'publish' }),
379
+ conditions: CanPublishStyle // Reusable condition
380
+ });
381
+ ```
382
+
383
+ ## Best Practices
384
+
385
+ ### 1. Condition Efficiency
386
+ ```typescript
387
+ // ✅ Check simple conditions first
388
+ const EfficientCheck = Condition.create({
389
+ name: 'EfficientCheck',
390
+ content: async function(this: Controller, event) {
391
+ // Simple check first
392
+ if (event.user.role === 'admin') {
393
+ return true;
394
+ }
395
+
396
+ // Then database query
397
+ const result = await this.system.storage.findOne(...);
398
+ return !!result;
399
+ }
400
+ });
401
+ ```
402
+
403
+ ### 2. Clear Error Context
404
+ ```typescript
405
+ // ✅ Provide meaningful error context
406
+ const WithContext = Condition.create({
407
+ name: 'WithContext',
408
+ content: async function(this: Controller, event) {
409
+ if (!event.user) {
410
+ event.error = 'User not authenticated';
411
+ return false;
412
+ }
413
+
414
+ if (event.user.role !== 'admin') {
415
+ event.error = 'Admin role required';
416
+ return false;
417
+ }
418
+
419
+ return true;
420
+ }
421
+ });
422
+ ```
423
+
424
+ ### 3. Reusable Conditions
425
+ ```typescript
426
+ // ✅ Create generic, reusable conditions
427
+ export const RequireRole = (role: string) => Condition.create({
428
+ name: `Require${role}Role`,
429
+ content: async function(this: Controller, event) {
430
+ return event.user?.role === role;
431
+ }
432
+ });
433
+
434
+ // Use in multiple places
435
+ const AdminOnly = RequireRole('admin');
436
+ const OperatorOnly = RequireRole('operator');
437
+ ```
438
+
439
+ ### When to Use Conditions.create
440
+ ```typescript
441
+ // Use when combining multiple conditions with BoolExp
442
+ conditions: Conditions.create({
443
+ content: BoolExp.atom(condition1)
444
+ .and(BoolExp.atom(condition2))
445
+ .or(BoolExp.atom(condition3))
446
+ })
447
+
448
+ // Single condition can be used directly
449
+ conditions: AdminRole
450
+ ```
451
+
452
+ ## Security Checklist
453
+ - [ ] Authentication check (user exists)
454
+ - [ ] Authorization check (user has permission)
455
+ - [ ] Resource existence validation
456
+ - [ ] Resource state validation
457
+ - [ ] Payload data validation
458
+ - [ ] BoolExp combinations wrapped with Conditions.create
459
+ - [ ] Efficient condition ordering (simple checks first)
460
+ - [ ] Clear error messages for debugging