counterfact 0.42.0 → 0.43.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.
- package/bin/counterfact.js +3 -3
- package/dist/{server/app.js → app.js} +10 -10
- package/dist/repl/repl.js +87 -0
- package/dist/server/convert-js-extensions-to-cjs.js +3 -1
- package/dist/server/is-proxy-enabled-for-path.js +10 -0
- package/dist/server/koa-middleware.js +3 -2
- package/dist/server/response-builder.js +5 -1
- package/dist/server/types.d.ts +1 -1
- package/dist/util/wait-for-event.js +4 -3
- package/package.json +9 -9
- package/dist/server/repl.js +0 -34
package/bin/counterfact.js
CHANGED
|
@@ -9,7 +9,7 @@ import { program } from "commander";
|
|
|
9
9
|
import createDebug from "debug";
|
|
10
10
|
import open from "open";
|
|
11
11
|
|
|
12
|
-
import { counterfact } from "../dist/
|
|
12
|
+
import { counterfact } from "../dist/app.js";
|
|
13
13
|
|
|
14
14
|
const MIN_NODE_VERSION = 17;
|
|
15
15
|
|
|
@@ -151,8 +151,8 @@ async function main(source, destination) {
|
|
|
151
151
|
includeSwaggerUi: true,
|
|
152
152
|
openApiPath: source,
|
|
153
153
|
port: options.port,
|
|
154
|
-
|
|
155
|
-
proxyUrl: options.proxyUrl,
|
|
154
|
+
proxyPaths: new Map([["", Boolean(options.proxyUrl)]]),
|
|
155
|
+
proxyUrl: options.proxyUrl ?? "",
|
|
156
156
|
routePrefix: options.prefix,
|
|
157
157
|
startRepl: options.repl,
|
|
158
158
|
startServer: options.serve,
|
|
@@ -3,16 +3,16 @@ import nodePath from "node:path";
|
|
|
3
3
|
import { createHttpTerminator } from "http-terminator";
|
|
4
4
|
import yaml from "js-yaml";
|
|
5
5
|
import $RefParser from "json-schema-ref-parser";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
6
|
+
import { startRepl } from "./repl/repl.js";
|
|
7
|
+
import { ContextRegistry } from "./server/context-registry.js";
|
|
8
|
+
import { createKoaApp } from "./server/create-koa-app.js";
|
|
9
|
+
import { Dispatcher } from "./server/dispatcher.js";
|
|
10
|
+
import { koaMiddleware } from "./server/koa-middleware.js";
|
|
11
|
+
import { ModuleLoader } from "./server/module-loader.js";
|
|
12
|
+
import { Registry } from "./server/registry.js";
|
|
13
|
+
import { Transpiler } from "./server/transpiler.js";
|
|
14
|
+
import { CodeGenerator } from "./typescript-generator/code-generator.js";
|
|
15
|
+
import { readFile } from "./util/read-file.js";
|
|
16
16
|
async function loadOpenApiDocument(source) {
|
|
17
17
|
try {
|
|
18
18
|
const text = await readFile(source);
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import repl from "node:repl";
|
|
2
|
+
function printToStdout(line) {
|
|
3
|
+
process.stdout.write(`${line}\n`);
|
|
4
|
+
}
|
|
5
|
+
export function startRepl(contextRegistry, config, print = printToStdout) {
|
|
6
|
+
// eslint-disable-next-line max-statements
|
|
7
|
+
function printProxyStatus() {
|
|
8
|
+
if (config.proxyUrl === "") {
|
|
9
|
+
print("The proxy URL is not set.");
|
|
10
|
+
print('To set it, type ".proxy url <url>');
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
print("Proxy Configuration:");
|
|
14
|
+
print("");
|
|
15
|
+
print(`The proxy URL is ${config.proxyUrl}`);
|
|
16
|
+
print("");
|
|
17
|
+
print("Paths prefixed with [+] will be proxied.");
|
|
18
|
+
print("Paths prefixed with [-] will not be proxied.");
|
|
19
|
+
print("");
|
|
20
|
+
// eslint-disable-next-line array-func/prefer-array-from
|
|
21
|
+
const entries = [...config.proxyPaths.entries()].sort(([path1], [path2]) => path1 < path2 ? -1 : 1);
|
|
22
|
+
for (const [path, state] of entries) {
|
|
23
|
+
print(`${state ? "[+]" : "[-]"} ${path}/`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function setProxyUrl(url) {
|
|
27
|
+
if (url === undefined) {
|
|
28
|
+
print("usage: .proxy url <url>");
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
config.proxyUrl = url;
|
|
32
|
+
print(`proxy URL is set to ${url}`);
|
|
33
|
+
}
|
|
34
|
+
function turnProxyOnOrOff(text) {
|
|
35
|
+
const [command, endpoint] = text.split(" ");
|
|
36
|
+
const printEndpoint = endpoint === undefined || endpoint === "" ? "/" : endpoint;
|
|
37
|
+
config.proxyPaths.set((endpoint ?? "").replace(/\/$/u, ""), command === "on");
|
|
38
|
+
if (command === "on") {
|
|
39
|
+
print(`Requests to ${printEndpoint} will be proxied to ${config.proxyUrl || "<proxy URL>"}${printEndpoint}`);
|
|
40
|
+
}
|
|
41
|
+
if (command === "off") {
|
|
42
|
+
print(`Requests to ${printEndpoint} will be handled by local code`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const replServer = repl.start({ prompt: "🤖> " });
|
|
46
|
+
replServer.defineCommand("counterfact", {
|
|
47
|
+
action() {
|
|
48
|
+
print("This is a read-eval-print loop (REPL), the same as the one you get when you run node with no arguments.");
|
|
49
|
+
print("Except that it's connected to the running server, which you can access with the following globals:");
|
|
50
|
+
print("");
|
|
51
|
+
print("- loadContext('/some/path'): to access the context object for a given path");
|
|
52
|
+
print("- context: the root context ( same as loadContext('/') )");
|
|
53
|
+
print("");
|
|
54
|
+
print("For more information, see https://counterfact.dev/docs/usage.html");
|
|
55
|
+
print("");
|
|
56
|
+
this.clearBufferedCommand();
|
|
57
|
+
this.displayPrompt();
|
|
58
|
+
},
|
|
59
|
+
help: "Get help with Counterfact",
|
|
60
|
+
});
|
|
61
|
+
replServer.defineCommand("proxy", {
|
|
62
|
+
action(text) {
|
|
63
|
+
if (text === "help" || text === "") {
|
|
64
|
+
print(".proxy [on|off] - turn the proxy on/off at the root level");
|
|
65
|
+
print(".proxy [on|off] <path-prefix> - turn the proxy on for a path");
|
|
66
|
+
print(".proxy status - show the proxy status");
|
|
67
|
+
print(".proxy help - show this message");
|
|
68
|
+
}
|
|
69
|
+
else if (text.startsWith("url")) {
|
|
70
|
+
setProxyUrl(text.split(" ")[1]);
|
|
71
|
+
}
|
|
72
|
+
else if (text === "status") {
|
|
73
|
+
printProxyStatus();
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
turnProxyOnOrOff(text);
|
|
77
|
+
}
|
|
78
|
+
this.clearBufferedCommand();
|
|
79
|
+
this.displayPrompt();
|
|
80
|
+
},
|
|
81
|
+
help: 'proxy configuration (".proxy help" for details)',
|
|
82
|
+
});
|
|
83
|
+
replServer.context.loadContext = (path) => contextRegistry.find(path);
|
|
84
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
|
|
85
|
+
replServer.context.context = replServer.context.loadContext("/");
|
|
86
|
+
return replServer;
|
|
87
|
+
}
|
|
@@ -17,7 +17,9 @@ export function convertFileExtensionsToCjs(code) {
|
|
|
17
17
|
typeof node.arguments[0].value === "string" &&
|
|
18
18
|
node.arguments[0].value.startsWith(".")) {
|
|
19
19
|
// Change the module string from "foo.js" to "foo.cjs"
|
|
20
|
-
node.arguments[0].value = node.arguments[0].value.replace(
|
|
20
|
+
node.arguments[0].value = node.arguments[0].value.replace(
|
|
21
|
+
// eslint-disable-next-line prefer-named-capture-group, regexp/no-unused-capturing-group, regexp/prefer-named-capture-group
|
|
22
|
+
/(\.js|\.ts)?$/u, ".cjs");
|
|
21
23
|
}
|
|
22
24
|
// Continue traversing the AST
|
|
23
25
|
this.traverse(path);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function isProxyEnabledForPath(path, config) {
|
|
2
|
+
if (config.proxyPaths.has(path)) {
|
|
3
|
+
return config.proxyPaths.get(path) ?? false;
|
|
4
|
+
}
|
|
5
|
+
if (path === "") {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
const parentPath = path.slice(0, Math.max(0, path.lastIndexOf("/")));
|
|
9
|
+
return isProxyEnabledForPath(parentPath, config);
|
|
10
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import createDebug from "debug";
|
|
2
2
|
import koaProxy from "koa-proxy";
|
|
3
|
+
import { isProxyEnabledForPath } from "./is-proxy-enabled-for-path.js";
|
|
3
4
|
const debug = createDebug("counterfact:server:create-koa-app");
|
|
4
5
|
const HTTP_STATUS_CODE_OK = 200;
|
|
5
6
|
function addCors(ctx, headers) {
|
|
@@ -26,7 +27,7 @@ function getAuthObject(ctx) {
|
|
|
26
27
|
export function koaMiddleware(dispatcher, config, proxy = koaProxy) {
|
|
27
28
|
// eslint-disable-next-line max-statements
|
|
28
29
|
return async function middleware(ctx, next) {
|
|
29
|
-
const {
|
|
30
|
+
const { proxyUrl, routePrefix } = config;
|
|
30
31
|
debug("middleware running for path: %s", ctx.request.path);
|
|
31
32
|
debug("routePrefix: %s", routePrefix);
|
|
32
33
|
if (!ctx.request.path.startsWith(routePrefix)) {
|
|
@@ -39,7 +40,7 @@ export function koaMiddleware(dispatcher, config, proxy = koaProxy) {
|
|
|
39
40
|
const path = ctx.request.path.slice(routePrefix.length);
|
|
40
41
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
41
42
|
const method = ctx.request.method;
|
|
42
|
-
if (
|
|
43
|
+
if (isProxyEnabledForPath(path, config) && proxyUrl) {
|
|
43
44
|
/* @ts-expect-error the body comes from koa-bodyparser, not sure how to fix this */
|
|
44
45
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
45
46
|
return proxy({ host: proxyUrl })(ctx, next);
|
|
@@ -46,7 +46,11 @@ export function createResponseBuilder(operation) {
|
|
|
46
46
|
return this.match("text/html", body);
|
|
47
47
|
},
|
|
48
48
|
json(body) {
|
|
49
|
-
return this.match("application/json", body)
|
|
49
|
+
return this.match("application/json", body)
|
|
50
|
+
.match("text/json", body)
|
|
51
|
+
.match("text/x-json", body)
|
|
52
|
+
.match("application/xml", body)
|
|
53
|
+
.match("text/xml", body);
|
|
50
54
|
},
|
|
51
55
|
match(contentType, body) {
|
|
52
56
|
return {
|
package/dist/server/types.d.ts
CHANGED
|
@@ -99,7 +99,7 @@ type GenericResponseBuilderInner<
|
|
|
99
99
|
? never
|
|
100
100
|
: HeaderFunction<Response>;
|
|
101
101
|
html: MaybeShortcut<"text/html", Response>;
|
|
102
|
-
json: MaybeShortcut<"application/json", Response>;
|
|
102
|
+
json: MaybeShortcut<"application/json" | "text/json" | "text/x-json" | "application/xml" | "text/xml", Response>;
|
|
103
103
|
match: [keyof Response["content"]] extends [never]
|
|
104
104
|
? never
|
|
105
105
|
: MatchFunction<Response>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
1
2
|
/**
|
|
2
3
|
* Creates a promise that resolves when a specified event is fired on the given EventTarget.
|
|
3
4
|
* @param {EventTarget | EventEmitter} target - The target to listen for the event on.
|
|
@@ -13,11 +14,11 @@ export async function waitForEvent(target, eventName) {
|
|
|
13
14
|
}
|
|
14
15
|
resolve(event);
|
|
15
16
|
};
|
|
16
|
-
if (target instanceof
|
|
17
|
-
target.
|
|
17
|
+
if (target instanceof EventEmitter) {
|
|
18
|
+
target.once(eventName, handler);
|
|
18
19
|
}
|
|
19
20
|
else {
|
|
20
|
-
target.
|
|
21
|
+
target.addEventListener(eventName, handler);
|
|
21
22
|
}
|
|
22
23
|
});
|
|
23
24
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "counterfact",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.43.0",
|
|
4
4
|
"description": "a library for building a fake REST API for testing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/server/counterfact.js",
|
|
@@ -44,11 +44,11 @@
|
|
|
44
44
|
"postinstall": "patch-package"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@changesets/cli": "2.27.
|
|
47
|
+
"@changesets/cli": "2.27.3",
|
|
48
48
|
"@stryker-mutator/core": "8.2.6",
|
|
49
49
|
"@stryker-mutator/jest-runner": "8.2.6",
|
|
50
50
|
"@stryker-mutator/typescript-checker": "8.2.6",
|
|
51
|
-
"@swc/core": "1.5.
|
|
51
|
+
"@swc/core": "1.5.7",
|
|
52
52
|
"@swc/jest": "0.2.36",
|
|
53
53
|
"@testing-library/dom": "10.1.0",
|
|
54
54
|
"@types/jest": "29.5.12",
|
|
@@ -63,17 +63,17 @@
|
|
|
63
63
|
"eslint-formatter-github-annotations": "0.1.0",
|
|
64
64
|
"eslint-import-resolver-typescript": "3.6.1",
|
|
65
65
|
"eslint-plugin-etc": "2.0.3",
|
|
66
|
-
"eslint-plugin-file-progress": "1.
|
|
66
|
+
"eslint-plugin-file-progress": "1.4.0",
|
|
67
67
|
"eslint-plugin-import": "2.29.1",
|
|
68
68
|
"eslint-plugin-jest": "28.5.0",
|
|
69
69
|
"eslint-plugin-jest-dom": "5.4.0",
|
|
70
70
|
"eslint-plugin-no-explicit-type-exports": "0.12.1",
|
|
71
|
-
"eslint-plugin-unused-imports": "
|
|
71
|
+
"eslint-plugin-unused-imports": "4.0.0",
|
|
72
72
|
"husky": "9.0.11",
|
|
73
73
|
"jest": "29.7.0",
|
|
74
74
|
"node-mocks-http": "1.14.1",
|
|
75
|
-
"nodemon": "3.1.
|
|
76
|
-
"rimraf": "5.0.
|
|
75
|
+
"nodemon": "3.1.1",
|
|
76
|
+
"rimraf": "5.0.7",
|
|
77
77
|
"stryker-cli": "1.0.2",
|
|
78
78
|
"supertest": "7.0.0",
|
|
79
79
|
"using-temporary-files": "2.2.1"
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"@types/json-schema": "7.0.15",
|
|
84
84
|
"ast-types": "0.14.2",
|
|
85
85
|
"chokidar": "3.6.0",
|
|
86
|
-
"commander": "12.
|
|
86
|
+
"commander": "12.1.0",
|
|
87
87
|
"debug": "4.3.4",
|
|
88
88
|
"fetch": "1.1.0",
|
|
89
89
|
"fs-extra": "11.2.0",
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
"patch-package": "8.0.0",
|
|
103
103
|
"precinct": "12.1.1",
|
|
104
104
|
"prettier": "3.2.5",
|
|
105
|
-
"recast": "0.23.
|
|
105
|
+
"recast": "0.23.7",
|
|
106
106
|
"typescript": "5.4.5"
|
|
107
107
|
}
|
|
108
108
|
}
|
package/dist/server/repl.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import repl from "node:repl";
|
|
2
|
-
export function startRepl(contextRegistry, config) {
|
|
3
|
-
const replServer = repl.start("🤖> ");
|
|
4
|
-
replServer.defineCommand("counterfact", {
|
|
5
|
-
action() {
|
|
6
|
-
process.stdout.write("This is a read-eval-print loop (REPL), the same as the one you get when you run node with no arguments.\n");
|
|
7
|
-
process.stdout.write("Except that it's connected to the running server, which you can access with the following globals:\n\n");
|
|
8
|
-
process.stdout.write("- loadContext('/some/path'): to access the context object for a given path\n");
|
|
9
|
-
process.stdout.write("- context: the root context ( same as loadContext('/') )\n");
|
|
10
|
-
process.stdout.write("\nFor more information, see https://counterfact.dev/docs/usage.html\n\n");
|
|
11
|
-
this.clearBufferedCommand();
|
|
12
|
-
this.displayPrompt();
|
|
13
|
-
},
|
|
14
|
-
help: "Get help with Counterfact",
|
|
15
|
-
});
|
|
16
|
-
replServer.defineCommand("proxy", {
|
|
17
|
-
action(state) {
|
|
18
|
-
if (state === "on") {
|
|
19
|
-
config.proxyEnabled = true;
|
|
20
|
-
}
|
|
21
|
-
if (state === "off") {
|
|
22
|
-
config.proxyEnabled = false;
|
|
23
|
-
}
|
|
24
|
-
process.stdout.write(`Proxy is ${config.proxyEnabled ? "on" : "off"}: ${config.proxyUrl}\n`);
|
|
25
|
-
this.clearBufferedCommand();
|
|
26
|
-
this.displayPrompt();
|
|
27
|
-
},
|
|
28
|
-
help: "proxy [on|off] - turn the proxy on or off; proxy - print proxy info",
|
|
29
|
-
});
|
|
30
|
-
replServer.context.loadContext = (path) => contextRegistry.find(path);
|
|
31
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
|
|
32
|
-
replServer.context.context = replServer.context.loadContext("/");
|
|
33
|
-
return replServer;
|
|
34
|
-
}
|