fexapi 0.1.1 → 0.1.3
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 +46 -5
- package/dist/cli/args.d.ts +9 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +48 -3
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +15 -4
- package/dist/commands/dev.d.ts +7 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +114 -0
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +10 -2
- package/dist/commands/init.d.ts +2 -2
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +186 -2
- package/dist/commands/serve.d.ts +8 -1
- package/dist/commands/serve.d.ts.map +1 -1
- package/dist/commands/serve.js +15 -3
- package/dist/index.js +76 -50
- package/dist/project/detect.d.ts +1 -1
- package/dist/project/detect.d.ts.map +1 -1
- package/dist/project/detect.js +18 -5
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +78 -40
- package/dist/server.d.ts +2 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +10 -9
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -22,25 +22,47 @@ 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
|
|
|
32
|
-
`fexapi/schema.fexapi`
|
|
40
|
+
`fexapi/schema.fexapi` supports readable multi-line route fields (single-line still works):
|
|
33
41
|
|
|
34
42
|
```txt
|
|
35
43
|
# Server
|
|
36
44
|
port: 4100
|
|
37
45
|
|
|
38
46
|
# Routes
|
|
39
|
-
GET /users:
|
|
40
|
-
|
|
47
|
+
GET /users:
|
|
48
|
+
id:uuid
|
|
49
|
+
name:name
|
|
50
|
+
email:email
|
|
51
|
+
age:number
|
|
52
|
+
phone:phone
|
|
53
|
+
pic:url
|
|
54
|
+
courseName:string
|
|
55
|
+
|
|
56
|
+
GET /courses:
|
|
57
|
+
id:uuid
|
|
58
|
+
courseName:string
|
|
59
|
+
mentor:name
|
|
60
|
+
|
|
61
|
+
# one-line format is also valid:
|
|
62
|
+
# GET /users: id:uuid,name:name,email:email
|
|
41
63
|
```
|
|
42
64
|
|
|
43
|
-
### 3) Generate artifacts (
|
|
65
|
+
### 3) Generate artifacts (updates migration)
|
|
44
66
|
|
|
45
67
|
```bash
|
|
46
68
|
npx fexapi generate
|
|
@@ -49,7 +71,7 @@ npx fexapi generate
|
|
|
49
71
|
Generates:
|
|
50
72
|
|
|
51
73
|
- `fexapi/generated.api.json`
|
|
52
|
-
- `fexapi/migrations
|
|
74
|
+
- `fexapi/migrations/schema.json`
|
|
53
75
|
|
|
54
76
|
### 4) Start server
|
|
55
77
|
|
|
@@ -57,12 +79,31 @@ Generates:
|
|
|
57
79
|
npx fexapi run
|
|
58
80
|
# or
|
|
59
81
|
npx fexapi serve
|
|
82
|
+
# request/response logging
|
|
83
|
+
npx fexapi serve --log
|
|
84
|
+
# or dev watch mode (nodemon-like)
|
|
85
|
+
npx fexapi dev --watch
|
|
86
|
+
# watch mode + request logs
|
|
87
|
+
npx fexapi dev --watch --log
|
|
60
88
|
# or (inside local workspace package)
|
|
61
89
|
npm run serve
|
|
62
90
|
```
|
|
63
91
|
|
|
64
92
|
Server port is read from `schema.fexapi` unless overridden by CLI `--port`.
|
|
65
93
|
|
|
94
|
+
`fexapi dev --watch` auto-reloads when these files change:
|
|
95
|
+
|
|
96
|
+
- `fexapi/schema.fexapi`
|
|
97
|
+
- `fexapi/generated.api.json`
|
|
98
|
+
- `fexapi.config.js`
|
|
99
|
+
- `fexapi.config.json`
|
|
100
|
+
- `schemas/*.yaml`
|
|
101
|
+
|
|
102
|
+
`fexapi serve --log` prints request logs like:
|
|
103
|
+
|
|
104
|
+
- `[GET] /users/1 → 200 (45ms)`
|
|
105
|
+
- `[POST] /posts → 201 (12ms)`
|
|
106
|
+
|
|
66
107
|
## Configuration File Support
|
|
67
108
|
|
|
68
109
|
Create a `fexapi.config.js` in your project root:
|
package/dist/cli/args.d.ts
CHANGED
|
@@ -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
|
};
|
package/dist/cli/args.d.ts.map
CHANGED
|
@@ -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;
|
|
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("-") &&
|
|
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;
|
package/dist/cli/help.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../src/cli/help.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS,
|
|
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
|
|
11
|
-
console.log(" fexapi
|
|
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
|
|
49
|
+
console.log(" fexapi/migrations/schema.json");
|
|
39
50
|
};
|
|
40
51
|
exports.printHelp = printHelp;
|
|
@@ -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":"
|
|
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,
|
|
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
|
|
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;
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"
|
|
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"}
|
package/dist/commands/init.js
CHANGED
|
@@ -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
|
|
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
|
}
|
package/dist/commands/serve.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
|
|
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":"
|
|
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"}
|
package/dist/commands/serve.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
12
|
-
if (
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
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
|
-
|
|
52
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
+
});
|
package/dist/project/detect.d.ts
CHANGED
|
@@ -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,
|
|
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,MA+BF,CAAC"}
|
package/dist/project/detect.js
CHANGED
|
@@ -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,12 +102,25 @@ const getSchemaTemplate = (framework) => {
|
|
|
102
102
|
return [
|
|
103
103
|
frameworkHint,
|
|
104
104
|
"# Server",
|
|
105
|
-
|
|
105
|
+
`port: ${port}`,
|
|
106
106
|
"",
|
|
107
107
|
"# Routes",
|
|
108
|
-
"# Format: METHOD /endpoint: field:type,field:type",
|
|
109
|
-
"
|
|
110
|
-
"
|
|
108
|
+
"# Format (single-line): METHOD /endpoint: field:type,field:type",
|
|
109
|
+
"# Format (multi-line):",
|
|
110
|
+
"# METHOD /endpoint:",
|
|
111
|
+
"# field:type",
|
|
112
|
+
"# field:type",
|
|
113
|
+
"GET /users:",
|
|
114
|
+
" id:uuid",
|
|
115
|
+
" fullName:name",
|
|
116
|
+
" username:string",
|
|
117
|
+
" email:email",
|
|
118
|
+
" avatarUrl:url",
|
|
119
|
+
"GET /posts:",
|
|
120
|
+
" id:uuid",
|
|
121
|
+
" title:string",
|
|
122
|
+
" body:string",
|
|
123
|
+
" createdAt:date",
|
|
111
124
|
].join("\n");
|
|
112
125
|
};
|
|
113
126
|
exports.getSchemaTemplate = getSchemaTemplate;
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -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;
|
|
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;AAqHF,eAAO,MAAM,iBAAiB,GAC5B,YAAY,MAAM,KACjB;IAAE,MAAM,CAAC,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAmG3C,CAAC"}
|
package/dist/schema.js
CHANGED
|
@@ -13,6 +13,28 @@ 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 (or multiline fields under METHOD /endpoint:)";
|
|
18
|
+
const isPortLine = (line) => /^port\s*:/i.test(line.trim());
|
|
19
|
+
const parseRouteHeader = (line) => {
|
|
20
|
+
const separatorIndex = line.indexOf(":");
|
|
21
|
+
if (separatorIndex === -1) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
const rawLeft = line.slice(0, separatorIndex);
|
|
25
|
+
const rawFields = line.slice(separatorIndex + 1);
|
|
26
|
+
const [rawMethod, rawPath] = rawLeft.trim().split(/\s+/, 2);
|
|
27
|
+
const method = rawMethod?.toUpperCase();
|
|
28
|
+
const path = rawPath?.trim();
|
|
29
|
+
if (!method || !path || !path.startsWith("/")) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
method,
|
|
34
|
+
path,
|
|
35
|
+
inlineFields: rawFields,
|
|
36
|
+
};
|
|
37
|
+
};
|
|
16
38
|
const parseField = (rawField) => {
|
|
17
39
|
const [rawName, rawType] = rawField.split(":");
|
|
18
40
|
const name = rawName?.trim();
|
|
@@ -33,42 +55,23 @@ const parseField = (rawField) => {
|
|
|
33
55
|
type: type,
|
|
34
56
|
};
|
|
35
57
|
};
|
|
36
|
-
const parseRoute = (
|
|
37
|
-
const separatorIndex = line.indexOf(":");
|
|
38
|
-
if (separatorIndex === -1) {
|
|
39
|
-
return {
|
|
40
|
-
error: "Invalid route definition. Expected format: METHOD /endpoint: field:type,field:type",
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
const rawLeft = line.slice(0, separatorIndex);
|
|
44
|
-
const rawFields = line.slice(separatorIndex + 1);
|
|
45
|
-
if (!rawLeft || !rawFields) {
|
|
46
|
-
return {
|
|
47
|
-
error: "Invalid route definition. Expected format: METHOD /endpoint: field:type,field:type",
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
const [rawMethod, rawPath] = rawLeft.trim().split(/\s+/, 2);
|
|
51
|
-
const method = rawMethod?.toUpperCase();
|
|
52
|
-
const path = rawPath?.trim();
|
|
58
|
+
const parseRoute = ({ method, path, rawFields, }) => {
|
|
53
59
|
if (!method || !path) {
|
|
54
|
-
return {
|
|
55
|
-
error: "Invalid route definition. Missing METHOD or /endpoint before ':'.",
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
if (!path.startsWith("/")) {
|
|
59
|
-
return { error: `Route path must start with '/': ${path}` };
|
|
60
|
+
return { error: ROUTE_FORMAT_ERROR_MESSAGE };
|
|
60
61
|
}
|
|
61
62
|
const fields = [];
|
|
62
|
-
for (const
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
63
|
+
for (const rawFieldLine of rawFields) {
|
|
64
|
+
for (const part of rawFieldLine.split(",")) {
|
|
65
|
+
const trimmedPart = part.trim();
|
|
66
|
+
if (!trimmedPart) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const parsedField = parseField(trimmedPart);
|
|
70
|
+
if ("error" in parsedField) {
|
|
71
|
+
return { error: parsedField.error };
|
|
72
|
+
}
|
|
73
|
+
fields.push(parsedField);
|
|
70
74
|
}
|
|
71
|
-
fields.push(parsedField);
|
|
72
75
|
}
|
|
73
76
|
if (fields.length === 0) {
|
|
74
77
|
return { error: `Route ${method} ${path} has no valid fields.` };
|
|
@@ -79,13 +82,15 @@ const parseFexapiSchema = (schemaText) => {
|
|
|
79
82
|
let port = DEFAULT_PORT;
|
|
80
83
|
const routes = [];
|
|
81
84
|
const errors = [];
|
|
82
|
-
const lines = schemaText
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
const lines = schemaText.split(/\r?\n/);
|
|
86
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
87
|
+
const rawLine = lines[index] ?? "";
|
|
88
|
+
const trimmedLine = rawLine.trim();
|
|
89
|
+
if (!trimmedLine || trimmedLine.startsWith("#")) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (isPortLine(trimmedLine)) {
|
|
93
|
+
const rawPort = trimmedLine.slice(trimmedLine.indexOf(":") + 1).trim();
|
|
89
94
|
const parsedPort = Number(rawPort);
|
|
90
95
|
if (!Number.isInteger(parsedPort) ||
|
|
91
96
|
parsedPort < 1 ||
|
|
@@ -97,12 +102,45 @@ const parseFexapiSchema = (schemaText) => {
|
|
|
97
102
|
}
|
|
98
103
|
continue;
|
|
99
104
|
}
|
|
100
|
-
const
|
|
105
|
+
const header = parseRouteHeader(trimmedLine);
|
|
106
|
+
if (!header) {
|
|
107
|
+
errors.push(ROUTE_FORMAT_ERROR_MESSAGE);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const rawFields = [];
|
|
111
|
+
if (header.inlineFields.trim()) {
|
|
112
|
+
rawFields.push(header.inlineFields);
|
|
113
|
+
}
|
|
114
|
+
let lookaheadIndex = index + 1;
|
|
115
|
+
while (lookaheadIndex < lines.length) {
|
|
116
|
+
const lookaheadRawLine = lines[lookaheadIndex] ?? "";
|
|
117
|
+
const lookaheadTrimmedLine = lookaheadRawLine.trim();
|
|
118
|
+
if (!lookaheadTrimmedLine || lookaheadTrimmedLine.startsWith("#")) {
|
|
119
|
+
lookaheadIndex += 1;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (isPortLine(lookaheadTrimmedLine) ||
|
|
123
|
+
parseRouteHeader(lookaheadTrimmedLine)) {
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
if (/^\s+/.test(lookaheadRawLine)) {
|
|
127
|
+
rawFields.push(lookaheadTrimmedLine.replace(/^-+\s*/, ""));
|
|
128
|
+
lookaheadIndex += 1;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
const parsedRoute = parseRoute({
|
|
134
|
+
method: header.method,
|
|
135
|
+
path: header.path,
|
|
136
|
+
rawFields,
|
|
137
|
+
});
|
|
101
138
|
if ("error" in parsedRoute) {
|
|
102
139
|
errors.push(parsedRoute.error);
|
|
103
140
|
continue;
|
|
104
141
|
}
|
|
105
142
|
routes.push(parsedRoute);
|
|
143
|
+
index = lookaheadIndex - 1;
|
|
106
144
|
}
|
|
107
145
|
if (routes.length === 0) {
|
|
108
146
|
errors.push("No routes defined in schema.fexapi.");
|
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
|
package/dist/server.d.ts.map
CHANGED
|
@@ -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;
|
|
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.
|
|
3
|
+
"version": "0.1.3",
|
|
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": {
|