@gcoredev/fastedge-test 0.0.1-beta.4 → 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 (41) hide show
  1. package/README.md +6 -6
  2. package/dist/fastedge-cli/METADATA.json +1 -3
  3. package/dist/fastedge-cli/{fastedge-run-linux-x64-unkown → fastedge-run-darwin-arm64} +0 -0
  4. package/dist/fastedge-cli/fastedge-run-linux-x64 +0 -0
  5. package/dist/fastedge-cli/fastedge-run.exe +0 -0
  6. package/dist/frontend/assets/index-CEFjsU8e.js +35 -0
  7. package/dist/frontend/assets/index-DdlINQc_.css +1 -0
  8. package/dist/frontend/index.html +2 -2
  9. package/dist/lib/index.cjs +299 -107
  10. package/dist/lib/index.js +301 -110
  11. package/dist/lib/runner/HostFunctions.d.ts +8 -0
  12. package/dist/lib/runner/HttpWasmRunner.d.ts +34 -14
  13. package/dist/lib/runner/IStateManager.d.ts +3 -2
  14. package/dist/lib/runner/IWasmRunner.d.ts +16 -1
  15. package/dist/lib/runner/NullStateManager.d.ts +1 -0
  16. package/dist/lib/runner/PortManager.d.ts +17 -19
  17. package/dist/lib/runner/ProxyWasmRunner.d.ts +7 -0
  18. package/dist/lib/schemas/api.d.ts +8 -2
  19. package/dist/lib/schemas/config.d.ts +4 -1
  20. package/dist/lib/test-framework/index.cjs +301 -108
  21. package/dist/lib/test-framework/index.js +303 -111
  22. package/dist/lib/test-framework/suite-runner.d.ts +1 -1
  23. package/dist/server.js +30 -29
  24. package/docs/API.md +758 -360
  25. package/docs/DEBUGGER.md +151 -0
  26. package/docs/INDEX.md +111 -0
  27. package/docs/RUNNER.md +582 -0
  28. package/docs/TEST_CONFIG.md +242 -0
  29. package/docs/TEST_FRAMEWORK.md +384 -284
  30. package/docs/WEBSOCKET.md +499 -0
  31. package/docs/quickstart.md +171 -0
  32. package/llms.txt +72 -14
  33. package/package.json +15 -5
  34. package/schemas/api-config.schema.json +12 -5
  35. package/schemas/api-load.schema.json +11 -6
  36. package/schemas/{test-config.schema.json → fastedge-config.test.schema.json} +12 -5
  37. package/dist/fastedge-cli/.gitkeep +0 -0
  38. package/dist/frontend/assets/index-CnXStFTd.css +0 -1
  39. package/dist/frontend/assets/index-FR9Oqsow.js +0 -37
  40. package/docs/HYBRID_LOADING.md +0 -546
  41. package/docs/LOCAL_SERVER.md +0 -153
@@ -31,6 +31,7 @@ export declare class HostFunctions {
31
31
  private pendingHttpCall;
32
32
  private httpCallResponse;
33
33
  private streamClosed;
34
+ private localResponse;
34
35
  private secretStore;
35
36
  private dictionary;
36
37
  constructor(memory: MemoryManager, propertyResolver: PropertyResolver, propertyAccessControl: PropertyAccessControl, getCurrentHook: () => HookContext | null, debug?: boolean, secretStore?: SecretStore, dictionary?: Dictionary);
@@ -53,6 +54,13 @@ export declare class HostFunctions {
53
54
  clearHttpCallResponse(): void;
54
55
  isStreamClosed(): boolean;
55
56
  resetStreamClosed(): void;
57
+ hasLocalResponse(): boolean;
58
+ getLocalResponse(): {
59
+ statusCode: number;
60
+ statusText: string;
61
+ body: Uint8Array;
62
+ } | null;
63
+ resetLocalResponse(): void;
56
64
  getRequestHeaders(): HeaderMap;
57
65
  getResponseHeaders(): HeaderMap;
58
66
  getRequestBody(): string;
@@ -18,10 +18,12 @@ export declare class HttpWasmRunner implements IWasmRunner {
18
18
  private port;
19
19
  private cliPath;
20
20
  private tempWasmPath;
21
+ private currentWasmPath;
21
22
  private logs;
22
23
  private stateManager;
23
24
  private portManager;
24
25
  private dotenvEnabled;
26
+ private dotenvPath;
25
27
  constructor(portManager: PortManager, dotenvEnabled?: boolean);
26
28
  /**
27
29
  * Load WASM binary and spawn fastedge-run process
@@ -34,11 +36,16 @@ export declare class HttpWasmRunner implements IWasmRunner {
34
36
  /**
35
37
  * Not supported for HTTP WASM (proxy-wasm only)
36
38
  */
37
- callHook(hookCall: HookCall): Promise<HookResult>;
39
+ callHook(_hookCall: HookCall): Promise<HookResult>;
38
40
  /**
39
41
  * Not supported for HTTP WASM (proxy-wasm only)
40
42
  */
41
- callFullFlow(url: string, method: string, headers: Record<string, string>, body: string, responseHeaders: Record<string, string>, responseBody: string, responseStatus: number, responseStatusText: string, properties: Record<string, unknown>, enforceProductionPropertyRules: boolean): Promise<FullFlowResult>;
43
+ callFullFlow(_url: string, _method: string, _headers: Record<string, string>, _body: string, _responseHeaders: Record<string, string>, _responseBody: string, _responseStatus: number, _responseStatusText: string, _properties: Record<string, unknown>, _enforceProductionPropertyRules: boolean): Promise<FullFlowResult>;
44
+ /**
45
+ * Apply dotenv settings by restarting the fastedge-run process.
46
+ * The WASM file is not re-read; only the --dotenv flag changes.
47
+ */
48
+ applyDotenv(enabled: boolean, dotenvPath?: string): Promise<void>;
42
49
  /**
43
50
  * Clean up resources
44
51
  */
@@ -47,10 +54,20 @@ export declare class HttpWasmRunner implements IWasmRunner {
47
54
  * Get runner type
48
55
  */
49
56
  getType(): WasmType;
57
+ /**
58
+ * Get the port the fastedge-run HTTP server is listening on
59
+ */
60
+ getPort(): number | null;
50
61
  /**
51
62
  * Set state manager
52
63
  */
53
64
  setStateManager(stateManager: IStateManager): void;
65
+ /**
66
+ * Parse log level from a process output line.
67
+ * Matches bare prefixes (e.g. "INFO target > msg") and bracketed prefixes (e.g. "[INFO] msg").
68
+ * Falls back to the provided default if no known level prefix is found.
69
+ */
70
+ private parseLogLevel;
54
71
  /**
55
72
  * Setup log capture from process stdout/stderr
56
73
  */
@@ -60,21 +77,24 @@ export declare class HttpWasmRunner implements IWasmRunner {
60
77
  */
61
78
  private setupErrorHandlers;
62
79
  /**
63
- * Probe port with a single HTTP GET, returning true if any response is received.
64
- * Uses Node.js http module with explicit content-length: 0 so that wasi-http
65
- * runtimes (fastedge-run) immediately signal EOF to the WASM request body
66
- * stream. Without this, newer fastedge-run builds hold the body stream open
67
- * on keep-alive connections, causing the WASM's event.request.text() to hang
68
- * indefinitely and never send a response.
69
- */
70
- private probePort;
71
- /**
72
- * Wait for server to be ready by polling
80
+ * Wait for the fastedge-run HTTP server to be ready by watching process logs
81
+ * for the "Listening on" message.
82
+ *
83
+ * We intentionally avoid HTTP probing here. HTTP probes trigger WASM execution
84
+ * (the app calls event.request.text() to read the body), which can hang for
85
+ * many seconds in CI due to WASM JIT compilation on the first request, or
86
+ * because newer fastedge-run builds hold the body stream open regardless of
87
+ * content-length. Watching logs avoids all of this: fastedge-run emits
88
+ * "Listening on http://127.0.0.1:<port>" as soon as the HTTP listener is bound,
89
+ * before any WASM execution occurs.
73
90
  */
74
91
  private waitForServerReady;
75
92
  /**
76
- * Kill the process gracefully (SIGINT) with fallback to SIGKILL
77
- * FastEdge-run responds to SIGINT for graceful shutdown
93
+ * Kill the process gracefully (SIGINT) with platform-specific force-kill fallback.
94
+ * SIGINT is sent first on all platforms — Node.js translates it for Windows.
95
+ * If the process does not exit within 2 seconds:
96
+ * - Windows: taskkill /F /T to terminate the process tree
97
+ * - Unix: SIGKILL
78
98
  */
79
99
  private killProcess;
80
100
  /**
@@ -45,9 +45,10 @@ export interface IStateManager {
45
45
  body: string;
46
46
  contentType: string | null;
47
47
  isBase64?: boolean;
48
- }, logs: Array<{
48
+ }, source?: EventSource): void;
49
+ emitHttpWasmLog(log: {
49
50
  level: number;
50
51
  message: string;
51
- }>, source?: EventSource): void;
52
+ }, source?: EventSource): void;
52
53
  emitReloadWorkspaceWasm(path: string, source?: EventSource): void;
53
54
  }
@@ -7,7 +7,14 @@ import type { IStateManager } from "./IStateManager.js";
7
7
  import type { HookCall, HookResult, FullFlowResult } from "./types.js";
8
8
  export type WasmType = "http-wasm" | "proxy-wasm";
9
9
  export interface RunnerConfig {
10
- dotenvEnabled?: boolean;
10
+ dotenv?: {
11
+ enabled?: boolean;
12
+ /** Directory path to load dotenv files from. Passes --dotenv <path> to fastedge-run.
13
+ * When omitted, fastedge-run uses process CWD (correct for npm package users whose
14
+ * .env files live at their project root). Use this only when dotenv files are not in CWD,
15
+ * e.g. test fixture directories or non-standard project layouts. */
16
+ path?: string;
17
+ };
11
18
  enforceProductionPropertyRules?: boolean;
12
19
  /** Override automatic WASM type detection. Use when detection produces wrong results. */
13
20
  runnerType?: WasmType;
@@ -73,6 +80,14 @@ export interface IWasmRunner {
73
80
  * @returns Full flow execution result
74
81
  */
75
82
  callFullFlow(url: string, method: string, headers: Record<string, string>, body: string, responseHeaders: Record<string, string>, responseBody: string, responseStatus: number, responseStatusText: string, properties: Record<string, unknown>, enforceProductionPropertyRules: boolean): Promise<FullFlowResult>;
83
+ /**
84
+ * Apply dotenv settings to the current runner without reloading the WASM.
85
+ * For ProxyWasmRunner: resets stores and re-loads dotenv files in-place.
86
+ * For HttpWasmRunner: restarts the fastedge-run process with updated flags.
87
+ * @param enabled Whether dotenv loading should be enabled
88
+ * @param path Optional directory to load dotenv files from
89
+ */
90
+ applyDotenv(enabled: boolean, path?: string): Promise<void>;
76
91
  /**
77
92
  * Clean up resources (processes, temp files, etc.)
78
93
  */
@@ -13,5 +13,6 @@ export declare class NullStateManager implements IStateManager {
13
13
  emitWasmLoaded(): void;
14
14
  emitPropertiesUpdated(): void;
15
15
  emitHttpWasmRequestCompleted(): void;
16
+ emitHttpWasmLog(): void;
16
17
  emitReloadWorkspaceWasm(): void;
17
18
  }
@@ -1,8 +1,12 @@
1
1
  /**
2
2
  * Port Manager
3
3
  *
4
- * Manages port allocation for HTTP WASM server instances
5
- * Allocates ports from 8100-8199 range
4
+ * Manages port allocation for HTTP WASM server instances.
5
+ * Allocates ports from 8100-8199 range.
6
+ *
7
+ * Uses OS-level availability checks in addition to in-memory tracking so that
8
+ * multiple server processes running simultaneously (one per app) don't collide
9
+ * on the same inner ports.
6
10
  */
7
11
  export declare class PortManager {
8
12
  private readonly minPort;
@@ -10,32 +14,26 @@ export declare class PortManager {
10
14
  private allocatedPorts;
11
15
  private lastAllocatedPort;
12
16
  /**
13
- * Allocate an available port from the pool
14
- * Sequential allocation to avoid reusing recently released ports (TCP TIME_WAIT)
15
- * Synchronous to prevent race conditions when allocating in parallel
17
+ * Check whether a port is actually free at the OS level.
18
+ * This is necessary when multiple server processes run simultaneously
19
+ * each has its own PortManager with independent in-memory state, so
20
+ * in-memory tracking alone is not enough to prevent cross-process conflicts.
21
+ */
22
+ private isPortFree;
23
+ /**
24
+ * Allocate an available port from the pool.
25
+ * Combines in-memory tracking (avoids TCP TIME_WAIT reuse within this process)
26
+ * with an OS-level check (avoids cross-process collisions).
16
27
  * @returns The allocated port number
17
28
  * @throws Error if no ports are available
18
29
  */
19
- allocate(): number;
30
+ allocate(): Promise<number>;
20
31
  /**
21
32
  * Release a previously allocated port back to the pool
22
- * @param port The port number to release
23
33
  */
24
34
  release(port: number): void;
25
- /**
26
- * Get the number of currently allocated ports
27
- */
28
35
  getAllocatedCount(): number;
29
- /**
30
- * Get the number of available ports
31
- */
32
36
  getAvailableCount(): number;
33
- /**
34
- * Check if a specific port is allocated
35
- */
36
37
  isAllocated(port: number): boolean;
37
- /**
38
- * Reset all allocations (useful for testing)
39
- */
40
38
  reset(): void;
41
39
  }
@@ -20,6 +20,7 @@ export declare class ProxyWasmRunner implements IWasmRunner {
20
20
  private secretStore;
21
21
  private dictionary;
22
22
  private dotenvEnabled;
23
+ private dotenvPath;
23
24
  constructor(fastEdgeConfig?: FastEdgeConfig, dotenvEnabled?: boolean);
24
25
  /**
25
26
  * Set state manager for event emission
@@ -29,6 +30,12 @@ export declare class ProxyWasmRunner implements IWasmRunner {
29
30
  * Load dotenv files if enabled and merge with existing FastEdge config
30
31
  */
31
32
  private loadDotenvIfEnabled;
33
+ /**
34
+ * Apply dotenv settings to the running module without recompiling.
35
+ * Resets SecretStore/Dictionary to empty, then re-loads dotenv files
36
+ * using the new settings. The compiled WASM module is untouched.
37
+ */
38
+ applyDotenv(enabled: boolean, dotenvPath?: string): Promise<void>;
32
39
  load(bufferOrPath: Buffer | string, config?: RunnerConfig): Promise<void>;
33
40
  /**
34
41
  * Original callFullFlow signature for backward compatibility
@@ -2,7 +2,10 @@ import { z } from 'zod';
2
2
  export declare const ApiLoadBodySchema: z.ZodObject<{
3
3
  wasmBase64: z.ZodOptional<z.ZodString>;
4
4
  wasmPath: z.ZodOptional<z.ZodString>;
5
- dotenvEnabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
5
+ dotenv: z.ZodOptional<z.ZodObject<{
6
+ enabled: z.ZodOptional<z.ZodBoolean>;
7
+ path: z.ZodOptional<z.ZodString>;
8
+ }, z.core.$strip>>;
6
9
  }, z.core.$strip>;
7
10
  export declare const ApiSendBodySchema: z.ZodObject<{
8
11
  url: z.ZodString;
@@ -54,7 +57,10 @@ export declare const ApiConfigBodySchema: z.ZodObject<{
54
57
  body: z.ZodDefault<z.ZodOptional<z.ZodString>>;
55
58
  }, z.core.$strip>>;
56
59
  properties: z.ZodDefault<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
57
- dotenvEnabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
60
+ dotenv: z.ZodOptional<z.ZodObject<{
61
+ enabled: z.ZodOptional<z.ZodBoolean>;
62
+ path: z.ZodOptional<z.ZodString>;
63
+ }, z.core.$strip>>;
58
64
  }, z.core.$strip>;
59
65
  }, z.core.$strip>;
60
66
  export type ApiLoadBody = z.infer<typeof ApiLoadBodySchema>;
@@ -31,7 +31,10 @@ export declare const TestConfigSchema: z.ZodObject<{
31
31
  body: z.ZodDefault<z.ZodOptional<z.ZodString>>;
32
32
  }, z.core.$strip>>;
33
33
  properties: z.ZodDefault<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
34
- dotenvEnabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
34
+ dotenv: z.ZodOptional<z.ZodObject<{
35
+ enabled: z.ZodOptional<z.ZodBoolean>;
36
+ path: z.ZodOptional<z.ZodString>;
37
+ }, z.core.$strip>>;
35
38
  }, z.core.$strip>;
36
39
  export type WasmConfig = z.infer<typeof WasmConfigSchema>;
37
40
  export type RequestConfig = z.infer<typeof RequestConfigSchema>;