@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.
- package/README.md +6 -6
- package/bin/fastedge-debug.js +2 -0
- package/dist/fastedge-cli/METADATA.json +1 -3
- package/dist/fastedge-cli/{fastedge-run-linux-x64-unkown → fastedge-run-darwin-arm64} +0 -0
- package/dist/fastedge-cli/fastedge-run-linux-x64 +0 -0
- package/dist/fastedge-cli/fastedge-run.exe +0 -0
- package/dist/frontend/assets/index-CEFjsU8e.js +35 -0
- package/dist/frontend/assets/index-DdlINQc_.css +1 -0
- package/dist/frontend/index.html +2 -2
- package/dist/lib/index.cjs +329 -112
- package/dist/lib/index.js +331 -115
- package/dist/lib/runner/HostFunctions.d.ts +8 -0
- package/dist/lib/runner/HttpWasmRunner.d.ts +34 -14
- package/dist/lib/runner/IStateManager.d.ts +3 -2
- package/dist/lib/runner/IWasmRunner.d.ts +18 -1
- package/dist/lib/runner/NullStateManager.d.ts +1 -0
- package/dist/lib/runner/PortManager.d.ts +17 -19
- package/dist/lib/runner/ProxyWasmRunner.d.ts +7 -0
- package/dist/lib/runner/standalone.d.ts +1 -1
- package/dist/lib/schemas/api.d.ts +8 -2
- package/dist/lib/schemas/config.d.ts +4 -1
- package/dist/lib/test-framework/index.cjs +330 -114
- package/dist/lib/test-framework/index.js +332 -117
- package/dist/lib/test-framework/suite-runner.d.ts +1 -1
- package/dist/server.js +30 -30
- package/docs/API.md +758 -360
- package/docs/DEBUGGER.md +151 -0
- package/docs/INDEX.md +111 -0
- package/docs/RUNNER.md +582 -0
- package/docs/TEST_CONFIG.md +242 -0
- package/docs/TEST_FRAMEWORK.md +384 -284
- package/docs/WEBSOCKET.md +499 -0
- package/docs/quickstart.md +171 -0
- package/llms.txt +72 -14
- package/package.json +17 -6
- package/schemas/api-config.schema.json +12 -5
- package/schemas/api-load.schema.json +11 -6
- package/schemas/{test-config.schema.json → fastedge-config.test.schema.json} +12 -5
- package/dist/fastedge-cli/.gitkeep +0 -0
- package/dist/frontend/assets/index-CnXStFTd.css +0 -1
- package/dist/frontend/assets/index-FR9Oqsow.js +0 -37
- package/docs/HYBRID_LOADING.md +0 -546
- package/docs/LOCAL_SERVER.md +0 -153
package/docs/API.md
CHANGED
|
@@ -1,579 +1,977 @@
|
|
|
1
|
-
#
|
|
1
|
+
# REST API Reference
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The `@gcoredev/fastedge-test` debugger server exposes a REST API for loading WASM modules, executing requests, and managing test configuration.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Base URL
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
```
|
|
8
|
+
http://localhost:5179
|
|
9
|
+
```
|
|
8
10
|
|
|
9
|
-
|
|
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
|
-
##
|
|
13
|
+
## Common Headers
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
### X-Source Header
|
|
16
16
|
|
|
17
|
-
|
|
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
|
-
|
|
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
|
-
|
|
26
|
+
```http
|
|
27
|
+
X-Source: ai_agent
|
|
28
|
+
```
|
|
22
29
|
|
|
23
|
-
|
|
30
|
+
---
|
|
24
31
|
|
|
25
|
-
|
|
26
|
-
|
|
32
|
+
## Health
|
|
33
|
+
|
|
34
|
+
### GET /health
|
|
35
|
+
|
|
36
|
+
Returns the server status and service identity.
|
|
37
|
+
|
|
38
|
+
**Response**
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
27
41
|
{
|
|
28
|
-
|
|
42
|
+
status: "ok";
|
|
43
|
+
service: "fastedge-debugger";
|
|
29
44
|
}
|
|
30
45
|
```
|
|
31
46
|
|
|
32
|
-
**
|
|
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
|
-
###
|
|
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
|
-
|
|
74
|
+
**Example**
|
|
45
75
|
|
|
46
|
-
|
|
76
|
+
```bash
|
|
77
|
+
curl http://localhost:5179/api/client-count
|
|
78
|
+
```
|
|
47
79
|
|
|
48
|
-
**Request Body**:
|
|
49
80
|
```json
|
|
50
81
|
{
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
**
|
|
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": "
|
|
133
|
+
"wasmType": "proxy-wasm",
|
|
134
|
+
"resolvedPath": "/home/user/project/build/module.wasm"
|
|
62
135
|
}
|
|
63
136
|
```
|
|
64
137
|
|
|
65
|
-
**
|
|
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
|
|
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
|
-
**
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
###
|
|
165
|
+
### PATCH /api/dotenv
|
|
89
166
|
|
|
90
|
-
|
|
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
|
-
|
|
169
|
+
Requires a WASM module to already be loaded via `POST /api/load`.
|
|
93
170
|
|
|
94
|
-
**Request Body
|
|
171
|
+
**Request Body**
|
|
95
172
|
|
|
96
|
-
|
|
97
|
-
```json
|
|
173
|
+
```typescript
|
|
98
174
|
{
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
107
|
-
|
|
182
|
+
**Response**
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
108
185
|
{
|
|
109
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
141
|
-
|
|
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
|
-
|
|
144
|
-
"
|
|
145
|
-
|
|
146
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
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
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
|
|
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://
|
|
183
|
-
"method": "
|
|
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
|
-
|
|
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
|
-
|
|
202
|
-
}
|
|
356
|
+
"headers": { "host": "example.com" },
|
|
357
|
+
"body": ""
|
|
203
358
|
},
|
|
204
359
|
"response": {
|
|
205
|
-
"
|
|
360
|
+
"headers": { "content-type": "text/html" },
|
|
361
|
+
"body": "<html/>",
|
|
206
362
|
"status": 200,
|
|
207
|
-
"
|
|
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
|
-
"
|
|
231
|
-
"
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
**
|
|
239
|
-
- `200 OK`: Configuration retrieved
|
|
240
|
-
- `404 Not Found`: No configuration file exists
|
|
407
|
+
**Error Responses**
|
|
241
408
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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
|
-
###
|
|
416
|
+
### POST /api/call
|
|
250
417
|
|
|
251
|
-
|
|
418
|
+
Calls a specific Proxy-WASM CDN hook directly. Only valid for Proxy-WASM modules.
|
|
252
419
|
|
|
253
|
-
|
|
420
|
+
Requires a WASM module to be loaded via `POST /api/load`.
|
|
254
421
|
|
|
255
|
-
**Request Body
|
|
256
|
-
|
|
422
|
+
**Request Body**
|
|
423
|
+
|
|
424
|
+
```typescript
|
|
257
425
|
{
|
|
258
|
-
"
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
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
|
-
|
|
275
|
-
|
|
439
|
+
`request` and `response` default to `{ headers: {}, body: "" }` if omitted.
|
|
440
|
+
|
|
441
|
+
**Response**
|
|
442
|
+
|
|
443
|
+
```typescript
|
|
276
444
|
{
|
|
277
|
-
|
|
445
|
+
ok: true;
|
|
446
|
+
result: HookResult;
|
|
278
447
|
}
|
|
279
448
|
```
|
|
280
449
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
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/
|
|
473
|
+
curl -X POST http://localhost:5179/api/call \
|
|
289
474
|
-H "Content-Type: application/json" \
|
|
290
475
|
-d '{
|
|
291
|
-
"
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
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
|
-
|
|
528
|
+
### POST /api/send
|
|
309
529
|
|
|
310
|
-
|
|
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
|
-
|
|
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
|
-
**
|
|
534
|
+
**Request Body**
|
|
315
535
|
|
|
316
|
-
|
|
317
|
-
```json
|
|
536
|
+
```typescript
|
|
318
537
|
{
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
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
|
-
|
|
330
|
-
|
|
553
|
+
**Response**
|
|
554
|
+
|
|
555
|
+
```typescript
|
|
331
556
|
{
|
|
332
|
-
|
|
333
|
-
|
|
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
|
-
"
|
|
336
|
-
"
|
|
337
|
-
"headers": {...},
|
|
338
|
-
"body": "...",
|
|
339
|
-
"contentType": "text/html"
|
|
587
|
+
"headers": { "content-type": "application/json" },
|
|
588
|
+
"body": "{\"result\":\"ok\"}"
|
|
340
589
|
},
|
|
341
|
-
"
|
|
342
|
-
|
|
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
|
-
"
|
|
351
|
-
"
|
|
352
|
-
"
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
-
|
|
362
|
-
|
|
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
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
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
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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
|
-
"
|
|
390
|
-
"
|
|
391
|
-
"
|
|
392
|
-
"
|
|
393
|
-
|
|
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
|
-
**
|
|
399
|
-
```javascript
|
|
400
|
-
import WebSocket from 'ws';
|
|
732
|
+
**Error Responses**
|
|
401
733
|
|
|
402
|
-
|
|
734
|
+
| Status | Condition |
|
|
735
|
+
| ------ | ------------------------------------------ |
|
|
736
|
+
| `404` | `fastedge-config.test.json` does not exist |
|
|
403
737
|
|
|
404
|
-
|
|
405
|
-
console.log('Connected to debugger WebSocket');
|
|
406
|
-
});
|
|
738
|
+
---
|
|
407
739
|
|
|
408
|
-
|
|
409
|
-
const event = JSON.parse(data.toString());
|
|
410
|
-
console.log('Event:', event.type, event.data);
|
|
411
|
-
});
|
|
740
|
+
### POST /api/config
|
|
412
741
|
|
|
413
|
-
|
|
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
|
-
|
|
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
|
-
|
|
776
|
+
**Response**
|
|
423
777
|
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
|
|
778
|
+
```typescript
|
|
779
|
+
{
|
|
780
|
+
ok: true;
|
|
781
|
+
}
|
|
782
|
+
```
|
|
427
783
|
|
|
428
|
-
|
|
429
|
-
curl -f http://localhost:5179/health || exit 1
|
|
784
|
+
**Example**
|
|
430
785
|
|
|
431
|
-
|
|
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
|
-
"
|
|
437
|
-
|
|
438
|
-
"
|
|
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
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
810
|
+
```json
|
|
811
|
+
{
|
|
812
|
+
"ok": true
|
|
813
|
+
}
|
|
814
|
+
```
|
|
448
815
|
|
|
449
|
-
|
|
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
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
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
|
-
|
|
467
|
-
```
|
|
823
|
+
---
|
|
468
824
|
|
|
469
|
-
###
|
|
825
|
+
### POST /api/config/save-as
|
|
470
826
|
|
|
471
|
-
|
|
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
|
-
|
|
481
|
-
timeout 30 bash -c 'until curl -f http://localhost:5179/health; do sleep 1; done'
|
|
829
|
+
**Request Body**
|
|
482
830
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
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
|
-
|
|
488
|
-
./test-scripts/run-tests.sh
|
|
838
|
+
**Response**
|
|
489
839
|
|
|
490
|
-
|
|
491
|
-
|
|
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
|
-
|
|
497
|
-
|
|
498
|
-
|
|
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":
|
|
503
|
-
"
|
|
868
|
+
"ok": true,
|
|
869
|
+
"savedPath": "/home/user/project/configs/staging.test.json"
|
|
504
870
|
}
|
|
505
871
|
```
|
|
506
872
|
|
|
507
|
-
**
|
|
873
|
+
**Error Responses**
|
|
508
874
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
875
|
+
| Status | Condition |
|
|
876
|
+
| ------ | ----------------------------------------------- |
|
|
877
|
+
| `400` | Missing `config` or `filePath` |
|
|
878
|
+
| `500` | File write or directory creation failed |
|
|
512
879
|
|
|
513
|
-
|
|
514
|
-
- Status: `400`
|
|
515
|
-
- Message: Specific parameter that's missing or invalid
|
|
880
|
+
---
|
|
516
881
|
|
|
517
|
-
|
|
518
|
-
- Status: `500`
|
|
519
|
-
- Message: Error from WASM execution
|
|
882
|
+
## Schema
|
|
520
883
|
|
|
521
|
-
|
|
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
|
-
|
|
888
|
+
The `:name` parameter is the schema name without the `.schema.json` suffix.
|
|
528
889
|
|
|
529
|
-
|
|
890
|
+
**Response**
|
|
530
891
|
|
|
531
|
-
|
|
892
|
+
Returns the JSON Schema document with `Content-Type: application/json`.
|
|
532
893
|
|
|
533
|
-
|
|
894
|
+
**Available Schemas**
|
|
534
895
|
|
|
535
|
-
|
|
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
|
-
|
|
905
|
+
#### Response / Type Schemas
|
|
546
906
|
|
|
547
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
941
|
+
**Error Responses**
|
|
561
942
|
|
|
562
|
-
|
|
563
|
-
|
|
943
|
+
| Status | Condition |
|
|
944
|
+
| ------ | --------------------- |
|
|
945
|
+
| `404` | Schema name not found |
|
|
564
946
|
|
|
565
947
|
---
|
|
566
948
|
|
|
567
|
-
##
|
|
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
|
-
|
|
570
|
-
|
|
571
|
-
|
|
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
|
-
##
|
|
972
|
+
## See Also
|
|
576
973
|
|
|
577
|
-
-
|
|
578
|
-
-
|
|
579
|
-
-
|
|
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
|