@mocklane/core 1.0.0 → 1.0.1
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 +30 -9
- package/dist/cli.js +53 -26
- package/dist/cli.js.map +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,24 +22,42 @@ Mocklane turns a pasted data model and a plain-language request into a structure
|
|
|
22
22
|
- Node.js 20 or newer
|
|
23
23
|
- A modern browser with WebGPU for Transformers.js and WebLLM
|
|
24
24
|
- Enough browser storage and memory for the selected local model
|
|
25
|
+
- pnpm 9 when installing from GitHub source
|
|
25
26
|
|
|
26
27
|
Native Browser AI is shown only when the browser exposes the `LanguageModel` API. The first Transformers.js or WebLLM load downloads model assets; later loads can reuse the browser cache.
|
|
27
28
|
|
|
28
|
-
##
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
Choose either npm or the GitHub source repository. Both launch the same `mocklane` CLI.
|
|
29
32
|
|
|
30
|
-
|
|
33
|
+
### Option 1: npm
|
|
34
|
+
|
|
35
|
+
Run Mocklane once with `npx`:
|
|
31
36
|
|
|
32
37
|
```bash
|
|
33
|
-
npx mocklane
|
|
38
|
+
npx @mocklane/core
|
|
34
39
|
```
|
|
35
40
|
|
|
36
|
-
|
|
41
|
+
Or install the package globally and run the installed `mocklane` command:
|
|
37
42
|
|
|
38
43
|
```bash
|
|
39
|
-
npm install --global mocklane
|
|
44
|
+
npm install --global @mocklane/core
|
|
40
45
|
mocklane
|
|
41
46
|
```
|
|
42
47
|
|
|
48
|
+
### Option 2: GitHub
|
|
49
|
+
|
|
50
|
+
Clone the source, install dependencies, and launch the locally built CLI:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
git clone https://github.com/aasispaudel/Mocklane.git
|
|
54
|
+
cd Mocklane
|
|
55
|
+
pnpm install
|
|
56
|
+
pnpm mocklane:build
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Quick Start
|
|
60
|
+
|
|
43
61
|
Mocklane starts two local servers and opens the browser workspace automatically:
|
|
44
62
|
|
|
45
63
|
```text
|
|
@@ -48,6 +66,8 @@ Mock API proxy: http://localhost:4000
|
|
|
48
66
|
Upstream server: none (standalone mode)
|
|
49
67
|
```
|
|
50
68
|
|
|
69
|
+
If the default ports are busy, Mocklane automatically tries the next local pair, such as `4002/4003`, then `4004/4005`.
|
|
70
|
+
|
|
51
71
|
In the browser:
|
|
52
72
|
|
|
53
73
|
1. Select an AI provider and model.
|
|
@@ -133,7 +153,7 @@ Invalid JSON cannot be saved. Mocklane displays the parser error in the editor s
|
|
|
133
153
|
Standalone mode is the default:
|
|
134
154
|
|
|
135
155
|
```bash
|
|
136
|
-
npx mocklane
|
|
156
|
+
npx @mocklane/core
|
|
137
157
|
```
|
|
138
158
|
|
|
139
159
|
Registered routes return their configured mock responses. Unmatched routes return `404`:
|
|
@@ -153,7 +173,7 @@ Mocks are matched by HTTP method and exact pathname. Query strings do not affect
|
|
|
153
173
|
Use proxy mode when an application already has a backend and only selected routes should be mocked:
|
|
154
174
|
|
|
155
175
|
```bash
|
|
156
|
-
npx mocklane --target http://localhost:8000
|
|
176
|
+
npx @mocklane/core --target http://localhost:8000
|
|
157
177
|
```
|
|
158
178
|
|
|
159
179
|
Point the application at `http://localhost:4000` instead of the upstream server:
|
|
@@ -192,19 +212,20 @@ Options:
|
|
|
192
212
|
### Custom ports
|
|
193
213
|
|
|
194
214
|
```bash
|
|
195
|
-
npx mocklane --proxy-port 5000 --ui-port 5001
|
|
215
|
+
npx @mocklane/core --proxy-port 5000 --ui-port 5001
|
|
196
216
|
```
|
|
197
217
|
|
|
198
218
|
### Proxy with custom ports
|
|
199
219
|
|
|
200
220
|
```bash
|
|
201
|
-
npx mocklane \
|
|
221
|
+
npx @mocklane/core \
|
|
202
222
|
--target http://localhost:8000 \
|
|
203
223
|
--proxy-port 5000 \
|
|
204
224
|
--ui-port 5001
|
|
205
225
|
```
|
|
206
226
|
|
|
207
227
|
The proxy and UI ports must be different. Both servers bind to `127.0.0.1` and are not exposed to the local network.
|
|
228
|
+
When custom ports are provided, Mocklane uses those exact ports.
|
|
208
229
|
|
|
209
230
|
## Local Control API
|
|
210
231
|
|
package/dist/cli.js
CHANGED
|
@@ -38,28 +38,40 @@ async function startMocklaneServers({
|
|
|
38
38
|
proxyPort,
|
|
39
39
|
uiPort,
|
|
40
40
|
target,
|
|
41
|
-
uiDirectory
|
|
41
|
+
uiDirectory,
|
|
42
|
+
allowPortFallback = false
|
|
42
43
|
}) {
|
|
43
44
|
const registry = new MockRegistry();
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
45
|
+
let candidateProxyPort = proxyPort;
|
|
46
|
+
let candidateUiPort = uiPort;
|
|
47
|
+
while (candidateProxyPort <= 65534 && candidateUiPort <= 65535) {
|
|
48
|
+
const activeProxyPort = candidateProxyPort;
|
|
49
|
+
const activeUiPort = candidateUiPort;
|
|
50
|
+
const proxyServer = createServer((request, response) => {
|
|
51
|
+
handleProxyRequest(request, response, registry, target);
|
|
52
|
+
});
|
|
53
|
+
const uiServer = createServer((request, response) => {
|
|
54
|
+
void handleUiRequest(request, response, registry, uiDirectory, activeProxyPort);
|
|
55
|
+
});
|
|
56
|
+
try {
|
|
57
|
+
await listen(proxyServer, activeProxyPort, host);
|
|
58
|
+
await listen(uiServer, activeUiPort, host);
|
|
59
|
+
return {
|
|
60
|
+
proxyPort: activeProxyPort,
|
|
61
|
+
uiPort: activeUiPort,
|
|
62
|
+
close: async () => {
|
|
63
|
+
await Promise.all([closeServer(proxyServer), closeServer(uiServer)]);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
} catch (error) {
|
|
67
|
+
await closeServer(proxyServer);
|
|
68
|
+
await closeServer(uiServer);
|
|
69
|
+
if (!allowPortFallback || !isAddressInUse(error)) throw error;
|
|
70
|
+
candidateProxyPort += 2;
|
|
71
|
+
candidateUiPort += 2;
|
|
61
72
|
}
|
|
62
|
-
}
|
|
73
|
+
}
|
|
74
|
+
throw new Error("Mocklane could not find an available local port pair.");
|
|
63
75
|
}
|
|
64
76
|
async function handleUiRequest(request, response, registry, uiDirectory, proxyPort) {
|
|
65
77
|
const url = new URL(request.url ?? "/", "http://localhost");
|
|
@@ -225,6 +237,9 @@ function listen(server, port, host) {
|
|
|
225
237
|
});
|
|
226
238
|
});
|
|
227
239
|
}
|
|
240
|
+
function isAddressInUse(error) {
|
|
241
|
+
return error instanceof Error && "code" in error && error.code === "EADDRINUSE";
|
|
242
|
+
}
|
|
228
243
|
function closeServer(server) {
|
|
229
244
|
return new Promise((resolvePromise) => {
|
|
230
245
|
if (!server.listening) {
|
|
@@ -251,11 +266,15 @@ async function main() {
|
|
|
251
266
|
proxyPort: options.proxyPort,
|
|
252
267
|
uiPort: options.uiPort,
|
|
253
268
|
target: options.target,
|
|
254
|
-
uiDirectory
|
|
269
|
+
uiDirectory,
|
|
270
|
+
allowPortFallback: !options.hasCustomPorts
|
|
255
271
|
});
|
|
256
|
-
const uiUrl = `http://localhost:${
|
|
272
|
+
const uiUrl = `http://localhost:${servers.uiPort}`;
|
|
273
|
+
if (!options.hasCustomPorts && (servers.proxyPort !== DEFAULT_PROXY_PORT || servers.uiPort !== DEFAULT_UI_PORT)) {
|
|
274
|
+
console.log(red(`Port ${DEFAULT_PROXY_PORT} is in use. Using ${servers.proxyPort} instead.`));
|
|
275
|
+
}
|
|
257
276
|
console.log(`Mocklane UI: ${uiUrl}`);
|
|
258
|
-
console.log(`Mock API proxy: http://localhost:${
|
|
277
|
+
console.log(`Mock API proxy: http://localhost:${servers.proxyPort}`);
|
|
259
278
|
console.log(`Upstream server: ${options.target?.origin ?? "none (standalone mode)"}`);
|
|
260
279
|
console.log("Press Ctrl+C to stop.");
|
|
261
280
|
openBrowser(uiUrl);
|
|
@@ -271,16 +290,21 @@ function parseArguments(argumentsList) {
|
|
|
271
290
|
let proxyPort = DEFAULT_PROXY_PORT;
|
|
272
291
|
let uiPort = DEFAULT_UI_PORT;
|
|
273
292
|
let help = false;
|
|
293
|
+
let hasCustomPorts = false;
|
|
274
294
|
for (let index = 0; index < argumentsList.length; index += 1) {
|
|
275
295
|
const argument = argumentsList[index];
|
|
276
296
|
if (argument === "--target") target = new URL(requireValue(argumentsList, ++index, argument));
|
|
277
|
-
else if (argument === "--proxy-port")
|
|
278
|
-
|
|
279
|
-
|
|
297
|
+
else if (argument === "--proxy-port") {
|
|
298
|
+
proxyPort = parsePort(requireValue(argumentsList, ++index, argument));
|
|
299
|
+
hasCustomPorts = true;
|
|
300
|
+
} else if (argument === "--ui-port") {
|
|
301
|
+
uiPort = parsePort(requireValue(argumentsList, ++index, argument));
|
|
302
|
+
hasCustomPorts = true;
|
|
303
|
+
} else if (argument === "--help" || argument === "-h") help = true;
|
|
280
304
|
else throw new Error(`Unknown argument: ${argument}`);
|
|
281
305
|
}
|
|
282
306
|
if (proxyPort === uiPort) throw new Error("Proxy and UI ports must be different.");
|
|
283
|
-
return { target, proxyPort, uiPort, help };
|
|
307
|
+
return { target, proxyPort, uiPort, help, hasCustomPorts };
|
|
284
308
|
}
|
|
285
309
|
function requireValue(argumentsList, index, flag) {
|
|
286
310
|
const value = argumentsList[index];
|
|
@@ -299,6 +323,9 @@ function openBrowser(url) {
|
|
|
299
323
|
const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
300
324
|
spawn(command, args, { detached: true, stdio: "ignore" }).unref();
|
|
301
325
|
}
|
|
326
|
+
function red(message) {
|
|
327
|
+
return `\x1B[31m${message}\x1B[0m`;
|
|
328
|
+
}
|
|
302
329
|
function printHelp() {
|
|
303
330
|
console.log(`Mocklane local mock proxy
|
|
304
331
|
|
package/dist/cli.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../cli/index.ts", "../cli/server.ts", "../cli/mockRegistry.ts"],
|
|
4
|
-
"sourcesContent": ["#!/usr/bin/env node\n\nimport { spawn } from \"node:child_process\";\nimport { fileURLToPath } from \"node:url\";\n\nimport { startMocklaneServers } from \"./server\";\n\nconst DEFAULT_HOST = \"127.0.0.1\";\nconst DEFAULT_PROXY_PORT = 4000;\nconst DEFAULT_UI_PORT = 4001;\n\nasync function main() {\n const options = parseArguments(process.argv.slice(2));\n if (options.help) {\n printHelp();\n return;\n }\n\n const uiDirectory = fileURLToPath(new URL(\"../build/client\", import.meta.url));\n const servers = await startMocklaneServers({\n host: DEFAULT_HOST,\n proxyPort: options.proxyPort,\n uiPort: options.uiPort,\n target: options.target,\n uiDirectory,\n });\n const uiUrl = `http://localhost:${options.uiPort}`;\n\n console.log(`Mocklane UI: ${uiUrl}`);\n console.log(`Mock API proxy: http://localhost:${options.proxyPort}`);\n console.log(`Upstream server: ${options.target?.origin ?? \"none (standalone mode)\"}`);\n console.log(\"Press Ctrl+C to stop.\");\n\n openBrowser(uiUrl);\n\n const shutdown = async () => {\n await servers.close();\n process.exit(0);\n };\n process.once(\"SIGINT\", () => void shutdown());\n process.once(\"SIGTERM\", () => void shutdown());\n}\n\nfunction parseArguments(argumentsList: string[]) {\n let target: URL | null = null;\n let proxyPort = DEFAULT_PROXY_PORT;\n let uiPort = DEFAULT_UI_PORT;\n let help = false;\n\n for (let index = 0; index < argumentsList.length; index += 1) {\n const argument = argumentsList[index];\n if (argument === \"--target\") target = new URL(requireValue(argumentsList, ++index, argument));\n else if (argument === \"--proxy-port\") proxyPort = parsePort(requireValue(argumentsList, ++index, argument));\n else if (argument === \"--ui-port\") uiPort = parsePort(requireValue(argumentsList, ++index, argument));\n else if (argument === \"--help\" || argument === \"-h\") help = true;\n else throw new Error(`Unknown argument: ${argument}`);\n }\n\n if (proxyPort === uiPort) throw new Error(\"Proxy and UI ports must be different.\");\n return { target, proxyPort, uiPort, help };\n}\n\nfunction requireValue(argumentsList: string[], index: number, flag: string) {\n const value = argumentsList[index];\n if (!value) throw new Error(`${flag} requires a value.`);\n return value;\n}\n\nfunction parsePort(value: string) {\n const port = Number(value);\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n throw new Error(`Invalid port: ${value}`);\n }\n return port;\n}\n\nfunction openBrowser(url: string) {\n const command = process.platform === \"darwin\" ? \"open\" : process.platform === \"win32\" ? \"cmd\" : \"xdg-open\";\n const args = process.platform === \"win32\" ? [\"/c\", \"start\", \"\", url] : [url];\n spawn(command, args, { detached: true, stdio: \"ignore\" }).unref();\n}\n\nfunction printHelp() {\n console.log(`Mocklane local mock proxy\n\nUsage:\n mocklane [options]\n\nOptions:\n --target <url> Optional upstream API URL for unmatched routes\n --proxy-port <port> Mock/proxy port (default: 4000)\n --ui-port <port> Mocklane UI port (default: 4001)\n -h, --help Show this help`);\n}\n\nvoid main().catch((error: unknown) => {\n console.error(error instanceof Error ? error.message : error);\n process.exit(1);\n});\n", "import { createReadStream, existsSync, statSync } from \"node:fs\";\nimport { createServer, type IncomingMessage, type Server, type ServerResponse } from \"node:http\";\nimport { request as requestHttp } from \"node:http\";\nimport { request as requestHttps } from \"node:https\";\nimport { extname, resolve } from \"node:path\";\n\nimport type { MockHttpResponse } from \"../src/types/mockApi\";\nimport { MockRegistry } from \"./mockRegistry\";\n\nconst MAX_CONTROL_BODY_BYTES = 10 * 1024 * 1024;\n\nexport type MocklaneServers = {\n close: () => Promise<void>;\n};\n\nexport async function startMocklaneServers({\n host,\n proxyPort,\n uiPort,\n target,\n uiDirectory,\n}: {\n host: string;\n proxyPort: number;\n uiPort: number;\n target: URL | null;\n uiDirectory: string;\n}): Promise<MocklaneServers> {\n const registry = new MockRegistry();\n const proxyServer = createServer((request, response) => {\n handleProxyRequest(request, response, registry, target);\n });\n const uiServer = createServer((request, response) => {\n void handleUiRequest(request, response, registry, uiDirectory, proxyPort);\n });\n\n try {\n await listen(proxyServer, proxyPort, host);\n await listen(uiServer, uiPort, host);\n } catch (error) {\n await closeServer(proxyServer);\n await closeServer(uiServer);\n throw error;\n }\n\n return {\n close: async () => {\n await Promise.all([closeServer(proxyServer), closeServer(uiServer)]);\n },\n };\n}\n\nasync function handleUiRequest(\n request: IncomingMessage,\n response: ServerResponse,\n registry: MockRegistry,\n uiDirectory: string,\n proxyPort: number,\n) {\n const url = new URL(request.url ?? \"/\", \"http://localhost\");\n\n if (url.pathname === \"/__mocklane/health\" && request.method === \"GET\") {\n sendJson(response, 200, { ready: true, proxyPort, mocks: registry.list().length });\n return;\n }\n\n if (url.pathname === \"/__mocklane/mocks\" && request.method === \"GET\") {\n sendJson(response, 200, registry.list());\n return;\n }\n\n if (url.pathname === \"/__mocklane/mocks\" && request.method === \"PUT\") {\n try {\n const mock = validateMockResponse(await readJsonBody(request));\n registry.set(mock);\n sendJson(response, 200, { active: true, method: mock.method, path: mock.path, proxyPort });\n } catch (error) {\n sendJson(response, 400, {\n error: error instanceof Error ? error.message : \"Invalid mock response.\",\n });\n }\n return;\n }\n\n if (url.pathname === \"/__mocklane/mocks\" && request.method === \"DELETE\") {\n try {\n const mock = validateMockResponse(await readJsonBody(request));\n registry.delete(mock.method, mock.path);\n sendJson(response, 200, { active: false, method: mock.method, path: mock.path });\n } catch (error) {\n sendJson(response, 400, {\n error: error instanceof Error ? error.message : \"Invalid mock response.\",\n });\n }\n return;\n }\n\n serveUiFile(response, uiDirectory, url.pathname, request.method === \"HEAD\");\n}\n\nfunction handleProxyRequest(\n request: IncomingMessage,\n response: ServerResponse,\n registry: MockRegistry,\n target: URL | null,\n) {\n const requestUrl = new URL(request.url ?? \"/\", target ?? \"http://localhost\");\n const preflightMethod = request.headers[\"access-control-request-method\"];\n const preflightMock =\n request.method === \"OPTIONS\" && typeof preflightMethod === \"string\"\n ? registry.get(preflightMethod, requestUrl.pathname)\n : undefined;\n if (preflightMock && typeof preflightMethod === \"string\") {\n response.writeHead(204, {\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-methods\": preflightMethod.toUpperCase(),\n \"access-control-allow-headers\": request.headers[\"access-control-request-headers\"] ?? \"content-type\",\n });\n response.end();\n return;\n }\n\n const mock = registry.get(request.method ?? \"GET\", requestUrl.pathname);\n\n if (mock) {\n response.writeHead(mock.status, {\n ...mock.headers,\n \"access-control-allow-origin\": mock.headers[\"access-control-allow-origin\"] ?? \"*\",\n });\n response.end(JSON.stringify(mock.body));\n return;\n }\n\n if (!target) {\n sendJson(response, 404, {\n error: \"No Mocklane mock matches this route.\",\n method: request.method ?? \"GET\",\n path: requestUrl.pathname,\n });\n return;\n }\n\n const upstreamUrl = new URL(request.url ?? \"/\", target);\n const upstreamRequest = (upstreamUrl.protocol === \"https:\" ? requestHttps : requestHttp)(\n upstreamUrl,\n {\n method: request.method,\n headers: { ...request.headers, host: upstreamUrl.host },\n },\n (upstreamResponse) => {\n response.writeHead(upstreamResponse.statusCode ?? 502, upstreamResponse.headers);\n upstreamResponse.pipe(response);\n },\n );\n\n upstreamRequest.on(\"error\", (error) => {\n if (!response.headersSent) {\n sendJson(response, 502, {\n error: \"Mocklane could not reach the upstream server.\",\n detail: error.message,\n });\n return;\n }\n response.destroy(error);\n });\n request.pipe(upstreamRequest);\n}\n\nfunction serveUiFile(\n response: ServerResponse,\n uiDirectory: string,\n pathname: string,\n headOnly: boolean,\n) {\n const requestedPath = pathname === \"/\" ? \"/index.html\" : pathname;\n const candidate = resolve(uiDirectory, `.${decodeURIComponent(requestedPath)}`);\n const uiRoot = resolve(uiDirectory);\n const filePath =\n candidate.startsWith(`${uiRoot}/`) && existsSync(candidate) && statSync(candidate).isFile()\n ? candidate\n : resolve(uiDirectory, \"index.html\");\n\n if (!existsSync(filePath)) {\n sendJson(response, 503, {\n error: \"Mocklane UI has not been built. Run `pnpm build` first.\",\n });\n return;\n }\n\n response.writeHead(200, {\n \"content-type\": contentType(filePath),\n \"cache-control\": filePath.endsWith(\"index.html\") ? \"no-cache\" : \"public, max-age=31536000, immutable\",\n });\n if (headOnly) {\n response.end();\n return;\n }\n createReadStream(filePath).pipe(response);\n}\n\nasync function readJsonBody(request: IncomingMessage) {\n const chunks: Buffer[] = [];\n let size = 0;\n for await (const chunk of request) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n size += buffer.length;\n if (size > MAX_CONTROL_BODY_BYTES) {\n throw new Error(\"Mock response exceeds the 10 MB limit.\");\n }\n chunks.push(buffer);\n }\n return JSON.parse(Buffer.concat(chunks).toString(\"utf8\")) as unknown;\n}\n\nfunction validateMockResponse(value: unknown): MockHttpResponse {\n if (!value || typeof value !== \"object\") throw new Error(\"Mock response must be an object.\");\n const mock = value as Partial<MockHttpResponse>;\n if (typeof mock.method !== \"string\" || !mock.method.trim()) throw new Error(\"Method is required.\");\n if (typeof mock.path !== \"string\" || !mock.path.startsWith(\"/\")) {\n throw new Error(\"Path must begin with `/`.\");\n }\n if (!Number.isInteger(mock.status) || Number(mock.status) < 100 || Number(mock.status) > 599) {\n throw new Error(\"Status must be a valid HTTP status code.\");\n }\n if (!mock.headers || typeof mock.headers !== \"object\" || Array.isArray(mock.headers)) {\n throw new Error(\"Headers must be an object.\");\n }\n return mock as MockHttpResponse;\n}\n\nfunction sendJson(response: ServerResponse, status: number, body: unknown) {\n response.writeHead(status, { \"content-type\": \"application/json; charset=utf-8\" });\n response.end(JSON.stringify(body));\n}\n\nfunction contentType(filePath: string) {\n const types: Record<string, string> = {\n \".css\": \"text/css; charset=utf-8\",\n \".html\": \"text/html; charset=utf-8\",\n \".js\": \"text/javascript; charset=utf-8\",\n \".json\": \"application/json; charset=utf-8\",\n \".png\": \"image/png\",\n \".svg\": \"image/svg+xml\",\n \".wasm\": \"application/wasm\",\n };\n return types[extname(filePath)] ?? \"application/octet-stream\";\n}\n\nfunction listen(server: Server, port: number, host: string) {\n return new Promise<void>((resolvePromise, reject) => {\n server.once(\"error\", reject);\n server.listen(port, host, () => {\n server.off(\"error\", reject);\n resolvePromise();\n });\n });\n}\n\nfunction closeServer(server: Server) {\n return new Promise<void>((resolvePromise) => {\n if (!server.listening) {\n resolvePromise();\n return;\n }\n server.close(() => resolvePromise());\n });\n}\n", "import type { MockHttpResponse } from \"../src/types/mockApi\";\n\nexport class MockRegistry {\n readonly #mocks = new Map<string, MockHttpResponse>();\n\n set(response: MockHttpResponse) {\n this.#mocks.set(mockKey(response.method, response.path), response);\n }\n\n get(method: string, path: string) {\n return this.#mocks.get(mockKey(method, path));\n }\n\n delete(method: string, path: string) {\n return this.#mocks.delete(mockKey(method, path));\n }\n\n list() {\n return [...this.#mocks.values()];\n }\n}\n\nfunction mockKey(method: string, path: string) {\n return `${method.toUpperCase()} ${path}`;\n}\n"],
|
|
5
|
-
"mappings": ";;;AAEA,SAAS,aAAa;AACtB,SAAS,qBAAqB;;;ACH9B,SAAS,kBAAkB,YAAY,gBAAgB;AACvD,SAAS,oBAA4E;AACrF,SAAS,WAAW,mBAAmB;AACvC,SAAS,WAAW,oBAAoB;AACxC,SAAS,SAAS,eAAe;;;ACF1B,IAAM,eAAN,MAAmB;AAAA,EACf,SAAS,oBAAI,IAA8B;AAAA,EAEpD,IAAI,UAA4B;AAC9B,SAAK,OAAO,IAAI,QAAQ,SAAS,QAAQ,SAAS,IAAI,GAAG,QAAQ;AAAA,EACnE;AAAA,EAEA,IAAI,QAAgB,MAAc;AAChC,WAAO,KAAK,OAAO,IAAI,QAAQ,QAAQ,IAAI,CAAC;AAAA,EAC9C;AAAA,EAEA,OAAO,QAAgB,MAAc;AACnC,WAAO,KAAK,OAAO,OAAO,QAAQ,QAAQ,IAAI,CAAC;AAAA,EACjD;AAAA,EAEA,OAAO;AACL,WAAO,CAAC,GAAG,KAAK,OAAO,OAAO,CAAC;AAAA,EACjC;AACF;AAEA,SAAS,QAAQ,QAAgB,MAAc;AAC7C,SAAO,GAAG,OAAO,YAAY,CAAC,IAAI,IAAI;AACxC;;;ADfA,IAAM,yBAAyB,KAAK,OAAO;
|
|
4
|
+
"sourcesContent": ["#!/usr/bin/env node\n\nimport { spawn } from \"node:child_process\";\nimport { fileURLToPath } from \"node:url\";\n\nimport { startMocklaneServers } from \"./server\";\n\nconst DEFAULT_HOST = \"127.0.0.1\";\nconst DEFAULT_PROXY_PORT = 4000;\nconst DEFAULT_UI_PORT = 4001;\n\nasync function main() {\n const options = parseArguments(process.argv.slice(2));\n if (options.help) {\n printHelp();\n return;\n }\n\n const uiDirectory = fileURLToPath(new URL(\"../build/client\", import.meta.url));\n const servers = await startMocklaneServers({\n host: DEFAULT_HOST,\n proxyPort: options.proxyPort,\n uiPort: options.uiPort,\n target: options.target,\n uiDirectory,\n allowPortFallback: !options.hasCustomPorts,\n });\n const uiUrl = `http://localhost:${servers.uiPort}`;\n\n if (!options.hasCustomPorts && (servers.proxyPort !== DEFAULT_PROXY_PORT || servers.uiPort !== DEFAULT_UI_PORT)) {\n console.log(red(`Port ${DEFAULT_PROXY_PORT} is in use. Using ${servers.proxyPort} instead.`));\n }\n console.log(`Mocklane UI: ${uiUrl}`);\n console.log(`Mock API proxy: http://localhost:${servers.proxyPort}`);\n console.log(`Upstream server: ${options.target?.origin ?? \"none (standalone mode)\"}`);\n console.log(\"Press Ctrl+C to stop.\");\n\n openBrowser(uiUrl);\n\n const shutdown = async () => {\n await servers.close();\n process.exit(0);\n };\n process.once(\"SIGINT\", () => void shutdown());\n process.once(\"SIGTERM\", () => void shutdown());\n}\n\nfunction parseArguments(argumentsList: string[]) {\n let target: URL | null = null;\n let proxyPort = DEFAULT_PROXY_PORT;\n let uiPort = DEFAULT_UI_PORT;\n let help = false;\n let hasCustomPorts = false;\n\n for (let index = 0; index < argumentsList.length; index += 1) {\n const argument = argumentsList[index];\n if (argument === \"--target\") target = new URL(requireValue(argumentsList, ++index, argument));\n else if (argument === \"--proxy-port\") {\n proxyPort = parsePort(requireValue(argumentsList, ++index, argument));\n hasCustomPorts = true;\n } else if (argument === \"--ui-port\") {\n uiPort = parsePort(requireValue(argumentsList, ++index, argument));\n hasCustomPorts = true;\n }\n else if (argument === \"--help\" || argument === \"-h\") help = true;\n else throw new Error(`Unknown argument: ${argument}`);\n }\n\n if (proxyPort === uiPort) throw new Error(\"Proxy and UI ports must be different.\");\n return { target, proxyPort, uiPort, help, hasCustomPorts };\n}\n\nfunction requireValue(argumentsList: string[], index: number, flag: string) {\n const value = argumentsList[index];\n if (!value) throw new Error(`${flag} requires a value.`);\n return value;\n}\n\nfunction parsePort(value: string) {\n const port = Number(value);\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n throw new Error(`Invalid port: ${value}`);\n }\n return port;\n}\n\nfunction openBrowser(url: string) {\n const command = process.platform === \"darwin\" ? \"open\" : process.platform === \"win32\" ? \"cmd\" : \"xdg-open\";\n const args = process.platform === \"win32\" ? [\"/c\", \"start\", \"\", url] : [url];\n spawn(command, args, { detached: true, stdio: \"ignore\" }).unref();\n}\n\nfunction red(message: string) {\n return `\\x1b[31m${message}\\x1b[0m`;\n}\n\nfunction printHelp() {\n console.log(`Mocklane local mock proxy\n\nUsage:\n mocklane [options]\n\nOptions:\n --target <url> Optional upstream API URL for unmatched routes\n --proxy-port <port> Mock/proxy port (default: 4000)\n --ui-port <port> Mocklane UI port (default: 4001)\n -h, --help Show this help`);\n}\n\nvoid main().catch((error: unknown) => {\n console.error(error instanceof Error ? error.message : error);\n process.exit(1);\n});\n", "import { createReadStream, existsSync, statSync } from \"node:fs\";\nimport { createServer, type IncomingMessage, type Server, type ServerResponse } from \"node:http\";\nimport { request as requestHttp } from \"node:http\";\nimport { request as requestHttps } from \"node:https\";\nimport { extname, resolve } from \"node:path\";\n\nimport type { MockHttpResponse } from \"../src/types/mockApi\";\nimport { MockRegistry } from \"./mockRegistry\";\n\nconst MAX_CONTROL_BODY_BYTES = 10 * 1024 * 1024;\n\nexport type MocklaneServers = {\n proxyPort: number;\n uiPort: number;\n close: () => Promise<void>;\n};\n\nexport async function startMocklaneServers({\n host,\n proxyPort,\n uiPort,\n target,\n uiDirectory,\n allowPortFallback = false,\n}: {\n host: string;\n proxyPort: number;\n uiPort: number;\n target: URL | null;\n uiDirectory: string;\n allowPortFallback?: boolean;\n}): Promise<MocklaneServers> {\n const registry = new MockRegistry();\n let candidateProxyPort = proxyPort;\n let candidateUiPort = uiPort;\n\n while (candidateProxyPort <= 65534 && candidateUiPort <= 65535) {\n const activeProxyPort = candidateProxyPort;\n const activeUiPort = candidateUiPort;\n const proxyServer = createServer((request, response) => {\n handleProxyRequest(request, response, registry, target);\n });\n const uiServer = createServer((request, response) => {\n void handleUiRequest(request, response, registry, uiDirectory, activeProxyPort);\n });\n\n try {\n await listen(proxyServer, activeProxyPort, host);\n await listen(uiServer, activeUiPort, host);\n return {\n proxyPort: activeProxyPort,\n uiPort: activeUiPort,\n close: async () => {\n await Promise.all([closeServer(proxyServer), closeServer(uiServer)]);\n },\n };\n } catch (error) {\n await closeServer(proxyServer);\n await closeServer(uiServer);\n if (!allowPortFallback || !isAddressInUse(error)) throw error;\n candidateProxyPort += 2;\n candidateUiPort += 2;\n }\n }\n\n throw new Error(\"Mocklane could not find an available local port pair.\");\n}\n\nasync function handleUiRequest(\n request: IncomingMessage,\n response: ServerResponse,\n registry: MockRegistry,\n uiDirectory: string,\n proxyPort: number,\n) {\n const url = new URL(request.url ?? \"/\", \"http://localhost\");\n\n if (url.pathname === \"/__mocklane/health\" && request.method === \"GET\") {\n sendJson(response, 200, { ready: true, proxyPort, mocks: registry.list().length });\n return;\n }\n\n if (url.pathname === \"/__mocklane/mocks\" && request.method === \"GET\") {\n sendJson(response, 200, registry.list());\n return;\n }\n\n if (url.pathname === \"/__mocklane/mocks\" && request.method === \"PUT\") {\n try {\n const mock = validateMockResponse(await readJsonBody(request));\n registry.set(mock);\n sendJson(response, 200, { active: true, method: mock.method, path: mock.path, proxyPort });\n } catch (error) {\n sendJson(response, 400, {\n error: error instanceof Error ? error.message : \"Invalid mock response.\",\n });\n }\n return;\n }\n\n if (url.pathname === \"/__mocklane/mocks\" && request.method === \"DELETE\") {\n try {\n const mock = validateMockResponse(await readJsonBody(request));\n registry.delete(mock.method, mock.path);\n sendJson(response, 200, { active: false, method: mock.method, path: mock.path });\n } catch (error) {\n sendJson(response, 400, {\n error: error instanceof Error ? error.message : \"Invalid mock response.\",\n });\n }\n return;\n }\n\n serveUiFile(response, uiDirectory, url.pathname, request.method === \"HEAD\");\n}\n\nfunction handleProxyRequest(\n request: IncomingMessage,\n response: ServerResponse,\n registry: MockRegistry,\n target: URL | null,\n) {\n const requestUrl = new URL(request.url ?? \"/\", target ?? \"http://localhost\");\n const preflightMethod = request.headers[\"access-control-request-method\"];\n const preflightMock =\n request.method === \"OPTIONS\" && typeof preflightMethod === \"string\"\n ? registry.get(preflightMethod, requestUrl.pathname)\n : undefined;\n if (preflightMock && typeof preflightMethod === \"string\") {\n response.writeHead(204, {\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-methods\": preflightMethod.toUpperCase(),\n \"access-control-allow-headers\": request.headers[\"access-control-request-headers\"] ?? \"content-type\",\n });\n response.end();\n return;\n }\n\n const mock = registry.get(request.method ?? \"GET\", requestUrl.pathname);\n\n if (mock) {\n response.writeHead(mock.status, {\n ...mock.headers,\n \"access-control-allow-origin\": mock.headers[\"access-control-allow-origin\"] ?? \"*\",\n });\n response.end(JSON.stringify(mock.body));\n return;\n }\n\n if (!target) {\n sendJson(response, 404, {\n error: \"No Mocklane mock matches this route.\",\n method: request.method ?? \"GET\",\n path: requestUrl.pathname,\n });\n return;\n }\n\n const upstreamUrl = new URL(request.url ?? \"/\", target);\n const upstreamRequest = (upstreamUrl.protocol === \"https:\" ? requestHttps : requestHttp)(\n upstreamUrl,\n {\n method: request.method,\n headers: { ...request.headers, host: upstreamUrl.host },\n },\n (upstreamResponse) => {\n response.writeHead(upstreamResponse.statusCode ?? 502, upstreamResponse.headers);\n upstreamResponse.pipe(response);\n },\n );\n\n upstreamRequest.on(\"error\", (error) => {\n if (!response.headersSent) {\n sendJson(response, 502, {\n error: \"Mocklane could not reach the upstream server.\",\n detail: error.message,\n });\n return;\n }\n response.destroy(error);\n });\n request.pipe(upstreamRequest);\n}\n\nfunction serveUiFile(\n response: ServerResponse,\n uiDirectory: string,\n pathname: string,\n headOnly: boolean,\n) {\n const requestedPath = pathname === \"/\" ? \"/index.html\" : pathname;\n const candidate = resolve(uiDirectory, `.${decodeURIComponent(requestedPath)}`);\n const uiRoot = resolve(uiDirectory);\n const filePath =\n candidate.startsWith(`${uiRoot}/`) && existsSync(candidate) && statSync(candidate).isFile()\n ? candidate\n : resolve(uiDirectory, \"index.html\");\n\n if (!existsSync(filePath)) {\n sendJson(response, 503, {\n error: \"Mocklane UI has not been built. Run `pnpm build` first.\",\n });\n return;\n }\n\n response.writeHead(200, {\n \"content-type\": contentType(filePath),\n \"cache-control\": filePath.endsWith(\"index.html\") ? \"no-cache\" : \"public, max-age=31536000, immutable\",\n });\n if (headOnly) {\n response.end();\n return;\n }\n createReadStream(filePath).pipe(response);\n}\n\nasync function readJsonBody(request: IncomingMessage) {\n const chunks: Buffer[] = [];\n let size = 0;\n for await (const chunk of request) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n size += buffer.length;\n if (size > MAX_CONTROL_BODY_BYTES) {\n throw new Error(\"Mock response exceeds the 10 MB limit.\");\n }\n chunks.push(buffer);\n }\n return JSON.parse(Buffer.concat(chunks).toString(\"utf8\")) as unknown;\n}\n\nfunction validateMockResponse(value: unknown): MockHttpResponse {\n if (!value || typeof value !== \"object\") throw new Error(\"Mock response must be an object.\");\n const mock = value as Partial<MockHttpResponse>;\n if (typeof mock.method !== \"string\" || !mock.method.trim()) throw new Error(\"Method is required.\");\n if (typeof mock.path !== \"string\" || !mock.path.startsWith(\"/\")) {\n throw new Error(\"Path must begin with `/`.\");\n }\n if (!Number.isInteger(mock.status) || Number(mock.status) < 100 || Number(mock.status) > 599) {\n throw new Error(\"Status must be a valid HTTP status code.\");\n }\n if (!mock.headers || typeof mock.headers !== \"object\" || Array.isArray(mock.headers)) {\n throw new Error(\"Headers must be an object.\");\n }\n return mock as MockHttpResponse;\n}\n\nfunction sendJson(response: ServerResponse, status: number, body: unknown) {\n response.writeHead(status, { \"content-type\": \"application/json; charset=utf-8\" });\n response.end(JSON.stringify(body));\n}\n\nfunction contentType(filePath: string) {\n const types: Record<string, string> = {\n \".css\": \"text/css; charset=utf-8\",\n \".html\": \"text/html; charset=utf-8\",\n \".js\": \"text/javascript; charset=utf-8\",\n \".json\": \"application/json; charset=utf-8\",\n \".png\": \"image/png\",\n \".svg\": \"image/svg+xml\",\n \".wasm\": \"application/wasm\",\n };\n return types[extname(filePath)] ?? \"application/octet-stream\";\n}\n\nfunction listen(server: Server, port: number, host: string) {\n return new Promise<void>((resolvePromise, reject) => {\n server.once(\"error\", reject);\n server.listen(port, host, () => {\n server.off(\"error\", reject);\n resolvePromise();\n });\n });\n}\n\nfunction isAddressInUse(error: unknown) {\n return error instanceof Error && \"code\" in error && error.code === \"EADDRINUSE\";\n}\n\nfunction closeServer(server: Server) {\n return new Promise<void>((resolvePromise) => {\n if (!server.listening) {\n resolvePromise();\n return;\n }\n server.close(() => resolvePromise());\n });\n}\n", "import type { MockHttpResponse } from \"../src/types/mockApi\";\n\nexport class MockRegistry {\n readonly #mocks = new Map<string, MockHttpResponse>();\n\n set(response: MockHttpResponse) {\n this.#mocks.set(mockKey(response.method, response.path), response);\n }\n\n get(method: string, path: string) {\n return this.#mocks.get(mockKey(method, path));\n }\n\n delete(method: string, path: string) {\n return this.#mocks.delete(mockKey(method, path));\n }\n\n list() {\n return [...this.#mocks.values()];\n }\n}\n\nfunction mockKey(method: string, path: string) {\n return `${method.toUpperCase()} ${path}`;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|