interaqt 0.8.4 → 0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent/.claude/agents/frontend-generation-handler.md +17 -0
- package/agent/.claude/agents/implement-integration-handler.md +257 -25
- package/agent/.claude/agents/requirements-analysis-handler.md +74 -0
- package/agent/.claude/settings.local.json +3 -1
- package/dist/index.js +16 -2
- package/dist/index.js.map +1 -1
- package/dist/shared/Entity.d.ts +14 -0
- package/dist/shared/Entity.d.ts.map +1 -1
- package/dist/shared/Relation.d.ts +3 -0
- package/dist/shared/Relation.d.ts.map +1 -1
- package/dist/storage/erstorage/MergedItemProcessor.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -205,6 +205,7 @@ npm run generate-frontend-api
|
|
|
205
205
|
- **Framework**: React + TypeScript
|
|
206
206
|
- **Build Tool**: Vite
|
|
207
207
|
- **Styling**: Tailwind CSS
|
|
208
|
+
- **Routing**: React Router
|
|
208
209
|
- **State Management**: React Context (for API client and global state)
|
|
209
210
|
|
|
210
211
|
**Project Structure:**
|
|
@@ -307,6 +308,22 @@ npm run generate-frontend-api
|
|
|
307
308
|
- Test on different screen sizes
|
|
308
309
|
- Ensure mobile-friendly layouts
|
|
309
310
|
|
|
311
|
+
7. **Modular Navigation:**
|
|
312
|
+
- **Module Entry Points**: Create a main navigation menu listing all modules
|
|
313
|
+
- **Route Organization**: Structure routes with module prefixes (e.g., `/donate/*`, `/livestream/*`)
|
|
314
|
+
- **Active Module Indicator**: Highlight current module in navigation menu
|
|
315
|
+
- **Breadcrumb Navigation**: Show module name → page hierarchy
|
|
316
|
+
- **Module Switching**: Enable seamless navigation between modules
|
|
317
|
+
- **Example Structure**:
|
|
318
|
+
```typescript
|
|
319
|
+
// Main App Router
|
|
320
|
+
<Routes>
|
|
321
|
+
<Route path="/" element={<ModuleSelector />} />
|
|
322
|
+
<Route path="/donate/*" element={<DonateModule />} />
|
|
323
|
+
<Route path="/livestream/*" element={<LivestreamModule />} />
|
|
324
|
+
</Routes>
|
|
325
|
+
```
|
|
326
|
+
|
|
310
327
|
**🔴 CRITICAL: Completeness Check**
|
|
311
328
|
|
|
312
329
|
Before marking Step 4 complete, verify:
|
|
@@ -302,6 +302,7 @@ interface IIntegration {
|
|
|
302
302
|
setup?(controller: Controller): Promise<any> // Setup phase with controller access
|
|
303
303
|
createSideEffects(): RecordMutationSideEffect[] // Listen to data mutations and create events
|
|
304
304
|
createAPIs?(): APIs // Expose custom APIs (e.g., webhook endpoints)
|
|
305
|
+
createMiddlewares?(): MiddlewareHandler[] // Optional: Create HTTP middleware for request processing
|
|
305
306
|
}
|
|
306
307
|
```
|
|
307
308
|
|
|
@@ -313,6 +314,63 @@ interface IIntegration {
|
|
|
313
314
|
1. Webhook endpoints to receive external system callbacks
|
|
314
315
|
2. Manual trigger/query APIs for status checks and retries
|
|
315
316
|
3. Frontend support APIs (e.g., pre-signed URLs for uploads)
|
|
317
|
+
- **createMiddlewares()**: Optional method to create HTTP middleware for request processing (e.g., authentication, authorization, request validation)
|
|
318
|
+
|
|
319
|
+
**🔴 CRITICAL: Separation Between API Layer and Integration Layer**
|
|
320
|
+
|
|
321
|
+
**API File Responsibilities** (`integrations/{name}/externalApi.ts` or `integrations/{name}/volcApi.ts`):
|
|
322
|
+
- Construct HTTP requests according to external API documentation
|
|
323
|
+
- Call external APIs and return raw responses
|
|
324
|
+
- **NO data transformation** - return data as-is from external system
|
|
325
|
+
- Define **strict TypeScript types** based on official API documentation:
|
|
326
|
+
- Input parameter types (exactly matching API requirements)
|
|
327
|
+
- Output response types (exactly matching API responses)
|
|
328
|
+
- Handle only HTTP-level errors (network failures, status codes)
|
|
329
|
+
|
|
330
|
+
**Integration File Responsibilities** (`integrations/{name}/index.ts`):
|
|
331
|
+
- Call API file methods to interact with external system
|
|
332
|
+
- **Transform external API responses** into internal event format
|
|
333
|
+
- Map external data structures to business event entity fields
|
|
334
|
+
- Create integration events following unified sequence
|
|
335
|
+
- Handle business-level error scenarios
|
|
336
|
+
|
|
337
|
+
**Example:**
|
|
338
|
+
```typescript
|
|
339
|
+
// ❌ WRONG: API file transforms data
|
|
340
|
+
// integrations/tts/externalApi.ts
|
|
341
|
+
export async function callTTSApi(params: TTSParams): Promise<{ audioUrl: string }> {
|
|
342
|
+
const response = await fetch(...)
|
|
343
|
+
const data = await response.json()
|
|
344
|
+
return { audioUrl: data.result.url } // ❌ Transformation in API file
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// ✅ CORRECT: API file returns raw response
|
|
348
|
+
// integrations/tts/externalApi.ts
|
|
349
|
+
export type TTSApiResponse = {
|
|
350
|
+
taskId: string
|
|
351
|
+
status: string
|
|
352
|
+
result?: {
|
|
353
|
+
url: string
|
|
354
|
+
duration: number
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
export async function callTTSApi(params: TTSParams): Promise<TTSApiResponse> {
|
|
359
|
+
const response = await fetch(...)
|
|
360
|
+
return await response.json() // ✅ Raw response with strict types
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// ✅ CORRECT: Integration file transforms data
|
|
364
|
+
// integrations/tts/index.ts
|
|
365
|
+
const apiResponse = await callTTSApi(requestParams)
|
|
366
|
+
|
|
367
|
+
// Transform to internal event format
|
|
368
|
+
await this.createIntegrationEvent(controller, apiCall.id, apiResponse.taskId, 'initialized', {
|
|
369
|
+
taskId: apiResponse.taskId, // Map to event fields
|
|
370
|
+
status: apiResponse.status,
|
|
371
|
+
audioUrl: apiResponse.result?.url // Extract what business needs
|
|
372
|
+
}, null)
|
|
373
|
+
```
|
|
316
374
|
|
|
317
375
|
# Task 4: Integration Implementation
|
|
318
376
|
|
|
@@ -831,14 +889,26 @@ mkdir -p integrations/{integration-name}
|
|
|
831
889
|
|
|
832
890
|
### 4.4.2 Create External API Wrapper (if no SDK)
|
|
833
891
|
|
|
892
|
+
**🔴 CRITICAL: API File Responsibilities**
|
|
893
|
+
|
|
894
|
+
This file MUST:
|
|
895
|
+
- Return raw API responses without transformation
|
|
896
|
+
- Define strict TypeScript types matching official API documentation
|
|
897
|
+
- Handle only HTTP-level errors
|
|
898
|
+
|
|
899
|
+
This file MUST NOT:
|
|
900
|
+
- Transform data to internal event format (that's integration file's job)
|
|
901
|
+
- Create any integration events
|
|
902
|
+
- Handle business logic
|
|
903
|
+
|
|
834
904
|
Create `integrations/{integration-name}/externalApi.ts`:
|
|
835
905
|
|
|
836
906
|
```typescript
|
|
837
907
|
/**
|
|
838
908
|
* External API wrapper for {System Name}
|
|
839
909
|
*
|
|
840
|
-
* This
|
|
841
|
-
*
|
|
910
|
+
* CRITICAL: This file returns raw API responses with strict types.
|
|
911
|
+
* NO data transformation - integration file handles that.
|
|
842
912
|
*/
|
|
843
913
|
|
|
844
914
|
export type ExternalApiConfig = {
|
|
@@ -846,16 +916,37 @@ export type ExternalApiConfig = {
|
|
|
846
916
|
baseUrl?: string
|
|
847
917
|
}
|
|
848
918
|
|
|
919
|
+
/**
|
|
920
|
+
* Request parameters - MUST match external API documentation exactly
|
|
921
|
+
*/
|
|
849
922
|
export type RequestParams = {
|
|
850
|
-
// Define
|
|
923
|
+
// Define according to official API docs
|
|
924
|
+
param1: string
|
|
925
|
+
param2: number
|
|
926
|
+
// ... more parameters
|
|
851
927
|
}
|
|
852
928
|
|
|
929
|
+
/**
|
|
930
|
+
* Response data - MUST match external API response exactly
|
|
931
|
+
*/
|
|
853
932
|
export type ResponseData = {
|
|
854
|
-
// Define
|
|
933
|
+
// Define according to official API docs
|
|
934
|
+
taskId: string
|
|
935
|
+
status: 'pending' | 'processing' | 'completed' | 'failed'
|
|
936
|
+
result?: {
|
|
937
|
+
// Define result structure from API docs
|
|
938
|
+
data: any
|
|
939
|
+
}
|
|
940
|
+
error?: {
|
|
941
|
+
code: string
|
|
942
|
+
message: string
|
|
943
|
+
}
|
|
855
944
|
}
|
|
856
945
|
|
|
857
946
|
/**
|
|
858
947
|
* Call external API
|
|
948
|
+
*
|
|
949
|
+
* Returns raw API response - NO transformation
|
|
859
950
|
*/
|
|
860
951
|
export async function callExternalApi(
|
|
861
952
|
params: RequestParams,
|
|
@@ -882,7 +973,8 @@ export async function callExternalApi(
|
|
|
882
973
|
throw new Error(`API call failed: ${response.statusText}`)
|
|
883
974
|
}
|
|
884
975
|
|
|
885
|
-
|
|
976
|
+
// Return raw response - integration file will transform it
|
|
977
|
+
const data: ResponseData = await response.json()
|
|
886
978
|
return data
|
|
887
979
|
} catch (error: any) {
|
|
888
980
|
console.error('[ExternalAPI] Call failed:', error.message)
|
|
@@ -892,12 +984,26 @@ export async function callExternalApi(
|
|
|
892
984
|
|
|
893
985
|
/**
|
|
894
986
|
* Query external status
|
|
987
|
+
*
|
|
988
|
+
* Returns raw status response - NO transformation
|
|
895
989
|
*/
|
|
896
990
|
export async function queryExternalStatus(
|
|
897
991
|
taskId: string,
|
|
898
992
|
config?: ExternalApiConfig
|
|
899
993
|
): Promise<ResponseData> {
|
|
900
|
-
// Similar implementation
|
|
994
|
+
// Similar implementation - return raw response
|
|
995
|
+
const apiKey = config?.apiKey
|
|
996
|
+
const baseUrl = config?.baseUrl
|
|
997
|
+
|
|
998
|
+
const response = await fetch(`${baseUrl}/v1/status/${taskId}`, {
|
|
999
|
+
method: 'GET',
|
|
1000
|
+
headers: {
|
|
1001
|
+
'Authorization': `Bearer ${apiKey}`
|
|
1002
|
+
}
|
|
1003
|
+
})
|
|
1004
|
+
|
|
1005
|
+
const data: ResponseData = await response.json()
|
|
1006
|
+
return data // Raw response
|
|
901
1007
|
}
|
|
902
1008
|
```
|
|
903
1009
|
|
|
@@ -912,8 +1018,9 @@ Create `integrations/{integration-name}/index.ts`:
|
|
|
912
1018
|
* Purpose: {Brief description}
|
|
913
1019
|
*
|
|
914
1020
|
* Features:
|
|
915
|
-
* - Listen to
|
|
916
|
-
* -
|
|
1021
|
+
* - Listen to APICall entity creation and trigger external API calls
|
|
1022
|
+
* - Transform external API responses to internal event format
|
|
1023
|
+
* - Create integration events following unified sequence
|
|
917
1024
|
* - Provide manual status refresh API
|
|
918
1025
|
* - Factory function pattern for configuration flexibility
|
|
919
1026
|
*/
|
|
@@ -1107,31 +1214,42 @@ export function create{IntegrationName}Integration(config: {IntegrationName}Conf
|
|
|
1107
1214
|
requestParams
|
|
1108
1215
|
})
|
|
1109
1216
|
|
|
1110
|
-
// Step 2: Call external API
|
|
1217
|
+
// Step 2: Call external API (returns raw response with strict types)
|
|
1111
1218
|
try {
|
|
1112
|
-
const
|
|
1219
|
+
const apiResponse = await callExternalApi(requestParams)
|
|
1220
|
+
// apiResponse has type ResponseData from API file
|
|
1113
1221
|
|
|
1222
|
+
// Step 3: Transform external response to internal event format
|
|
1114
1223
|
// Determine externalId: use API's task ID or generate one
|
|
1115
|
-
const externalId =
|
|
1224
|
+
const externalId = apiResponse.taskId
|
|
1116
1225
|
|
|
1117
1226
|
console.log('[{IntegrationName}] External API called', {
|
|
1118
1227
|
apiCallId: apiCall.id,
|
|
1119
1228
|
externalId,
|
|
1120
|
-
hasTaskId: !!(
|
|
1229
|
+
hasTaskId: !!(apiResponse.taskId)
|
|
1121
1230
|
})
|
|
1122
1231
|
|
|
1232
|
+
// Step 4: Transform and create 'initialized' event
|
|
1233
|
+
// Map external fields to internal event format
|
|
1234
|
+
const eventData = {
|
|
1235
|
+
taskId: apiResponse.taskId,
|
|
1236
|
+
status: apiResponse.status,
|
|
1237
|
+
// Map other fields as needed for business logic
|
|
1238
|
+
rawResponse: apiResponse // Keep raw response if needed
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1123
1241
|
// ALWAYS create 'initialized' event with both entityId and externalId
|
|
1124
1242
|
await self.createIntegrationEvent(
|
|
1125
1243
|
this,
|
|
1126
1244
|
apiCall.id, // entityId - APICall's id
|
|
1127
1245
|
externalId, // externalId - task ID or generated UUID
|
|
1128
1246
|
'initialized',
|
|
1129
|
-
|
|
1247
|
+
eventData, // Transformed data, not raw response
|
|
1130
1248
|
null
|
|
1131
1249
|
)
|
|
1132
1250
|
|
|
1133
1251
|
// For sync APIs (no task ID): immediately create processing and completed events
|
|
1134
|
-
if (!
|
|
1252
|
+
if (!apiResponse.taskId) {
|
|
1135
1253
|
// Immediately create processing event
|
|
1136
1254
|
await self.createIntegrationEvent(
|
|
1137
1255
|
this,
|
|
@@ -1142,19 +1260,26 @@ export function create{IntegrationName}Integration(config: {IntegrationName}Conf
|
|
|
1142
1260
|
null
|
|
1143
1261
|
)
|
|
1144
1262
|
|
|
1145
|
-
//
|
|
1263
|
+
// Transform completion data
|
|
1264
|
+
const completedData = {
|
|
1265
|
+
status: 'completed',
|
|
1266
|
+
result: apiResponse.result,
|
|
1267
|
+
// Map other completion fields
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
// Immediately create completed event with transformed data
|
|
1146
1271
|
await self.createIntegrationEvent(
|
|
1147
1272
|
this,
|
|
1148
1273
|
null,
|
|
1149
1274
|
externalId,
|
|
1150
1275
|
'completed',
|
|
1151
|
-
|
|
1276
|
+
completedData, // Transformed data
|
|
1152
1277
|
null
|
|
1153
1278
|
)
|
|
1154
1279
|
|
|
1155
1280
|
console.log('[{IntegrationName}] Sync API completed with unified event sequence')
|
|
1156
1281
|
}
|
|
1157
|
-
// For async APIs:
|
|
1282
|
+
// For async APIs: status will come later via webhook or polling
|
|
1158
1283
|
|
|
1159
1284
|
} catch (error: any) {
|
|
1160
1285
|
console.error('[{IntegrationName}] External API call failed', {
|
|
@@ -1212,10 +1337,11 @@ export function create{IntegrationName}Integration(config: {IntegrationName}Conf
|
|
|
1212
1337
|
apiCallId: string
|
|
1213
1338
|
}) {
|
|
1214
1339
|
try {
|
|
1215
|
-
await self.checkAndUpdateStatus(params.apiCallId)
|
|
1340
|
+
const apiResponse = await self.checkAndUpdateStatus(params.apiCallId)
|
|
1216
1341
|
return {
|
|
1217
1342
|
success: true,
|
|
1218
|
-
message: 'Status check triggered, integration event created'
|
|
1343
|
+
message: 'Status check triggered, integration event created',
|
|
1344
|
+
response: apiResponse
|
|
1219
1345
|
}
|
|
1220
1346
|
} catch (error: any) {
|
|
1221
1347
|
console.error('[{IntegrationName}] Failed to query status', {
|
|
@@ -1343,25 +1469,36 @@ export function create{IntegrationName}Integration(config: {IntegrationName}Conf
|
|
|
1343
1469
|
throw new Error(`No external ID found for APICall: ${apiCallId}`)
|
|
1344
1470
|
}
|
|
1345
1471
|
|
|
1346
|
-
// Query external system
|
|
1347
|
-
const
|
|
1472
|
+
// Query external system (returns raw response)
|
|
1473
|
+
const apiResponse = await queryExternalStatus(externalId)
|
|
1348
1474
|
|
|
1349
1475
|
console.log('[{IntegrationName}] Status checked', {
|
|
1350
1476
|
apiCallId,
|
|
1351
1477
|
externalId,
|
|
1352
|
-
status:
|
|
1478
|
+
status: apiResponse.status
|
|
1353
1479
|
})
|
|
1354
1480
|
|
|
1481
|
+
// Transform external response to internal event format
|
|
1482
|
+
const eventType = apiResponse.status // Map external status to event type
|
|
1483
|
+
const eventData = apiResponse.result ? {
|
|
1484
|
+
status: apiResponse.status,
|
|
1485
|
+
result: apiResponse.result,
|
|
1486
|
+
// Map other fields as needed
|
|
1487
|
+
} : null
|
|
1488
|
+
const errorMessage = apiResponse.error?.message || null
|
|
1489
|
+
|
|
1355
1490
|
// Create integration event based on status
|
|
1356
1491
|
// entityId is null because APICall already exists (not 'initialized' event)
|
|
1357
1492
|
await this.createIntegrationEvent(
|
|
1358
1493
|
this.controller,
|
|
1359
1494
|
null, // entityId - not needed for status updates
|
|
1360
1495
|
externalId, // externalId - to match with existing APICall
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1496
|
+
eventType, // eventType - 'processing' | 'completed' | 'failed'
|
|
1497
|
+
eventData, // Transformed data, not raw response
|
|
1498
|
+
errorMessage // Extracted error message
|
|
1364
1499
|
)
|
|
1500
|
+
|
|
1501
|
+
return apiResponse
|
|
1365
1502
|
}
|
|
1366
1503
|
}
|
|
1367
1504
|
}
|
|
@@ -1687,6 +1824,101 @@ createAPIs() {
|
|
|
1687
1824
|
}
|
|
1688
1825
|
```
|
|
1689
1826
|
|
|
1827
|
+
## Integration Middleware
|
|
1828
|
+
|
|
1829
|
+
**🔴 CRITICAL: Middleware for Request Processing**
|
|
1830
|
+
|
|
1831
|
+
Integrations can provide HTTP middleware to handle cross-cutting concerns like authentication, authorization, request validation, logging, etc.
|
|
1832
|
+
|
|
1833
|
+
**When to use middleware:**
|
|
1834
|
+
- Authentication and authorization (e.g., JWT verification)
|
|
1835
|
+
- Request/response transformation
|
|
1836
|
+
- Logging and monitoring
|
|
1837
|
+
- Rate limiting
|
|
1838
|
+
- CORS handling
|
|
1839
|
+
- Custom header processing
|
|
1840
|
+
|
|
1841
|
+
**Middleware execution:**
|
|
1842
|
+
- Middleware runs BEFORE API handlers
|
|
1843
|
+
- Can access and modify request context
|
|
1844
|
+
- Can short-circuit request processing
|
|
1845
|
+
- Can inject context data for API handlers
|
|
1846
|
+
|
|
1847
|
+
**Example: Authentication Middleware**
|
|
1848
|
+
|
|
1849
|
+
```typescript
|
|
1850
|
+
export function createAuthIntegration(config: AuthIntegrationConfig) {
|
|
1851
|
+
return class AuthIntegration implements IIntegration {
|
|
1852
|
+
createMiddlewares(): MiddlewareHandler[] {
|
|
1853
|
+
return [
|
|
1854
|
+
async (c, next) => {
|
|
1855
|
+
// Extract token from multiple sources
|
|
1856
|
+
let token: string | undefined
|
|
1857
|
+
const authToken = c.req.query('authToken')
|
|
1858
|
+
const authHeader = c.req.header('authorization')
|
|
1859
|
+
const cookieHeader = c.req.header('cookie')
|
|
1860
|
+
|
|
1861
|
+
if (authToken) {
|
|
1862
|
+
token = authToken
|
|
1863
|
+
} else if (authHeader?.startsWith('Bearer ')) {
|
|
1864
|
+
token = authHeader.substring(7)
|
|
1865
|
+
} else if (cookieHeader) {
|
|
1866
|
+
const allCookies: Record<string, string> = {}
|
|
1867
|
+
cookieHeader.split(';').forEach((cookie) => {
|
|
1868
|
+
const [name, value] = cookie.trim().split('=')
|
|
1869
|
+
if (name && value) {
|
|
1870
|
+
allCookies[name] = decodeURIComponent(value)
|
|
1871
|
+
}
|
|
1872
|
+
})
|
|
1873
|
+
token = allCookies.token
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
// Verify token and inject userId into context
|
|
1877
|
+
if (token) {
|
|
1878
|
+
try {
|
|
1879
|
+
const decoded = await verify(token, jwtSecret) as any
|
|
1880
|
+
c.set('userId', decoded.userId)
|
|
1881
|
+
} catch (err) {
|
|
1882
|
+
console.log('Invalid token', err)
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
await next()
|
|
1887
|
+
}
|
|
1888
|
+
]
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
createAPIs() {
|
|
1892
|
+
return {
|
|
1893
|
+
login: createAPI(
|
|
1894
|
+
async function(context, params: { identifier: string; password: string }) {
|
|
1895
|
+
// Authenticate user
|
|
1896
|
+
// Generate JWT token
|
|
1897
|
+
// Return token to client
|
|
1898
|
+
},
|
|
1899
|
+
{ allowAnonymous: true }
|
|
1900
|
+
)
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
```
|
|
1906
|
+
|
|
1907
|
+
**Key principles:**
|
|
1908
|
+
- **Middleware is optional**: Only use when needed for cross-cutting concerns
|
|
1909
|
+
- **Order matters**: Middleware executes in the order returned from createMiddlewares()
|
|
1910
|
+
- **Context injection**: Use `c.set()` to inject data into context for API handlers
|
|
1911
|
+
- **Non-blocking**: Always call `await next()` to continue the middleware chain
|
|
1912
|
+
- **Error handling**: Middleware can catch and handle errors before they reach API handlers
|
|
1913
|
+
|
|
1914
|
+
**Common use cases:**
|
|
1915
|
+
1. **Authentication** (`integrations/auth/index.ts`): JWT verification, session management
|
|
1916
|
+
2. **Authorization**: Role-based access control, permission checks
|
|
1917
|
+
3. **Request validation**: Schema validation, sanitization
|
|
1918
|
+
4. **Logging**: Request/response logging, performance monitoring
|
|
1919
|
+
5. **Rate limiting**: Throttle requests per user/IP
|
|
1920
|
+
6. **CORS**: Handle cross-origin requests
|
|
1921
|
+
|
|
1690
1922
|
## Example: Stripe Payment Integration
|
|
1691
1923
|
|
|
1692
1924
|
```typescript
|
|
@@ -553,6 +553,80 @@ git commit -m "feat: Task 1.2 - Complete functional requirements analysis"
|
|
|
553
553
|
|
|
554
554
|
**External side-effects requiring third-party APIs must be identified separately.**
|
|
555
555
|
|
|
556
|
+
### Special Case: Authentication Integration
|
|
557
|
+
|
|
558
|
+
**⚠️ CRITICAL: Authentication for Basic Module**
|
|
559
|
+
|
|
560
|
+
If the current module is "basic", authentication (login/registration) requirements must be handled as an integration named **"auth"**.
|
|
561
|
+
|
|
562
|
+
**Authentication vs Business Logic:**
|
|
563
|
+
1. **Authentication (Integration)**:
|
|
564
|
+
- User login with credentials
|
|
565
|
+
- User registration/signup
|
|
566
|
+
- Password reset/recovery
|
|
567
|
+
- OAuth/social login
|
|
568
|
+
- These are NOT part of business logic - treat as integration "auth"
|
|
569
|
+
|
|
570
|
+
2. **User Management (Business Logic)**:
|
|
571
|
+
- Administrator creates user accounts
|
|
572
|
+
- Administrator updates user information
|
|
573
|
+
- Administrator deactivates users
|
|
574
|
+
- These ARE business data operations - design as regular interactions
|
|
575
|
+
|
|
576
|
+
**Handling Authentication Requirements:**
|
|
577
|
+
|
|
578
|
+
**Case 1: User explicitly described login/registration requirements**
|
|
579
|
+
- Extract ALL authentication-related requirements into integration "auth"
|
|
580
|
+
- Do NOT create interactions for login/registration in Task 1.5
|
|
581
|
+
- Document the specific authentication flow in integration.json
|
|
582
|
+
|
|
583
|
+
**Case 2: User did NOT mention login/registration**
|
|
584
|
+
- For "basic" module, assume authentication is needed
|
|
585
|
+
- Design a simple password-based authentication integration
|
|
586
|
+
- Use default specification (see example below)
|
|
587
|
+
|
|
588
|
+
**Default Password Authentication Integration:**
|
|
589
|
+
```json
|
|
590
|
+
{
|
|
591
|
+
"id": "INT_AUTH",
|
|
592
|
+
"name": "auth",
|
|
593
|
+
"type": "stateful-system",
|
|
594
|
+
"type_explanation": "Authentication system maintains session state and user credentials. Requires bidirectional communication for login, registration, and session management.",
|
|
595
|
+
"external_system": "Authentication Service",
|
|
596
|
+
"purpose": "Handle user authentication and registration for the system",
|
|
597
|
+
"related_requirements": ["User login", "User registration"],
|
|
598
|
+
"flow_description": "For registration: User provides username/email and password in current system. System sends registration request (username, password) to authentication service. Authentication service validates uniqueness, hashes password, creates auth record, and returns user credentials. Current system receives response and creates User entity with returned user ID. For login: User provides credentials in current system. System sends login request to authentication service. Authentication service validates credentials and returns session token. Current system stores session and grants access.",
|
|
599
|
+
"user_interactions": {
|
|
600
|
+
"in_current_system": [
|
|
601
|
+
"User enters username/email and password for registration",
|
|
602
|
+
"User enters credentials for login",
|
|
603
|
+
"User views login success/failure messages"
|
|
604
|
+
],
|
|
605
|
+
"in_external_system": [
|
|
606
|
+
"Authentication service validates credential format",
|
|
607
|
+
"Authentication service checks username/email uniqueness",
|
|
608
|
+
"Authentication service hashes password securely",
|
|
609
|
+
"Authentication service generates session tokens"
|
|
610
|
+
]
|
|
611
|
+
},
|
|
612
|
+
"current_system_data": [
|
|
613
|
+
{
|
|
614
|
+
"entity": "User",
|
|
615
|
+
"properties": ["id", "username", "email"],
|
|
616
|
+
"usage": "Created after successful registration using ID returned from authentication service. Read during login to fetch user profile."
|
|
617
|
+
}
|
|
618
|
+
],
|
|
619
|
+
"notes": "This is a default password-based authentication. If user requirements specify OAuth, biometric, or other authentication methods, adjust accordingly."
|
|
620
|
+
}
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
**Important Notes:**
|
|
624
|
+
- Authentication integration should ALWAYS be named "auth" for consistency
|
|
625
|
+
- Do NOT create "User" entity properties for password, passwordHash, or session tokens - these belong to the auth integration
|
|
626
|
+
- User entity should only contain business-related properties (username, email, profile info)
|
|
627
|
+
- In Task 1.4, do NOT create entities for authentication (no AuthSession, no Credentials entities)
|
|
628
|
+
- In Task 1.5, do NOT create login/registration interactions - these are handled by the auth integration
|
|
629
|
+
|
|
556
630
|
### What is NOT an Integration
|
|
557
631
|
|
|
558
632
|
**⚠️ CRITICAL: Intra-Project Module Access**
|
|
@@ -73,7 +73,9 @@
|
|
|
73
73
|
"Bash(else echo \"VOLC_ACCESS_KEY_ID is set\")",
|
|
74
74
|
"Bash(fi:*)",
|
|
75
75
|
"Bash(if [ -z \"$VOLC_SECRET_ACCESS_KEY\" ])",
|
|
76
|
-
"Bash(then echo \"VOLC_SECRET_ACCESS_KEY is empty or not set\")"
|
|
76
|
+
"Bash(then echo \"VOLC_SECRET_ACCESS_KEY is empty or not set\")",
|
|
77
|
+
"Bash(npm run)",
|
|
78
|
+
"Bash(chmod:*)"
|
|
77
79
|
],
|
|
78
80
|
"deny": [],
|
|
79
81
|
"ask": []
|
package/dist/index.js
CHANGED
|
@@ -93,7 +93,7 @@ const Ie = /^[a-zA-Z0-9_]+$/;
|
|
|
93
93
|
class I {
|
|
94
94
|
// for Merged Entity
|
|
95
95
|
constructor(t, e) {
|
|
96
|
-
this._type = "Entity", this._options = e, this.uuid = k(e), this.name = t.name, this.properties = t.properties || [], this.computation = t.computation, this.baseEntity = t.baseEntity, this.matchExpression = t.matchExpression, this.inputEntities = t.inputEntities;
|
|
96
|
+
this._type = "Entity", this._options = e, this.uuid = k(e), this.name = t.name, this.properties = t.properties || [], this.computation = t.computation, this.baseEntity = t.baseEntity, this.matchExpression = t.matchExpression, this.inputEntities = t.inputEntities, this.commonProperties = t.commonProperties;
|
|
97
97
|
}
|
|
98
98
|
static {
|
|
99
99
|
this.isKlass = !0;
|
|
@@ -122,6 +122,15 @@ class I {
|
|
|
122
122
|
},
|
|
123
123
|
defaultValue: () => []
|
|
124
124
|
},
|
|
125
|
+
commonProperties: {
|
|
126
|
+
type: "Property",
|
|
127
|
+
collection: !0,
|
|
128
|
+
required: !1,
|
|
129
|
+
constraints: {
|
|
130
|
+
eachNameUnique: ({ properties: t }) => new Set(t.map((i) => i.name)).size === t.length
|
|
131
|
+
},
|
|
132
|
+
defaultValue: () => []
|
|
133
|
+
},
|
|
125
134
|
computation: {
|
|
126
135
|
type: [],
|
|
127
136
|
collection: !1,
|
|
@@ -390,7 +399,7 @@ class P {
|
|
|
390
399
|
if (s.target !== i.target)
|
|
391
400
|
throw new Error("All inputRelations must have the same target");
|
|
392
401
|
}
|
|
393
|
-
this.inputRelations = t.inputRelations, this.source = i.source, this.target = i.target, this.sourceProperty = t.sourceProperty, this.targetProperty = t.targetProperty, this.type = i.type, this.isTargetReliance = i.isTargetReliance, this._name = t.name;
|
|
402
|
+
this.inputRelations = t.inputRelations, this.source = i.source, this.target = i.target, this.sourceProperty = t.sourceProperty, this.targetProperty = t.targetProperty, this.type = i.type, this.isTargetReliance = i.isTargetReliance, this._name = t.name, this.commonProperties = t.commonProperties;
|
|
394
403
|
} else if (t.baseRelation) {
|
|
395
404
|
if (!t.sourceProperty || !t.targetProperty)
|
|
396
405
|
throw new Error("Filtered relation must have sourceProperty and targetProperty");
|
|
@@ -5536,6 +5545,11 @@ function He(u, t, e, i) {
|
|
|
5536
5545
|
);
|
|
5537
5546
|
t.replace(n, u), c !== n && t.add(c);
|
|
5538
5547
|
const l = pt(u);
|
|
5548
|
+
if (u.commonProperties) {
|
|
5549
|
+
const d = l.filter((h) => u.commonProperties.some((p) => !h.properties.some((y) => y.name === p.name && y.type === p.type)));
|
|
5550
|
+
if (d.length > 0)
|
|
5551
|
+
throw new Error(`Merged ${e} ${u.name} defined commonProperties, but these ${e}s do not have commonProperties: ${d.map((h) => h.name).join(", ")}`);
|
|
5552
|
+
}
|
|
5539
5553
|
if (l)
|
|
5540
5554
|
for (const d of l)
|
|
5541
5555
|
je(
|