fexapi 0.1.1 → 0.1.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.
package/README.md CHANGED
@@ -22,10 +22,18 @@ fexapi [options]
22
22
  pnpm dlx fexapi init
23
23
  ```
24
24
 
25
+ `fexapi init` runs an interactive setup wizard and asks:
26
+
27
+ - What port? (default: 3000)
28
+ - Enable CORS? (Y/n)
29
+ - Generate sample schemas? (Y/n)
30
+
25
31
  Creates:
26
32
 
27
33
  - `fexapi/schema.fexapi`
28
34
  - `fexapi.config.json`
35
+ - `fexapi.config.js`
36
+ - `schemas/user.yaml` and `schemas/post.yaml` (if sample schemas are enabled)
29
37
 
30
38
  ### 2) Edit schema file
31
39
 
@@ -40,7 +48,7 @@ GET /users: id:uuid,name:name,email:email,age:number,phone:phone,pic:url,courseN
40
48
  GET /courses: id:uuid,courseName:string,mentor:name
41
49
  ```
42
50
 
43
- ### 3) Generate artifacts (like migrations)
51
+ ### 3) Generate artifacts (updates migration)
44
52
 
45
53
  ```bash
46
54
  npx fexapi generate
@@ -49,7 +57,7 @@ npx fexapi generate
49
57
  Generates:
50
58
 
51
59
  - `fexapi/generated.api.json`
52
- - `fexapi/migrations/*.json`
60
+ - `fexapi/migrations/schema.json`
53
61
 
54
62
  ### 4) Start server
55
63
 
@@ -57,12 +65,31 @@ Generates:
57
65
  npx fexapi run
58
66
  # or
59
67
  npx fexapi serve
68
+ # request/response logging
69
+ npx fexapi serve --log
70
+ # or dev watch mode (nodemon-like)
71
+ npx fexapi dev --watch
72
+ # watch mode + request logs
73
+ npx fexapi dev --watch --log
60
74
  # or (inside local workspace package)
61
75
  npm run serve
62
76
  ```
63
77
 
64
78
  Server port is read from `schema.fexapi` unless overridden by CLI `--port`.
65
79
 
80
+ `fexapi dev --watch` auto-reloads when these files change:
81
+
82
+ - `fexapi/schema.fexapi`
83
+ - `fexapi/generated.api.json`
84
+ - `fexapi.config.js`
85
+ - `fexapi.config.json`
86
+ - `schemas/*.yaml`
87
+
88
+ `fexapi serve --log` prints request logs like:
89
+
90
+ - `[GET] /users/1 → 200 (45ms)`
91
+ - `[POST] /posts → 201 (12ms)`
92
+
66
93
  ## Configuration File Support
67
94
 
68
95
  Create a `fexapi.config.js` in your project root:
@@ -9,6 +9,15 @@ export declare const parseGenerateOptions: (generateArgs: string[]) => {
9
9
  export declare const parseServeOptions: (serveArgs: string[]) => {
10
10
  host: string;
11
11
  port?: number;
12
+ logEnabled: boolean;
13
+ } | {
14
+ error: string;
15
+ };
16
+ export declare const parseDevOptions: (devArgs: string[]) => {
17
+ host: string;
18
+ port?: number;
19
+ watchEnabled: boolean;
20
+ logEnabled: boolean;
12
21
  } | {
13
22
  error: string;
14
23
  };
@@ -1 +1 @@
1
- {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,GAC3B,UAAU,MAAM,EAAE,KACjB;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAWtC,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,cAAc,MAAM,EAAE,KACrB;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAQlB,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,WAAW,MAAM,EAAE,KAClB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CA+CnD,CAAC"}
1
+ {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,GAC3B,UAAU,MAAM,EAAE,KACjB;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAWtC,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,cAAc,MAAM,EAAE,KACrB;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAQlB,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,WAAW,MAAM,EAAE,KAClB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAkDxE,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,SAAS,MAAM,EAAE,KAEf;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,GAC3E;IAAE,KAAK,EAAE,MAAM,CAAA;CAwDlB,CAAC"}
package/dist/cli/args.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseServeOptions = exports.parseGenerateOptions = exports.parseInitOptions = void 0;
3
+ exports.parseDevOptions = exports.parseServeOptions = exports.parseGenerateOptions = exports.parseInitOptions = void 0;
4
4
  const parseInitOptions = (initArgs) => {
5
5
  const validFlags = new Set(["--force"]);
6
6
  const invalidFlags = initArgs.filter((value) => value.startsWith("-") && !validFlags.has(value));
@@ -30,7 +30,10 @@ const parseServeOptions = (serveArgs) => {
30
30
  }
31
31
  return value;
32
32
  };
33
- const unknownFlags = serveArgs.filter((value) => value.startsWith("-") && value !== "--host" && value !== "--port");
33
+ const unknownFlags = serveArgs.filter((value) => value.startsWith("-") &&
34
+ value !== "--host" &&
35
+ value !== "--port" &&
36
+ value !== "--log");
34
37
  if (unknownFlags.length > 0) {
35
38
  return { error: `Unknown option(s): ${unknownFlags.join(", ")}` };
36
39
  }
@@ -48,6 +51,48 @@ const parseServeOptions = (serveArgs) => {
48
51
  (!Number.isInteger(port) || port < 1 || port > 65535)) {
49
52
  return { error: `Invalid port: ${portValue ?? ""}`.trim() };
50
53
  }
51
- return { host, port };
54
+ return { host, port, logEnabled: serveArgs.includes("--log") };
52
55
  };
53
56
  exports.parseServeOptions = parseServeOptions;
57
+ const parseDevOptions = (devArgs) => {
58
+ const getFlagValue = (flagName) => {
59
+ const index = devArgs.indexOf(flagName);
60
+ if (index === -1) {
61
+ return undefined;
62
+ }
63
+ const value = devArgs[index + 1];
64
+ if (!value || value.startsWith("-")) {
65
+ return { error: `Missing value for ${flagName}` };
66
+ }
67
+ return value;
68
+ };
69
+ const unknownFlags = devArgs.filter((value) => value.startsWith("-") &&
70
+ value !== "--host" &&
71
+ value !== "--port" &&
72
+ value !== "--watch" &&
73
+ value !== "--log");
74
+ if (unknownFlags.length > 0) {
75
+ return { error: `Unknown option(s): ${unknownFlags.join(", ")}` };
76
+ }
77
+ const hostValue = getFlagValue("--host");
78
+ if (hostValue && typeof hostValue !== "string") {
79
+ return hostValue;
80
+ }
81
+ const portValue = getFlagValue("--port");
82
+ if (portValue && typeof portValue !== "string") {
83
+ return portValue;
84
+ }
85
+ const host = hostValue ?? "127.0.0.1";
86
+ const port = portValue ? Number(portValue) : undefined;
87
+ if (port !== undefined &&
88
+ (!Number.isInteger(port) || port < 1 || port > 65535)) {
89
+ return { error: `Invalid port: ${portValue ?? ""}`.trim() };
90
+ }
91
+ return {
92
+ host,
93
+ port,
94
+ watchEnabled: devArgs.includes("--watch"),
95
+ logEnabled: devArgs.includes("--log"),
96
+ };
97
+ };
98
+ exports.parseDevOptions = parseDevOptions;
@@ -1 +1 @@
1
- {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../src/cli/help.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS,YAmCrB,CAAC"}
1
+ {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../src/cli/help.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS,YAgDrB,CAAC"}
package/dist/cli/help.js CHANGED
@@ -7,15 +7,19 @@ const printHelp = () => {
7
7
  console.log("Usage:");
8
8
  console.log(" fexapi init [--force]");
9
9
  console.log(" fexapi generate");
10
- console.log(" fexapi serve [--host <host>] [--port <number>]");
11
- console.log(" fexapi run [--host <host>] [--port <number>]");
12
- console.log(" fexapi [--host <host>] [--port <number>]");
10
+ console.log(" fexapi dev [--watch] [--host <host>] [--port <number>] [--log]");
11
+ console.log(" fexapi serve [--host <host>] [--port <number>] [--log]");
12
+ console.log(" fexapi run [--host <host>] [--port <number>] [--log]");
13
+ console.log(" fexapi [--host <host>] [--port <number>] [--log]");
13
14
  console.log(" fexapi --help");
14
15
  console.log("");
15
16
  console.log("Examples:");
16
17
  console.log(" fexapi init");
17
18
  console.log(" fexapi init --force");
18
19
  console.log(" fexapi generate");
20
+ console.log(" fexapi dev --watch");
21
+ console.log(" fexapi dev --watch --log");
22
+ console.log(" fexapi serve --log");
19
23
  console.log(" fexapi serve --host 127.0.0.1 --port 5000");
20
24
  console.log(" fexapi --port 4000");
21
25
  console.log("");
@@ -26,7 +30,14 @@ const printHelp = () => {
26
30
  console.log("");
27
31
  console.log("`fexapi init` creates:");
28
32
  console.log(" fexapi.config.json");
33
+ console.log(" fexapi.config.js");
29
34
  console.log(" fexapi/schema.fexapi");
35
+ console.log(" schemas/*.yaml (optional, via wizard)");
36
+ console.log("");
37
+ console.log("Init wizard asks:");
38
+ console.log(" What port? (default: 3000)");
39
+ console.log(" Enable CORS? (Y/n)");
40
+ console.log(" Generate sample schemas? (Y/n)");
30
41
  console.log("");
31
42
  console.log("Then run:");
32
43
  console.log(" # edit fexapi/schema.fexapi");
@@ -35,6 +46,6 @@ const printHelp = () => {
35
46
  console.log("");
36
47
  console.log("Generate output:");
37
48
  console.log(" fexapi/generated.api.json");
38
- console.log(" fexapi/migrations/*.json");
49
+ console.log(" fexapi/migrations/schema.json");
39
50
  };
40
51
  exports.printHelp = printHelp;
@@ -0,0 +1,7 @@
1
+ export declare const runDevCommand: ({ host, port, watchEnabled, logEnabled, }: {
2
+ host: string;
3
+ port?: number;
4
+ watchEnabled: boolean;
5
+ logEnabled: boolean;
6
+ }) => number;
7
+ //# sourceMappingURL=dev.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAgCA,eAAO,MAAM,aAAa,GAAI,2CAK3B;IACD,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;CACrB,KAAG,MAuHH,CAAC"}
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runDevCommand = void 0;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ const paths_1 = require("../project/paths");
7
+ const serve_1 = require("./serve");
8
+ const WATCH_DEBOUNCE_MS = 150;
9
+ const normalizePath = (pathValue) => {
10
+ return pathValue.replace(/\\/g, "/").toLowerCase();
11
+ };
12
+ const isWatchedPath = (projectRoot, changedPath) => {
13
+ const relativePath = normalizePath((0, node_path_1.relative)(projectRoot, changedPath));
14
+ if (relativePath === "fexapi.config.js" ||
15
+ relativePath === "fexapi.config.json") {
16
+ return true;
17
+ }
18
+ if (relativePath.startsWith("fexapi/")) {
19
+ return true;
20
+ }
21
+ return (relativePath.startsWith("schemas/") &&
22
+ (relativePath.endsWith(".yaml") || relativePath.endsWith(".yml")));
23
+ };
24
+ const runDevCommand = ({ host, port, watchEnabled, logEnabled, }) => {
25
+ if (!watchEnabled) {
26
+ return (0, serve_1.serveProject)({ host, port, logEnabled });
27
+ }
28
+ const projectRoot = (0, paths_1.resolveProjectRoot)();
29
+ if (!projectRoot) {
30
+ console.error("Could not find package.json in this directory or parent directories.");
31
+ return 1;
32
+ }
33
+ let currentServer = (0, serve_1.createProjectServer)({ host, port, logEnabled });
34
+ if (!currentServer) {
35
+ return 1;
36
+ }
37
+ console.log("Watch mode enabled. Restarting on config/schema changes...");
38
+ let restartTimer;
39
+ let restartQueued = false;
40
+ let restartInProgress = false;
41
+ const restartServer = async (reason) => {
42
+ if (!currentServer) {
43
+ return;
44
+ }
45
+ if (restartInProgress) {
46
+ restartQueued = true;
47
+ return;
48
+ }
49
+ restartInProgress = true;
50
+ console.log(`\n[watch] change detected (${reason})`);
51
+ await new Promise((resolve) => {
52
+ currentServer?.close(() => {
53
+ resolve();
54
+ });
55
+ });
56
+ currentServer = (0, serve_1.createProjectServer)({ host, port, logEnabled });
57
+ restartInProgress = false;
58
+ if (restartQueued) {
59
+ restartQueued = false;
60
+ await restartServer("queued changes");
61
+ }
62
+ };
63
+ const scheduleRestart = (reason) => {
64
+ if (restartTimer) {
65
+ clearTimeout(restartTimer);
66
+ }
67
+ restartTimer = setTimeout(() => {
68
+ void restartServer(reason);
69
+ }, WATCH_DEBOUNCE_MS);
70
+ };
71
+ const activeWatchers = [];
72
+ const watchTargets = [
73
+ (0, node_path_1.join)(projectRoot, "fexapi"),
74
+ (0, node_path_1.join)(projectRoot, "schemas"),
75
+ projectRoot,
76
+ ];
77
+ for (const watchTarget of watchTargets) {
78
+ if (!(0, node_fs_1.existsSync)(watchTarget)) {
79
+ continue;
80
+ }
81
+ const watcher = (0, node_fs_1.watch)(watchTarget, { recursive: true }, (_event, file) => {
82
+ if (!file) {
83
+ scheduleRestart("unknown file");
84
+ return;
85
+ }
86
+ const changedPath = (0, node_path_1.join)(watchTarget, file.toString());
87
+ if (isWatchedPath(projectRoot, changedPath)) {
88
+ scheduleRestart((0, node_path_1.relative)(projectRoot, changedPath));
89
+ }
90
+ });
91
+ activeWatchers.push(watcher);
92
+ }
93
+ const cleanupAndExit = async () => {
94
+ if (restartTimer) {
95
+ clearTimeout(restartTimer);
96
+ restartTimer = undefined;
97
+ }
98
+ for (const watcher of activeWatchers) {
99
+ watcher.close();
100
+ }
101
+ await new Promise((resolve) => {
102
+ currentServer?.close(() => resolve());
103
+ });
104
+ process.exit(0);
105
+ };
106
+ process.on("SIGINT", () => {
107
+ void cleanupAndExit();
108
+ });
109
+ process.on("SIGTERM", () => {
110
+ void cleanupAndExit();
111
+ });
112
+ return 0;
113
+ };
114
+ exports.runDevCommand = runDevCommand;
@@ -1 +1 @@
1
- {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,kBAAkB,QAAO,MAkGrC,CAAC"}
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,kBAAkB,QAAO,MAyGrC,CAAC"}
@@ -37,8 +37,16 @@ const generateFromSchema = () => {
37
37
  routes: parsed.schema.routes,
38
38
  };
39
39
  (0, node_fs_1.mkdirSync)(migrationsDirectoryPath, { recursive: true });
40
+ const existingMigrationFiles = (0, node_fs_1.readdirSync)(migrationsDirectoryPath, {
41
+ withFileTypes: true,
42
+ })
43
+ .filter((entry) => entry.isFile() && entry.name.endsWith(".json"))
44
+ .map((entry) => (0, node_path_1.join)(migrationsDirectoryPath, entry.name));
45
+ for (const migrationFilePath of existingMigrationFiles) {
46
+ (0, node_fs_1.unlinkSync)(migrationFilePath);
47
+ }
40
48
  const migrationId = new Date().toISOString().replace(/[.:]/g, "-");
41
- const migrationPath = (0, node_path_1.join)(migrationsDirectoryPath, `${migrationId}_schema.json`);
49
+ const migrationPath = (0, node_path_1.join)(migrationsDirectoryPath, "schema.json");
42
50
  const migration = {
43
51
  migrationId,
44
52
  sourceSchema: "fexapi/schema.fexapi",
@@ -65,7 +73,7 @@ const generateFromSchema = () => {
65
73
  };
66
74
  (0, node_fs_1.writeFileSync)(configPath, `${JSON.stringify(updatedConfig, null, 2)}\n`, "utf-8");
67
75
  console.log(`Generated API spec at ${generatedPath}`);
68
- console.log(`Migration created at ${migrationPath}`);
76
+ console.log(`Migration updated at ${migrationPath}`);
69
77
  console.log(`Routes generated: ${parsed.schema.routes.length}`);
70
78
  console.log(`Configured server port: ${parsed.schema.port}`);
71
79
  return 0;
@@ -1,4 +1,4 @@
1
- export declare const initializeProject: ({ force }: {
1
+ export declare const initializeProject: ({ force, }: {
2
2
  force: boolean;
3
- }) => number;
3
+ }) => Promise<number>;
4
4
  //# sourceMappingURL=init.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,iBAAiB,GAAI,WAAW;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,KAAG,MAwEjE,CAAC"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAwKA,eAAO,MAAM,iBAAiB,GAAU,YAErC;IACD,KAAK,EAAE,OAAO,CAAC;CAChB,KAAG,OAAO,CAAC,MAAM,CAmJjB,CAAC"}
@@ -3,10 +3,125 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.initializeProject = void 0;
4
4
  const node_fs_1 = require("node:fs");
5
5
  const node_path_1 = require("node:path");
6
+ const promises_1 = require("node:readline/promises");
7
+ const node_process_1 = require("node:process");
6
8
  const constants_1 = require("../constants");
7
9
  const detect_1 = require("../project/detect");
8
10
  const paths_1 = require("../project/paths");
9
- const initializeProject = ({ force }) => {
11
+ const DEFAULT_INIT_PORT = 3000;
12
+ const parseYesNo = (value, defaultValue) => {
13
+ const normalized = value.trim().toLowerCase();
14
+ if (!normalized) {
15
+ return defaultValue;
16
+ }
17
+ if (normalized === "y" || normalized === "yes") {
18
+ return true;
19
+ }
20
+ if (normalized === "n" || normalized === "no") {
21
+ return false;
22
+ }
23
+ return undefined;
24
+ };
25
+ const askInitWizardQuestions = async () => {
26
+ if (!node_process_1.stdin.isTTY || !node_process_1.stdout.isTTY) {
27
+ return {
28
+ port: DEFAULT_INIT_PORT,
29
+ cors: true,
30
+ generateSampleSchemas: true,
31
+ };
32
+ }
33
+ const questionInterface = (0, promises_1.createInterface)({ input: node_process_1.stdin, output: node_process_1.stdout });
34
+ try {
35
+ let port = DEFAULT_INIT_PORT;
36
+ while (true) {
37
+ const answer = await questionInterface.question(`What port? (default: ${DEFAULT_INIT_PORT}) `);
38
+ if (!answer.trim()) {
39
+ break;
40
+ }
41
+ const parsedPort = Number(answer.trim());
42
+ if (Number.isInteger(parsedPort) &&
43
+ parsedPort >= 1 &&
44
+ parsedPort <= 65535) {
45
+ port = parsedPort;
46
+ break;
47
+ }
48
+ console.log("Please enter a valid port (1-65535).\n");
49
+ }
50
+ let cors = true;
51
+ while (true) {
52
+ const answer = await questionInterface.question("Enable CORS? (Y/n) ");
53
+ const parsed = parseYesNo(answer, true);
54
+ if (parsed !== undefined) {
55
+ cors = parsed;
56
+ break;
57
+ }
58
+ console.log("Please answer with Y/Yes or N/No.\n");
59
+ }
60
+ let generateSampleSchemas = true;
61
+ while (true) {
62
+ const answer = await questionInterface.question("Generate sample schemas? (Y/n) ");
63
+ const parsed = parseYesNo(answer, true);
64
+ if (parsed !== undefined) {
65
+ generateSampleSchemas = parsed;
66
+ break;
67
+ }
68
+ console.log("Please answer with Y/Yes or N/No.\n");
69
+ }
70
+ console.log("");
71
+ return {
72
+ port,
73
+ cors,
74
+ generateSampleSchemas,
75
+ };
76
+ }
77
+ finally {
78
+ questionInterface.close();
79
+ }
80
+ };
81
+ const getRuntimeConfigTemplate = ({ port, cors, includeSampleRoutes, }) => {
82
+ const routeSection = includeSampleRoutes
83
+ ? [
84
+ " routes: {",
85
+ ' "/users": { count: 25, schema: "user" },',
86
+ ' "/posts": { count: 40, schema: "post" },',
87
+ " },",
88
+ ]
89
+ : [];
90
+ return [
91
+ "module.exports = {",
92
+ ` port: ${port},`,
93
+ ` cors: ${cors},`,
94
+ " delay: 0,",
95
+ ...routeSection,
96
+ "};",
97
+ ].join("\n");
98
+ };
99
+ const SAMPLE_USER_SCHEMA = [
100
+ "id:",
101
+ " type: uuid",
102
+ "fullName:",
103
+ " type: name",
104
+ " faker: person.fullName",
105
+ "email:",
106
+ " type: email",
107
+ " faker: internet.email",
108
+ "avatarUrl:",
109
+ " type: url",
110
+ " faker: image.avatar",
111
+ ].join("\n");
112
+ const SAMPLE_POST_SCHEMA = [
113
+ "id:",
114
+ " type: uuid",
115
+ "title:",
116
+ " type: string",
117
+ " faker: lorem.sentence",
118
+ "body:",
119
+ " type: string",
120
+ " faker: lorem.paragraph",
121
+ "createdAt:",
122
+ " type: date",
123
+ ].join("\n");
124
+ const initializeProject = async ({ force, }) => {
10
125
  const packageJsonPath = (0, paths_1.findClosestPackageJson)(process.cwd());
11
126
  if (!packageJsonPath) {
12
127
  console.error("Could not find package.json in this directory or parent directories.");
@@ -17,6 +132,11 @@ const initializeProject = ({ force }) => {
17
132
  const fexapiDirectoryPath = (0, node_path_1.join)(projectRoot, "fexapi");
18
133
  const schemaPath = (0, node_path_1.join)(fexapiDirectoryPath, "schema.fexapi");
19
134
  const configPath = (0, node_path_1.join)(projectRoot, "fexapi.config.json");
135
+ const runtimeConfigPath = (0, node_path_1.join)(projectRoot, "fexapi.config.js");
136
+ const schemasDirectoryPath = (0, node_path_1.join)(projectRoot, "schemas");
137
+ const userSchemaPath = (0, node_path_1.join)(schemasDirectoryPath, "user.yaml");
138
+ const postSchemaPath = (0, node_path_1.join)(schemasDirectoryPath, "post.yaml");
139
+ const wizardAnswers = await askInitWizardQuestions();
20
140
  (0, node_fs_1.mkdirSync)(fexapiDirectoryPath, { recursive: true });
21
141
  const configExists = (0, node_fs_1.existsSync)(configPath);
22
142
  const schemaExists = (0, node_fs_1.existsSync)(schemaPath);
@@ -26,13 +146,45 @@ const initializeProject = ({ force }) => {
26
146
  tooling: detectedProject.tooling,
27
147
  schemaPath: "fexapi/schema.fexapi",
28
148
  generatedPath: constants_1.GENERATED_SPEC_RELATIVE_PATH,
149
+ defaultPort: wizardAnswers.port,
150
+ corsEnabled: wizardAnswers.cors,
151
+ sampleSchemasGenerated: wizardAnswers.generateSampleSchemas,
29
152
  createdAt: new Date().toISOString(),
30
153
  };
31
154
  if (!configExists || force) {
32
155
  (0, node_fs_1.writeFileSync)(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf-8");
33
156
  }
34
157
  if (!schemaExists || force) {
35
- (0, node_fs_1.writeFileSync)(schemaPath, `${(0, detect_1.getSchemaTemplate)(detectedProject.primaryFramework)}\n`, "utf-8");
158
+ (0, node_fs_1.writeFileSync)(schemaPath, `${(0, detect_1.getSchemaTemplate)(detectedProject.primaryFramework, wizardAnswers.port)}\n`, "utf-8");
159
+ }
160
+ const runtimeConfigExists = (0, node_fs_1.existsSync)(runtimeConfigPath);
161
+ if (!runtimeConfigExists || force) {
162
+ (0, node_fs_1.writeFileSync)(runtimeConfigPath, `${getRuntimeConfigTemplate({
163
+ port: wizardAnswers.port,
164
+ cors: wizardAnswers.cors,
165
+ includeSampleRoutes: wizardAnswers.generateSampleSchemas,
166
+ })}\n`, "utf-8");
167
+ }
168
+ let userSchemaStatus = "skipped";
169
+ let postSchemaStatus = "skipped";
170
+ if (wizardAnswers.generateSampleSchemas) {
171
+ (0, node_fs_1.mkdirSync)(schemasDirectoryPath, { recursive: true });
172
+ const userSchemaExists = (0, node_fs_1.existsSync)(userSchemaPath);
173
+ if (!userSchemaExists || force) {
174
+ (0, node_fs_1.writeFileSync)(userSchemaPath, `${SAMPLE_USER_SCHEMA}\n`, "utf-8");
175
+ userSchemaStatus = userSchemaExists ? "overwritten" : "created";
176
+ }
177
+ else {
178
+ userSchemaStatus = "exists";
179
+ }
180
+ const postSchemaExists = (0, node_fs_1.existsSync)(postSchemaPath);
181
+ if (!postSchemaExists || force) {
182
+ (0, node_fs_1.writeFileSync)(postSchemaPath, `${SAMPLE_POST_SCHEMA}\n`, "utf-8");
183
+ postSchemaStatus = postSchemaExists ? "overwritten" : "created";
184
+ }
185
+ else {
186
+ postSchemaStatus = "exists";
187
+ }
36
188
  }
37
189
  console.log(`Initialized Fexapi in ${projectRoot}`);
38
190
  console.log(`Detected framework: ${detectedProject.primaryFramework}`);
@@ -58,6 +210,38 @@ const initializeProject = ({ force }) => {
58
210
  else {
59
211
  console.log(`Created ${schemaPath}`);
60
212
  }
213
+ if (runtimeConfigExists && !force) {
214
+ console.log(`Exists ${runtimeConfigPath}`);
215
+ }
216
+ else if (runtimeConfigExists && force) {
217
+ console.log(`Overwritten ${runtimeConfigPath}`);
218
+ }
219
+ else {
220
+ console.log(`Created ${runtimeConfigPath}`);
221
+ }
222
+ if (wizardAnswers.generateSampleSchemas) {
223
+ if (userSchemaStatus === "exists") {
224
+ console.log(`Exists ${userSchemaPath}`);
225
+ }
226
+ else if (userSchemaStatus === "overwritten") {
227
+ console.log(`Overwritten ${userSchemaPath}`);
228
+ }
229
+ else if (userSchemaStatus === "created") {
230
+ console.log(`Created ${userSchemaPath}`);
231
+ }
232
+ if (postSchemaStatus === "exists") {
233
+ console.log(`Exists ${postSchemaPath}`);
234
+ }
235
+ else if (postSchemaStatus === "overwritten") {
236
+ console.log(`Overwritten ${postSchemaPath}`);
237
+ }
238
+ else if (postSchemaStatus === "created") {
239
+ console.log(`Created ${postSchemaPath}`);
240
+ }
241
+ }
242
+ else {
243
+ console.log("Sample schemas were skipped.");
244
+ }
61
245
  if (detectedProject.primaryFramework === "unknown") {
62
246
  console.log("No known framework dependency found. Update fexapi.config.json and schema.fexapi if needed.");
63
247
  }
@@ -1,5 +1,12 @@
1
- export declare const serveProject: ({ host, port, }: {
1
+ import type { Server } from "node:http";
2
+ export declare const createProjectServer: ({ host, port, logEnabled, }: {
2
3
  host: string;
3
4
  port?: number;
5
+ logEnabled?: boolean;
6
+ }) => Server | undefined;
7
+ export declare const serveProject: ({ host, port, logEnabled, }: {
8
+ host: string;
9
+ port?: number;
10
+ logEnabled?: boolean;
4
11
  }) => number;
5
12
  //# sourceMappingURL=serve.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,YAAY,GAAI,iBAG1B;IACD,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,KAAG,MAkEH,CAAC"}
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAQxC,eAAO,MAAM,mBAAmB,GAAI,6BAIjC;IACD,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,KAAG,MAAM,GAAG,SA2DZ,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,6BAI1B;IACD,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,KAAG,MAsBH,CAAC"}
@@ -1,14 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.serveProject = void 0;
3
+ exports.serveProject = exports.createProjectServer = void 0;
4
4
  const constants_1 = require("../constants");
5
5
  const generated_spec_1 = require("../config/generated-spec");
6
6
  const runtime_config_1 = require("../config/runtime-config");
7
7
  const schema_definitions_1 = require("../config/schema-definitions");
8
8
  const paths_1 = require("../project/paths");
9
9
  const server_1 = require("../server");
10
- const serveProject = ({ host, port, }) => {
10
+ const createProjectServer = ({ host, port, logEnabled = false, }) => {
11
11
  const projectRoot = (0, paths_1.resolveProjectRoot)();
12
+ if (!projectRoot) {
13
+ console.error("Could not find package.json in this directory or parent directories.");
14
+ return undefined;
15
+ }
12
16
  const runtimeConfig = projectRoot
13
17
  ? (0, runtime_config_1.loadFexapiRuntimeConfig)(projectRoot)
14
18
  : undefined;
@@ -33,13 +37,21 @@ const serveProject = ({ host, port, }) => {
33
37
  Object.keys(runtimeConfig.routes).length === 0) {
34
38
  console.log("No generated schema found. Run `fexapi generate` to serve schema-defined endpoints.");
35
39
  }
36
- const server = (0, server_1.startServer)({
40
+ return (0, server_1.startServer)({
37
41
  host,
38
42
  port: effectivePort,
39
43
  apiSpec: generatedSpec,
40
44
  runtimeConfig,
41
45
  schemaDefinitions,
46
+ logRequests: logEnabled,
42
47
  });
48
+ };
49
+ exports.createProjectServer = createProjectServer;
50
+ const serveProject = ({ host, port, logEnabled, }) => {
51
+ const server = (0, exports.createProjectServer)({ host, port, logEnabled });
52
+ if (!server) {
53
+ return 1;
54
+ }
43
55
  const shutdown = () => {
44
56
  server.close((error) => {
45
57
  if (error) {
package/dist/index.js CHANGED
@@ -2,71 +2,97 @@
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const args_1 = require("./cli/args");
5
+ const dev_1 = require("./commands/dev");
5
6
  const help_1 = require("./cli/help");
6
7
  const generate_1 = require("./commands/generate");
7
8
  const init_1 = require("./commands/init");
8
9
  const serve_1 = require("./commands/serve");
9
10
  const args = process.argv.slice(2);
10
11
  const [firstArg, ...restArgs] = args;
11
- if (firstArg === "init") {
12
- if (restArgs.includes("--help") || restArgs.includes("-h")) {
13
- console.log("Usage: fexapi init [--force]");
14
- console.log("Detects frameworks/tooling and creates fexapi.config.json + fexapi/schema.fexapi.");
15
- console.log("Use --force to overwrite existing files.");
16
- process.exit(0);
12
+ const main = async () => {
13
+ if (firstArg === "init") {
14
+ if (restArgs.includes("--help") || restArgs.includes("-h")) {
15
+ console.log("Usage: fexapi init [--force]");
16
+ console.log("Runs an interactive setup wizard and creates fexapi config/schema files.");
17
+ console.log("Use --force to overwrite existing files.");
18
+ process.exit(0);
19
+ }
20
+ const initOptions = (0, args_1.parseInitOptions)(restArgs);
21
+ if ("error" in initOptions) {
22
+ console.error(initOptions.error);
23
+ console.log("");
24
+ console.log("Usage: fexapi init [--force]");
25
+ process.exit(1);
26
+ }
27
+ process.exit(await (0, init_1.initializeProject)({ force: initOptions.force }));
17
28
  }
18
- const initOptions = (0, args_1.parseInitOptions)(restArgs);
19
- if ("error" in initOptions) {
20
- console.error(initOptions.error);
21
- console.log("");
22
- console.log("Usage: fexapi init [--force]");
23
- process.exit(1);
29
+ else if (firstArg === "generate") {
30
+ if (restArgs.includes("--help") || restArgs.includes("-h")) {
31
+ console.log("Usage: fexapi generate");
32
+ console.log("Reads fexapi/schema.fexapi and updates generated API artifacts + migration.");
33
+ process.exit(0);
34
+ }
35
+ const generateOptions = (0, args_1.parseGenerateOptions)(restArgs);
36
+ if (generateOptions.error) {
37
+ console.error(generateOptions.error);
38
+ console.log("");
39
+ console.log("Usage: fexapi generate");
40
+ process.exit(1);
41
+ }
42
+ process.exit((0, generate_1.generateFromSchema)());
24
43
  }
25
- process.exit((0, init_1.initializeProject)({ force: initOptions.force }));
26
- }
27
- else if (firstArg === "generate") {
28
- if (restArgs.includes("--help") || restArgs.includes("-h")) {
29
- console.log("Usage: fexapi generate");
30
- console.log("Reads fexapi/schema.fexapi and creates generated API artifacts + migrations.");
31
- process.exit(0);
44
+ else if (firstArg === "dev") {
45
+ if (restArgs.includes("--help") || restArgs.includes("-h")) {
46
+ console.log("Usage: fexapi dev [--watch] [--host <host>] [--port <number>] [--log]");
47
+ console.log("Starts development server and optionally auto-reloads when config/schema files change.");
48
+ process.exit(0);
49
+ }
50
+ const devOptions = (0, args_1.parseDevOptions)(restArgs);
51
+ if ("error" in devOptions) {
52
+ console.error(devOptions.error);
53
+ console.log("");
54
+ console.log("Usage: fexapi dev [--watch] [--host <host>] [--port <number>] [--log]");
55
+ process.exit(1);
56
+ }
57
+ const exitCode = (0, dev_1.runDevCommand)(devOptions);
58
+ if (exitCode !== 0) {
59
+ process.exit(exitCode);
60
+ }
32
61
  }
33
- const generateOptions = (0, args_1.parseGenerateOptions)(restArgs);
34
- if (generateOptions.error) {
35
- console.error(generateOptions.error);
36
- console.log("");
37
- console.log("Usage: fexapi generate");
38
- process.exit(1);
62
+ else if (!firstArg ||
63
+ firstArg === "serve" ||
64
+ firstArg === "run" ||
65
+ firstArg.startsWith("-")) {
66
+ const serveArgs = firstArg === "serve" || firstArg === "run" ? restArgs : args;
67
+ if (serveArgs.includes("--help") || serveArgs.includes("-h")) {
68
+ (0, help_1.printHelp)();
69
+ process.exit(0);
70
+ }
71
+ const options = (0, args_1.parseServeOptions)(serveArgs);
72
+ if ("error" in options) {
73
+ console.error(options.error);
74
+ console.log("");
75
+ (0, help_1.printHelp)();
76
+ process.exit(1);
77
+ }
78
+ const exitCode = (0, serve_1.serveProject)(options);
79
+ if (exitCode !== 0) {
80
+ process.exit(exitCode);
81
+ }
39
82
  }
40
- process.exit((0, generate_1.generateFromSchema)());
41
- }
42
- else if (!firstArg ||
43
- firstArg === "serve" ||
44
- firstArg === "run" ||
45
- firstArg.startsWith("-")) {
46
- const serveArgs = firstArg === "serve" || firstArg === "run" ? restArgs : args;
47
- if (serveArgs.includes("--help") || serveArgs.includes("-h")) {
83
+ else if (firstArg === "help") {
48
84
  (0, help_1.printHelp)();
49
85
  process.exit(0);
50
86
  }
51
- const options = (0, args_1.parseServeOptions)(serveArgs);
52
- if ("error" in options) {
53
- console.error(options.error);
87
+ else {
88
+ console.error(`Unknown command: ${firstArg}`);
54
89
  console.log("");
55
90
  (0, help_1.printHelp)();
56
91
  process.exit(1);
57
92
  }
58
- const exitCode = (0, serve_1.serveProject)(options);
59
- if (exitCode !== 0) {
60
- process.exit(exitCode);
61
- }
62
- }
63
- else if (firstArg === "help") {
64
- (0, help_1.printHelp)();
65
- process.exit(0);
66
- }
67
- else {
68
- console.error(`Unknown command: ${firstArg}`);
69
- console.log("");
70
- (0, help_1.printHelp)();
93
+ };
94
+ void main().catch((error) => {
95
+ const message = error instanceof Error ? error.message : String(error);
96
+ console.error(`Unexpected error: ${message}`);
71
97
  process.exit(1);
72
- }
98
+ });
@@ -1,4 +1,4 @@
1
1
  import type { DetectedProject, SupportedFramework } from "../types/project";
2
2
  export declare const detectProject: (packageJsonPath: string, projectRoot: string) => DetectedProject;
3
- export declare const getSchemaTemplate: (framework: SupportedFramework) => string;
3
+ export declare const getSchemaTemplate: (framework: SupportedFramework, port?: number) => string;
4
4
  //# sourceMappingURL=detect.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../src/project/detect.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAyD5E,eAAO,MAAM,aAAa,GACxB,iBAAiB,MAAM,EACvB,aAAa,MAAM,KAClB,eAwDF,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,WAAW,kBAAkB,KAAG,MAkBjE,CAAC"}
1
+ {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../src/project/detect.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAyD5E,eAAO,MAAM,aAAa,GACxB,iBAAiB,MAAM,EACvB,aAAa,MAAM,KAClB,eAwDF,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,WAAW,kBAAkB,EAC7B,aAAW,KACV,MAkBF,CAAC"}
@@ -93,7 +93,7 @@ const detectProject = (packageJsonPath, projectRoot) => {
93
93
  };
94
94
  };
95
95
  exports.detectProject = detectProject;
96
- const getSchemaTemplate = (framework) => {
96
+ const getSchemaTemplate = (framework, port = 4000) => {
97
97
  const frameworkHint = framework === "nextjs"
98
98
  ? "# Framework: Next.js"
99
99
  : framework === "reactjs"
@@ -102,7 +102,7 @@ const getSchemaTemplate = (framework) => {
102
102
  return [
103
103
  frameworkHint,
104
104
  "# Server",
105
- "port: 4000",
105
+ `port: ${port}`,
106
106
  "",
107
107
  "# Routes",
108
108
  "# Format: METHOD /endpoint: field:type,field:type",
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GACvB,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,MAAM,GACN,MAAM,GACN,OAAO,GACP,KAAK,GACL,MAAM,GACN,OAAO,CAAC;AAEZ,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,eAAe,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB,CAAC;AAkGF,eAAO,MAAM,iBAAiB,GAC5B,YAAY,MAAM,KACjB;IAAE,MAAM,CAAC,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAoD3C,CAAC"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GACvB,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,MAAM,GACN,MAAM,GACN,OAAO,GACP,KAAK,GACL,MAAM,GACN,OAAO,CAAC;AAEZ,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,eAAe,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB,CAAC;AAiGF,eAAO,MAAM,iBAAiB,GAC5B,YAAY,MAAM,KACjB;IAAE,MAAM,CAAC,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAoD3C,CAAC"}
package/dist/schema.js CHANGED
@@ -13,6 +13,8 @@ const VALID_TYPES = [
13
13
  "phone",
14
14
  ];
15
15
  const DEFAULT_PORT = 4000;
16
+ const ROUTE_FORMAT_ERROR_MESSAGE = "Invalid route definition. Expected format: " +
17
+ "METHOD /endpoint: field:type,field:type";
16
18
  const parseField = (rawField) => {
17
19
  const [rawName, rawType] = rawField.split(":");
18
20
  const name = rawName?.trim();
@@ -36,16 +38,12 @@ const parseField = (rawField) => {
36
38
  const parseRoute = (line) => {
37
39
  const separatorIndex = line.indexOf(":");
38
40
  if (separatorIndex === -1) {
39
- return {
40
- error: "Invalid route definition. Expected format: METHOD /endpoint: field:type,field:type",
41
- };
41
+ return { error: ROUTE_FORMAT_ERROR_MESSAGE };
42
42
  }
43
43
  const rawLeft = line.slice(0, separatorIndex);
44
44
  const rawFields = line.slice(separatorIndex + 1);
45
45
  if (!rawLeft || !rawFields) {
46
- return {
47
- error: "Invalid route definition. Expected format: METHOD /endpoint: field:type,field:type",
48
- };
46
+ return { error: ROUTE_FORMAT_ERROR_MESSAGE };
49
47
  }
50
48
  const [rawMethod, rawPath] = rawLeft.trim().split(/\s+/, 2);
51
49
  const method = rawMethod?.toUpperCase();
package/dist/server.d.ts CHANGED
@@ -7,10 +7,11 @@ export type ServerOptions = {
7
7
  apiSpec?: GeneratedApiSpec;
8
8
  runtimeConfig?: FexapiRuntimeConfig;
9
9
  schemaDefinitions?: FexapiSchemaDefinitions;
10
+ logRequests?: boolean;
10
11
  };
11
12
  export type GeneratedApiSpec = {
12
13
  port: number;
13
14
  routes: FexapiRoute[];
14
15
  };
15
- export declare const startServer: ({ host, port, apiSpec, runtimeConfig, schemaDefinitions, }?: ServerOptions) => import("http").Server<typeof import("http").IncomingMessage, typeof ServerResponse>;
16
+ export declare const startServer: ({ host, port, apiSpec, runtimeConfig, schemaDefinitions, logRequests, }?: ServerOptions) => import("http").Server<typeof import("http").IncomingMessage, typeof ServerResponse>;
16
17
  //# sourceMappingURL=server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAe,WAAW,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,KAAK,EACV,mBAAmB,EACnB,uBAAuB,EAExB,MAAM,gBAAgB,CAAC;AAExB,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,iBAAiB,CAAC,EAAE,uBAAuB,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB,CAAC;AAyLF,eAAO,MAAM,WAAW,GAAI,6DAMzB,aAAkB,wFAqGpB,CAAC"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAe,WAAW,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,KAAK,EACV,mBAAmB,EACnB,uBAAuB,EAExB,MAAM,gBAAgB,CAAC;AAExB,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,iBAAiB,CAAC,EAAE,uBAAuB,CAAC;IAC5C,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB,CAAC;AAyLF,eAAO,MAAM,WAAW,GAAI,0EAOzB,aAAkB,wFAmGpB,CAAC"}
package/dist/server.js CHANGED
@@ -143,7 +143,7 @@ const getCountFromUrl = (urlText, fallback = 5) => {
143
143
  }
144
144
  return Math.min(Math.max(Math.floor(rawCount), 1), 50);
145
145
  };
146
- const startServer = ({ host = DEFAULT_HOST, port = DEFAULT_PORT, apiSpec, runtimeConfig, schemaDefinitions = {}, } = {}) => {
146
+ const startServer = ({ host = DEFAULT_HOST, port = DEFAULT_PORT, apiSpec, runtimeConfig, schemaDefinitions = {}, logRequests = false, } = {}) => {
147
147
  const corsEnabled = runtimeConfig?.cors ?? false;
148
148
  const responseDelay = runtimeConfig?.delay ?? 0;
149
149
  const configuredRoutes = runtimeConfig?.routes ?? {};
@@ -152,11 +152,19 @@ const startServer = ({ host = DEFAULT_HOST, port = DEFAULT_PORT, apiSpec, runtim
152
152
  ? apiSpec.routes.map((route) => `${route.method} ${route.path}`)
153
153
  : [];
154
154
  const availableRoutes = [
155
- "GET /health",
156
155
  ...new Set([...availableConfiguredRoutes, ...availableSchemaRoutes]),
157
156
  ];
158
157
  const server = (0, node_http_1.createServer)((request, response) => {
158
+ const requestStartedAt = Date.now();
159
159
  const pathname = new URL(request.url ?? "/", "http://localhost").pathname;
160
+ if (logRequests) {
161
+ response.on("finish", () => {
162
+ const method = request.method ?? "UNKNOWN";
163
+ const durationMs = Date.now() - requestStartedAt;
164
+ const statusCode = response.statusCode;
165
+ console.log(`[${method}] ${pathname} → ${statusCode} (${durationMs}ms)`);
166
+ });
167
+ }
160
168
  if (corsEnabled && request.method === "OPTIONS") {
161
169
  response.writeHead(204, {
162
170
  "Access-Control-Allow-Origin": "*",
@@ -166,13 +174,6 @@ const startServer = ({ host = DEFAULT_HOST, port = DEFAULT_PORT, apiSpec, runtim
166
174
  response.end();
167
175
  return;
168
176
  }
169
- if (request.method === "GET" && pathname === "/health") {
170
- sendJson(response, 200, {
171
- ok: true,
172
- timestamp: new Date().toISOString(),
173
- }, { cors: corsEnabled, delay: responseDelay });
174
- return;
175
- }
176
177
  if (request.method === "GET") {
177
178
  const configuredRoute = configuredRoutes[pathname];
178
179
  if (configuredRoute) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fexapi",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Mock API generation CLI tool for local development and testing",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -13,6 +13,7 @@
13
13
  ],
14
14
  "scripts": {
15
15
  "build": "tsc -p tsconfig.json",
16
+ "lint": "pnpm exec eslint . --max-warnings 0",
16
17
  "check-types": "tsc --noEmit",
17
18
  "start": "node dist/index.js",
18
19
  "serve": "node dist/index.js serve",
@@ -40,7 +41,9 @@
40
41
  },
41
42
  "homepage": "https://github.com/shreeteja172/fexapi#readme",
42
43
  "devDependencies": {
44
+ "@repo/eslint-config": "workspace:*",
43
45
  "@types/node": "^22.15.3",
46
+ "eslint": "^9.39.1",
44
47
  "typescript": "5.9.2"
45
48
  },
46
49
  "dependencies": {