@mocklane/core 0.1.0

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.
@@ -0,0 +1,13 @@
1
+ <!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="modulepreload" href="/assets/manifest-f8a16862.js"/><link rel="modulepreload" href="/assets/entry.client-Cw45cONA.js"/><link rel="modulepreload" href="/assets/jsx-runtime-BZbza59V.js"/><link rel="modulepreload" href="/assets/root-CCK9sNPJ.js"/><link rel="stylesheet" href="/assets/root--WDECO_x.css"/></head><body><script>
2
+ console.log(
3
+ "💿 Hey developer 👋. You can provide a way better UX than this " +
4
+ "when your app is loading JS modules and/or running `clientLoader` " +
5
+ "functions. Check out https://reactrouter.com/start/framework/route-module#hydratefallback " +
6
+ "for more information."
7
+ );
8
+ </script><script>window.__reactRouterContext = {"basename":"/","future":{"unstable_optimizeDeps":false,"v8_passThroughRequests":false,"v8_trailingSlashAwareDataRequests":false,"unstable_previewServerPrerendering":false,"v8_middleware":false,"v8_splitRouteModules":false,"v8_viteEnvironmentApi":false},"routeDiscovery":{"mode":"initial"},"ssr":false,"isSpaMode":true};window.__reactRouterContext.stream = new ReadableStream({start(controller){window.__reactRouterContext.streamController = controller;}}).pipeThrough(new TextEncoderStream());</script><script type="module" async="">import "/assets/manifest-f8a16862.js";
9
+ import * as route0 from "/assets/root-CCK9sNPJ.js";
10
+
11
+ window.__reactRouterRouteModules = {"root":route0};
12
+
13
+ import("/assets/entry.client-Cw45cONA.js");</script><!--$--><script>window.__reactRouterContext.streamController.enqueue("[{\"_1\":2,\"_3\":-5,\"_4\":-5},\"loaderData\",{},\"actionData\",\"errors\"]\n");</script><!--$--><script>window.__reactRouterContext.streamController.close();</script><!--/$--><!--/$--></body></html>
package/dist/cli.js ADDED
@@ -0,0 +1,318 @@
1
+ #!/usr/bin/env node
2
+
3
+ // cli/index.ts
4
+ import { spawn } from "node:child_process";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ // cli/server.ts
8
+ import { createReadStream, existsSync, statSync } from "node:fs";
9
+ import { createServer } from "node:http";
10
+ import { request as requestHttp } from "node:http";
11
+ import { request as requestHttps } from "node:https";
12
+ import { extname, resolve } from "node:path";
13
+
14
+ // cli/mockRegistry.ts
15
+ var MockRegistry = class {
16
+ #mocks = /* @__PURE__ */ new Map();
17
+ set(response) {
18
+ this.#mocks.set(mockKey(response.method, response.path), response);
19
+ }
20
+ get(method, path) {
21
+ return this.#mocks.get(mockKey(method, path));
22
+ }
23
+ delete(method, path) {
24
+ return this.#mocks.delete(mockKey(method, path));
25
+ }
26
+ list() {
27
+ return [...this.#mocks.values()];
28
+ }
29
+ };
30
+ function mockKey(method, path) {
31
+ return `${method.toUpperCase()} ${path}`;
32
+ }
33
+
34
+ // cli/server.ts
35
+ var MAX_CONTROL_BODY_BYTES = 10 * 1024 * 1024;
36
+ async function startMocklaneServers({
37
+ host,
38
+ proxyPort,
39
+ uiPort,
40
+ target,
41
+ uiDirectory
42
+ }) {
43
+ const registry = new MockRegistry();
44
+ const proxyServer = createServer((request, response) => {
45
+ handleProxyRequest(request, response, registry, target);
46
+ });
47
+ const uiServer = createServer((request, response) => {
48
+ void handleUiRequest(request, response, registry, uiDirectory, proxyPort);
49
+ });
50
+ try {
51
+ await listen(proxyServer, proxyPort, host);
52
+ await listen(uiServer, uiPort, host);
53
+ } catch (error) {
54
+ await closeServer(proxyServer);
55
+ await closeServer(uiServer);
56
+ throw error;
57
+ }
58
+ return {
59
+ close: async () => {
60
+ await Promise.all([closeServer(proxyServer), closeServer(uiServer)]);
61
+ }
62
+ };
63
+ }
64
+ async function handleUiRequest(request, response, registry, uiDirectory, proxyPort) {
65
+ const url = new URL(request.url ?? "/", "http://localhost");
66
+ if (url.pathname === "/__mocklane/health" && request.method === "GET") {
67
+ sendJson(response, 200, { ready: true, proxyPort, mocks: registry.list().length });
68
+ return;
69
+ }
70
+ if (url.pathname === "/__mocklane/mocks" && request.method === "GET") {
71
+ sendJson(response, 200, registry.list());
72
+ return;
73
+ }
74
+ if (url.pathname === "/__mocklane/mocks" && request.method === "PUT") {
75
+ try {
76
+ const mock = validateMockResponse(await readJsonBody(request));
77
+ registry.set(mock);
78
+ sendJson(response, 200, { active: true, method: mock.method, path: mock.path, proxyPort });
79
+ } catch (error) {
80
+ sendJson(response, 400, {
81
+ error: error instanceof Error ? error.message : "Invalid mock response."
82
+ });
83
+ }
84
+ return;
85
+ }
86
+ if (url.pathname === "/__mocklane/mocks" && request.method === "DELETE") {
87
+ try {
88
+ const mock = validateMockResponse(await readJsonBody(request));
89
+ registry.delete(mock.method, mock.path);
90
+ sendJson(response, 200, { active: false, method: mock.method, path: mock.path });
91
+ } catch (error) {
92
+ sendJson(response, 400, {
93
+ error: error instanceof Error ? error.message : "Invalid mock response."
94
+ });
95
+ }
96
+ return;
97
+ }
98
+ serveUiFile(response, uiDirectory, url.pathname, request.method === "HEAD");
99
+ }
100
+ function handleProxyRequest(request, response, registry, target) {
101
+ const requestUrl = new URL(request.url ?? "/", target ?? "http://localhost");
102
+ const preflightMethod = request.headers["access-control-request-method"];
103
+ const preflightMock = request.method === "OPTIONS" && typeof preflightMethod === "string" ? registry.get(preflightMethod, requestUrl.pathname) : void 0;
104
+ if (preflightMock && typeof preflightMethod === "string") {
105
+ response.writeHead(204, {
106
+ "access-control-allow-origin": "*",
107
+ "access-control-allow-methods": preflightMethod.toUpperCase(),
108
+ "access-control-allow-headers": request.headers["access-control-request-headers"] ?? "content-type"
109
+ });
110
+ response.end();
111
+ return;
112
+ }
113
+ const mock = registry.get(request.method ?? "GET", requestUrl.pathname);
114
+ if (mock) {
115
+ response.writeHead(mock.status, {
116
+ ...mock.headers,
117
+ "access-control-allow-origin": mock.headers["access-control-allow-origin"] ?? "*"
118
+ });
119
+ response.end(JSON.stringify(mock.body));
120
+ return;
121
+ }
122
+ if (!target) {
123
+ sendJson(response, 404, {
124
+ error: "No Mocklane mock matches this route.",
125
+ method: request.method ?? "GET",
126
+ path: requestUrl.pathname
127
+ });
128
+ return;
129
+ }
130
+ const upstreamUrl = new URL(request.url ?? "/", target);
131
+ const upstreamRequest = (upstreamUrl.protocol === "https:" ? requestHttps : requestHttp)(
132
+ upstreamUrl,
133
+ {
134
+ method: request.method,
135
+ headers: { ...request.headers, host: upstreamUrl.host }
136
+ },
137
+ (upstreamResponse) => {
138
+ response.writeHead(upstreamResponse.statusCode ?? 502, upstreamResponse.headers);
139
+ upstreamResponse.pipe(response);
140
+ }
141
+ );
142
+ upstreamRequest.on("error", (error) => {
143
+ if (!response.headersSent) {
144
+ sendJson(response, 502, {
145
+ error: "Mocklane could not reach the upstream server.",
146
+ detail: error.message
147
+ });
148
+ return;
149
+ }
150
+ response.destroy(error);
151
+ });
152
+ request.pipe(upstreamRequest);
153
+ }
154
+ function serveUiFile(response, uiDirectory, pathname, headOnly) {
155
+ const requestedPath = pathname === "/" ? "/index.html" : pathname;
156
+ const candidate = resolve(uiDirectory, `.${decodeURIComponent(requestedPath)}`);
157
+ const uiRoot = resolve(uiDirectory);
158
+ const filePath = candidate.startsWith(`${uiRoot}/`) && existsSync(candidate) && statSync(candidate).isFile() ? candidate : resolve(uiDirectory, "index.html");
159
+ if (!existsSync(filePath)) {
160
+ sendJson(response, 503, {
161
+ error: "Mocklane UI has not been built. Run `pnpm build` first."
162
+ });
163
+ return;
164
+ }
165
+ response.writeHead(200, {
166
+ "content-type": contentType(filePath),
167
+ "cache-control": filePath.endsWith("index.html") ? "no-cache" : "public, max-age=31536000, immutable"
168
+ });
169
+ if (headOnly) {
170
+ response.end();
171
+ return;
172
+ }
173
+ createReadStream(filePath).pipe(response);
174
+ }
175
+ async function readJsonBody(request) {
176
+ const chunks = [];
177
+ let size = 0;
178
+ for await (const chunk of request) {
179
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
180
+ size += buffer.length;
181
+ if (size > MAX_CONTROL_BODY_BYTES) {
182
+ throw new Error("Mock response exceeds the 10 MB limit.");
183
+ }
184
+ chunks.push(buffer);
185
+ }
186
+ return JSON.parse(Buffer.concat(chunks).toString("utf8"));
187
+ }
188
+ function validateMockResponse(value) {
189
+ if (!value || typeof value !== "object") throw new Error("Mock response must be an object.");
190
+ const mock = value;
191
+ if (typeof mock.method !== "string" || !mock.method.trim()) throw new Error("Method is required.");
192
+ if (typeof mock.path !== "string" || !mock.path.startsWith("/")) {
193
+ throw new Error("Path must begin with `/`.");
194
+ }
195
+ if (!Number.isInteger(mock.status) || Number(mock.status) < 100 || Number(mock.status) > 599) {
196
+ throw new Error("Status must be a valid HTTP status code.");
197
+ }
198
+ if (!mock.headers || typeof mock.headers !== "object" || Array.isArray(mock.headers)) {
199
+ throw new Error("Headers must be an object.");
200
+ }
201
+ return mock;
202
+ }
203
+ function sendJson(response, status, body) {
204
+ response.writeHead(status, { "content-type": "application/json; charset=utf-8" });
205
+ response.end(JSON.stringify(body));
206
+ }
207
+ function contentType(filePath) {
208
+ const types = {
209
+ ".css": "text/css; charset=utf-8",
210
+ ".html": "text/html; charset=utf-8",
211
+ ".js": "text/javascript; charset=utf-8",
212
+ ".json": "application/json; charset=utf-8",
213
+ ".png": "image/png",
214
+ ".svg": "image/svg+xml",
215
+ ".wasm": "application/wasm"
216
+ };
217
+ return types[extname(filePath)] ?? "application/octet-stream";
218
+ }
219
+ function listen(server, port, host) {
220
+ return new Promise((resolvePromise, reject) => {
221
+ server.once("error", reject);
222
+ server.listen(port, host, () => {
223
+ server.off("error", reject);
224
+ resolvePromise();
225
+ });
226
+ });
227
+ }
228
+ function closeServer(server) {
229
+ return new Promise((resolvePromise) => {
230
+ if (!server.listening) {
231
+ resolvePromise();
232
+ return;
233
+ }
234
+ server.close(() => resolvePromise());
235
+ });
236
+ }
237
+
238
+ // cli/index.ts
239
+ var DEFAULT_HOST = "127.0.0.1";
240
+ var DEFAULT_PROXY_PORT = 4e3;
241
+ var DEFAULT_UI_PORT = 4001;
242
+ async function main() {
243
+ const options = parseArguments(process.argv.slice(2));
244
+ if (options.help) {
245
+ printHelp();
246
+ return;
247
+ }
248
+ const uiDirectory = fileURLToPath(new URL("../build/client", import.meta.url));
249
+ const servers = await startMocklaneServers({
250
+ host: DEFAULT_HOST,
251
+ proxyPort: options.proxyPort,
252
+ uiPort: options.uiPort,
253
+ target: options.target,
254
+ uiDirectory
255
+ });
256
+ const uiUrl = `http://localhost:${options.uiPort}`;
257
+ console.log(`Mocklane UI: ${uiUrl}`);
258
+ console.log(`Mock API proxy: http://localhost:${options.proxyPort}`);
259
+ console.log(`Upstream server: ${options.target?.origin ?? "none (standalone mode)"}`);
260
+ console.log("Press Ctrl+C to stop.");
261
+ openBrowser(uiUrl);
262
+ const shutdown = async () => {
263
+ await servers.close();
264
+ process.exit(0);
265
+ };
266
+ process.once("SIGINT", () => void shutdown());
267
+ process.once("SIGTERM", () => void shutdown());
268
+ }
269
+ function parseArguments(argumentsList) {
270
+ let target = null;
271
+ let proxyPort = DEFAULT_PROXY_PORT;
272
+ let uiPort = DEFAULT_UI_PORT;
273
+ let help = false;
274
+ for (let index = 0; index < argumentsList.length; index += 1) {
275
+ const argument = argumentsList[index];
276
+ if (argument === "--target") target = new URL(requireValue(argumentsList, ++index, argument));
277
+ else if (argument === "--proxy-port") proxyPort = parsePort(requireValue(argumentsList, ++index, argument));
278
+ else if (argument === "--ui-port") uiPort = parsePort(requireValue(argumentsList, ++index, argument));
279
+ else if (argument === "--help" || argument === "-h") help = true;
280
+ else throw new Error(`Unknown argument: ${argument}`);
281
+ }
282
+ if (proxyPort === uiPort) throw new Error("Proxy and UI ports must be different.");
283
+ return { target, proxyPort, uiPort, help };
284
+ }
285
+ function requireValue(argumentsList, index, flag) {
286
+ const value = argumentsList[index];
287
+ if (!value) throw new Error(`${flag} requires a value.`);
288
+ return value;
289
+ }
290
+ function parsePort(value) {
291
+ const port = Number(value);
292
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
293
+ throw new Error(`Invalid port: ${value}`);
294
+ }
295
+ return port;
296
+ }
297
+ function openBrowser(url) {
298
+ const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
299
+ const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
300
+ spawn(command, args, { detached: true, stdio: "ignore" }).unref();
301
+ }
302
+ function printHelp() {
303
+ console.log(`Mocklane local mock proxy
304
+
305
+ Usage:
306
+ mocklane [options]
307
+
308
+ Options:
309
+ --target <url> Optional upstream API URL for unmatched routes
310
+ --proxy-port <port> Mock/proxy port (default: 4000)
311
+ --ui-port <port> Mocklane UI port (default: 4001)
312
+ -h, --help Show this help`);
313
+ }
314
+ void main().catch((error) => {
315
+ console.error(error instanceof Error ? error.message : error);
316
+ process.exit(1);
317
+ });
318
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 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": "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",
6
+ "names": []
7
+ }
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@mocklane/core",
3
+ "version": "0.1.0",
4
+ "description": "Browser-local AI mock API generator and proxy.",
5
+ "author": "Asis Paudel",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/aasispaudel/Mocklane.git"
10
+ },
11
+ "homepage": "https://github.com/aasispaudel/Mocklane#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/aasispaudel/Mocklane/issues"
14
+ },
15
+ "keywords": [
16
+ "ai",
17
+ "mock-api",
18
+ "transformers",
19
+ "cli",
20
+ "proxy"
21
+ ],
22
+ "type": "module",
23
+ "packageManager": "pnpm@9.15.4",
24
+ "bin": {
25
+ "mocklane": "dist/cli.js"
26
+ },
27
+ "files": [
28
+ "dist",
29
+ "build/client",
30
+ "README.md"
31
+ ],
32
+ "engines": {
33
+ "node": ">=20"
34
+ },
35
+ "scripts": {
36
+ "dev": "react-router dev --port 3010",
37
+ "build": "react-router build",
38
+ "build:cli": "esbuild cli/index.ts --bundle --platform=node --format=esm --target=node20 --sourcemap --outfile=dist/cli.js",
39
+ "build:package": "pnpm build && pnpm build:cli",
40
+ "cli": "tsx cli/index.ts",
41
+ "test:cli": "tsx --test cli/server.test.ts",
42
+ "mocklane": "tsx cli/index.ts",
43
+ "mocklane:build": "pnpm build:package && node dist/cli.js",
44
+ "prepack": "pnpm build:package",
45
+ "start": "react-router-serve ./build/server/index.js",
46
+ "typecheck": "react-router typegen && tsc"
47
+ },
48
+ "devDependencies": {
49
+ "@huggingface/transformers": "^4.2.0",
50
+ "@mlc-ai/web-llm": "^0.2.84",
51
+ "@react-router/dev": "^7.17.0",
52
+ "@react-router/node": "^7.17.0",
53
+ "@react-router/serve": "^7.17.0",
54
+ "@types/node": "^22.15.30",
55
+ "@types/react": "^19.1.8",
56
+ "@types/react-dom": "^19.1.6",
57
+ "esbuild": "^0.28.1",
58
+ "isbot": "^5.1.31",
59
+ "lucide-react": "^1.17.0",
60
+ "react": "^19.1.0",
61
+ "react-dom": "^19.1.0",
62
+ "react-router": "^7.17.0",
63
+ "tsx": "^4.22.4",
64
+ "typescript": "^5.8.3",
65
+ "vite": "^6.3.5"
66
+ }
67
+ }