@positronic/spec 0.0.51 → 0.0.53

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/dist/api.d.ts CHANGED
@@ -113,29 +113,44 @@ export declare const brains: {
113
113
  */
114
114
  killSuspended(fetch: Fetch, loopBrainIdentifier: string, webhookSlug: string, webhookPayload: Record<string, any>): Promise<boolean>;
115
115
  /**
116
- * Test that loop steps emit proper LOOP_* events in the SSE stream.
117
- * Requires a brain with a loop step that will pause on a webhook.
116
+ * Test that agent steps emit proper AGENT_* events in the SSE stream.
117
+ * Requires a brain with an agent step that will pause on a webhook.
118
118
  *
119
119
  * Expected events before webhook pause:
120
- * - LOOP_START (with prompt and optional system)
121
- * - LOOP_ITERATION
122
- * - LOOP_TOOL_CALL
123
- * - LOOP_WEBHOOK (before WEBHOOK event)
120
+ * - AGENT_START (with prompt and optional system)
121
+ * - AGENT_ITERATION
122
+ * - AGENT_TOOL_CALL
123
+ * - AGENT_WEBHOOK (before WEBHOOK event)
124
124
  * - WEBHOOK
125
125
  */
126
- watchLoopEvents(fetch: Fetch, loopBrainIdentifier: string): Promise<boolean>;
126
+ watchAgentEvents(fetch: Fetch, agentBrainIdentifier: string): Promise<boolean>;
127
127
  /**
128
- * Test full loop webhook resumption flow:
129
- * 1. Start a loop brain that will pause on a webhook
128
+ * Test full agent webhook resumption flow:
129
+ * 1. Start an agent brain that will pause on a webhook
130
130
  * 2. Verify it pauses with WEBHOOK event
131
131
  * 3. Trigger the webhook with a response
132
- * 4. Verify the brain resumes and emits WEBHOOK_RESPONSE and LOOP_TOOL_RESULT
132
+ * 4. Verify the brain resumes and emits WEBHOOK_RESPONSE and AGENT_TOOL_RESULT
133
133
  *
134
134
  * Requires:
135
- * - A brain with a loop step that calls a tool returning { waitFor: webhook(...) }
135
+ * - A brain with an agent step that calls a tool returning { waitFor: webhook(...) }
136
136
  * - The webhook slug and identifier to trigger
137
137
  */
138
- loopWebhookResume(fetch: Fetch, loopBrainIdentifier: string, webhookSlug: string, webhookPayload: Record<string, any>): Promise<boolean>;
138
+ agentWebhookResume(fetch: Fetch, agentBrainIdentifier: string, webhookSlug: string, webhookPayload: Record<string, any>): Promise<boolean>;
139
+ /**
140
+ * Test that inner brain COMPLETE events don't overwrite outer brain status.
141
+ *
142
+ * This test verifies that when a nested inner brain completes, the outer brain's
143
+ * status in history remains RUNNING (not prematurely set to COMPLETE).
144
+ *
145
+ * Test scenario:
146
+ * 1. Run outer brain that contains inner brain + webhook step after inner brain
147
+ * 2. Watch SSE until outer brain's WEBHOOK event (after inner completes)
148
+ * 3. Query history for the brain
149
+ * 4. Assert status is RUNNING (not COMPLETE from inner brain)
150
+ * 5. Trigger webhook to complete outer brain
151
+ * 6. Assert final status is COMPLETE
152
+ */
153
+ innerBrainCompleteDoesNotAffectOuterStatus(fetch: Fetch, outerBrainIdentifier: string, webhookSlug: string, webhookPayload: Record<string, any>): Promise<boolean>;
139
154
  };
140
155
  export declare const schedules: {
141
156
  /**
@@ -193,6 +208,20 @@ export declare const webhooks: {
193
208
  * Test POST /webhooks/:slug with non-existent webhook - Should return 404
194
209
  */
195
210
  notFound(fetch: Fetch, slug: string): Promise<boolean>;
211
+ /**
212
+ * Test POST /webhooks/system/ui-form - Built-in webhook for UI form submissions.
213
+ * This is used by pages generated via .ui() steps to submit form data.
214
+ *
215
+ * The endpoint:
216
+ * - Accepts form data (application/x-www-form-urlencoded or multipart/form-data)
217
+ * - Requires an `identifier` query parameter to match the waiting brain
218
+ * - Returns { received: true, action: 'resumed' | 'not_found', ... }
219
+ */
220
+ uiForm(fetch: Fetch, identifier: string, formData: Record<string, string | string[]>): Promise<boolean>;
221
+ /**
222
+ * Test POST /webhooks/system/ui-form with missing identifier - Should return 400
223
+ */
224
+ uiFormMissingIdentifier(fetch: Fetch): Promise<boolean>;
196
225
  };
197
226
  export declare const pages: {
198
227
  /**
@@ -238,5 +267,20 @@ export declare const pages: {
238
267
  */
239
268
  deletePreservesResources(fetch: Fetch): Promise<boolean>;
240
269
  };
270
+ /**
271
+ * Bundle API Tests
272
+ *
273
+ * Tests for the /bundle/components.js endpoint which serves the component bundle.
274
+ *
275
+ * NOTE: These tests only verify the API endpoint behavior. The bundle build and
276
+ * upload process is backend-specific and must be tested separately by each
277
+ * backend implementation.
278
+ */
279
+ export declare const bundle: {
280
+ /**
281
+ * Test GET /bundle/components.js - Serve the component bundle
282
+ */
283
+ get(fetch: Fetch): Promise<boolean>;
284
+ };
241
285
  export {};
242
286
  //# sourceMappingURL=api.d.ts.map
package/dist/api.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
package/dist/api.js CHANGED
@@ -1048,23 +1048,23 @@ export const brains = {
1048
1048
  }
1049
1049
  },
1050
1050
  /**
1051
- * Test that loop steps emit proper LOOP_* events in the SSE stream.
1052
- * Requires a brain with a loop step that will pause on a webhook.
1051
+ * Test that agent steps emit proper AGENT_* events in the SSE stream.
1052
+ * Requires a brain with an agent step that will pause on a webhook.
1053
1053
  *
1054
1054
  * Expected events before webhook pause:
1055
- * - LOOP_START (with prompt and optional system)
1056
- * - LOOP_ITERATION
1057
- * - LOOP_TOOL_CALL
1058
- * - LOOP_WEBHOOK (before WEBHOOK event)
1055
+ * - AGENT_START (with prompt and optional system)
1056
+ * - AGENT_ITERATION
1057
+ * - AGENT_TOOL_CALL
1058
+ * - AGENT_WEBHOOK (before WEBHOOK event)
1059
1059
  * - WEBHOOK
1060
1060
  */
1061
- async watchLoopEvents(fetch, loopBrainIdentifier) {
1061
+ async watchAgentEvents(fetch, agentBrainIdentifier) {
1062
1062
  try {
1063
- // Start the loop brain
1063
+ // Start the agent brain
1064
1064
  const runRequest = new Request('http://example.com/brains/runs', {
1065
1065
  method: 'POST',
1066
1066
  headers: { 'Content-Type': 'application/json' },
1067
- body: JSON.stringify({ identifier: loopBrainIdentifier }),
1067
+ body: JSON.stringify({ identifier: agentBrainIdentifier }),
1068
1068
  });
1069
1069
  const runResponse = await fetch(runRequest);
1070
1070
  if (runResponse.status !== 201) {
@@ -1120,72 +1120,72 @@ export const brains = {
1120
1120
  await reader.cancel();
1121
1121
  }
1122
1122
  }
1123
- // Verify required loop events are present
1124
- const hasLoopStart = events.some((e) => e.type === BRAIN_EVENTS.LOOP_START);
1125
- if (!hasLoopStart) {
1126
- console.error('Missing LOOP_START event in SSE stream');
1123
+ // Verify required agent events are present
1124
+ const hasAgentStart = events.some((e) => e.type === BRAIN_EVENTS.AGENT_START);
1125
+ if (!hasAgentStart) {
1126
+ console.error('Missing AGENT_START event in SSE stream');
1127
1127
  return false;
1128
1128
  }
1129
- // Verify LOOP_START has prompt field
1130
- const loopStartEvent = events.find((e) => e.type === BRAIN_EVENTS.LOOP_START);
1131
- if (!loopStartEvent.prompt || typeof loopStartEvent.prompt !== 'string') {
1132
- console.error('LOOP_START event missing prompt field');
1129
+ // Verify AGENT_START has prompt field
1130
+ const agentStartEvent = events.find((e) => e.type === BRAIN_EVENTS.AGENT_START);
1131
+ if (!agentStartEvent.prompt || typeof agentStartEvent.prompt !== 'string') {
1132
+ console.error('AGENT_START event missing prompt field');
1133
1133
  return false;
1134
1134
  }
1135
- const hasLoopIteration = events.some((e) => e.type === BRAIN_EVENTS.LOOP_ITERATION);
1136
- if (!hasLoopIteration) {
1137
- console.error('Missing LOOP_ITERATION event in SSE stream');
1135
+ const hasAgentIteration = events.some((e) => e.type === BRAIN_EVENTS.AGENT_ITERATION);
1136
+ if (!hasAgentIteration) {
1137
+ console.error('Missing AGENT_ITERATION event in SSE stream');
1138
1138
  return false;
1139
1139
  }
1140
- const hasLoopToolCall = events.some((e) => e.type === BRAIN_EVENTS.LOOP_TOOL_CALL);
1141
- if (!hasLoopToolCall) {
1142
- console.error('Missing LOOP_TOOL_CALL event in SSE stream');
1140
+ const hasAgentToolCall = events.some((e) => e.type === BRAIN_EVENTS.AGENT_TOOL_CALL);
1141
+ if (!hasAgentToolCall) {
1142
+ console.error('Missing AGENT_TOOL_CALL event in SSE stream');
1143
1143
  return false;
1144
1144
  }
1145
- // If we got a WEBHOOK event, verify LOOP_WEBHOOK came before it
1145
+ // If we got a WEBHOOK event, verify AGENT_WEBHOOK came before it
1146
1146
  const webhookIndex = events.findIndex((e) => e.type === BRAIN_EVENTS.WEBHOOK);
1147
1147
  if (webhookIndex !== -1) {
1148
- const loopWebhookIndex = events.findIndex((e) => e.type === BRAIN_EVENTS.LOOP_WEBHOOK);
1149
- if (loopWebhookIndex === -1) {
1150
- console.error('Missing LOOP_WEBHOOK event before WEBHOOK event');
1148
+ const agentWebhookIndex = events.findIndex((e) => e.type === BRAIN_EVENTS.AGENT_WEBHOOK);
1149
+ if (agentWebhookIndex === -1) {
1150
+ console.error('Missing AGENT_WEBHOOK event before WEBHOOK event');
1151
1151
  return false;
1152
1152
  }
1153
- if (loopWebhookIndex >= webhookIndex) {
1154
- console.error('LOOP_WEBHOOK event must come before WEBHOOK event');
1153
+ if (agentWebhookIndex >= webhookIndex) {
1154
+ console.error('AGENT_WEBHOOK event must come before WEBHOOK event');
1155
1155
  return false;
1156
1156
  }
1157
- // Verify LOOP_WEBHOOK has required fields
1158
- const loopWebhookEvent = events[loopWebhookIndex];
1159
- if (!loopWebhookEvent.toolCallId || !loopWebhookEvent.toolName) {
1160
- console.error('LOOP_WEBHOOK event missing toolCallId or toolName fields');
1157
+ // Verify AGENT_WEBHOOK has required fields
1158
+ const agentWebhookEvent = events[agentWebhookIndex];
1159
+ if (!agentWebhookEvent.toolCallId || !agentWebhookEvent.toolName) {
1160
+ console.error('AGENT_WEBHOOK event missing toolCallId or toolName fields');
1161
1161
  return false;
1162
1162
  }
1163
1163
  }
1164
1164
  return true;
1165
1165
  }
1166
1166
  catch (error) {
1167
- console.error(`Failed to test loop events for ${loopBrainIdentifier}:`, error);
1167
+ console.error(`Failed to test agent events for ${agentBrainIdentifier}:`, error);
1168
1168
  return false;
1169
1169
  }
1170
1170
  },
1171
1171
  /**
1172
- * Test full loop webhook resumption flow:
1173
- * 1. Start a loop brain that will pause on a webhook
1172
+ * Test full agent webhook resumption flow:
1173
+ * 1. Start an agent brain that will pause on a webhook
1174
1174
  * 2. Verify it pauses with WEBHOOK event
1175
1175
  * 3. Trigger the webhook with a response
1176
- * 4. Verify the brain resumes and emits WEBHOOK_RESPONSE and LOOP_TOOL_RESULT
1176
+ * 4. Verify the brain resumes and emits WEBHOOK_RESPONSE and AGENT_TOOL_RESULT
1177
1177
  *
1178
1178
  * Requires:
1179
- * - A brain with a loop step that calls a tool returning { waitFor: webhook(...) }
1179
+ * - A brain with an agent step that calls a tool returning { waitFor: webhook(...) }
1180
1180
  * - The webhook slug and identifier to trigger
1181
1181
  */
1182
- async loopWebhookResume(fetch, loopBrainIdentifier, webhookSlug, webhookPayload) {
1182
+ async agentWebhookResume(fetch, agentBrainIdentifier, webhookSlug, webhookPayload) {
1183
1183
  try {
1184
- // Step 1: Start the loop brain
1184
+ // Step 1: Start the agent brain
1185
1185
  const runRequest = new Request('http://example.com/brains/runs', {
1186
1186
  method: 'POST',
1187
1187
  headers: { 'Content-Type': 'application/json' },
1188
- body: JSON.stringify({ identifier: loopBrainIdentifier }),
1188
+ body: JSON.stringify({ identifier: agentBrainIdentifier }),
1189
1189
  });
1190
1190
  const runResponse = await fetch(runRequest);
1191
1191
  if (runResponse.status !== 201) {
@@ -1313,10 +1313,10 @@ export const brains = {
1313
1313
  console.error('Missing WEBHOOK_RESPONSE event after resume');
1314
1314
  return false;
1315
1315
  }
1316
- // Verify LOOP_TOOL_RESULT event is present (with the webhook response as result)
1317
- const hasLoopToolResult = resumeEvents.some((e) => e.type === BRAIN_EVENTS.LOOP_TOOL_RESULT);
1318
- if (!hasLoopToolResult) {
1319
- console.error('Missing LOOP_TOOL_RESULT event after resume');
1316
+ // Verify AGENT_TOOL_RESULT event is present (with the webhook response as result)
1317
+ const hasAgentToolResult = resumeEvents.some((e) => e.type === BRAIN_EVENTS.AGENT_TOOL_RESULT);
1318
+ if (!hasAgentToolResult) {
1319
+ console.error('Missing AGENT_TOOL_RESULT event after resume');
1320
1320
  return false;
1321
1321
  }
1322
1322
  // Verify brain completed successfully
@@ -1332,7 +1332,197 @@ export const brains = {
1332
1332
  return true;
1333
1333
  }
1334
1334
  catch (error) {
1335
- console.error(`Failed to test loop webhook resume for ${loopBrainIdentifier}:`, error);
1335
+ console.error(`Failed to test agent webhook resume for ${agentBrainIdentifier}:`, error);
1336
+ return false;
1337
+ }
1338
+ },
1339
+ /**
1340
+ * Test that inner brain COMPLETE events don't overwrite outer brain status.
1341
+ *
1342
+ * This test verifies that when a nested inner brain completes, the outer brain's
1343
+ * status in history remains RUNNING (not prematurely set to COMPLETE).
1344
+ *
1345
+ * Test scenario:
1346
+ * 1. Run outer brain that contains inner brain + webhook step after inner brain
1347
+ * 2. Watch SSE until outer brain's WEBHOOK event (after inner completes)
1348
+ * 3. Query history for the brain
1349
+ * 4. Assert status is RUNNING (not COMPLETE from inner brain)
1350
+ * 5. Trigger webhook to complete outer brain
1351
+ * 6. Assert final status is COMPLETE
1352
+ */
1353
+ async innerBrainCompleteDoesNotAffectOuterStatus(fetch, outerBrainIdentifier, webhookSlug, webhookPayload) {
1354
+ try {
1355
+ // Step 1: Start the outer brain
1356
+ const runRequest = new Request('http://example.com/brains/runs', {
1357
+ method: 'POST',
1358
+ headers: { 'Content-Type': 'application/json' },
1359
+ body: JSON.stringify({ identifier: outerBrainIdentifier }),
1360
+ });
1361
+ const runResponse = await fetch(runRequest);
1362
+ if (runResponse.status !== 201) {
1363
+ console.error(`POST /brains/runs returned ${runResponse.status}, expected 201`);
1364
+ return false;
1365
+ }
1366
+ const { brainRunId } = (await runResponse.json());
1367
+ // Step 2: Watch SSE until WEBHOOK event from outer brain (after inner completes)
1368
+ const watchRequest = new Request(`http://example.com/brains/runs/${brainRunId}/watch`, { method: 'GET' });
1369
+ const watchResponse = await fetch(watchRequest);
1370
+ if (!watchResponse.ok) {
1371
+ console.error(`GET /brains/runs/${brainRunId}/watch returned ${watchResponse.status}`);
1372
+ return false;
1373
+ }
1374
+ let foundOuterWebhook = false;
1375
+ let innerCompleteCount = 0;
1376
+ if (watchResponse.body) {
1377
+ const reader = watchResponse.body.getReader();
1378
+ const decoder = new TextDecoder();
1379
+ let buffer = '';
1380
+ try {
1381
+ while (!foundOuterWebhook) {
1382
+ const { value, done } = await reader.read();
1383
+ if (done)
1384
+ break;
1385
+ buffer += decoder.decode(value, { stream: true });
1386
+ let eventEndIndex;
1387
+ while ((eventEndIndex = buffer.indexOf('\n\n')) !== -1) {
1388
+ const message = buffer.substring(0, eventEndIndex);
1389
+ buffer = buffer.substring(eventEndIndex + 2);
1390
+ if (message.startsWith('data: ')) {
1391
+ try {
1392
+ const event = JSON.parse(message.substring(6));
1393
+ // Track inner brain completes
1394
+ if (event.type === BRAIN_EVENTS.COMPLETE) {
1395
+ innerCompleteCount++;
1396
+ // First complete is inner brain, second would be outer
1397
+ }
1398
+ // Outer brain webhook (happens after inner brain completes)
1399
+ if (event.type === BRAIN_EVENTS.WEBHOOK) {
1400
+ foundOuterWebhook = true;
1401
+ break;
1402
+ }
1403
+ if (event.type === BRAIN_EVENTS.ERROR) {
1404
+ console.error(`Brain errored: ${JSON.stringify(event.error)}`);
1405
+ return false;
1406
+ }
1407
+ }
1408
+ catch {
1409
+ // Ignore parse errors
1410
+ }
1411
+ }
1412
+ }
1413
+ }
1414
+ }
1415
+ finally {
1416
+ await reader.cancel();
1417
+ }
1418
+ }
1419
+ if (!foundOuterWebhook) {
1420
+ console.error('Did not receive outer brain WEBHOOK event');
1421
+ return false;
1422
+ }
1423
+ if (innerCompleteCount === 0) {
1424
+ console.error('Inner brain did not emit COMPLETE event');
1425
+ return false;
1426
+ }
1427
+ // Step 3: Query history - status should be RUNNING, not COMPLETE
1428
+ const historyRequest = new Request(`http://example.com/brains/${encodeURIComponent(outerBrainIdentifier)}/history?limit=1`, { method: 'GET' });
1429
+ const historyResponse = await fetch(historyRequest);
1430
+ if (!historyResponse.ok) {
1431
+ console.error(`GET /brains/${outerBrainIdentifier}/history returned ${historyResponse.status}`);
1432
+ return false;
1433
+ }
1434
+ const historyData = (await historyResponse.json());
1435
+ const ourRun = historyData.runs.find((r) => r.brainRunId === brainRunId);
1436
+ if (!ourRun) {
1437
+ console.error(`Run ${brainRunId} not found in history`);
1438
+ return false;
1439
+ }
1440
+ // KEY ASSERTION: Status should be RUNNING despite inner brain COMPLETE event
1441
+ if (ourRun.status !== STATUS.RUNNING) {
1442
+ console.error(`Expected status '${STATUS.RUNNING}' but got '${ourRun.status}'. ` +
1443
+ `Bug: Inner brain COMPLETE event overwrote outer brain status!`);
1444
+ return false;
1445
+ }
1446
+ // Step 4: Trigger webhook to complete outer brain
1447
+ const webhookRequest = new Request(`http://example.com/webhooks/${encodeURIComponent(webhookSlug)}`, {
1448
+ method: 'POST',
1449
+ headers: { 'Content-Type': 'application/json' },
1450
+ body: JSON.stringify(webhookPayload),
1451
+ });
1452
+ const webhookResponse = await fetch(webhookRequest);
1453
+ if (!webhookResponse.ok) {
1454
+ console.error(`POST /webhooks/${webhookSlug} returned ${webhookResponse.status}`);
1455
+ return false;
1456
+ }
1457
+ // Step 5: Watch for final completion
1458
+ const resumeWatchRequest = new Request(`http://example.com/brains/runs/${brainRunId}/watch`, { method: 'GET' });
1459
+ const resumeWatchResponse = await fetch(resumeWatchRequest);
1460
+ if (!resumeWatchResponse.ok) {
1461
+ console.error(`Resume watch returned ${resumeWatchResponse.status}`);
1462
+ return false;
1463
+ }
1464
+ // Wait for the OUTER brain's COMPLETE event specifically (by tracking depth)
1465
+ // When resuming, the SSE stream includes historical events, so we need to
1466
+ // track START/COMPLETE events to know when the outer brain truly finishes
1467
+ let foundFinalComplete = false;
1468
+ let depth = 0;
1469
+ if (resumeWatchResponse.body) {
1470
+ const reader = resumeWatchResponse.body.getReader();
1471
+ const decoder = new TextDecoder();
1472
+ let buffer = '';
1473
+ try {
1474
+ while (!foundFinalComplete) {
1475
+ const { value, done } = await reader.read();
1476
+ if (done)
1477
+ break;
1478
+ buffer += decoder.decode(value, { stream: true });
1479
+ let eventEndIndex;
1480
+ while ((eventEndIndex = buffer.indexOf('\n\n')) !== -1) {
1481
+ const message = buffer.substring(0, eventEndIndex);
1482
+ buffer = buffer.substring(eventEndIndex + 2);
1483
+ if (message.startsWith('data: ')) {
1484
+ try {
1485
+ const event = JSON.parse(message.substring(6));
1486
+ // Track depth to find the outer brain's COMPLETE
1487
+ if (event.type === BRAIN_EVENTS.START) {
1488
+ depth++;
1489
+ }
1490
+ else if (event.type === BRAIN_EVENTS.COMPLETE) {
1491
+ depth--;
1492
+ // When depth reaches 0, the outer brain has completed
1493
+ if (depth <= 0) {
1494
+ foundFinalComplete = true;
1495
+ break;
1496
+ }
1497
+ }
1498
+ }
1499
+ catch {
1500
+ // Ignore parse errors
1501
+ }
1502
+ }
1503
+ }
1504
+ }
1505
+ }
1506
+ finally {
1507
+ await reader.cancel();
1508
+ }
1509
+ }
1510
+ // Step 6: Verify final status is COMPLETE
1511
+ const finalHistoryResponse = await fetch(historyRequest);
1512
+ if (!finalHistoryResponse.ok) {
1513
+ console.error('Final history query failed');
1514
+ return false;
1515
+ }
1516
+ const finalHistoryData = (await finalHistoryResponse.json());
1517
+ const finalRun = finalHistoryData.runs.find((r) => r.brainRunId === brainRunId);
1518
+ if (!finalRun || finalRun.status !== STATUS.COMPLETE) {
1519
+ console.error(`Expected final status '${STATUS.COMPLETE}' but got '${finalRun?.status}'`);
1520
+ return false;
1521
+ }
1522
+ return true;
1523
+ }
1524
+ catch (error) {
1525
+ console.error(`Failed to test inner brain complete status for ${outerBrainIdentifier}:`, error);
1336
1526
  return false;
1337
1527
  }
1338
1528
  },
@@ -1784,6 +1974,97 @@ export const webhooks = {
1784
1974
  return false;
1785
1975
  }
1786
1976
  },
1977
+ /**
1978
+ * Test POST /webhooks/system/ui-form - Built-in webhook for UI form submissions.
1979
+ * This is used by pages generated via .ui() steps to submit form data.
1980
+ *
1981
+ * The endpoint:
1982
+ * - Accepts form data (application/x-www-form-urlencoded or multipart/form-data)
1983
+ * - Requires an `identifier` query parameter to match the waiting brain
1984
+ * - Returns { received: true, action: 'resumed' | 'not_found', ... }
1985
+ */
1986
+ async uiForm(fetch, identifier, formData) {
1987
+ try {
1988
+ // Build URLSearchParams from form data
1989
+ const params = new URLSearchParams();
1990
+ for (const [key, value] of Object.entries(formData)) {
1991
+ if (Array.isArray(value)) {
1992
+ for (const v of value) {
1993
+ params.append(`${key}[]`, v);
1994
+ }
1995
+ }
1996
+ else {
1997
+ params.append(key, value);
1998
+ }
1999
+ }
2000
+ const request = new Request(`http://example.com/webhooks/system/ui-form?identifier=${encodeURIComponent(identifier)}`, {
2001
+ method: 'POST',
2002
+ headers: {
2003
+ 'Content-Type': 'application/x-www-form-urlencoded',
2004
+ },
2005
+ body: params.toString(),
2006
+ });
2007
+ const response = await fetch(request);
2008
+ // Accept 200 (found and processed) or 404 (no brain waiting)
2009
+ if (response.status !== 200 && response.status !== 404) {
2010
+ console.error(`POST /webhooks/system/ui-form returned ${response.status}, expected 200 or 404`);
2011
+ return false;
2012
+ }
2013
+ const data = (await response.json());
2014
+ // Validate response structure
2015
+ if (typeof data.received !== 'boolean') {
2016
+ console.error(`Expected received to be boolean, got ${typeof data.received}`);
2017
+ return false;
2018
+ }
2019
+ if (!data.action || typeof data.action !== 'string') {
2020
+ console.error(`Expected action to be string, got ${typeof data.action}`);
2021
+ return false;
2022
+ }
2023
+ // Action should be 'resumed' or 'not_found'
2024
+ if (data.action !== 'resumed' && data.action !== 'not_found') {
2025
+ console.error(`Expected action to be 'resumed' or 'not_found', got '${data.action}'`);
2026
+ return false;
2027
+ }
2028
+ return true;
2029
+ }
2030
+ catch (error) {
2031
+ console.error('Failed to test POST /webhooks/system/ui-form:', error);
2032
+ return false;
2033
+ }
2034
+ },
2035
+ /**
2036
+ * Test POST /webhooks/system/ui-form with missing identifier - Should return 400
2037
+ */
2038
+ async uiFormMissingIdentifier(fetch) {
2039
+ try {
2040
+ const request = new Request('http://example.com/webhooks/system/ui-form', {
2041
+ method: 'POST',
2042
+ headers: {
2043
+ 'Content-Type': 'application/x-www-form-urlencoded',
2044
+ },
2045
+ body: 'test=data',
2046
+ });
2047
+ const response = await fetch(request);
2048
+ if (response.status !== 400) {
2049
+ console.error(`POST /webhooks/system/ui-form without identifier returned ${response.status}, expected 400`);
2050
+ return false;
2051
+ }
2052
+ const data = (await response.json());
2053
+ if (!data.error || typeof data.error !== 'string') {
2054
+ console.error(`Expected error to be string, got ${typeof data.error}`);
2055
+ return false;
2056
+ }
2057
+ if (!data.error.toLowerCase().includes('identifier')) {
2058
+ console.error(`Expected error message to mention identifier, got: ${data.error}`);
2059
+ return false;
2060
+ }
2061
+ return true;
2062
+ }
2063
+ catch (error) {
2064
+ console.error('Failed to test POST /webhooks/system/ui-form without identifier:', error);
2065
+ return false;
2066
+ }
2067
+ },
1787
2068
  };
1788
2069
  export const pages = {
1789
2070
  /**
@@ -2116,4 +2397,59 @@ export const pages = {
2116
2397
  }
2117
2398
  },
2118
2399
  };
2400
+ /**
2401
+ * Bundle API Tests
2402
+ *
2403
+ * Tests for the /bundle/components.js endpoint which serves the component bundle.
2404
+ *
2405
+ * NOTE: These tests only verify the API endpoint behavior. The bundle build and
2406
+ * upload process is backend-specific and must be tested separately by each
2407
+ * backend implementation.
2408
+ */
2409
+ export const bundle = {
2410
+ /**
2411
+ * Test GET /bundle/components.js - Serve the component bundle
2412
+ */
2413
+ async get(fetch) {
2414
+ try {
2415
+ const request = new Request('http://example.com/bundle/components.js', {
2416
+ method: 'GET',
2417
+ });
2418
+ const response = await fetch(request);
2419
+ // Bundle may or may not exist depending on project setup
2420
+ // 200 = bundle exists and served correctly
2421
+ // 404 = bundle not found (expected if no components/ directory)
2422
+ if (response.status !== 200 && response.status !== 404) {
2423
+ console.error(`GET /bundle/components.js returned unexpected status ${response.status}`);
2424
+ return false;
2425
+ }
2426
+ const contentType = response.headers.get('Content-Type');
2427
+ if (!contentType || !contentType.includes('application/javascript')) {
2428
+ console.error(`Expected Content-Type application/javascript, got ${contentType}`);
2429
+ return false;
2430
+ }
2431
+ // If 200, verify we got some content
2432
+ if (response.status === 200) {
2433
+ const content = await response.text();
2434
+ if (!content || content.length === 0) {
2435
+ console.error('Bundle endpoint returned 200 but empty content');
2436
+ return false;
2437
+ }
2438
+ }
2439
+ // If 404, verify we got the helpful error message
2440
+ if (response.status === 404) {
2441
+ const content = await response.text();
2442
+ if (!content.includes('Bundle not found')) {
2443
+ console.error('Bundle 404 response missing helpful error message');
2444
+ return false;
2445
+ }
2446
+ }
2447
+ return true;
2448
+ }
2449
+ catch (error) {
2450
+ console.error(`Failed to test GET /bundle/components.js:`, error);
2451
+ return false;
2452
+ }
2453
+ },
2454
+ };
2119
2455
  //# sourceMappingURL=api.js.map