@positronic/spec 0.0.55 → 0.0.57

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 (68) hide show
  1. package/dist/{api.d.ts → api/brains.d.ts} +2 -160
  2. package/dist/api/brains.d.ts.map +1 -0
  3. package/dist/{api.js → api/brains.js} +18 -1234
  4. package/dist/api/brains.js.map +1 -0
  5. package/dist/api/bundle.d.ts +17 -0
  6. package/dist/api/bundle.d.ts.map +1 -0
  7. package/dist/api/bundle.js +56 -0
  8. package/dist/api/bundle.js.map +1 -0
  9. package/dist/api/index.d.ts +12 -0
  10. package/dist/api/index.d.ts.map +1 -0
  11. package/dist/api/index.js +11 -0
  12. package/dist/api/index.js.map +1 -0
  13. package/dist/api/pages.d.ts +46 -0
  14. package/dist/api/pages.d.ts.map +1 -0
  15. package/dist/api/pages.js +338 -0
  16. package/dist/api/pages.js.map +1 -0
  17. package/dist/api/resources.d.ts +28 -0
  18. package/dist/api/resources.d.ts.map +1 -0
  19. package/dist/api/resources.js +272 -0
  20. package/dist/api/resources.js.map +1 -0
  21. package/dist/api/schedules.d.ts +20 -0
  22. package/dist/api/schedules.d.ts.map +1 -0
  23. package/dist/api/schedules.js +159 -0
  24. package/dist/api/schedules.js.map +1 -0
  25. package/dist/api/secrets.d.ts +27 -0
  26. package/dist/api/secrets.d.ts.map +1 -0
  27. package/dist/api/secrets.js +175 -0
  28. package/dist/api/secrets.js.map +1 -0
  29. package/dist/api/signals.d.ts +53 -0
  30. package/dist/api/signals.d.ts.map +1 -0
  31. package/dist/api/signals.js +276 -0
  32. package/dist/api/signals.js.map +1 -0
  33. package/dist/api/status.d.ts +3 -0
  34. package/dist/api/status.d.ts.map +1 -0
  35. package/dist/api/status.js +23 -0
  36. package/dist/api/status.js.map +1 -0
  37. package/dist/api/types.d.ts +2 -0
  38. package/dist/api/types.d.ts.map +1 -0
  39. package/dist/api/types.js +2 -0
  40. package/dist/api/types.js.map +1 -0
  41. package/dist/api/users.d.ts +43 -0
  42. package/dist/api/users.d.ts.map +1 -0
  43. package/dist/api/users.js +221 -0
  44. package/dist/api/users.js.map +1 -0
  45. package/dist/api/webhooks.d.ts +30 -0
  46. package/dist/api/webhooks.d.ts.map +1 -0
  47. package/dist/api/webhooks.js +208 -0
  48. package/dist/api/webhooks.js.map +1 -0
  49. package/dist/index.d.ts +1 -1
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +1 -1
  52. package/dist/index.js.map +1 -1
  53. package/dist/src/{api.js → api/brains.js} +1378 -4108
  54. package/dist/src/api/bundle.js +228 -0
  55. package/dist/src/api/index.js +10 -0
  56. package/dist/src/api/pages.js +840 -0
  57. package/dist/src/api/resources.js +707 -0
  58. package/dist/src/api/schedules.js +479 -0
  59. package/dist/src/api/secrets.js +518 -0
  60. package/dist/src/api/signals.js +778 -0
  61. package/dist/src/api/status.js +180 -0
  62. package/dist/src/api/types.js +1 -0
  63. package/dist/src/api/users.js +650 -0
  64. package/dist/src/api/webhooks.js +639 -0
  65. package/dist/src/index.js +1 -1
  66. package/package.json +1 -1
  67. package/dist/api.d.ts.map +0 -1
  68. package/dist/api.js.map +0 -1
@@ -1,297 +1,4 @@
1
1
  import { STATUS, BRAIN_EVENTS } from '@positronic/core';
2
- export async function testStatus(fetch) {
3
- try {
4
- const request = new Request('http://example.com/status', {
5
- method: 'GET',
6
- });
7
- const response = await fetch(request);
8
- if (!response.ok) {
9
- console.error(`Status endpoint returned ${response.status}`);
10
- return false;
11
- }
12
- const data = await response.json();
13
- if (data.ready !== true) {
14
- console.error(`Expected { ready: true }, got ${JSON.stringify(data)}`);
15
- return false;
16
- }
17
- return true;
18
- }
19
- catch (error) {
20
- console.error(`Failed to test status endpoint:`, error);
21
- return false;
22
- }
23
- }
24
- export const resources = {
25
- /**
26
- * Test GET /resources - List all resources
27
- */
28
- async list(fetch) {
29
- try {
30
- const request = new Request('http://example.com/resources', {
31
- method: 'GET',
32
- });
33
- const response = await fetch(request);
34
- if (!response.ok) {
35
- console.error(`GET /resources returned ${response.status}`);
36
- return false;
37
- }
38
- const data = await response.json();
39
- // Validate response structure
40
- if (!Array.isArray(data.resources)) {
41
- console.error(`Expected resources to be an array, got ${typeof data.resources}`);
42
- return false;
43
- }
44
- if (typeof data.truncated !== 'boolean') {
45
- console.error(`Expected truncated to be boolean, got ${typeof data.truncated}`);
46
- return false;
47
- }
48
- if (typeof data.count !== 'number') {
49
- console.error(`Expected count to be number, got ${typeof data.count}`);
50
- return false;
51
- }
52
- // Validate each resource has required fields
53
- for (const resource of data.resources) {
54
- if (!resource.key ||
55
- !resource.type ||
56
- typeof resource.size !== 'number' ||
57
- !resource.lastModified ||
58
- typeof resource.local !== 'boolean') {
59
- console.error(`Resource missing required fields: ${JSON.stringify(resource)}`);
60
- return false;
61
- }
62
- if (!['text', 'binary'].includes(resource.type)) {
63
- console.error(`Invalid resource type: ${resource.type}`);
64
- return false;
65
- }
66
- }
67
- return true;
68
- }
69
- catch (error) {
70
- console.error(`Failed to test GET /resources:`, error);
71
- return false;
72
- }
73
- },
74
- /**
75
- * Test POST /resources - Upload a resource
76
- */
77
- async upload(fetch) {
78
- try {
79
- const formData = new FormData();
80
- formData.append('file', new Blob(['test content'], { type: 'text/plain' }), 'test.txt');
81
- formData.append('type', 'text');
82
- formData.append('key', 'test-resource.txt');
83
- formData.append('local', 'false');
84
- const request = new Request('http://example.com/resources', {
85
- method: 'POST',
86
- body: formData,
87
- });
88
- const response = await fetch(request);
89
- if (response.status !== 201) {
90
- console.error(`POST /resources returned ${response.status}, expected 201`);
91
- return false;
92
- }
93
- const data = await response.json();
94
- // Validate response has required fields
95
- if (!data.key ||
96
- !data.type ||
97
- typeof data.size !== 'number' ||
98
- !data.lastModified ||
99
- typeof data.local !== 'boolean') {
100
- console.error(`Response missing required fields: ${JSON.stringify(data)}`);
101
- return false;
102
- }
103
- if (data.key !== 'test-resource.txt') {
104
- console.error(`Expected key to be 'test-resource.txt', got ${data.key}`);
105
- return false;
106
- }
107
- if (data.type !== 'text') {
108
- console.error(`Expected type to be 'text', got ${data.type}`);
109
- return false;
110
- }
111
- if (data.local !== false) {
112
- console.error(`Expected local to be false, got ${data.local}`);
113
- return false;
114
- }
115
- return true;
116
- }
117
- catch (error) {
118
- console.error(`Failed to test POST /resources:`, error);
119
- return false;
120
- }
121
- },
122
- /**
123
- * Test DELETE /resources/:key - Delete a specific resource
124
- */
125
- async delete(fetch, key) {
126
- try {
127
- const request = new Request(`http://example.com/resources/${encodeURIComponent(key)}`, {
128
- method: 'DELETE',
129
- });
130
- const response = await fetch(request);
131
- if (response.status !== 204) {
132
- console.error(`DELETE /resources/${key} returned ${response.status}, expected 204`);
133
- return false;
134
- }
135
- return true;
136
- }
137
- catch (error) {
138
- console.error(`Failed to test DELETE /resources/${key}:`, error);
139
- return false;
140
- }
141
- },
142
- /**
143
- * Test DELETE /resources - Bulk delete all resources (dev mode only)
144
- */
145
- async deleteAll(fetch) {
146
- try {
147
- const request = new Request('http://example.com/resources', {
148
- method: 'DELETE',
149
- });
150
- const response = await fetch(request);
151
- // In production mode, this should return 403
152
- if (response.status === 403) {
153
- const data = await response.json();
154
- if (data.error === 'Bulk delete is only available in development mode') {
155
- // This is expected behavior in production
156
- return true;
157
- }
158
- }
159
- if (response.status !== 200) {
160
- console.error(`DELETE /resources returned ${response.status}, expected 200 or 403`);
161
- return false;
162
- }
163
- const data = await response.json();
164
- if (typeof data.deletedCount !== 'number') {
165
- console.error(`Expected deletedCount to be number, got ${typeof data.deletedCount}`);
166
- return false;
167
- }
168
- return true;
169
- }
170
- catch (error) {
171
- console.error(`Failed to test DELETE /resources:`, error);
172
- return false;
173
- }
174
- },
175
- /**
176
- * Test DELETE /resources preserves pages - bulk delete should not delete pages
177
- */
178
- async deleteAllPreservesPages(fetch) {
179
- try {
180
- // First create a page
181
- const pageSlug = `spec-test-page-${Date.now()}`;
182
- const pageHtml = '<html><body>Test page for spec</body></html>';
183
- const brainRunId = 'spec-test-brain-run-id';
184
- const createPageRequest = new Request('http://example.com/pages', {
185
- method: 'POST',
186
- headers: {
187
- 'Content-Type': 'application/json',
188
- },
189
- body: JSON.stringify({
190
- slug: pageSlug,
191
- html: pageHtml,
192
- brainRunId,
193
- }),
194
- });
195
- const createPageResponse = await fetch(createPageRequest);
196
- if (createPageResponse.status !== 201) {
197
- console.error(`Failed to create test page: ${createPageResponse.status}`);
198
- return false;
199
- }
200
- // Now bulk delete all resources
201
- const deleteRequest = new Request('http://example.com/resources', {
202
- method: 'DELETE',
203
- });
204
- const deleteResponse = await fetch(deleteRequest);
205
- // Skip test if in production mode (403)
206
- if (deleteResponse.status === 403) {
207
- // Clean up the page we created
208
- await fetch(new Request(`http://example.com/pages/${encodeURIComponent(pageSlug)}`, { method: 'DELETE' }));
209
- console.log('Skipping deleteAllPreservesPages test - bulk delete not available in production');
210
- return true;
211
- }
212
- if (deleteResponse.status !== 200) {
213
- console.error(`DELETE /resources returned ${deleteResponse.status}, expected 200`);
214
- return false;
215
- }
216
- // Verify the page still exists
217
- const getPageRequest = new Request(`http://example.com/pages/${encodeURIComponent(pageSlug)}`, { method: 'GET' });
218
- const getPageResponse = await fetch(getPageRequest);
219
- if (getPageResponse.status === 404) {
220
- console.error('DELETE /resources incorrectly deleted pages - page not found after bulk delete');
221
- return false;
222
- }
223
- if (!getPageResponse.ok) {
224
- console.error(`GET /pages/${pageSlug} returned ${getPageResponse.status}`);
225
- return false;
226
- }
227
- // Clean up: delete the test page
228
- await fetch(new Request(`http://example.com/pages/${encodeURIComponent(pageSlug)}`, { method: 'DELETE' }));
229
- return true;
230
- }
231
- catch (error) {
232
- console.error(`Failed to test DELETE /resources preserves pages:`, error);
233
- return false;
234
- }
235
- },
236
- /**
237
- * Test POST /resources/presigned-link - Generate presigned URL for upload
238
- */
239
- async generatePresignedLink(fetch) {
240
- try {
241
- const request = new Request('http://example.com/resources/presigned-link', {
242
- method: 'POST',
243
- headers: {
244
- 'Content-Type': 'application/json',
245
- },
246
- body: JSON.stringify({
247
- key: 'test-files/large-video.mp4',
248
- type: 'binary',
249
- size: 150 * 1024 * 1024, // 150MB - larger than Worker limit
250
- }),
251
- });
252
- const response = await fetch(request);
253
- // If credentials are not configured, expect 400
254
- if (response.status === 400) {
255
- const data = await response.json();
256
- // This is acceptable - implementation may not have credentials configured
257
- console.log('Presigned URL generation not available - this is acceptable');
258
- return true;
259
- }
260
- if (response.status !== 200) {
261
- console.error(`POST /resources/presigned-link returned ${response.status}, expected 200 or 400`);
262
- return false;
263
- }
264
- const data = await response.json();
265
- // Validate response structure (backend-agnostic)
266
- if (!data.url || typeof data.url !== 'string') {
267
- console.error(`Expected url to be string, got ${typeof data.url}`);
268
- return false;
269
- }
270
- if (!data.method || data.method !== 'PUT') {
271
- console.error(`Expected method to be 'PUT', got ${data.method}`);
272
- return false;
273
- }
274
- if (typeof data.expiresIn !== 'number' || data.expiresIn <= 0) {
275
- console.error(`Expected expiresIn to be positive number, got ${data.expiresIn}`);
276
- return false;
277
- }
278
- // Basic URL validation - just ensure it's a valid URL
279
- try {
280
- new URL(data.url);
281
- console.log('Presigned URL structure validated successfully');
282
- }
283
- catch (error) {
284
- console.error(`Invalid URL returned: ${data.url}`);
285
- return false;
286
- }
287
- return true;
288
- }
289
- catch (error) {
290
- console.error(`Failed to test POST /resources/presigned-link:`, error);
291
- return false;
292
- }
293
- },
294
- };
295
2
  export const brains = {
296
3
  /**
297
4
  * Test POST /brains/runs - Create a new brain run
@@ -314,7 +21,7 @@ export const brains = {
314
21
  console.error(`POST /brains/runs returned ${response.status}, expected 201`);
315
22
  return null;
316
23
  }
317
- const data = await response.json();
24
+ const data = (await response.json());
318
25
  if (!data.brainRunId || typeof data.brainRunId !== 'string') {
319
26
  console.error(`Expected brainRunId to be string, got ${typeof data.brainRunId}`);
320
27
  return null;
@@ -343,7 +50,7 @@ export const brains = {
343
50
  console.error(`POST /brains/runs with options returned ${response.status}, expected 201`);
344
51
  return null;
345
52
  }
346
- const data = await response.json();
53
+ const data = (await response.json());
347
54
  if (!data.brainRunId || typeof data.brainRunId !== 'string') {
348
55
  console.error(`Expected brainRunId to be string, got ${typeof data.brainRunId}`);
349
56
  return null;
@@ -372,7 +79,7 @@ export const brains = {
372
79
  console.error(`POST /brains/runs with non-existent brain returned ${response.status}, expected 404`);
373
80
  return false;
374
81
  }
375
- const data = await response.json();
82
+ const data = (await response.json());
376
83
  if (!data.error || typeof data.error !== 'string') {
377
84
  console.error(`Expected error to be string, got ${typeof data.error}`);
378
85
  return false;
@@ -458,7 +165,7 @@ export const brains = {
458
165
  console.error(`GET /brains/${identifier}/history returned ${response.status}`);
459
166
  return false;
460
167
  }
461
- const data = await response.json();
168
+ const data = (await response.json());
462
169
  // Validate response structure
463
170
  if (!data.runs || !Array.isArray(data.runs)) {
464
171
  console.error(`Expected runs to be an array, got ${typeof data.runs}`);
@@ -541,7 +248,7 @@ export const brains = {
541
248
  console.error(`GET /brains returned ${response.status}`);
542
249
  return false;
543
250
  }
544
- const data = await response.json();
251
+ const data = (await response.json());
545
252
  // Validate response structure
546
253
  if (!Array.isArray(data.brains)) {
547
254
  console.error(`Expected brains to be an array, got ${typeof data.brains}`);
@@ -588,7 +295,7 @@ export const brains = {
588
295
  console.error(`GET /brains?q=${query} returned ${response.status}`);
589
296
  return null;
590
297
  }
591
- const data = await response.json();
298
+ const data = (await response.json());
592
299
  // Validate response structure
593
300
  if (!Array.isArray(data.brains)) {
594
301
  console.error(`Expected brains to be an array, got ${typeof data.brains}`);
@@ -634,7 +341,7 @@ export const brains = {
634
341
  console.error(`GET /brains/${identifier} returned ${response.status}`);
635
342
  return false;
636
343
  }
637
- const data = await response.json();
344
+ const data = (await response.json());
638
345
  // Validate response structure
639
346
  if (!data.title || typeof data.title !== 'string') {
640
347
  console.error(`Expected title to be string, got ${typeof data.title}`);
@@ -687,7 +394,7 @@ export const brains = {
687
394
  console.error(`GET /brains/${identifier}/active-runs returned ${response.status}`);
688
395
  return false;
689
396
  }
690
- const data = await response.json();
397
+ const data = (await response.json());
691
398
  // Validate response structure
692
399
  if (!data.runs || !Array.isArray(data.runs)) {
693
400
  console.error(`Expected runs to be an array, got ${typeof data.runs}`);
@@ -804,7 +511,7 @@ export const brains = {
804
511
  console.error(`GET /brains/${ambiguousIdentifier} with ambiguous identifier returned ${response.status}, expected 300`);
805
512
  return false;
806
513
  }
807
- const data = await response.json();
514
+ const data = (await response.json());
808
515
  if (data.matchType !== 'multiple') {
809
516
  console.error(`Expected matchType to be 'multiple', got ${data.matchType}`);
810
517
  return false;
@@ -838,7 +545,7 @@ export const brains = {
838
545
  console.error(`POST /brains/runs with ambiguous identifier returned ${response.status}, expected 300 or 409`);
839
546
  return false;
840
547
  }
841
- const data = await response.json();
548
+ const data = (await response.json());
842
549
  if (data.matchType !== 'multiple') {
843
550
  console.error(`Expected matchType to be 'multiple', got ${data.matchType}`);
844
551
  return false;
@@ -885,7 +592,7 @@ export const brains = {
885
592
  console.error(`POST /brains/runs/rerun returned ${response.status}, expected 201`);
886
593
  return null;
887
594
  }
888
- const data = await response.json();
595
+ const data = (await response.json());
889
596
  if (!data.brainRunId || typeof data.brainRunId !== 'string') {
890
597
  console.error(`Expected brainRunId to be string, got ${typeof data.brainRunId}`);
891
598
  return null;
@@ -1128,7 +835,8 @@ export const brains = {
1128
835
  }
1129
836
  // Verify AGENT_START has prompt field
1130
837
  const agentStartEvent = events.find((e) => e.type === BRAIN_EVENTS.AGENT_START);
1131
- if (!agentStartEvent.prompt || typeof agentStartEvent.prompt !== 'string') {
838
+ if (!agentStartEvent.prompt ||
839
+ typeof agentStartEvent.prompt !== 'string') {
1132
840
  console.error('AGENT_START event missing prompt field');
1133
841
  return false;
1134
842
  }
@@ -1437,9 +1145,10 @@ export const brains = {
1437
1145
  console.error(`Run ${brainRunId} not found in history`);
1438
1146
  return false;
1439
1147
  }
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}'. ` +
1148
+ // KEY ASSERTION: Status should be WAITING (for webhook) despite inner brain COMPLETE event
1149
+ // The outer brain emitted a WEBHOOK event, so it should be WAITING, not COMPLETE
1150
+ if (ourRun.status !== STATUS.WAITING) {
1151
+ console.error(`Expected status '${STATUS.WAITING}' but got '${ourRun.status}'. ` +
1443
1152
  `Bug: Inner brain COMPLETE event overwrote outer brain status!`);
1444
1153
  return false;
1445
1154
  }
@@ -1527,929 +1236,4 @@ export const brains = {
1527
1236
  }
1528
1237
  },
1529
1238
  };
1530
- export const schedules = {
1531
- /**
1532
- * Test POST /brains/schedules - Create a new schedule
1533
- */
1534
- async create(fetch, identifier, cronExpression) {
1535
- try {
1536
- const request = new Request('http://example.com/brains/schedules', {
1537
- method: 'POST',
1538
- headers: {
1539
- 'Content-Type': 'application/json',
1540
- },
1541
- body: JSON.stringify({ identifier, cronExpression }),
1542
- });
1543
- const response = await fetch(request);
1544
- if (response.status !== 201) {
1545
- console.error(`POST /brains/schedules returned ${response.status}, expected 201`);
1546
- return null;
1547
- }
1548
- const data = await response.json();
1549
- // Validate response structure
1550
- if (!data.id || typeof data.id !== 'string') {
1551
- console.error(`Expected id to be string, got ${typeof data.id}`);
1552
- return null;
1553
- }
1554
- // TODO: Once backend is updated, validate that the returned brain matches the identifier
1555
- // For now, we accept any valid response
1556
- if (data.cronExpression !== cronExpression) {
1557
- console.error(`Expected cronExpression to be '${cronExpression}', got ${data.cronExpression}`);
1558
- return null;
1559
- }
1560
- if (typeof data.enabled !== 'boolean') {
1561
- console.error(`Expected enabled to be boolean, got ${typeof data.enabled}`);
1562
- return null;
1563
- }
1564
- if (typeof data.createdAt !== 'number') {
1565
- console.error(`Expected createdAt to be number, got ${typeof data.createdAt}`);
1566
- return null;
1567
- }
1568
- return data.id;
1569
- }
1570
- catch (error) {
1571
- console.error(`Failed to test POST /brains/schedules:`, error);
1572
- return null;
1573
- }
1574
- },
1575
- /**
1576
- * Test GET /brains/schedules - List all schedules
1577
- */
1578
- async list(fetch) {
1579
- try {
1580
- const request = new Request('http://example.com/brains/schedules', {
1581
- method: 'GET',
1582
- });
1583
- const response = await fetch(request);
1584
- if (!response.ok) {
1585
- console.error(`GET /brains/schedules returned ${response.status}`);
1586
- return false;
1587
- }
1588
- const data = await response.json();
1589
- // Validate response structure
1590
- if (!Array.isArray(data.schedules)) {
1591
- console.error(`Expected schedules to be an array, got ${typeof data.schedules}`);
1592
- return false;
1593
- }
1594
- if (typeof data.count !== 'number') {
1595
- console.error(`Expected count to be number, got ${typeof data.count}`);
1596
- return false;
1597
- }
1598
- // Validate each schedule has required fields
1599
- for (const schedule of data.schedules) {
1600
- if (!schedule.id ||
1601
- !schedule.brainTitle ||
1602
- !schedule.cronExpression ||
1603
- typeof schedule.enabled !== 'boolean' ||
1604
- typeof schedule.createdAt !== 'number') {
1605
- console.error(`Schedule missing required fields: ${JSON.stringify(schedule)}`);
1606
- return false;
1607
- }
1608
- }
1609
- return true;
1610
- }
1611
- catch (error) {
1612
- console.error(`Failed to test GET /brains/schedules:`, error);
1613
- return false;
1614
- }
1615
- },
1616
- /**
1617
- * Test DELETE /brains/schedules/:scheduleId - Delete a schedule
1618
- */
1619
- async delete(fetch, scheduleId) {
1620
- try {
1621
- const request = new Request(`http://example.com/brains/schedules/${scheduleId}`, {
1622
- method: 'DELETE',
1623
- });
1624
- const response = await fetch(request);
1625
- if (response.status !== 204) {
1626
- console.error(`DELETE /brains/schedules/${scheduleId} returned ${response.status}, expected 204`);
1627
- return false;
1628
- }
1629
- return true;
1630
- }
1631
- catch (error) {
1632
- console.error(`Failed to test DELETE /brains/schedules/${scheduleId}:`, error);
1633
- return false;
1634
- }
1635
- },
1636
- /**
1637
- * Test GET /brains/schedules/runs - Get history of scheduled runs
1638
- */
1639
- async runs(fetch, scheduleId, limit) {
1640
- try {
1641
- const url = new URL('http://example.com/brains/schedules/runs');
1642
- if (scheduleId !== undefined) {
1643
- url.searchParams.set('scheduleId', scheduleId);
1644
- }
1645
- if (limit !== undefined) {
1646
- url.searchParams.set('limit', limit.toString());
1647
- }
1648
- const request = new Request(url.toString(), {
1649
- method: 'GET',
1650
- });
1651
- const response = await fetch(request);
1652
- if (!response.ok) {
1653
- console.error(`GET /brains/schedules/runs returned ${response.status}`);
1654
- return false;
1655
- }
1656
- const data = await response.json();
1657
- // Validate response structure
1658
- if (!Array.isArray(data.runs)) {
1659
- console.error(`Expected runs to be an array, got ${typeof data.runs}`);
1660
- return false;
1661
- }
1662
- if (typeof data.count !== 'number') {
1663
- console.error(`Expected count to be number, got ${typeof data.count}`);
1664
- return false;
1665
- }
1666
- // Validate each run has required fields
1667
- for (const run of data.runs) {
1668
- if (!run.id ||
1669
- !run.scheduleId ||
1670
- !run.status ||
1671
- typeof run.ranAt !== 'number') {
1672
- console.error(`Scheduled run missing required fields: ${JSON.stringify(run)}`);
1673
- return false;
1674
- }
1675
- if (!['triggered', 'failed'].includes(run.status)) {
1676
- console.error(`Invalid run status: ${run.status}`);
1677
- return false;
1678
- }
1679
- }
1680
- return true;
1681
- }
1682
- catch (error) {
1683
- console.error(`Failed to test GET /brains/schedules/runs:`, error);
1684
- return false;
1685
- }
1686
- },
1687
- };
1688
- export const secrets = {
1689
- /**
1690
- * Test POST /secrets - Create or update a secret
1691
- */
1692
- async create(fetch, name, value) {
1693
- try {
1694
- const request = new Request('http://example.com/secrets', {
1695
- method: 'POST',
1696
- headers: {
1697
- 'Content-Type': 'application/json',
1698
- },
1699
- body: JSON.stringify({ name, value }),
1700
- });
1701
- const response = await fetch(request);
1702
- if (response.status !== 201) {
1703
- console.error(`POST /secrets returned ${response.status}, expected 201`);
1704
- return false;
1705
- }
1706
- const data = await response.json();
1707
- // Validate response structure
1708
- if (!data.name || typeof data.name !== 'string') {
1709
- console.error(`Expected name to be string, got ${typeof data.name}`);
1710
- return false;
1711
- }
1712
- if (data.name !== name) {
1713
- console.error(`Expected name to be '${name}', got ${data.name}`);
1714
- return false;
1715
- }
1716
- if (typeof data.createdAt !== 'string') {
1717
- console.error(`Expected createdAt to be string, got ${typeof data.createdAt}`);
1718
- return false;
1719
- }
1720
- if (typeof data.updatedAt !== 'string') {
1721
- console.error(`Expected updatedAt to be string, got ${typeof data.updatedAt}`);
1722
- return false;
1723
- }
1724
- return true;
1725
- }
1726
- catch (error) {
1727
- console.error(`Failed to test POST /secrets:`, error);
1728
- return false;
1729
- }
1730
- },
1731
- /**
1732
- * Test GET /secrets - List all secrets (names only, not values)
1733
- */
1734
- async list(fetch) {
1735
- try {
1736
- const request = new Request('http://example.com/secrets', {
1737
- method: 'GET',
1738
- });
1739
- const response = await fetch(request);
1740
- if (!response.ok) {
1741
- console.error(`GET /secrets returned ${response.status}`);
1742
- return false;
1743
- }
1744
- const data = await response.json();
1745
- // Validate response structure
1746
- if (!Array.isArray(data.secrets)) {
1747
- console.error(`Expected secrets to be an array, got ${typeof data.secrets}`);
1748
- return false;
1749
- }
1750
- if (typeof data.count !== 'number') {
1751
- console.error(`Expected count to be number, got ${typeof data.count}`);
1752
- return false;
1753
- }
1754
- // Validate each secret has required fields (but NOT the value)
1755
- for (const secret of data.secrets) {
1756
- if (!secret.name ||
1757
- typeof secret.name !== 'string' ||
1758
- typeof secret.createdAt !== 'string' ||
1759
- typeof secret.updatedAt !== 'string') {
1760
- console.error(`Secret missing required fields: ${JSON.stringify(secret)}`);
1761
- return false;
1762
- }
1763
- // Ensure value is NOT included
1764
- if ('value' in secret) {
1765
- console.error(`Secret should not include value field: ${JSON.stringify(secret)}`);
1766
- return false;
1767
- }
1768
- }
1769
- return true;
1770
- }
1771
- catch (error) {
1772
- console.error(`Failed to test GET /secrets:`, error);
1773
- return false;
1774
- }
1775
- },
1776
- /**
1777
- * Test DELETE /secrets/:name - Delete a specific secret
1778
- */
1779
- async delete(fetch, name) {
1780
- try {
1781
- const request = new Request(`http://example.com/secrets/${encodeURIComponent(name)}`, {
1782
- method: 'DELETE',
1783
- });
1784
- const response = await fetch(request);
1785
- if (response.status !== 204) {
1786
- console.error(`DELETE /secrets/${name} returned ${response.status}, expected 204`);
1787
- return false;
1788
- }
1789
- return true;
1790
- }
1791
- catch (error) {
1792
- console.error(`Failed to test DELETE /secrets/${name}:`, error);
1793
- return false;
1794
- }
1795
- },
1796
- /**
1797
- * Test GET /secrets/:name/exists - Check if a secret exists
1798
- */
1799
- async exists(fetch, name) {
1800
- try {
1801
- const request = new Request(`http://example.com/secrets/${encodeURIComponent(name)}/exists`, {
1802
- method: 'GET',
1803
- });
1804
- const response = await fetch(request);
1805
- if (!response.ok) {
1806
- console.error(`GET /secrets/${name}/exists returned ${response.status}`);
1807
- return false;
1808
- }
1809
- const data = await response.json();
1810
- // Validate response structure
1811
- if (typeof data.exists !== 'boolean') {
1812
- console.error(`Expected exists to be boolean, got ${typeof data.exists}`);
1813
- return false;
1814
- }
1815
- return true;
1816
- }
1817
- catch (error) {
1818
- console.error(`Failed to test GET /secrets/${name}/exists:`, error);
1819
- return false;
1820
- }
1821
- },
1822
- /**
1823
- * Test POST /secrets/bulk - Create multiple secrets
1824
- */
1825
- async bulk(fetch, secrets) {
1826
- try {
1827
- const request = new Request('http://example.com/secrets/bulk', {
1828
- method: 'POST',
1829
- headers: {
1830
- 'Content-Type': 'application/json',
1831
- },
1832
- body: JSON.stringify({ secrets }),
1833
- });
1834
- const response = await fetch(request);
1835
- if (response.status !== 201) {
1836
- console.error(`POST /secrets/bulk returned ${response.status}, expected 201`);
1837
- return false;
1838
- }
1839
- const data = await response.json();
1840
- // Validate response structure
1841
- if (typeof data.created !== 'number') {
1842
- console.error(`Expected created to be number, got ${typeof data.created}`);
1843
- return false;
1844
- }
1845
- if (typeof data.updated !== 'number') {
1846
- console.error(`Expected updated to be number, got ${typeof data.updated}`);
1847
- return false;
1848
- }
1849
- // Total should match input
1850
- if (data.created + data.updated !== secrets.length) {
1851
- console.error(`Expected total (${data.created + data.updated}) to match input length (${secrets.length})`);
1852
- return false;
1853
- }
1854
- return true;
1855
- }
1856
- catch (error) {
1857
- console.error(`Failed to test POST /secrets/bulk:`, error);
1858
- return false;
1859
- }
1860
- },
1861
- };
1862
- export const webhooks = {
1863
- /**
1864
- * Test GET /webhooks - List all available webhook handlers
1865
- */
1866
- async list(fetch) {
1867
- try {
1868
- const request = new Request('http://example.com/webhooks', {
1869
- method: 'GET',
1870
- });
1871
- const response = await fetch(request);
1872
- if (!response.ok) {
1873
- console.error(`GET /webhooks returned ${response.status}`);
1874
- return false;
1875
- }
1876
- const data = await response.json();
1877
- // Validate response structure
1878
- if (!Array.isArray(data.webhooks)) {
1879
- console.error(`Expected webhooks to be an array, got ${typeof data.webhooks}`);
1880
- return false;
1881
- }
1882
- if (typeof data.count !== 'number') {
1883
- console.error(`Expected count to be number, got ${typeof data.count}`);
1884
- return false;
1885
- }
1886
- // Validate each webhook has required fields
1887
- for (const webhook of data.webhooks) {
1888
- if (!webhook.slug || typeof webhook.slug !== 'string') {
1889
- console.error(`Webhook missing slug or has invalid type: ${JSON.stringify(webhook)}`);
1890
- return false;
1891
- }
1892
- // Description is optional
1893
- if (webhook.description !== undefined &&
1894
- typeof webhook.description !== 'string') {
1895
- console.error(`Webhook description has invalid type: ${JSON.stringify(webhook)}`);
1896
- return false;
1897
- }
1898
- }
1899
- return true;
1900
- }
1901
- catch (error) {
1902
- console.error(`Failed to test GET /webhooks:`, error);
1903
- return false;
1904
- }
1905
- },
1906
- /**
1907
- * Test POST /webhooks/:slug - Receive an incoming webhook from an external service
1908
- */
1909
- async receive(fetch, slug, payload) {
1910
- try {
1911
- const request = new Request(`http://example.com/webhooks/${encodeURIComponent(slug)}`, {
1912
- method: 'POST',
1913
- headers: {
1914
- 'Content-Type': 'application/json',
1915
- },
1916
- body: JSON.stringify(payload),
1917
- });
1918
- const response = await fetch(request);
1919
- // Accept either 200 (OK) or 202 (Accepted)
1920
- if (response.status !== 200 && response.status !== 202) {
1921
- console.error(`POST /webhooks/${slug} returned ${response.status}, expected 200 or 202`);
1922
- return false;
1923
- }
1924
- const data = await response.json();
1925
- // Validate response structure
1926
- if (typeof data.received !== 'boolean') {
1927
- console.error(`Expected received to be boolean, got ${typeof data.received}`);
1928
- return false;
1929
- }
1930
- // Action field is optional
1931
- if (data.action !== undefined &&
1932
- !['resumed', 'started', 'queued', 'no-match'].includes(data.action)) {
1933
- console.error(`Invalid action value: ${data.action}`);
1934
- return false;
1935
- }
1936
- return true;
1937
- }
1938
- catch (error) {
1939
- console.error(`Failed to test POST /webhooks/${slug}:`, error);
1940
- return false;
1941
- }
1942
- },
1943
- /**
1944
- * Test POST /webhooks/:slug with non-existent webhook - Should return 404
1945
- */
1946
- async notFound(fetch, slug) {
1947
- try {
1948
- const request = new Request(`http://example.com/webhooks/${encodeURIComponent(slug)}`, {
1949
- method: 'POST',
1950
- headers: {
1951
- 'Content-Type': 'application/json',
1952
- },
1953
- body: JSON.stringify({}),
1954
- });
1955
- const response = await fetch(request);
1956
- if (response.status !== 404) {
1957
- console.error(`POST /webhooks/${slug} with non-existent webhook returned ${response.status}, expected 404`);
1958
- return false;
1959
- }
1960
- const data = await response.json();
1961
- if (!data.error || typeof data.error !== 'string') {
1962
- console.error(`Expected error to be string, got ${typeof data.error}`);
1963
- return false;
1964
- }
1965
- // Verify error message mentions the webhook slug
1966
- if (!data.error.toLowerCase().includes('webhook')) {
1967
- console.error(`Expected error message to mention webhook, got: ${data.error}`);
1968
- return false;
1969
- }
1970
- return true;
1971
- }
1972
- catch (error) {
1973
- console.error(`Failed to test POST /webhooks/${slug} with non-existent webhook:`, error);
1974
- return false;
1975
- }
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
- },
2068
- };
2069
- export const pages = {
2070
- /**
2071
- * Test GET /pages - List all pages
2072
- */
2073
- async list(fetch) {
2074
- try {
2075
- const request = new Request('http://example.com/pages', {
2076
- method: 'GET',
2077
- });
2078
- const response = await fetch(request);
2079
- if (!response.ok) {
2080
- console.error(`GET /pages returned ${response.status}`);
2081
- return false;
2082
- }
2083
- const data = (await response.json());
2084
- // Validate response structure
2085
- if (!Array.isArray(data.pages)) {
2086
- console.error(`Expected pages to be an array, got ${typeof data.pages}`);
2087
- return false;
2088
- }
2089
- if (typeof data.count !== 'number') {
2090
- console.error(`Expected count to be number, got ${typeof data.count}`);
2091
- return false;
2092
- }
2093
- // Validate each page has required fields
2094
- for (const page of data.pages) {
2095
- if (!page.slug ||
2096
- typeof page.slug !== 'string' ||
2097
- !page.url ||
2098
- typeof page.url !== 'string' ||
2099
- !page.brainRunId ||
2100
- typeof page.brainRunId !== 'string' ||
2101
- typeof page.persist !== 'boolean' ||
2102
- !page.createdAt ||
2103
- typeof page.createdAt !== 'string' ||
2104
- typeof page.size !== 'number') {
2105
- console.error(`Page missing required fields or has invalid types: ${JSON.stringify(page)}`);
2106
- return false;
2107
- }
2108
- // ttl is optional
2109
- if (page.ttl !== undefined && typeof page.ttl !== 'number') {
2110
- console.error(`Page ttl has invalid type: ${typeof page.ttl}`);
2111
- return false;
2112
- }
2113
- }
2114
- return true;
2115
- }
2116
- catch (error) {
2117
- console.error(`Failed to test GET /pages:`, error);
2118
- return false;
2119
- }
2120
- },
2121
- /**
2122
- * Test POST /pages - Create a new page
2123
- */
2124
- async create(fetch, slug, html, brainRunId, options) {
2125
- try {
2126
- const body = { slug, html, brainRunId };
2127
- if (options?.persist !== undefined) {
2128
- body.persist = options.persist;
2129
- }
2130
- if (options?.ttl !== undefined) {
2131
- body.ttl = options.ttl;
2132
- }
2133
- const request = new Request('http://example.com/pages', {
2134
- method: 'POST',
2135
- headers: {
2136
- 'Content-Type': 'application/json',
2137
- },
2138
- body: JSON.stringify(body),
2139
- });
2140
- const response = await fetch(request);
2141
- if (response.status !== 201) {
2142
- console.error(`POST /pages returned ${response.status}, expected 201`);
2143
- return null;
2144
- }
2145
- const data = (await response.json());
2146
- // Validate response structure
2147
- if (!data.slug || typeof data.slug !== 'string') {
2148
- console.error(`Expected slug to be string, got ${typeof data.slug}`);
2149
- return null;
2150
- }
2151
- if (data.slug !== slug) {
2152
- console.error(`Expected slug to be '${slug}', got ${data.slug}`);
2153
- return null;
2154
- }
2155
- if (!data.url || typeof data.url !== 'string') {
2156
- console.error(`Expected url to be string, got ${typeof data.url}`);
2157
- return null;
2158
- }
2159
- if (!data.brainRunId || typeof data.brainRunId !== 'string') {
2160
- console.error(`Expected brainRunId to be string, got ${typeof data.brainRunId}`);
2161
- return null;
2162
- }
2163
- if (typeof data.persist !== 'boolean') {
2164
- console.error(`Expected persist to be boolean, got ${typeof data.persist}`);
2165
- return null;
2166
- }
2167
- if (!data.createdAt || typeof data.createdAt !== 'string') {
2168
- console.error(`Expected createdAt to be string, got ${typeof data.createdAt}`);
2169
- return null;
2170
- }
2171
- return data.slug;
2172
- }
2173
- catch (error) {
2174
- console.error(`Failed to test POST /pages:`, error);
2175
- return null;
2176
- }
2177
- },
2178
- /**
2179
- * Test GET /pages/:slug - Get page HTML content
2180
- */
2181
- async get(fetch, slug) {
2182
- try {
2183
- const request = new Request(`http://example.com/pages/${encodeURIComponent(slug)}`, {
2184
- method: 'GET',
2185
- });
2186
- const response = await fetch(request);
2187
- if (response.status === 404) {
2188
- return null;
2189
- }
2190
- if (!response.ok) {
2191
- console.error(`GET /pages/${slug} returned ${response.status}`);
2192
- return null;
2193
- }
2194
- // Check content type is HTML
2195
- const contentType = response.headers.get('content-type');
2196
- if (!contentType || !contentType.includes('text/html')) {
2197
- console.error(`Expected content-type to be text/html, got ${contentType}`);
2198
- return null;
2199
- }
2200
- const html = await response.text();
2201
- return html;
2202
- }
2203
- catch (error) {
2204
- console.error(`Failed to test GET /pages/${slug}:`, error);
2205
- return null;
2206
- }
2207
- },
2208
- /**
2209
- * Test GET /pages/:slug/meta - Get page metadata
2210
- */
2211
- async getMeta(fetch, slug) {
2212
- try {
2213
- const request = new Request(`http://example.com/pages/${encodeURIComponent(slug)}/meta`, {
2214
- method: 'GET',
2215
- });
2216
- const response = await fetch(request);
2217
- if (response.status === 404) {
2218
- return null;
2219
- }
2220
- if (!response.ok) {
2221
- console.error(`GET /pages/${slug}/meta returned ${response.status}`);
2222
- return null;
2223
- }
2224
- const data = (await response.json());
2225
- // Validate response structure
2226
- if (!data.slug ||
2227
- typeof data.slug !== 'string' ||
2228
- !data.brainRunId ||
2229
- typeof data.brainRunId !== 'string' ||
2230
- typeof data.persist !== 'boolean' ||
2231
- !data.createdAt ||
2232
- typeof data.createdAt !== 'string' ||
2233
- typeof data.size !== 'number') {
2234
- console.error(`Page metadata missing required fields: ${JSON.stringify(data)}`);
2235
- return null;
2236
- }
2237
- return data;
2238
- }
2239
- catch (error) {
2240
- console.error(`Failed to test GET /pages/${slug}/meta:`, error);
2241
- return null;
2242
- }
2243
- },
2244
- /**
2245
- * Test PUT /pages/:slug - Update page HTML content
2246
- */
2247
- async update(fetch, slug, html) {
2248
- try {
2249
- const request = new Request(`http://example.com/pages/${encodeURIComponent(slug)}`, {
2250
- method: 'PUT',
2251
- headers: {
2252
- 'Content-Type': 'application/json',
2253
- },
2254
- body: JSON.stringify({ html }),
2255
- });
2256
- const response = await fetch(request);
2257
- if (response.status === 404) {
2258
- console.error(`PUT /pages/${slug} returned 404 - page not found`);
2259
- return false;
2260
- }
2261
- if (!response.ok) {
2262
- console.error(`PUT /pages/${slug} returned ${response.status}`);
2263
- return false;
2264
- }
2265
- const data = (await response.json());
2266
- // Validate response structure
2267
- if (!data.slug || data.slug !== slug) {
2268
- console.error(`Expected slug to be '${slug}', got ${data.slug}`);
2269
- return false;
2270
- }
2271
- if (!data.url || typeof data.url !== 'string') {
2272
- console.error(`Expected url to be string, got ${typeof data.url}`);
2273
- return false;
2274
- }
2275
- if (!data.updatedAt || typeof data.updatedAt !== 'string') {
2276
- console.error(`Expected updatedAt to be string, got ${typeof data.updatedAt}`);
2277
- return false;
2278
- }
2279
- return true;
2280
- }
2281
- catch (error) {
2282
- console.error(`Failed to test PUT /pages/${slug}:`, error);
2283
- return false;
2284
- }
2285
- },
2286
- /**
2287
- * Test DELETE /pages/:slug - Delete a page
2288
- */
2289
- async delete(fetch, slug) {
2290
- try {
2291
- const request = new Request(`http://example.com/pages/${encodeURIComponent(slug)}`, {
2292
- method: 'DELETE',
2293
- });
2294
- const response = await fetch(request);
2295
- if (response.status !== 204) {
2296
- console.error(`DELETE /pages/${slug} returned ${response.status}, expected 204`);
2297
- return false;
2298
- }
2299
- return true;
2300
- }
2301
- catch (error) {
2302
- console.error(`Failed to test DELETE /pages/${slug}:`, error);
2303
- return false;
2304
- }
2305
- },
2306
- /**
2307
- * Test GET /pages/:slug with non-existent page - Should return 404
2308
- */
2309
- async notFound(fetch, slug) {
2310
- try {
2311
- const request = new Request(`http://example.com/pages/${encodeURIComponent(slug)}`, {
2312
- method: 'GET',
2313
- });
2314
- const response = await fetch(request);
2315
- if (response.status !== 404) {
2316
- console.error(`GET /pages/${slug} with non-existent page returned ${response.status}, expected 404`);
2317
- return false;
2318
- }
2319
- return true;
2320
- }
2321
- catch (error) {
2322
- console.error(`Failed to test GET /pages/${slug} with non-existent page:`, error);
2323
- return false;
2324
- }
2325
- },
2326
- /**
2327
- * Test DELETE /pages/:slug preserves resources - deleting a page should not delete resources
2328
- */
2329
- async deletePreservesResources(fetch) {
2330
- try {
2331
- // First create a resource
2332
- const formData = new FormData();
2333
- formData.append('file', new Blob(['test content for spec'], { type: 'text/plain' }), 'spec-test-resource.txt');
2334
- formData.append('type', 'text');
2335
- formData.append('key', 'spec-test-resource.txt');
2336
- formData.append('local', 'false');
2337
- const createResourceRequest = new Request('http://example.com/resources', {
2338
- method: 'POST',
2339
- body: formData,
2340
- });
2341
- const createResourceResponse = await fetch(createResourceRequest);
2342
- if (createResourceResponse.status !== 201) {
2343
- console.error(`Failed to create test resource: ${createResourceResponse.status}`);
2344
- return false;
2345
- }
2346
- // Create a page to delete
2347
- const pageSlug = `spec-test-page-${Date.now()}`;
2348
- const createPageRequest = new Request('http://example.com/pages', {
2349
- method: 'POST',
2350
- headers: {
2351
- 'Content-Type': 'application/json',
2352
- },
2353
- body: JSON.stringify({
2354
- slug: pageSlug,
2355
- html: '<html><body>Test page</body></html>',
2356
- brainRunId: 'spec-test-brain-run-id',
2357
- }),
2358
- });
2359
- const createPageResponse = await fetch(createPageRequest);
2360
- if (createPageResponse.status !== 201) {
2361
- console.error(`Failed to create test page: ${createPageResponse.status}`);
2362
- // Clean up resource
2363
- await fetch(new Request('http://example.com/resources/spec-test-resource.txt', { method: 'DELETE' }));
2364
- return false;
2365
- }
2366
- // Delete the page
2367
- const deletePageRequest = new Request(`http://example.com/pages/${encodeURIComponent(pageSlug)}`, { method: 'DELETE' });
2368
- const deletePageResponse = await fetch(deletePageRequest);
2369
- if (deletePageResponse.status !== 204) {
2370
- console.error(`DELETE /pages/${pageSlug} returned ${deletePageResponse.status}, expected 204`);
2371
- // Clean up resource
2372
- await fetch(new Request('http://example.com/resources/spec-test-resource.txt', { method: 'DELETE' }));
2373
- return false;
2374
- }
2375
- // Verify the resource still exists
2376
- const listResourcesRequest = new Request('http://example.com/resources', {
2377
- method: 'GET',
2378
- });
2379
- const listResourcesResponse = await fetch(listResourcesRequest);
2380
- if (!listResourcesResponse.ok) {
2381
- console.error(`GET /resources returned ${listResourcesResponse.status}`);
2382
- return false;
2383
- }
2384
- const resourcesData = (await listResourcesResponse.json());
2385
- const resourceExists = resourcesData.resources.some((r) => r.key === 'spec-test-resource.txt');
2386
- if (!resourceExists) {
2387
- console.error('DELETE /pages incorrectly deleted resources - resource not found after page delete');
2388
- return false;
2389
- }
2390
- // Clean up: delete the test resource
2391
- await fetch(new Request('http://example.com/resources/spec-test-resource.txt', { method: 'DELETE' }));
2392
- return true;
2393
- }
2394
- catch (error) {
2395
- console.error(`Failed to test DELETE /pages preserves resources:`, error);
2396
- return false;
2397
- }
2398
- },
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
- };
2455
- //# sourceMappingURL=api.js.map
1239
+ //# sourceMappingURL=brains.js.map