@gcoredev/fastedge-test 0.0.1-beta.0 → 0.1.0-beta.2

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 (43) hide show
  1. package/README.md +6 -6
  2. package/bin/fastedge-debug.js +2 -0
  3. package/dist/fastedge-cli/METADATA.json +1 -3
  4. package/dist/fastedge-cli/{fastedge-run-linux-x64-unkown → fastedge-run-darwin-arm64} +0 -0
  5. package/dist/fastedge-cli/fastedge-run-linux-x64 +0 -0
  6. package/dist/fastedge-cli/fastedge-run.exe +0 -0
  7. package/dist/frontend/assets/index-CEFjsU8e.js +35 -0
  8. package/dist/frontend/assets/index-DdlINQc_.css +1 -0
  9. package/dist/frontend/index.html +2 -2
  10. package/dist/lib/index.cjs +329 -112
  11. package/dist/lib/index.js +331 -115
  12. package/dist/lib/runner/HostFunctions.d.ts +8 -0
  13. package/dist/lib/runner/HttpWasmRunner.d.ts +34 -14
  14. package/dist/lib/runner/IStateManager.d.ts +3 -2
  15. package/dist/lib/runner/IWasmRunner.d.ts +18 -1
  16. package/dist/lib/runner/NullStateManager.d.ts +1 -0
  17. package/dist/lib/runner/PortManager.d.ts +17 -19
  18. package/dist/lib/runner/ProxyWasmRunner.d.ts +7 -0
  19. package/dist/lib/runner/standalone.d.ts +1 -1
  20. package/dist/lib/schemas/api.d.ts +8 -2
  21. package/dist/lib/schemas/config.d.ts +4 -1
  22. package/dist/lib/test-framework/index.cjs +330 -114
  23. package/dist/lib/test-framework/index.js +332 -117
  24. package/dist/lib/test-framework/suite-runner.d.ts +1 -1
  25. package/dist/server.js +30 -30
  26. package/docs/API.md +758 -360
  27. package/docs/DEBUGGER.md +151 -0
  28. package/docs/INDEX.md +111 -0
  29. package/docs/RUNNER.md +582 -0
  30. package/docs/TEST_CONFIG.md +242 -0
  31. package/docs/TEST_FRAMEWORK.md +384 -284
  32. package/docs/WEBSOCKET.md +499 -0
  33. package/docs/quickstart.md +171 -0
  34. package/llms.txt +72 -14
  35. package/package.json +17 -6
  36. package/schemas/api-config.schema.json +12 -5
  37. package/schemas/api-load.schema.json +11 -6
  38. package/schemas/{test-config.schema.json → fastedge-config.test.schema.json} +12 -5
  39. package/dist/fastedge-cli/.gitkeep +0 -0
  40. package/dist/frontend/assets/index-CnXStFTd.css +0 -1
  41. package/dist/frontend/assets/index-FR9Oqsow.js +0 -37
  42. package/docs/HYBRID_LOADING.md +0 -546
  43. package/docs/LOCAL_SERVER.md +0 -153
package/docs/API.md CHANGED
@@ -1,579 +1,977 @@
1
- # FastEdge Debugger REST API Documentation
1
+ # REST API Reference
2
2
 
3
- ## Overview
3
+ The `@gcoredev/fastedge-test` debugger server exposes a REST API for loading WASM modules, executing requests, and managing test configuration.
4
4
 
5
- The FastEdge Debugger provides a comprehensive REST API for programmatic testing and debugging of FastEdge applications. This API enables AI agents, CI/CD pipelines, and automated testing tools to interact with the debugger.
5
+ ## Base URL
6
6
 
7
- **Base URL**: `http://localhost:5179`
7
+ ```
8
+ http://localhost:5179
9
+ ```
8
10
 
9
- **Default Ports**:
10
- - REST API & Web UI: `5179`
11
- - WebSocket (logs): `5178`
11
+ The port can be overridden via the `PORT` environment variable. When `WORKSPACE_PATH` is set, the active port is written to `$WORKSPACE_PATH/.debug-port` on startup and deleted on shutdown.
12
12
 
13
- ## Authentication
13
+ ## Common Headers
14
14
 
15
- Currently, no authentication is required. The debugger is intended for local development use only.
15
+ ### X-Source Header
16
16
 
17
- ## API Endpoints
17
+ The `POST /api/execute`, `POST /api/send`, and `POST /api/config` endpoints accept an optional `X-Source` request header that tags the origin of the operation in WebSocket broadcast events.
18
18
 
19
- ### Health Check
19
+ | Value | Description |
20
+ | ---------- | ------------------------------------------------------- |
21
+ | `ui` | Request originated from the web UI (default if omitted) |
22
+ | `ai_agent` | Request originated from an AI agent |
23
+ | `api` | Request originated from direct API usage |
24
+ | `system` | Request originated from an automated system |
20
25
 
21
- Check if the debugger server is running and healthy.
26
+ ```http
27
+ X-Source: ai_agent
28
+ ```
22
29
 
23
- **Endpoint**: `GET /health`
30
+ ---
24
31
 
25
- **Response**:
26
- ```json
32
+ ## Health
33
+
34
+ ### GET /health
35
+
36
+ Returns the server status and service identity.
37
+
38
+ **Response**
39
+
40
+ ```typescript
27
41
  {
28
- "status": "ok"
42
+ status: "ok";
43
+ service: "fastedge-debugger";
29
44
  }
30
45
  ```
31
46
 
32
- **Status Codes**:
33
- - `200 OK`: Server is healthy and running
47
+ **Example**
34
48
 
35
- **Example**:
36
49
  ```bash
37
50
  curl http://localhost:5179/health
38
51
  ```
39
52
 
53
+ ```json
54
+ {
55
+ "status": "ok",
56
+ "service": "fastedge-debugger"
57
+ }
58
+ ```
59
+
40
60
  ---
41
61
 
42
- ### Load WASM Module
62
+ ### GET /api/client-count
63
+
64
+ Returns the number of currently connected WebSocket clients. Useful in CI tooling to wait until the UI has connected before proceeding.
65
+
66
+ **Response**
67
+
68
+ ```typescript
69
+ {
70
+ count: number;
71
+ }
72
+ ```
43
73
 
44
- Load a WebAssembly module into the debugger. This must be called before executing any requests.
74
+ **Example**
45
75
 
46
- **Endpoint**: `POST /api/load`
76
+ ```bash
77
+ curl http://localhost:5179/api/client-count
78
+ ```
47
79
 
48
- **Request Body**:
49
80
  ```json
50
81
  {
51
- "wasmBase64": "string", // Base64-encoded WASM binary (provide this OR wasmPath)
52
- "wasmPath": "string", // Absolute path to WASM file on disk (provide this OR wasmBase64)
53
- "dotenvEnabled": boolean // Enable .env file loading (optional, default: true)
82
+ "count": 1
83
+ }
84
+ ```
85
+
86
+ ---
87
+
88
+ ## WASM Loading
89
+
90
+ ### POST /api/load
91
+
92
+ Loads a WASM binary into the runner. Accepts either a file path or a base64-encoded binary. Automatically detects whether the module is HTTP-WASM or Proxy-WASM.
93
+
94
+ **Request Body**
95
+
96
+ Exactly one of `wasmPath` or `wasmBase64` must be provided.
97
+
98
+ ```typescript
99
+ {
100
+ wasmPath?: string; // Absolute path to a .wasm file on the server filesystem
101
+ wasmBase64?: string; // Base64-encoded WASM binary
102
+ dotenv?: {
103
+ enabled: boolean; // Whether to load .env files for this module
104
+ path?: string; // Directory containing .env files (defaults to server CWD)
105
+ };
106
+ }
107
+ ```
108
+
109
+ **Response**
110
+
111
+ ```typescript
112
+ {
113
+ ok: true;
114
+ wasmType: "http-wasm" | "proxy-wasm";
115
+ resolvedPath?: string; // Absolute path used when wasmPath was provided
54
116
  }
55
117
  ```
56
118
 
57
- **Response**:
119
+ **Example — load from path**
120
+
121
+ ```bash
122
+ curl -X POST http://localhost:5179/api/load \
123
+ -H "Content-Type: application/json" \
124
+ -d '{
125
+ "wasmPath": "/home/user/project/build/module.wasm",
126
+ "dotenv": { "enabled": true }
127
+ }'
128
+ ```
129
+
58
130
  ```json
59
131
  {
60
132
  "ok": true,
61
- "wasmType": "http-wasm" | "proxy-wasm"
133
+ "wasmType": "proxy-wasm",
134
+ "resolvedPath": "/home/user/project/build/module.wasm"
62
135
  }
63
136
  ```
64
137
 
65
- **Status Codes**:
66
- - `200 OK`: WASM loaded successfully
67
- - `400 Bad Request`: Missing or invalid wasmBase64
68
- - `500 Internal Server Error`: Failed to load WASM
138
+ **Example — load from base64**
69
139
 
70
- **Example**:
71
140
  ```bash
72
- # Read WASM file and encode to base64
73
- WASM_BASE64=$(base64 -w 0 ./dist/app.wasm)
74
-
75
- # Load into debugger
76
141
  curl -X POST http://localhost:5179/api/load \
77
142
  -H "Content-Type: application/json" \
78
- -d "{\"wasmBase64\": \"$WASM_BASE64\", \"dotenvEnabled\": true}"
143
+ -d '{
144
+ "wasmBase64": "AGFzbQEAAAA...",
145
+ "dotenv": { "enabled": false }
146
+ }'
147
+ ```
148
+
149
+ ```json
150
+ {
151
+ "ok": true,
152
+ "wasmType": "http-wasm"
153
+ }
79
154
  ```
80
155
 
81
- **Notes**:
82
- - The debugger automatically detects whether the WASM is HTTP or CDN (proxy-wasm) type
83
- - Previous WASM modules are automatically cleaned up when loading a new one
84
- - Set `X-Source` header to track the source of the load (e.g., "cli", "vscode", "ci")
156
+ **Error Responses**
157
+
158
+ | Status | Condition |
159
+ | ------ | ----------------------------------------------------------------------------------------------------------- |
160
+ | `400` | Validation failed, missing both `wasmPath` and `wasmBase64`, invalid path, or path does not end in `.wasm` |
161
+ | `500` | WASM load failed or runner initialization error |
85
162
 
86
163
  ---
87
164
 
88
- ### Execute Request
165
+ ### PATCH /api/dotenv
89
166
 
90
- Execute an HTTP request or CDN full flow against the loaded WASM module.
167
+ Applies updated dotenv settings to the currently loaded WASM module without reloading the binary. For Proxy-WASM, this resets stores and reloads dotenv files in-place. For HTTP-WASM, this restarts the underlying process with updated flags.
91
168
 
92
- **Endpoint**: `POST /api/execute`
169
+ Requires a WASM module to already be loaded via `POST /api/load`.
93
170
 
94
- **Request Body**:
171
+ **Request Body**
95
172
 
96
- For **HTTP WASM**:
97
- ```json
173
+ ```typescript
98
174
  {
99
- "url": "string", // Full URL or path (required)
100
- "method": "string", // HTTP method (optional, default: "GET")
101
- "headers": object, // Request headers (optional)
102
- "body": "string" // Request body (optional)
175
+ dotenv: {
176
+ enabled: boolean; // Whether dotenv loading should be enabled
177
+ path?: string; // Directory containing .env files (defaults to server CWD)
178
+ };
103
179
  }
104
180
  ```
105
181
 
106
- For **CDN (Proxy WASM)**:
107
- ```json
182
+ **Response**
183
+
184
+ ```typescript
108
185
  {
109
- "url": "string", // Target URL (required)
110
- "request": {
111
- "method": "string", // HTTP method (optional, default: "GET")
112
- "headers": object, // Request headers (optional)
113
- "body": "string" // Request body (optional)
114
- },
115
- "response": {
116
- "body": "string", // Origin response body (optional)
117
- "status": number, // HTTP status code (optional, default: 200)
118
- "statusText": "string", // Status text (optional)
119
- "headers": object // Response headers (optional)
120
- },
121
- "properties": object // CDN properties (optional)
186
+ ok: true;
122
187
  }
123
188
  ```
124
189
 
125
- **Response**:
190
+ **Example**
191
+
192
+ ```bash
193
+ curl -X PATCH http://localhost:5179/api/dotenv \
194
+ -H "Content-Type: application/json" \
195
+ -d '{
196
+ "dotenv": { "enabled": true, "path": "/home/user/project" }
197
+ }'
198
+ ```
126
199
 
127
- For **HTTP WASM**:
128
200
  ```json
129
201
  {
130
- "ok": true,
131
- "status": number,
132
- "statusText": "string",
133
- "headers": object,
134
- "body": "string",
135
- "contentType": "string",
136
- "isBase64": boolean
202
+ "ok": true
137
203
  }
138
204
  ```
139
205
 
140
- For **CDN (Proxy WASM)**:
141
- ```json
206
+ **Error Responses**
207
+
208
+ | Status | Condition |
209
+ | ------ | -------------------------------------------------------------- |
210
+ | `400` | `dotenv.enabled` is not a boolean, or no WASM module is loaded |
211
+ | `500` | Failed to apply dotenv settings |
212
+
213
+ ---
214
+
215
+ ## Test Execution
216
+
217
+ ### POST /api/execute
218
+
219
+ Executes a request through the loaded WASM module. Behavior differs based on the detected runner type. This endpoint does not use schema validation — fields are read directly from the request body.
220
+
221
+ Requires a WASM module to be loaded via `POST /api/load`. Accepts an optional [`X-Source`](#x-source-header) request header.
222
+
223
+ **Request Body**
224
+
225
+ For **HTTP-WASM**, the top-level `url`, `method`, `headers`, and `body` fields are used directly as the request:
226
+
227
+ ```typescript
142
228
  {
143
- "ok": true,
144
- "hookResults": {
145
- "onRequestHeader": object,
146
- "onRequestBody": object,
147
- "onResponseHeader": object,
148
- "onResponseBody": object
149
- },
150
- "finalResponse": {
151
- "status": number,
152
- "statusText": "string",
153
- "headers": object,
154
- "body": "string"
155
- },
156
- "calculatedProperties": object
229
+ url: string; // Full URL (path and query extracted from this)
230
+ method?: string; // HTTP method (default: "GET")
231
+ headers?: Record<string, string>; // Request headers
232
+ body?: string; // Request body
157
233
  }
158
234
  ```
159
235
 
160
- **Status Codes**:
161
- - `200 OK`: Request executed successfully
162
- - `400 Bad Request`: No WASM loaded or invalid request
163
- - `500 Internal Server Error`: Execution failed
236
+ For **Proxy-WASM**, the same top-level `url` field is required for URL parsing. The full CDN flow is controlled via nested `request`, `response`, and `properties` fields:
164
237
 
165
- **Examples**:
238
+ ```typescript
239
+ {
240
+ url: string; // Request URL (required)
241
+ method?: string; // Unused for Proxy-WASM (use request.method)
242
+ headers?: Record<string, string>; // Unused for Proxy-WASM (use request.headers)
243
+ body?: string; // Unused for Proxy-WASM (use request.body)
244
+ request?: {
245
+ method?: string; // HTTP method (default: "GET")
246
+ headers?: Record<string, string>; // Request headers (default: {})
247
+ body?: string; // Request body (default: "")
248
+ };
249
+ response?: {
250
+ headers?: Record<string, string>; // Simulated upstream response headers (default: {})
251
+ body?: string; // Simulated upstream response body (default: "")
252
+ status?: number; // Simulated upstream response status (default: 200)
253
+ statusText?: string; // Simulated upstream response status text (default: "OK")
254
+ };
255
+ properties?: Record<string, unknown>; // CDN properties (default: {})
256
+ }
257
+ ```
166
258
 
167
- **HTTP WASM** - Simple GET:
168
- ```bash
169
- curl -X POST http://localhost:5179/api/execute \
170
- -H "Content-Type: application/json" \
171
- -d '{
172
- "url": "http://localhost/api/test",
173
- "method": "GET"
174
- }'
259
+ **Response — HTTP-WASM**
260
+
261
+ ```typescript
262
+ {
263
+ ok: true;
264
+ result: {
265
+ status: number;
266
+ statusText: string;
267
+ headers: Record<string, string>;
268
+ body: string;
269
+ contentType: string | null;
270
+ isBase64?: boolean;
271
+ logs: Array<{ level: number; message: string }>;
272
+ };
273
+ }
274
+ ```
275
+
276
+ **Response — Proxy-WASM**
277
+
278
+ ```typescript
279
+ {
280
+ ok: true;
281
+ hookResults: Record<string, HookResult>;
282
+ finalResponse: {
283
+ status: number;
284
+ statusText: string;
285
+ headers: Record<string, string>;
286
+ body: string;
287
+ contentType: string;
288
+ isBase64?: boolean;
289
+ };
290
+ calculatedProperties?: Record<string, unknown>;
291
+ }
292
+ ```
293
+
294
+ Where `HookResult` is:
295
+
296
+ ```typescript
297
+ type HookResult = {
298
+ returnCode: number | null;
299
+ logs: Array<{ level: number; message: string }>;
300
+ input: {
301
+ request: { headers: Record<string, string>; body: string };
302
+ response: { headers: Record<string, string>; body: string };
303
+ properties?: Record<string, unknown>;
304
+ };
305
+ output: {
306
+ request: { headers: Record<string, string>; body: string };
307
+ response: { headers: Record<string, string>; body: string };
308
+ properties?: Record<string, unknown>;
309
+ };
310
+ properties: Record<string, unknown>;
311
+ };
175
312
  ```
176
313
 
177
- **HTTP WASM** - POST with body:
314
+ `hookResults` is keyed by hook name (e.g. `"onRequestHeaders"`, `"onResponseHeaders"`). `calculatedProperties` is present only when the runner derives request-derived properties; keys follow the `request.*` pattern (e.g. `request.url`, `request.host`, `request.path`, `request.query`, `request.scheme`, `request.extension`, `request.method`).
315
+
316
+ **Example — HTTP-WASM**
317
+
178
318
  ```bash
179
319
  curl -X POST http://localhost:5179/api/execute \
180
320
  -H "Content-Type: application/json" \
321
+ -H "X-Source: api" \
181
322
  -d '{
182
- "url": "http://localhost/api/data",
183
- "method": "POST",
184
- "headers": {
185
- "Content-Type": "application/json",
186
- "Authorization": "Bearer token123"
187
- },
188
- "body": "{\"key\": \"value\"}"
323
+ "url": "http://example.com/api/data",
324
+ "method": "GET",
325
+ "headers": { "accept": "application/json" }
189
326
  }'
190
327
  ```
191
328
 
192
- **CDN (Proxy WASM)** - Full flow:
329
+ ```json
330
+ {
331
+ "ok": true,
332
+ "result": {
333
+ "status": 200,
334
+ "statusText": "OK",
335
+ "headers": { "content-type": "application/json" },
336
+ "body": "{\"hello\":\"world\"}",
337
+ "contentType": "application/json",
338
+ "isBase64": false,
339
+ "logs": [
340
+ { "level": 2, "message": "request received" }
341
+ ]
342
+ }
343
+ }
344
+ ```
345
+
346
+ **Example — Proxy-WASM**
347
+
193
348
  ```bash
194
349
  curl -X POST http://localhost:5179/api/execute \
195
350
  -H "Content-Type: application/json" \
351
+ -H "X-Source: api" \
196
352
  -d '{
197
353
  "url": "https://example.com/page",
198
354
  "request": {
199
355
  "method": "GET",
200
- "headers": {
201
- "User-Agent": "Mozilla/5.0"
202
- }
356
+ "headers": { "host": "example.com" },
357
+ "body": ""
203
358
  },
204
359
  "response": {
205
- "body": "<html>Original content</html>",
360
+ "headers": { "content-type": "text/html" },
361
+ "body": "<html/>",
206
362
  "status": 200,
207
- "headers": {
208
- "Content-Type": "text/html"
209
- }
363
+ "statusText": "OK"
210
364
  },
211
- "properties": {
212
- "client.ip": "1.2.3.4",
213
- "request.path": "/page"
214
- }
365
+ "properties": {}
215
366
  }'
216
367
  ```
217
368
 
218
- ---
219
-
220
- ### Get Configuration
221
-
222
- Retrieve the current test configuration including environment variables and properties.
223
-
224
- **Endpoint**: `GET /api/config`
225
-
226
- **Response**:
227
369
  ```json
228
370
  {
229
371
  "ok": true,
230
- "config": {
231
- "envVars": object,
232
- "secrets": object,
233
- "properties": object
372
+ "hookResults": {
373
+ "onRequestHeaders": {
374
+ "returnCode": 0,
375
+ "logs": [{ "level": 2, "message": "onRequestHeaders called" }],
376
+ "input": {
377
+ "request": { "headers": { "host": "example.com" }, "body": "" },
378
+ "response": { "headers": {}, "body": "" },
379
+ "properties": {}
380
+ },
381
+ "output": {
382
+ "request": { "headers": { "host": "example.com", "x-added": "1" }, "body": "" },
383
+ "response": { "headers": {}, "body": "" }
384
+ },
385
+ "properties": {}
386
+ }
387
+ },
388
+ "finalResponse": {
389
+ "status": 200,
390
+ "statusText": "OK",
391
+ "headers": { "content-type": "text/html" },
392
+ "body": "<html/>",
393
+ "contentType": "text/html"
394
+ },
395
+ "calculatedProperties": {
396
+ "request.url": "https://example.com/page",
397
+ "request.host": "example.com",
398
+ "request.path": "/page",
399
+ "request.query": "",
400
+ "request.scheme": "https",
401
+ "request.extension": "",
402
+ "request.method": "GET"
234
403
  }
235
404
  }
236
405
  ```
237
406
 
238
- **Status Codes**:
239
- - `200 OK`: Configuration retrieved
240
- - `404 Not Found`: No configuration file exists
407
+ **Error Responses**
241
408
 
242
- **Example**:
243
- ```bash
244
- curl http://localhost:5179/api/config
245
- ```
409
+ | Status | Condition |
410
+ | ------ | ------------------------------------------ |
411
+ | `400` | No WASM module loaded, or `url` is missing |
412
+ | `500` | Execution failed |
246
413
 
247
414
  ---
248
415
 
249
- ### Update Configuration
416
+ ### POST /api/call
250
417
 
251
- Update the test configuration including environment variables, secrets, and properties.
418
+ Calls a specific Proxy-WASM CDN hook directly. Only valid for Proxy-WASM modules.
252
419
 
253
- **Endpoint**: `POST /api/config`
420
+ Requires a WASM module to be loaded via `POST /api/load`.
254
421
 
255
- **Request Body**:
256
- ```json
422
+ **Request Body**
423
+
424
+ ```typescript
257
425
  {
258
- "config": {
259
- "envVars": {
260
- "DEBUG": "true",
261
- "API_URL": "https://api.example.com"
262
- },
263
- "secrets": {
264
- "api_key": "secret_name"
265
- },
266
- "properties": {
267
- "client.ip": "1.2.3.4",
268
- "request.path": "/test"
269
- }
270
- }
426
+ hook: "onRequestHeaders" | "onRequestBody" | "onResponseHeaders" | "onResponseBody";
427
+ request?: {
428
+ headers: Record<string, string>;
429
+ body: string;
430
+ };
431
+ response?: {
432
+ headers: Record<string, string>;
433
+ body: string;
434
+ };
435
+ properties: Record<string, unknown>; // Required; use {} if none
271
436
  }
272
437
  ```
273
438
 
274
- **Response**:
275
- ```json
439
+ `request` and `response` default to `{ headers: {}, body: "" }` if omitted.
440
+
441
+ **Response**
442
+
443
+ ```typescript
276
444
  {
277
- "ok": true
445
+ ok: true;
446
+ result: HookResult;
278
447
  }
279
448
  ```
280
449
 
281
- **Status Codes**:
282
- - `200 OK`: Configuration updated
283
- - `400 Bad Request`: Missing config in request body
284
- - `500 Internal Server Error`: Failed to save configuration
450
+ Where `HookResult` is:
451
+
452
+ ```typescript
453
+ type HookResult = {
454
+ returnCode: number | null;
455
+ logs: Array<{ level: number; message: string }>;
456
+ input: {
457
+ request: { headers: Record<string, string>; body: string };
458
+ response: { headers: Record<string, string>; body: string };
459
+ properties?: Record<string, unknown>;
460
+ };
461
+ output: {
462
+ request: { headers: Record<string, string>; body: string };
463
+ response: { headers: Record<string, string>; body: string };
464
+ properties?: Record<string, unknown>;
465
+ };
466
+ properties: Record<string, unknown>;
467
+ };
468
+ ```
469
+
470
+ **Example**
285
471
 
286
- **Example**:
287
472
  ```bash
288
- curl -X POST http://localhost:5179/api/config \
473
+ curl -X POST http://localhost:5179/api/call \
289
474
  -H "Content-Type: application/json" \
290
475
  -d '{
291
- "config": {
292
- "envVars": {
293
- "DEBUG": "true",
294
- "LOG_LEVEL": "verbose"
295
- }
476
+ "hook": "onRequestHeaders",
477
+ "request": {
478
+ "headers": { "host": "example.com", "user-agent": "curl/8.0" },
479
+ "body": ""
480
+ },
481
+ "response": {
482
+ "headers": {},
483
+ "body": ""
484
+ },
485
+ "properties": {
486
+ "client.geo.country": "US"
296
487
  }
297
488
  }'
298
489
  ```
299
490
 
300
- **Notes**:
301
- - Configuration is saved to `test-config.json` in the project root
302
- - Environment variables are available to the WASM module via `getEnv()`
303
- - Properties are used for CDN (proxy-wasm) applications
304
- - Configuration must be set BEFORE loading the WASM module to take effect
491
+ ```json
492
+ {
493
+ "ok": true,
494
+ "result": {
495
+ "returnCode": 0,
496
+ "logs": [
497
+ { "level": 2, "message": "processing request headers" }
498
+ ],
499
+ "input": {
500
+ "request": {
501
+ "headers": { "host": "example.com", "user-agent": "curl/8.0" },
502
+ "body": ""
503
+ },
504
+ "response": { "headers": {}, "body": "" },
505
+ "properties": { "client.geo.country": "US" }
506
+ },
507
+ "output": {
508
+ "request": {
509
+ "headers": { "host": "example.com", "user-agent": "curl/8.0", "x-country": "US" },
510
+ "body": ""
511
+ },
512
+ "response": { "headers": {}, "body": "" }
513
+ },
514
+ "properties": { "client.geo.country": "US" }
515
+ }
516
+ }
517
+ ```
518
+
519
+ **Error Responses**
520
+
521
+ | Status | Condition |
522
+ | ------ | ---------------------------------------------------------------------------------- |
523
+ | `400` | Validation failed (invalid hook name, missing `properties`), or no WASM module loaded |
524
+ | `500` | Hook execution failed |
305
525
 
306
526
  ---
307
527
 
308
- ## WebSocket API
528
+ ### POST /api/send
309
529
 
310
- The debugger provides real-time log streaming via WebSocket.
530
+ Executes the full Proxy-WASM CDN request/response flow. Equivalent to `POST /api/execute` for Proxy-WASM, but uses stricter Zod schema validation. Only valid for Proxy-WASM modules.
311
531
 
312
- **Endpoint**: `ws://localhost:5178/ws` (or port specified by `WS_PORT` env var)
532
+ Requires a WASM module to be loaded via `POST /api/load`. Accepts an optional [`X-Source`](#x-source-header) request header.
313
533
 
314
- **Events**:
534
+ **Request Body**
315
535
 
316
- ### WASM Loaded
317
- ```json
536
+ ```typescript
318
537
  {
319
- "type": "wasm_loaded",
320
- "data": {
321
- "name": "binary.wasm",
322
- "size": 123456,
323
- "timestamp": "2025-01-15T10:30:00Z",
324
- "source": "ui" | "cli" | "vscode"
325
- }
538
+ url: string; // Full request URL (required)
539
+ request?: {
540
+ method?: string; // HTTP method (default: "GET")
541
+ url?: string;
542
+ headers?: Record<string, string>; // Request headers (default: {})
543
+ body?: string; // Request body (default: "")
544
+ };
545
+ response?: {
546
+ headers?: Record<string, string>; // Simulated upstream response headers (default: {})
547
+ body?: string; // Simulated upstream response body (default: "")
548
+ };
549
+ properties: Record<string, unknown>; // CDN properties (required; use {} if none)
326
550
  }
327
551
  ```
328
552
 
329
- ### HTTP Request Completed
330
- ```json
553
+ **Response**
554
+
555
+ ```typescript
331
556
  {
332
- "type": "http_request_completed",
333
- "data": {
557
+ ok: true;
558
+ hookResults: Record<string, HookResult>;
559
+ finalResponse: {
560
+ status: number;
561
+ statusText: string;
562
+ headers: Record<string, string>;
563
+ body: string;
564
+ contentType: string;
565
+ isBase64?: boolean;
566
+ };
567
+ calculatedProperties?: Record<string, unknown>;
568
+ }
569
+ ```
570
+
571
+ `HookResult` has the same shape as documented in [`POST /api/call`](#post-apicall). `hookResults` is keyed by hook name. `calculatedProperties` keys follow the `request.*` pattern.
572
+
573
+ **Example**
574
+
575
+ ```bash
576
+ curl -X POST http://localhost:5179/api/send \
577
+ -H "Content-Type: application/json" \
578
+ -H "X-Source: ai_agent" \
579
+ -d '{
580
+ "url": "https://example.com/api/resource",
581
+ "request": {
582
+ "method": "POST",
583
+ "headers": { "content-type": "application/json" },
584
+ "body": "{\"key\":\"value\"}"
585
+ },
334
586
  "response": {
335
- "status": 200,
336
- "statusText": "OK",
337
- "headers": {...},
338
- "body": "...",
339
- "contentType": "text/html"
587
+ "headers": { "content-type": "application/json" },
588
+ "body": "{\"result\":\"ok\"}"
340
589
  },
341
- "timestamp": "2025-01-15T10:30:01Z",
342
- "source": "ui"
343
- }
344
- }
590
+ "properties": {
591
+ "client.geo.country": "DE"
592
+ }
593
+ }'
345
594
  ```
346
595
 
347
- ### CDN Request Completed
348
596
  ```json
349
597
  {
350
- "type": "request_completed",
351
- "data": {
352
- "hookResults": {...},
353
- "finalResponse": {...},
354
- "calculatedProperties": {...},
355
- "timestamp": "2025-01-15T10:30:01Z",
356
- "source": "ui"
598
+ "ok": true,
599
+ "hookResults": {
600
+ "onRequestHeaders": {
601
+ "returnCode": 0,
602
+ "logs": [],
603
+ "input": {
604
+ "request": { "headers": { "content-type": "application/json" }, "body": "" },
605
+ "response": { "headers": {}, "body": "" },
606
+ "properties": { "client.geo.country": "DE" }
607
+ },
608
+ "output": {
609
+ "request": { "headers": { "content-type": "application/json" }, "body": "" },
610
+ "response": { "headers": {}, "body": "" }
611
+ },
612
+ "properties": { "client.geo.country": "DE" }
613
+ },
614
+ "onResponseHeaders": {
615
+ "returnCode": 0,
616
+ "logs": [],
617
+ "input": {
618
+ "request": { "headers": { "content-type": "application/json" }, "body": "" },
619
+ "response": { "headers": { "content-type": "application/json" }, "body": "" },
620
+ "properties": { "client.geo.country": "DE" }
621
+ },
622
+ "output": {
623
+ "request": { "headers": { "content-type": "application/json" }, "body": "" },
624
+ "response": { "headers": { "content-type": "application/json" }, "body": "" }
625
+ },
626
+ "properties": { "client.geo.country": "DE" }
627
+ }
628
+ },
629
+ "finalResponse": {
630
+ "status": 200,
631
+ "statusText": "OK",
632
+ "headers": { "content-type": "application/json" },
633
+ "body": "{\"result\":\"ok\"}",
634
+ "contentType": "application/json"
635
+ },
636
+ "calculatedProperties": {
637
+ "request.url": "https://example.com/api/resource",
638
+ "request.host": "example.com",
639
+ "request.path": "/api/resource",
640
+ "request.query": "",
641
+ "request.scheme": "https",
642
+ "request.extension": "",
643
+ "request.method": "POST"
357
644
  }
358
645
  }
359
646
  ```
360
647
 
361
- ### Request Failed
362
- ```json
648
+ **Error Responses**
649
+
650
+ | Status | Condition |
651
+ | ------ | --------------------------------------------------------------------------- |
652
+ | `400` | Validation failed (missing `url` or `properties`), or no WASM module loaded |
653
+ | `500` | Execution failed |
654
+
655
+ ---
656
+
657
+ ## Configuration
658
+
659
+ ### GET /api/config
660
+
661
+ Reads the `fastedge-config.test.json` file from the project root and returns it along with a validation result.
662
+
663
+ **Response**
664
+
665
+ ```typescript
363
666
  {
364
- "type": "request_failed",
365
- "data": {
366
- "message": "Error message",
367
- "error": "Error details",
368
- "timestamp": "2025-01-15T10:30:01Z",
369
- "source": "ui"
370
- }
667
+ ok: true;
668
+ config: TestConfig;
669
+ valid: boolean;
670
+ validationErrors?: {
671
+ formErrors: string[];
672
+ fieldErrors: Record<string, string[]>;
673
+ };
371
674
  }
372
675
  ```
373
676
 
374
- ### Properties Updated
375
- ```json
376
- {
377
- "type": "properties_updated",
378
- "data": {
379
- "properties": {...},
380
- "timestamp": "2025-01-15T10:30:01Z",
381
- "source": "ui"
382
- }
383
- }
677
+ Where `TestConfig` is:
678
+
679
+ ```typescript
680
+ type TestConfig = {
681
+ $schema?: string;
682
+ description?: string;
683
+ wasm?: {
684
+ path: string;
685
+ description?: string;
686
+ };
687
+ request: {
688
+ method: string;
689
+ url: string;
690
+ headers: Record<string, string>;
691
+ body: string;
692
+ };
693
+ response?: {
694
+ headers: Record<string, string>;
695
+ body: string;
696
+ };
697
+ properties: Record<string, unknown>;
698
+ dotenv?: {
699
+ enabled?: boolean;
700
+ path?: string;
701
+ };
702
+ };
703
+ ```
704
+
705
+ **Example**
706
+
707
+ ```bash
708
+ curl http://localhost:5179/api/config
384
709
  ```
385
710
 
386
- ### Console Log
387
711
  ```json
388
712
  {
389
- "type": "console_log",
390
- "data": {
391
- "message": "Log message from WASM",
392
- "level": "info" | "warn" | "error",
393
- "timestamp": "2025-01-15T10:30:01Z"
394
- }
713
+ "ok": true,
714
+ "config": {
715
+ "$schema": "http://localhost:5179/api/schema/fastedge-config.test",
716
+ "request": {
717
+ "method": "GET",
718
+ "url": "https://example.com/",
719
+ "headers": {},
720
+ "body": ""
721
+ },
722
+ "response": {
723
+ "headers": {},
724
+ "body": ""
725
+ },
726
+ "properties": {}
727
+ },
728
+ "valid": true
395
729
  }
396
730
  ```
397
731
 
398
- **Example WebSocket Client** (Node.js):
399
- ```javascript
400
- import WebSocket from 'ws';
732
+ **Error Responses**
401
733
 
402
- const ws = new WebSocket('ws://localhost:5178/ws');
734
+ | Status | Condition |
735
+ | ------ | ------------------------------------------ |
736
+ | `404` | `fastedge-config.test.json` does not exist |
403
737
 
404
- ws.on('open', () => {
405
- console.log('Connected to debugger WebSocket');
406
- });
738
+ ---
407
739
 
408
- ws.on('message', (data) => {
409
- const event = JSON.parse(data.toString());
410
- console.log('Event:', event.type, event.data);
411
- });
740
+ ### POST /api/config
412
741
 
413
- ws.on('error', (error) => {
414
- console.error('WebSocket error:', error);
415
- });
416
- ```
742
+ Saves the provided configuration object to `fastedge-config.test.json` in the project root. If the config includes a `properties` field, a WebSocket event is broadcast to connected clients.
417
743
 
418
- ---
744
+ Accepts an optional [`X-Source`](#x-source-header) request header.
419
745
 
420
- ## Common Workflows
746
+ **Request Body**
747
+
748
+ ```typescript
749
+ {
750
+ config: {
751
+ $schema?: string;
752
+ description?: string;
753
+ wasm?: {
754
+ path: string;
755
+ description?: string;
756
+ };
757
+ request: { // Required
758
+ method: string;
759
+ url: string;
760
+ headers: Record<string, string>;
761
+ body: string;
762
+ };
763
+ response?: {
764
+ headers: Record<string, string>;
765
+ body: string;
766
+ };
767
+ properties: Record<string, unknown>; // Required
768
+ dotenv?: {
769
+ enabled?: boolean;
770
+ path?: string;
771
+ };
772
+ };
773
+ }
774
+ ```
421
775
 
422
- ### Complete Test Workflow
776
+ **Response**
423
777
 
424
- ```bash
425
- #!/bin/bash
426
- set -e
778
+ ```typescript
779
+ {
780
+ ok: true;
781
+ }
782
+ ```
427
783
 
428
- # 1. Check health
429
- curl -f http://localhost:5179/health || exit 1
784
+ **Example**
430
785
 
431
- # 2. Update configuration
786
+ ```bash
432
787
  curl -X POST http://localhost:5179/api/config \
433
788
  -H "Content-Type: application/json" \
789
+ -H "X-Source: api" \
434
790
  -d '{
435
791
  "config": {
436
- "envVars": {
437
- "DEBUG": "true",
438
- "API_URL": "https://api.example.com"
792
+ "$schema": "http://localhost:5179/api/schema/fastedge-config.test",
793
+ "request": {
794
+ "method": "GET",
795
+ "url": "https://example.com/",
796
+ "headers": { "accept": "text/html" },
797
+ "body": ""
798
+ },
799
+ "response": {
800
+ "headers": {},
801
+ "body": ""
802
+ },
803
+ "properties": {
804
+ "client.geo.country": "US"
439
805
  }
440
806
  }
441
807
  }'
808
+ ```
442
809
 
443
- # 3. Load WASM
444
- WASM_BASE64=$(base64 -w 0 ./dist/app.wasm)
445
- curl -X POST http://localhost:5179/api/load \
446
- -H "Content-Type: application/json" \
447
- -d "{\"wasmBase64\": \"$WASM_BASE64\"}"
810
+ ```json
811
+ {
812
+ "ok": true
813
+ }
814
+ ```
448
815
 
449
- # 4. Execute test requests
450
- curl -X POST http://localhost:5179/api/execute \
451
- -H "Content-Type: application/json" \
452
- -d '{
453
- "url": "http://localhost/",
454
- "method": "GET"
455
- }' | jq .
816
+ **Error Responses**
456
817
 
457
- curl -X POST http://localhost:5179/api/execute \
458
- -H "Content-Type: application/json" \
459
- -d '{
460
- "url": "http://localhost/api/data",
461
- "method": "POST",
462
- "headers": {"Content-Type": "application/json"},
463
- "body": "{\"test\": \"data\"}"
464
- }' | jq .
818
+ | Status | Condition |
819
+ | ------ | -------------------------------------------------------------------- |
820
+ | `400` | Validation failed (missing `config.request` or `config.properties`) |
821
+ | `500` | File write failed |
465
822
 
466
- echo "Tests completed successfully!"
467
- ```
823
+ ---
468
824
 
469
- ### CI/CD Integration
825
+ ### POST /api/config/save-as
470
826
 
471
- ```yaml
472
- # .github/workflows/test.yml
473
- - name: Test with FastEdge Debugger
474
- run: |
475
- # Start debugger
476
- cd fastedge-debugger
477
- npm start &
478
- DEBUGGER_PID=$!
827
+ Saves the provided configuration to an arbitrary file path. The path can be absolute or relative to the project root. Creates intermediate directories as needed. Appends `.json` if the path does not already end in `.json`.
479
828
 
480
- # Wait for health check
481
- timeout 30 bash -c 'until curl -f http://localhost:5179/health; do sleep 1; done'
829
+ **Request Body**
482
830
 
483
- # Build and test application
484
- cd ../my-app
485
- npm run build
831
+ ```typescript
832
+ {
833
+ config: object; // The configuration object to serialize as JSON
834
+ filePath: string; // Target file path (absolute or relative to project root)
835
+ }
836
+ ```
486
837
 
487
- # Run automated tests
488
- ./test-scripts/run-tests.sh
838
+ **Response**
489
839
 
490
- # Cleanup
491
- kill $DEBUGGER_PID
840
+ ```typescript
841
+ {
842
+ ok: true;
843
+ savedPath: string; // Resolved absolute path where the file was written
844
+ }
492
845
  ```
493
846
 
494
- ---
847
+ **Example**
495
848
 
496
- ## Error Handling
497
-
498
- All API endpoints return consistent error responses:
849
+ ```bash
850
+ curl -X POST http://localhost:5179/api/config/save-as \
851
+ -H "Content-Type: application/json" \
852
+ -d '{
853
+ "config": {
854
+ "request": {
855
+ "method": "GET",
856
+ "url": "https://example.com/",
857
+ "headers": {},
858
+ "body": ""
859
+ },
860
+ "properties": {}
861
+ },
862
+ "filePath": "configs/staging.test"
863
+ }'
864
+ ```
499
865
 
500
866
  ```json
501
867
  {
502
- "ok": false,
503
- "error": "Error message describing what went wrong"
868
+ "ok": true,
869
+ "savedPath": "/home/user/project/configs/staging.test.json"
504
870
  }
505
871
  ```
506
872
 
507
- **Common Error Scenarios**:
873
+ **Error Responses**
508
874
 
509
- 1. **WASM not loaded**:
510
- - Status: `400`
511
- - Message: "No WASM module loaded. Call /api/load first."
875
+ | Status | Condition |
876
+ | ------ | ----------------------------------------------- |
877
+ | `400` | Missing `config` or `filePath` |
878
+ | `500` | File write or directory creation failed |
512
879
 
513
- 2. **Invalid parameters**:
514
- - Status: `400`
515
- - Message: Specific parameter that's missing or invalid
880
+ ---
516
881
 
517
- 3. **Execution failure**:
518
- - Status: `500`
519
- - Message: Error from WASM execution
882
+ ## Schema
520
883
 
521
- 4. **Configuration error**:
522
- - Status: `404` or `500`
523
- - Message: Configuration file issue
884
+ ### GET /api/schema/:name
524
885
 
525
- ---
886
+ Serves a JSON Schema file by name. Use these schemas for request validation in test tooling or editor integrations.
526
887
 
527
- ## Rate Limiting
888
+ The `:name` parameter is the schema name without the `.schema.json` suffix.
528
889
 
529
- Currently, no rate limiting is enforced. The debugger is designed for local development use.
890
+ **Response**
530
891
 
531
- ---
892
+ Returns the JSON Schema document with `Content-Type: application/json`.
532
893
 
533
- ## Best Practices
894
+ **Available Schemas**
534
895
 
535
- 1. **Always check health** before running tests
536
- 2. **Set configuration BEFORE loading WASM** - env vars must be set before module initialization
537
- 3. **Use descriptive X-Source headers** - helps with debugging and tracking
538
- 4. **Clean shutdown** - ensure proper cleanup in CI/CD scripts
539
- 5. **Monitor WebSocket** for real-time logs and debugging
540
- 6. **Test incrementally** - test simple endpoints before complex flows
541
- 7. **Use absolute paths** for WASM files to avoid path resolution issues
896
+ #### Request Schemas
542
897
 
543
- ---
898
+ | Name | Description |
899
+ | ------------ | ------------------------------------------ |
900
+ | `api-load` | Request body schema for `POST /api/load` |
901
+ | `api-send` | Request body schema for `POST /api/send` |
902
+ | `api-call` | Request body schema for `POST /api/call` |
903
+ | `api-config` | Request body schema for `POST /api/config` |
544
904
 
545
- ## Environment Variables
905
+ #### Response / Type Schemas
546
906
 
547
- Configure the debugger server:
907
+ | Name | Description |
908
+ | ---------------------- | ------------------------------------------------------------- |
909
+ | `fastedge-config.test` | Schema for `fastedge-config.test.json` config files |
910
+ | `hook-result` | Shape of a single `HookResult` object |
911
+ | `hook-call` | Shape of a `HookCall` input object |
912
+ | `full-flow-result` | Shape of the `FullFlowResult` returned by full-flow endpoints |
913
+ | `http-request` | Shape of an `HttpRequest` for HTTP-WASM execution |
914
+ | `http-response` | Shape of an `HttpResponse` returned by HTTP-WASM execution |
548
915
 
549
- - `PORT` - HTTP server port (default: 5179)
550
- - `WS_PORT` - WebSocket port (default: 5178)
551
- - `PROXY_RUNNER_DEBUG` - Enable debug logging (set to "1")
916
+ **Example**
552
917
 
553
- Example:
554
918
  ```bash
555
- PORT=3000 WS_PORT=3001 npm start
919
+ curl http://localhost:5179/api/schema/api-send
556
920
  ```
557
921
 
558
- ---
922
+ ```bash
923
+ curl http://localhost:5179/api/schema/fastedge-config.test
924
+ ```
925
+
926
+ **Using the schema in a config file**
927
+
928
+ ```json
929
+ {
930
+ "$schema": "http://localhost:5179/api/schema/fastedge-config.test",
931
+ "request": {
932
+ "method": "GET",
933
+ "url": "https://example.com/",
934
+ "headers": {},
935
+ "body": ""
936
+ },
937
+ "properties": {}
938
+ }
939
+ ```
559
940
 
560
- ## Version
941
+ **Error Responses**
561
942
 
562
- **API Version**: 1.0.0
563
- **Debugger Version**: See package.json
943
+ | Status | Condition |
944
+ | ------ | --------------------- |
945
+ | `404` | Schema name not found |
564
946
 
565
947
  ---
566
948
 
567
- ## Support
949
+ ## Error Handling
950
+
951
+ All error responses follow a consistent shape:
952
+
953
+ ```typescript
954
+ {
955
+ ok: false;
956
+ error: string | { formErrors: string[]; fieldErrors: Record<string, string[]> };
957
+ }
958
+ ```
959
+
960
+ When a request body fails schema validation (Zod), `error` is the flattened Zod error object with `formErrors` and `fieldErrors`. For runtime errors, `error` is a plain string.
961
+
962
+ **Common status codes**
568
963
 
569
- For issues, questions, or contributions:
570
- - Repository: https://github.com/G-Core/fastedge-debugger
571
- - Issues: https://github.com/G-Core/fastedge-debugger/issues
964
+ | Status | Meaning |
965
+ | ------ | -------------------------------------------------------------------------------------------- |
966
+ | `400` | Invalid request body, missing required fields, or precondition not met (e.g. no WASM loaded) |
967
+ | `404` | Resource not found (config file, schema file) |
968
+ | `500` | Internal server error during execution or I/O |
572
969
 
573
970
  ---
574
971
 
575
- ## Related Documentation
972
+ ## See Also
576
973
 
577
- - **User Guide**: See README.md for web UI usage
578
- - **Development**: See DEVELOPMENT.md for contributing
579
- - **Architecture**: See context/architecture/ for implementation details
974
+ - [WEBSOCKET.md](./WEBSOCKET.md) WebSocket protocol and event types
975
+ - [DEBUGGER.md](./DEBUGGER.md) Server startup, configuration, and environment variables
976
+ - [TEST_FRAMEWORK.md](./TEST_FRAMEWORK.md) Programmatic test framework API
977
+ - [RUNNER.md](./RUNNER.md) — Runner API and WASM type detection