counterfact 1.0.1 → 1.0.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 (35) hide show
  1. package/bin/counterfact.js +2 -8
  2. package/dist/app.js +0 -4
  3. package/dist/migrate/paths-to-routes.js +0 -3
  4. package/dist/repl/repl.js +0 -3
  5. package/dist/server/context-registry.js +0 -4
  6. package/dist/server/convert-js-extensions-to-cjs.js +1 -2
  7. package/dist/server/create-koa-app.js +0 -2
  8. package/dist/server/determine-module-kind.js +1 -2
  9. package/dist/server/dispatcher.js +1 -8
  10. package/dist/server/json-to-xml.js +0 -4
  11. package/dist/server/koa-middleware.js +0 -8
  12. package/dist/server/module-dependency-graph.js +0 -2
  13. package/dist/server/module-loader.js +3 -10
  14. package/dist/server/module-tree.js +0 -6
  15. package/dist/server/openapi-middleware.js +0 -3
  16. package/dist/server/page-middleware.js +0 -2
  17. package/dist/server/registry.js +1 -3
  18. package/dist/server/response-builder.js +1 -5
  19. package/dist/server/tools.js +0 -1
  20. package/dist/server/transpiler.js +1 -7
  21. package/dist/server/types.ts +11 -15
  22. package/dist/server/uncached-import.js +0 -1
  23. package/dist/server/uncached-require.cjs +1 -1
  24. package/dist/typescript-generator/code-generator.js +1 -3
  25. package/dist/typescript-generator/generate.js +1 -1
  26. package/dist/typescript-generator/operation-coder.js +0 -1
  27. package/dist/typescript-generator/operation-type-coder.js +4 -5
  28. package/dist/typescript-generator/repository.js +1 -3
  29. package/dist/typescript-generator/requirement.js +0 -4
  30. package/dist/typescript-generator/schema-type-coder.js +0 -1
  31. package/dist/typescript-generator/script.js +0 -4
  32. package/dist/typescript-generator/specification.js +1 -4
  33. package/dist/util/read-file.js +0 -1
  34. package/dist/util/wait-for-event.js +0 -1
  35. package/package.json +22 -21
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- /* eslint-disable complexity */
3
2
 
4
3
  import fs from "node:fs";
5
4
  import { readFile } from "node:fs/promises";
@@ -19,7 +18,7 @@ if (Number.parseInt(process.versions.node.split("."), 10) < MIN_NODE_VERSION) {
19
18
  process.stdout.write(
20
19
  `Counterfact works with Node version ${MIN_NODE_VERSION}+. You are running version ${process.version}`,
21
20
  );
22
- // eslint-disable-next-line n/no-process-exit
21
+
23
22
  process.exit(1);
24
23
  }
25
24
 
@@ -46,7 +45,6 @@ function padTagLine(tagLine) {
46
45
  return `${padding}${tagLine}`;
47
46
  }
48
47
 
49
- // eslint-disable-next-line max-statements
50
48
  function createWatchMessage(config) {
51
49
  let watchMessage = "";
52
50
 
@@ -98,13 +96,10 @@ function createWatchMessage(config) {
98
96
 
99
97
  return watchMessage;
100
98
  }
101
-
102
- // eslint-disable-next-line max-statements, sonarjs/cognitive-complexity
103
99
  async function main(source, destination) {
104
100
  debug("executing the main function");
105
101
 
106
102
  const options = program.opts();
107
- // eslint-disable-next-line sonar/process-argv
108
103
  const args = process.argv;
109
104
 
110
105
  const destinationPath = nodePath
@@ -193,7 +188,7 @@ async function main(source, destination) {
193
188
 
194
189
  const introduction = [
195
190
  "____ ____ _ _ _ _ ___ ____ ____ ____ ____ ____ ___",
196
- "|___ [__] |__| |\\| | |=== |--< |--- |--| |___ | ",
191
+ String.raw`|___ [__] |__| |\| | |=== |--< |--- |--| |___ | `,
197
192
  padTagLine(taglines[Math.floor(Math.random() * taglines.length)]),
198
193
  "",
199
194
  `| API Base URL ==> ${url}`,
@@ -270,5 +265,4 @@ program
270
265
  "",
271
266
  )
272
267
  .action(main)
273
- // eslint-disable-next-line sonar/process-argv
274
268
  .parse(process.argv);
package/dist/app.js CHANGED
@@ -17,14 +17,12 @@ async function loadOpenApiDocument(source) {
17
17
  try {
18
18
  const text = await readFile(source);
19
19
  const openApiDocument = await yaml.load(text);
20
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-unnecessary-type-assertion
21
20
  return (await dereference(openApiDocument));
22
21
  }
23
22
  catch {
24
23
  return undefined;
25
24
  }
26
25
  }
27
- // eslint-disable-next-line max-statements
28
26
  export async function counterfact(config) {
29
27
  const modulesPath = config.basePath;
30
28
  const compiledPathsDirectory = nodePath
@@ -39,7 +37,6 @@ export async function counterfact(config) {
39
37
  const moduleLoader = new ModuleLoader(compiledPathsDirectory, registry, contextRegistry);
40
38
  const middleware = koaMiddleware(dispatcher, config);
41
39
  const koaApp = createKoaApp(registry, middleware, config);
42
- // eslint-disable-next-line max-statements
43
40
  async function start(options) {
44
41
  const { generate, startRepl: shouldStartRepl, startServer, watch, } = options;
45
42
  if (generate.routes || generate.types) {
@@ -48,7 +45,6 @@ export async function counterfact(config) {
48
45
  if (watch.routes || watch.types) {
49
46
  await codeGenerator.watch();
50
47
  }
51
- // eslint-disable-next-line @typescript-eslint/init-declarations
52
48
  let httpTerminator;
53
49
  if (startServer) {
54
50
  await transpiler.watch();
@@ -1,8 +1,5 @@
1
- /* eslint-disable no-await-in-loop */
2
- /* eslint-disable no-console */
3
1
  import { promises as fs } from "node:fs";
4
2
  import path from "node:path";
5
- // eslint-disable-next-line max-statements
6
3
  async function copyAndModifyFiles(sourceDirectory, destinationDirectory) {
7
4
  try {
8
5
  const entries = await fs.readdir(sourceDirectory, { withFileTypes: true });
package/dist/repl/repl.js CHANGED
@@ -3,7 +3,6 @@ function printToStdout(line) {
3
3
  process.stdout.write(`${line}\n`);
4
4
  }
5
5
  export function startRepl(contextRegistry, config, print = printToStdout) {
6
- // eslint-disable-next-line max-statements
7
6
  function printProxyStatus() {
8
7
  if (config.proxyUrl === "") {
9
8
  print("The proxy URL is not set.");
@@ -17,7 +16,6 @@ export function startRepl(contextRegistry, config, print = printToStdout) {
17
16
  print("Paths prefixed with [+] will be proxied.");
18
17
  print("Paths prefixed with [-] will not be proxied.");
19
18
  print("");
20
- // eslint-disable-next-line array-func/prefer-array-from
21
19
  const entries = [...config.proxyPaths.entries()].sort(([path1], [path2]) => path1 < path2 ? -1 : 1);
22
20
  for (const [path, state] of entries) {
23
21
  print(`${state ? "[+]" : "[-]"} ${path}/`);
@@ -81,7 +79,6 @@ export function startRepl(contextRegistry, config, print = printToStdout) {
81
79
  help: 'proxy configuration (".proxy help" for details)',
82
80
  });
83
81
  replServer.context.loadContext = (path) => contextRegistry.find(path);
84
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
85
82
  replServer.context.context = replServer.context.loadContext("/");
86
83
  return replServer;
87
84
  }
@@ -1,8 +1,5 @@
1
- /* eslint-disable max-classes-per-file */
2
- /* eslint-disable max-statements */
3
1
  import cloneDeep from "lodash/cloneDeep.js";
4
2
  export class Context {
5
- // eslint-disable-next-line @typescript-eslint/no-useless-constructor, @typescript-eslint/no-empty-function
6
3
  constructor() { }
7
4
  }
8
5
  export function parentPath(path) {
@@ -47,7 +44,6 @@ export class ContextRegistry {
47
44
  context[property] = updatedContext[property];
48
45
  }
49
46
  }
50
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
51
47
  Object.setPrototypeOf(context, Object.getPrototypeOf(updatedContext));
52
48
  this.cache.set(path, cloneDeep(updatedContext));
53
49
  }
@@ -1,7 +1,6 @@
1
1
  import { namedTypes, visit } from "ast-types";
2
2
  import { parse, print } from "recast";
3
3
  export function convertFileExtensionsToCjs(code) {
4
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
5
4
  const ast = parse(code);
6
5
  // Visit the nodes in the AST looking for `require` calls
7
6
  visit(ast, {
@@ -18,7 +17,7 @@ export function convertFileExtensionsToCjs(code) {
18
17
  node.arguments[0].value.startsWith(".")) {
19
18
  // Change the module string from "foo.js" to "foo.cjs"
20
19
  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
20
+ // eslint-disable-next-line regexp/prefer-named-capture-group
22
21
  /(\.js|\.ts)?$/u, ".cjs");
23
22
  }
24
23
  // Continue traversing the AST
@@ -6,7 +6,6 @@ import { koaSwagger } from "koa2-swagger-ui";
6
6
  import { openapiMiddleware } from "./openapi-middleware.js";
7
7
  import { pageMiddleware } from "./page-middleware.js";
8
8
  const debug = createDebug("counterfact:server:create-koa-app");
9
- // eslint-disable-next-line max-statements
10
9
  export function createKoaApp(registry, koaMiddleware, config) {
11
10
  const app = new Koa();
12
11
  app.use(openapiMiddleware(config.openApiPath, `//localhost:${config.port}${config.routePrefix}`));
@@ -34,7 +33,6 @@ export function createKoaApp(registry, koaMiddleware, config) {
34
33
  ctx.redirect("/counterfact/");
35
34
  return;
36
35
  }
37
- // eslint-disable-next-line n/callback-return
38
36
  await next();
39
37
  });
40
38
  app.use(pageMiddleware("/counterfact/rapidoc", "rapi-doc", {
@@ -1,8 +1,8 @@
1
+ /* eslint-disable n/no-sync */
1
2
  import { existsSync } from "node:fs";
2
3
  import fs from "node:fs/promises";
3
4
  import path from "node:path";
4
5
  const DEFAULT_MODULE_KIND = "commonjs";
5
- // eslint-disable-next-line max-statements
6
6
  export async function determineModuleKind(modulePath) {
7
7
  if (modulePath.endsWith(".cjs")) {
8
8
  return "commonjs";
@@ -16,7 +16,6 @@ export async function determineModuleKind(modulePath) {
16
16
  const packageJsonPath = path.join(modulePath, "package.json");
17
17
  if (existsSync(packageJsonPath)) {
18
18
  try {
19
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
20
19
  const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
21
20
  if (typeof packageJson.type !== "string") {
22
21
  return DEFAULT_MODULE_KIND;
@@ -1,7 +1,5 @@
1
- /* eslint-disable import/newline-after-import */
2
1
  import { mediaTypes } from "@hapi/accept";
3
2
  import createDebugger from "debug";
4
- // eslint-disable-next-line @typescript-eslint/no-shadow
5
3
  import fetch, { Headers } from "node-fetch";
6
4
  import { createResponseBuilder } from "./response-builder.js";
7
5
  import { Tools } from "./tools.js";
@@ -43,9 +41,7 @@ export class Dispatcher {
43
41
  if (this.openApiDocument) {
44
42
  for (const key in this.openApiDocument.paths) {
45
43
  if (key.toLowerCase() === path.toLowerCase()) {
46
- return this.openApiDocument.paths[key]?.[
47
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
48
- method.toLowerCase()];
44
+ return this.openApiDocument.paths[key]?.[method.toLowerCase()];
49
45
  }
50
46
  }
51
47
  }
@@ -84,7 +80,6 @@ export class Dispatcher {
84
80
  }
85
81
  const normalizedResponse = {
86
82
  ...response,
87
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
88
83
  body: content.body,
89
84
  contentType: content.type,
90
85
  };
@@ -120,13 +115,11 @@ export class Dispatcher {
120
115
  }
121
116
  return false;
122
117
  }
123
- // eslint-disable-next-line sonarjs/cognitive-complexity, max-statements
124
118
  async request({ auth, body, headers = {}, method, path, query, req, }) {
125
119
  debug(`request: ${method} ${path}`);
126
120
  // If the incoming path includes the base path, remove it
127
121
  if (this.openApiDocument?.basePath !== undefined &&
128
122
  path.toLowerCase().startsWith(this.openApiDocument.basePath.toLowerCase())) {
129
- // eslint-disable-next-line security/detect-non-literal-regexp
130
123
  path = path.replace(new RegExp(this.openApiDocument.basePath, "iu"), "");
131
124
  }
132
125
  const { matchedPath } = this.registry.handler(path);
@@ -1,5 +1,4 @@
1
1
  function xmlEscape(xmlString) {
2
- // eslint-disable-next-line unicorn/prefer-string-replace-all
3
2
  return xmlString.replace(/["&'<>]/gu, (character) => {
4
3
  switch (character) {
5
4
  case "<": {
@@ -28,12 +27,10 @@ function objectToXml(json, schema, name) {
28
27
  const attributes = [];
29
28
  Object.entries(json).forEach(([key, value]) => {
30
29
  const properties = schema?.properties?.[key];
31
- // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
32
30
  if (properties?.attribute) {
33
31
  attributes.push(` ${key}="${xmlEscape(String(value))}"`);
34
32
  }
35
33
  else {
36
- // eslint-disable-next-line @typescript-eslint/no-use-before-define
37
34
  xml.push(jsonToXml(value, properties, key));
38
35
  }
39
36
  });
@@ -45,7 +42,6 @@ export function jsonToXml(json, schema, keyName = "root") {
45
42
  const items = json
46
43
  .map((item) => jsonToXml(item, schema?.items, name))
47
44
  .join("");
48
- // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
49
45
  if (schema?.xml?.wrapped) {
50
46
  return `<${name}>${items}</${name}>`;
51
47
  }
@@ -25,23 +25,18 @@ function getAuthObject(ctx) {
25
25
  return { password, username };
26
26
  }
27
27
  export function koaMiddleware(dispatcher, config, proxy = koaProxy) {
28
- // eslint-disable-next-line max-statements
29
28
  return async function middleware(ctx, next) {
30
29
  const { proxyUrl, routePrefix } = config;
31
30
  debug("middleware running for path: %s", ctx.request.path);
32
31
  debug("routePrefix: %s", routePrefix);
33
32
  if (!ctx.request.path.startsWith(routePrefix)) {
34
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
35
33
  return await next();
36
34
  }
37
35
  const auth = getAuthObject(ctx);
38
- /* @ts-expect-error the body comes from koa-bodyparser, not sure how to fix this */
39
36
  const { body, headers, query } = ctx.request;
40
37
  const path = ctx.request.path.slice(routePrefix.length);
41
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
42
38
  const method = ctx.request.method;
43
39
  if (isProxyEnabledForPath(path, config) && proxyUrl) {
44
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
45
40
  return proxy("/", { changeOrigin: true, target: proxyUrl })(ctx, next);
46
41
  }
47
42
  addCors(ctx, headers);
@@ -51,7 +46,6 @@ export function koaMiddleware(dispatcher, config, proxy = koaProxy) {
51
46
  }
52
47
  const response = await dispatcher.request({
53
48
  auth,
54
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
55
49
  body,
56
50
  /* @ts-expect-error the value of a header can be an array and we don't have a solution for that yet */
57
51
  headers,
@@ -61,7 +55,6 @@ export function koaMiddleware(dispatcher, config, proxy = koaProxy) {
61
55
  query,
62
56
  req: { path: "", ...ctx.req },
63
57
  });
64
- /* eslint-disable require-atomic-updates */
65
58
  ctx.body = response.body;
66
59
  if (response.headers) {
67
60
  for (const [key, value] of Object.entries(response.headers)) {
@@ -69,7 +62,6 @@ export function koaMiddleware(dispatcher, config, proxy = koaProxy) {
69
62
  }
70
63
  }
71
64
  ctx.status = response.status ?? HTTP_STATUS_CODE_OK;
72
- /* eslint-enable require-atomic-updates */
73
65
  return undefined;
74
66
  };
75
67
  }
@@ -4,7 +4,6 @@ export class ModuleDependencyGraph {
4
4
  dependents = new Map();
5
5
  loadDependencies(path) {
6
6
  try {
7
- // eslint-disable-next-line import/no-named-as-default-member
8
7
  return precinct.paperwork(path);
9
8
  }
10
9
  catch {
@@ -29,7 +28,6 @@ export class ModuleDependencyGraph {
29
28
  this.dependents.get(key)?.add(path);
30
29
  }
31
30
  }
32
- // eslint-disable-next-line max-statements, sonarjs/cognitive-complexity
33
31
  dependentsOf(path) {
34
32
  const marked = new Set();
35
33
  const dependents = new Set();
@@ -1,3 +1,4 @@
1
+ /* eslint-disable n/no-sync */
1
2
  import { once } from "node:events";
2
3
  import { existsSync } from "node:fs";
3
4
  import fs from "node:fs/promises";
@@ -20,9 +21,7 @@ export class ModuleLoader extends EventTarget {
20
21
  watcher;
21
22
  contextRegistry;
22
23
  dependencyGraph = new ModuleDependencyGraph();
23
- uncachedImport =
24
- // eslint-disable-next-line @typescript-eslint/require-await
25
- async function (moduleName) {
24
+ uncachedImport = async function (moduleName) {
26
25
  throw new Error(`uncachedImport not set up; importing ${moduleName}`);
27
26
  };
28
27
  constructor(basePath, registry, contextRegistry = new ContextRegistry()) {
@@ -32,9 +31,7 @@ export class ModuleLoader extends EventTarget {
32
31
  this.contextRegistry = contextRegistry;
33
32
  }
34
33
  async watch() {
35
- this.watcher = watch(`${this.basePath}/**/*.{js,mjs,ts,mts,cjs,cts}`, CHOKIDAR_OPTIONS).on("all",
36
- // eslint-disable-next-line max-statements
37
- (eventName, pathNameOriginal) => {
34
+ this.watcher = watch(`${this.basePath}/**/*.{js,mjs,ts,mts,cjs,cts}`, CHOKIDAR_OPTIONS).on("all", (eventName, pathNameOriginal) => {
38
35
  const pathName = pathNameOriginal.replaceAll("\\", "/");
39
36
  if (pathName.includes("$.context") && eventName === "add") {
40
37
  process.stdout.write(`\n\n!!! The file at ${pathName} needs a minor update.\n See https://github.com/pmcelhaney/counterfact/blob/main/docs/context-change.md\n\n\n`);
@@ -85,7 +82,6 @@ export class ModuleLoader extends EventTarget {
85
82
  });
86
83
  await Promise.all(imports);
87
84
  }
88
- // eslint-disable-next-line max-statements
89
85
  async loadEndpoint(pathName) {
90
86
  debug("importing module: %s", pathName);
91
87
  const directory = dirname(pathName.slice(this.basePath.length)).replaceAll("\\", "/");
@@ -97,7 +93,6 @@ export class ModuleLoader extends EventTarget {
97
93
  const doImport = (await determineModuleKind(pathName)) === "commonjs"
98
94
  ? uncachedRequire
99
95
  : uncachedImport;
100
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
101
96
  const endpoint = (await doImport(pathName));
102
97
  this.dispatchEvent(new Event("add"));
103
98
  if (basename(pathName).startsWith("_.context")) {
@@ -105,14 +100,12 @@ export class ModuleLoader extends EventTarget {
105
100
  const loadContext = (path) => this.contextRegistry.find(path);
106
101
  this.contextRegistry.update(directory,
107
102
  // @ts-expect-error TS says Context has no constructable signatures but that's not true?
108
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
109
103
  new endpoint.Context({
110
104
  loadContext,
111
105
  }));
112
106
  }
113
107
  }
114
108
  else {
115
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
116
109
  this.registry.add(url, endpoint);
117
110
  }
118
111
  }
@@ -26,7 +26,6 @@ export class ModuleTree {
26
26
  };
27
27
  return;
28
28
  }
29
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
30
29
  directory.directories[segment.toLowerCase()] ??= {
31
30
  directories: {},
32
31
  files: {},
@@ -48,7 +47,6 @@ export class ModuleTree {
48
47
  return;
49
48
  }
50
49
  if (remainingSegments.length === 0) {
51
- // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
52
50
  delete directory.files[segment.toLowerCase()];
53
51
  return;
54
52
  }
@@ -58,7 +56,6 @@ export class ModuleTree {
58
56
  const segments = url.split("/").slice(1);
59
57
  this.removeModuleFromDirectory(this.root, segments);
60
58
  }
61
- // eslint-disable-next-line max-params
62
59
  buildMatch(directory, segment, pathVariables, matchedPath) {
63
60
  const match = directory.files[segment.toLowerCase()] ??
64
61
  Object.values(directory.files).find((file) => file.isWildcard);
@@ -81,7 +78,6 @@ export class ModuleTree {
81
78
  pathVariables,
82
79
  };
83
80
  }
84
- // eslint-disable-next-line max-statements, max-params
85
81
  matchWithinDirectory(directory, segments, pathVariables, matchedPath) {
86
82
  if (segments.length === 0) {
87
83
  return undefined;
@@ -124,12 +120,10 @@ export class ModuleTree {
124
120
  });
125
121
  });
126
122
  }
127
- // eslint-disable-next-line unicorn/consistent-function-scoping
128
123
  function stripBrackets(string) {
129
124
  return string.replaceAll(/\{|\}/gu, "");
130
125
  }
131
126
  traverse(this.root, "");
132
- // eslint-disable-next-line etc/no-assign-mutated-array
133
127
  return routes.sort((first, second) => stripBrackets(first.path).localeCompare(stripBrackets(second.path)));
134
128
  }
135
129
  }
@@ -3,7 +3,6 @@ import { readFile } from "../util/read-file.js";
3
3
  export function openapiMiddleware(openApiPath, url) {
4
4
  return async (ctx, next) => {
5
5
  if (ctx.URL.pathname === "/counterfact/openapi") {
6
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
7
6
  const openApiDocument = (await yaml.load(await readFile(openApiPath)));
8
7
  openApiDocument.servers ??= [];
9
8
  openApiDocument.servers.unshift({
@@ -12,11 +11,9 @@ export function openapiMiddleware(openApiPath, url) {
12
11
  });
13
12
  // OpenApi 2 support:
14
13
  openApiDocument.host = url;
15
- // eslint-disable-next-line require-atomic-updates
16
14
  ctx.body = yaml.dump(openApiDocument);
17
15
  return;
18
16
  }
19
- // eslint-disable-next-line n/callback-return
20
17
  await next();
21
18
  };
22
19
  }
@@ -3,7 +3,6 @@ import { fileURLToPath } from "node:url";
3
3
  import createDebug from "debug";
4
4
  import Handlebars from "handlebars";
5
5
  import { readFile } from "../util/read-file.js";
6
- // eslint-disable-next-line no-underscore-dangle
7
6
  const __dirname = nodePath.dirname(fileURLToPath(import.meta.url));
8
7
  const debug = createDebug("counterfact:server:page-middleware");
9
8
  Handlebars.registerHelper("escape_route", (route) => route.replaceAll(/[^\w/]/gu, "-"));
@@ -19,7 +18,6 @@ export function pageMiddleware(pathname, templateName, locals) {
19
18
  ctx.body = render(locals);
20
19
  return;
21
20
  }
22
- // eslint-disable-next-line n/callback-return
23
21
  await next();
24
22
  };
25
23
  }
@@ -6,8 +6,7 @@ function castParameters(parameters, parameterTypes) {
6
6
  Object.entries(copy).forEach(([key, value]) => {
7
7
  copy[key] =
8
8
  parameterTypes?.[key] === "number"
9
- ? // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
10
- Number.parseInt(value, 10)
9
+ ? Number.parseInt(value, 10)
11
10
  : value;
12
11
  });
13
12
  return copy;
@@ -54,7 +53,6 @@ export class Registry {
54
53
  path: castParameters(handler.path, parameterTypes.path),
55
54
  query: castParameters(requestData.query, parameterTypes.query),
56
55
  };
57
- // eslint-disable-next-line id-length
58
56
  operationArgument.x = operationArgument;
59
57
  return await execute(operationArgument);
60
58
  };
@@ -29,9 +29,7 @@ function unknownStatusCodeResponse(statusCode) {
29
29
  };
30
30
  }
31
31
  export function createResponseBuilder(operation) {
32
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
33
32
  return new Proxy({}, {
34
- // eslint-disable-next-line sonarjs/cognitive-complexity
35
33
  get: (target, statusCode) => ({
36
34
  header(name, value) {
37
35
  return {
@@ -78,9 +76,7 @@ export function createResponseBuilder(operation) {
78
76
  ...this,
79
77
  content: Object.keys(content).map((type) => ({
80
78
  body: convertToXmlIfNecessary(type, content[type]?.examples
81
- ? oneOf(
82
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
83
- Object.values(content[type]?.examples ?? []).map((example) => example.value))
79
+ ? oneOf(Object.values(content[type]?.examples ?? []).map((example) => example.value))
84
80
  : JSONSchemaFaker.generate(content[type]?.schema ?? { type: "object" }), content[type]?.schema),
85
81
  type,
86
82
  })),
@@ -6,7 +6,6 @@ export class Tools {
6
6
  this.headers = headers;
7
7
  }
8
8
  oneOf(array) {
9
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
10
9
  return array[Math.floor(Math.random() * array.length)];
11
10
  }
12
11
  accepts(contentType) {
@@ -31,9 +31,7 @@ export class Transpiler extends EventTarget {
31
31
  ignoreInitial: false,
32
32
  });
33
33
  const transpiles = [];
34
- this.watcher.on("all",
35
- // eslint-disable-next-line @typescript-eslint/no-misused-promises, max-statements
36
- async (eventName, sourcePathOriginal) => {
34
+ this.watcher.on("all", async (eventName, sourcePathOriginal) => {
37
35
  debug("transpiler event: %s <%s>", eventName, sourcePathOriginal);
38
36
  const sourcePath = sourcePathOriginal.replaceAll("\\", "/");
39
37
  const destinationPath = sourcePath
@@ -48,7 +46,6 @@ export class Transpiler extends EventTarget {
48
46
  await fs.rm(destinationPath);
49
47
  }
50
48
  catch (error) {
51
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
52
49
  if (error.code !== "ENOENT") {
53
50
  debug("error removing %s: %o", destinationPath, error);
54
51
  this.dispatchEvent(new Event("error"));
@@ -64,18 +61,15 @@ export class Transpiler extends EventTarget {
64
61
  async stopWatching() {
65
62
  await this.watcher?.close();
66
63
  }
67
- // eslint-disable-next-line max-statements
68
64
  async transpileFile(eventName, sourcePath, destinationPath) {
69
65
  ensureDirectoryExists(destinationPath);
70
66
  const source = await fs.readFile(sourcePath, "utf8");
71
- /* eslint-disable import/no-named-as-default-member */
72
67
  const result = ts.transpileModule(source, {
73
68
  compilerOptions: {
74
69
  module: ts.ModuleKind[this.moduleKind.toLowerCase() === "module" ? "ES2022" : "CommonJS"],
75
70
  target: ts.ScriptTarget.ES2015,
76
71
  },
77
72
  }).outputText;
78
- /* eslint-enable import/no-named-as-default-member */
79
73
  const fullDestination = nodePath
80
74
  .join(sourcePath
81
75
  .replace(this.sourcePath, this.destinationPath)
@@ -1,5 +1,3 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- /* eslint-disable etc/no-t */
3
1
  interface OpenApiHeader {
4
2
  schema: unknown;
5
3
  }
@@ -52,8 +50,8 @@ type IfHasKey<SomeObject, Keys extends (keyof any)[], Yes, No> = Keys extends [
52
50
  ? FirstKey extends keyof SomeObject
53
51
  ? Yes
54
52
  : RestKeys extends (keyof any)[]
55
- ? IfHasKey<SomeObject, RestKeys, Yes, No>
56
- : No
53
+ ? IfHasKey<SomeObject, RestKeys, Yes, No>
54
+ : No
57
55
  : No;
58
56
 
59
57
  type SchemasOf<T extends { [key: string]: { schema: any } }> = {
@@ -74,7 +72,6 @@ type MaybeShortcut<
74
72
  never
75
73
  >;
76
74
 
77
- // eslint-disable-next-line @typescript-eslint/ban-types
78
75
  type NeverIfEmpty<Record> = {} extends Record ? never : Record;
79
76
 
80
77
  type MatchFunction<Response extends OpenApiResponse> = <
@@ -100,7 +97,6 @@ type HeaderFunction<Response extends OpenApiResponse> = <
100
97
  }>;
101
98
 
102
99
  type RandomFunction<Response extends OpenApiResponse> = <
103
- // eslint-disable-next-line etc/no-misused-generics, unused-imports/no-unused-vars
104
100
  Header extends string & keyof Response["headers"],
105
101
  >() => COUNTERFACT_RESPONSE;
106
102
 
@@ -148,15 +144,15 @@ type GenericResponseBuilderInner<
148
144
 
149
145
  type GenericResponseBuilder<
150
146
  Response extends OpenApiResponse = OpenApiResponse,
151
- // eslint-disable-next-line @typescript-eslint/ban-types
152
- > = {} extends OmitValueWhenNever<Response>
153
- ? COUNTERFACT_RESPONSE
154
- : keyof OmitValueWhenNever<Response> extends "headers"
155
- ? {
156
- ALL_REMAINING_HEADERS_ARE_OPTIONAL: COUNTERFACT_RESPONSE;
157
- header: HeaderFunction<Response>;
158
- }
159
- : GenericResponseBuilderInner<Response>;
147
+ > =
148
+ object extends OmitValueWhenNever<Response>
149
+ ? COUNTERFACT_RESPONSE
150
+ : keyof OmitValueWhenNever<Response> extends "headers"
151
+ ? {
152
+ ALL_REMAINING_HEADERS_ARE_OPTIONAL: COUNTERFACT_RESPONSE;
153
+ header: HeaderFunction<Response>;
154
+ }
155
+ : GenericResponseBuilderInner<Response>;
160
156
 
161
157
  type ResponseBuilderFactory<
162
158
  Responses extends OpenApiResponses = OpenApiResponses,
@@ -1,6 +1,5 @@
1
1
  import { pathToFileURL } from "node:url";
2
2
  export async function uncachedImport(pathName) {
3
3
  const fileUrl = `${pathToFileURL(pathName).toString()}?cacheBust=${Date.now()}`;
4
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return, import/no-dynamic-require, no-unsanitized/method
5
4
  return await import(fileUrl);
6
5
  }
@@ -4,7 +4,7 @@ module.exports = {
4
4
  uncachedRequire: function uncachedRequire(moduleName) {
5
5
  delete require.cache[require.resolve(moduleName)];
6
6
 
7
- // eslint-disable-next-line n/global-require, security/detect-non-literal-require, import/no-dynamic-require
7
+ // eslint-disable-next-line security/detect-non-literal-require
8
8
  return Promise.resolve(require(moduleName));
9
9
  },
10
10
  };
@@ -21,9 +21,7 @@ export class CodeGenerator extends EventTarget {
21
21
  return;
22
22
  }
23
23
  this.watcher = watch(this.openapiPath, CHOKIDAR_OPTIONS).on("change", () => {
24
- void generate(this.openapiPath, this.destination, this.generateOptions)
25
- // eslint-disable-next-line promise/prefer-await-to-then
26
- .then(() => {
24
+ void generate(this.openapiPath, this.destination, this.generateOptions).then(() => {
27
25
  this.dispatchEvent(new Event("generate"));
28
26
  return true;
29
27
  }, () => {
@@ -1,3 +1,4 @@
1
+ /* eslint-disable n/no-sync */
1
2
  import { existsSync } from "node:fs";
2
3
  import fs from "node:fs/promises";
3
4
  import nodePath from "node:path";
@@ -29,7 +30,6 @@ async function getPathsFromSpecification(specification) {
29
30
  return new Set();
30
31
  }
31
32
  }
32
- // eslint-disable-next-line max-statements, max-params
33
33
  export async function generate(source, destination, generateOptions, repository = new Repository()) {
34
34
  debug("generating code from %s to %s", source, destination);
35
35
  debug("initializing the .cache directory");
@@ -1,4 +1,3 @@
1
- /* eslint-disable no-magic-numbers */
2
1
  import nodePath from "node:path";
3
2
  import { Coder } from "./coder.js";
4
3
  import { OperationTypeCoder } from "./operation-type-coder.js";
@@ -63,9 +63,7 @@ export class OperationTypeCoder extends TypeCoder {
63
63
  }
64
64
  return "never";
65
65
  }
66
- // eslint-disable-next-line max-statements
67
66
  writeCode(script) {
68
- // eslint-disable-next-line no-param-reassign
69
67
  script.comments = READ_ONLY_COMMENTS;
70
68
  const xType = script.importSharedType("WideOperationArgument");
71
69
  script.importSharedType("OmitValueWhenNever");
@@ -75,10 +73,11 @@ export class OperationTypeCoder extends TypeCoder {
75
73
  const queryType = new ParametersTypeCoder(parameters, "query").write(script);
76
74
  const pathType = new ParametersTypeCoder(parameters, "path").write(script);
77
75
  const headerType = new ParametersTypeCoder(parameters, "header").write(script);
78
- const bodyRequirement = this.requirement.get("consumes")
76
+ const bodyRequirement = this.requirement.get("consumes") ||
77
+ this.requirement.specification?.rootRequirement?.get("consumes")
79
78
  ? parameters
80
- .find((parameter) => ["body", "formData"].includes(parameter.get("in").data))
81
- .get("schema")
79
+ ?.find((parameter) => ["body", "formData"].includes(parameter.get("in").data))
80
+ ?.get("schema")
82
81
  : this.requirement.select("requestBody/content/application~1json/schema");
83
82
  const bodyType = bodyRequirement === undefined
84
83
  ? "never"
@@ -1,4 +1,4 @@
1
- /* eslint-disable max-statements */
1
+ /* eslint-disable n/no-sync */
2
2
  import { existsSync } from "node:fs";
3
3
  import fs from "node:fs/promises";
4
4
  import nodePath, { dirname } from "node:path";
@@ -8,7 +8,6 @@ import { ensureDirectoryExists } from "../util/ensure-directory-exists.js";
8
8
  import { CONTEXT_FILE_TOKEN } from "./context-file-token.js";
9
9
  import { Script } from "./script.js";
10
10
  const debug = createDebug("counterfact:server:repository");
11
- // eslint-disable-next-line no-underscore-dangle
12
11
  const __dirname = dirname(fileURLToPath(import.meta.url)).replaceAll("\\", "/");
13
12
  debug("dirname is %s", __dirname);
14
13
  export class Repository {
@@ -29,7 +28,6 @@ export class Repository {
29
28
  async finished() {
30
29
  while (Array.from(this.scripts.values()).some((script) => script.isInProgress())) {
31
30
  debug("waiting for %i scripts to finish", this.scripts.size);
32
- // eslint-disable-next-line no-await-in-loop
33
31
  await Promise.all(Array.from(this.scripts.values(), (script) => script.finished()));
34
32
  }
35
33
  }
@@ -37,19 +37,15 @@ export class Requirement {
37
37
  }
38
38
  map(callback) {
39
39
  const result = [];
40
- // eslint-disable-next-line array-callback-return
41
40
  this.forEach((value, key) => result.push(callback(value, key)));
42
41
  return result;
43
42
  }
44
43
  flatMap(callback) {
45
- // eslint-disable-next-line unicorn/prefer-array-flat-map
46
44
  return this.map(callback).flat();
47
45
  }
48
46
  find(callback) {
49
- // eslint-disable-next-line init-declarations
50
47
  let result;
51
48
  this.forEach((value, key) => {
52
- // eslint-disable-next-line n/callback-return
53
49
  if (result === undefined && callback(value, key)) {
54
50
  result = value;
55
51
  }
@@ -39,7 +39,6 @@ export class SchemaTypeCoder extends TypeCoder {
39
39
  }
40
40
  return value;
41
41
  }
42
- // eslint-disable-next-line max-statements
43
42
  writeType(script, type) {
44
43
  if (Array.isArray(type)) {
45
44
  return type.map((item) => this.writeType(script, item)).join(" | ");
@@ -45,18 +45,15 @@ export class Script {
45
45
  };
46
46
  exportStatement.promise = coder
47
47
  .delegate()
48
- // eslint-disable-next-line promise/prefer-await-to-then
49
48
  .then((availableCoder) => {
50
49
  exportStatement.name = name;
51
50
  exportStatement.code = availableCoder.write(this);
52
51
  return availableCoder;
53
52
  })
54
- // eslint-disable-next-line promise/prefer-await-to-then
55
53
  .catch((error) => {
56
54
  exportStatement.code = `{/* error creating export "${name}" for ${this.path}: ${error.stack} */}`;
57
55
  exportStatement.error = error;
58
56
  })
59
- // eslint-disable-next-line promise/prefer-await-to-then
60
57
  .finally(() => {
61
58
  exportStatement.done = true;
62
59
  });
@@ -66,7 +63,6 @@ export class Script {
66
63
  exportDefault(coder, isType = false) {
67
64
  this.export(coder, isType, true);
68
65
  }
69
- // eslint-disable-next-line max-statements
70
66
  import(coder, isType = false, isDefault = false) {
71
67
  debug("import coder: %s", coder.id);
72
68
  const modulePath = coder.modulePath();
@@ -1,8 +1,6 @@
1
- /* eslint-disable n/no-missing-import */
2
1
  import nodePath from "node:path";
3
2
  import createDebug from "debug";
4
3
  import yaml from "js-yaml";
5
- // eslint-disable-next-line import/no-unresolved
6
4
  import { readFile } from "../util/read-file.js";
7
5
  import { Requirement } from "./requirement.js";
8
6
  const debug = createDebug("counterfact:typescript-generator:specification");
@@ -25,7 +23,7 @@ export class Specification {
25
23
  const filePath = nodePath
26
24
  .join(fromUrl.split("#").at(0), file)
27
25
  .replaceAll("\\", "/")
28
- // eslint-disable-next-line prefer-named-capture-group, regexp/prefer-named-capture-group
26
+ // eslint-disable-next-line regexp/prefer-named-capture-group
29
27
  .replace(/:\/([^/])/u, "://$1");
30
28
  const fileUrl = filePath === "." ? this.rootUrl : filePath;
31
29
  debug("reading specification at %s", fileUrl);
@@ -34,7 +32,6 @@ export class Specification {
34
32
  const rootRequirement = new Requirement(data, `${fileUrl}#`, this);
35
33
  return rootRequirement.select(path.slice(1));
36
34
  }
37
- // eslint-disable-next-line max-statements
38
35
  async loadFile(urlOrPath) {
39
36
  debug("loading file %s", urlOrPath);
40
37
  if (this.cache.has(urlOrPath)) {
@@ -6,7 +6,6 @@ export async function readFile(urlOrPath) {
6
6
  return await response.text();
7
7
  }
8
8
  if (urlOrPath.startsWith("file")) {
9
- // eslint-disable-next-line total-functions/no-partial-url-constructor
10
9
  return await fs.readFile(new URL(urlOrPath), "utf8");
11
10
  }
12
11
  return await fs.readFile(urlOrPath, "utf8");
@@ -6,7 +6,6 @@ import { EventEmitter } from "node:events";
6
6
  * @returns {Promise<Event>} A promise that resolves with the event object when the event is fired.
7
7
  */
8
8
  export async function waitForEvent(target, eventName) {
9
- // eslint-disable-next-line promise/avoid-new
10
9
  return await new Promise((resolve) => {
11
10
  const handler = (event) => {
12
11
  if (target instanceof EventTarget) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "counterfact",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
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,49 +44,50 @@
44
44
  "postinstall": "patch-package"
45
45
  },
46
46
  "devDependencies": {
47
- "@changesets/cli": "2.27.7",
48
- "@stryker-mutator/core": "8.2.6",
49
- "@stryker-mutator/jest-runner": "8.2.6",
50
- "@stryker-mutator/typescript-checker": "8.2.6",
51
- "@swc/core": "1.7.3",
47
+ "@changesets/cli": "2.27.8",
48
+ "@stryker-mutator/core": "8.5.0",
49
+ "@stryker-mutator/jest-runner": "8.5.0",
50
+ "@stryker-mutator/typescript-checker": "8.5.0",
51
+ "@swc/core": "1.7.26",
52
52
  "@swc/jest": "0.2.36",
53
53
  "@testing-library/dom": "10.4.0",
54
- "@types/jest": "29.5.12",
54
+ "@types/jest": "29.5.13",
55
55
  "@types/js-yaml": "4.0.9",
56
56
  "@types/koa": "2.15.0",
57
57
  "@types/koa-bodyparser": "4.3.12",
58
58
  "@types/koa-proxy": "1.0.7",
59
59
  "@types/koa-static": "4.0.4",
60
- "@types/lodash": "4.17.7",
60
+ "@types/lodash": "4.17.9",
61
61
  "copyfiles": "2.4.1",
62
- "eslint": "8.57.0",
63
- "eslint-config-hardcore": "41.3.0",
62
+ "eslint": "9.11.1",
63
+ "eslint-config-hardcore": "47.0.1",
64
64
  "eslint-formatter-github-annotations": "0.1.0",
65
- "eslint-import-resolver-typescript": "3.6.1",
65
+ "eslint-import-resolver-typescript": "3.6.3",
66
66
  "eslint-plugin-etc": "2.0.3",
67
- "eslint-plugin-file-progress": "1.4.0",
68
- "eslint-plugin-import": "2.29.1",
69
- "eslint-plugin-jest": "28.6.0",
67
+ "eslint-plugin-file-progress": "1.5.0",
68
+ "eslint-plugin-import": "2.30.0",
69
+ "eslint-plugin-jest": "28.8.3",
70
70
  "eslint-plugin-jest-dom": "5.4.0",
71
71
  "eslint-plugin-no-explicit-type-exports": "0.12.1",
72
- "eslint-plugin-unused-imports": "4.0.1",
73
- "husky": "9.1.4",
72
+ "eslint-plugin-prettier": "5.2.1",
73
+ "eslint-plugin-unused-imports": "4.1.4",
74
+ "husky": "9.1.6",
74
75
  "jest": "29.7.0",
75
- "node-mocks-http": "1.15.1",
76
- "nodemon": "3.1.4",
76
+ "node-mocks-http": "1.16.0",
77
+ "nodemon": "3.1.7",
77
78
  "rimraf": "6.0.1",
78
79
  "stryker-cli": "1.0.2",
79
80
  "supertest": "7.0.0",
80
81
  "using-temporary-files": "2.2.1"
81
82
  },
82
83
  "dependencies": {
83
- "@apidevtools/json-schema-ref-parser": "11.6.4",
84
+ "@apidevtools/json-schema-ref-parser": "11.7.0",
84
85
  "@hapi/accept": "6.0.3",
85
86
  "@types/json-schema": "7.0.15",
86
87
  "ast-types": "0.14.2",
87
88
  "chokidar": "3.6.0",
88
89
  "commander": "12.1.0",
89
- "debug": "4.3.6",
90
+ "debug": "4.3.7",
90
91
  "fetch": "1.1.0",
91
92
  "fs-extra": "11.2.0",
92
93
  "handlebars": "4.7.8",
@@ -105,6 +106,6 @@
105
106
  "precinct": "12.1.2",
106
107
  "prettier": "3.3.3",
107
108
  "recast": "0.23.9",
108
- "typescript": "5.5.4"
109
+ "typescript": "5.6.2"
109
110
  }
110
111
  }