fexapi 0.2.3 → 0.2.5
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/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +4 -2
- package/dist/cli/ui.d.ts +1 -0
- package/dist/cli/ui.d.ts.map +1 -1
- package/dist/cli/ui.js +26 -2
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +117 -21
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +2 -124
- package/dist/commands/serve.js +1 -1
- package/dist/index.js +4 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +14 -12
- package/package.json +1 -1
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":"
|
|
1
|
+
{"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../src/cli/help.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,SAAS,YAgErB,CAAC"}
|
package/dist/cli/help.js
CHANGED
|
@@ -3,7 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.printHelp = void 0;
|
|
4
4
|
const ui_1 = require("./ui");
|
|
5
5
|
const printHelp = () => {
|
|
6
|
+
const version = (0, ui_1.getCliVersion)();
|
|
6
7
|
(0, ui_1.printBanner)();
|
|
8
|
+
console.log(ui_1.ui.dim(`version ${version}`));
|
|
7
9
|
(0, ui_1.printSpacer)();
|
|
8
10
|
console.log(ui_1.ui.bold("Usage"));
|
|
9
11
|
console.log(` ${(0, ui_1.formatCommand)("fexapi init [--force]")}`);
|
|
@@ -13,6 +15,8 @@ const printHelp = () => {
|
|
|
13
15
|
console.log(` ${(0, ui_1.formatCommand)("fexapi serve [--host <host>] [--port <number>] [--log]")}`);
|
|
14
16
|
console.log(` ${(0, ui_1.formatCommand)("fexapi run [--host <host>] [--port <number>] [--log]")}`);
|
|
15
17
|
console.log(` ${(0, ui_1.formatCommand)("fexapi [--host <host>] [--port <number>] [--log]")}`);
|
|
18
|
+
console.log(` ${(0, ui_1.formatCommand)("fexapi --version")}`);
|
|
19
|
+
console.log(` ${(0, ui_1.formatCommand)("fexapi version")}`);
|
|
16
20
|
console.log(` ${(0, ui_1.formatCommand)("fexapi --help")}`);
|
|
17
21
|
(0, ui_1.printSpacer)();
|
|
18
22
|
console.log(ui_1.ui.bold("Examples"));
|
|
@@ -34,12 +38,10 @@ const printHelp = () => {
|
|
|
34
38
|
console.log(ui_1.ui.bold("fexapi init creates"));
|
|
35
39
|
console.log(` ${ui_1.ui.dim("fexapi.config.js")}`);
|
|
36
40
|
console.log(` ${ui_1.ui.dim("fexapi/schema.fexapi")}`);
|
|
37
|
-
console.log(` ${ui_1.ui.dim("fexapi/schemas/*.yaml (optional, via wizard)")}`);
|
|
38
41
|
(0, ui_1.printSpacer)();
|
|
39
42
|
console.log(ui_1.ui.bold("Init wizard asks"));
|
|
40
43
|
console.log(` ${ui_1.ui.dim("What port? (default: 4000)")}`);
|
|
41
44
|
console.log(` ${ui_1.ui.dim("Enable CORS? (Y/n)")}`);
|
|
42
|
-
console.log(` ${ui_1.ui.dim("Generate sample schemas? (Y/n)")}`);
|
|
43
45
|
(0, ui_1.printSpacer)();
|
|
44
46
|
console.log(ui_1.ui.bold("Then run"));
|
|
45
47
|
console.log(` ${ui_1.ui.dim("# edit fexapi/schema.fexapi")}`);
|
package/dist/cli/ui.d.ts
CHANGED
|
@@ -32,5 +32,6 @@ export declare const logWarn: (message: string) => void;
|
|
|
32
32
|
export declare const logError: (message: string) => void;
|
|
33
33
|
export declare const logStep: (message: string) => void;
|
|
34
34
|
export declare const formatCommand: (command: string) => string;
|
|
35
|
+
export declare const getCliVersion: () => string;
|
|
35
36
|
export {};
|
|
36
37
|
//# sourceMappingURL=ui.d.ts.map
|
package/dist/cli/ui.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../src/cli/ui.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../src/cli/ui.ts"],"names":[],"mappings":"AAmBA,eAAO,MAAM,EAAE;iBACA,MAAM,KAAG,MAAM;gBAChB,MAAM,KAAG,MAAM;iBACd,MAAM,KAAG,MAAM;oBACZ,MAAM,KAAG,MAAM;iBAClB,MAAM,KAAG,MAAM;kBACd,MAAM,KAAG,MAAM;mBACd,MAAM,KAAG,MAAM;gBAClB,MAAM,KAAG,MAAM;iBACd,MAAM,KAAG,MAAM;kBACd,MAAM,KAAG,MAAM;CAC9B,CAAC;AA4DF,KAAK,iBAAiB,GAAG;IACvB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,aAAa,MAAM,KAAG,iBA0ClD,CAAC;AAEF,eAAO,MAAM,KAAK,QAAO,MAAoB,CAAC;AAE9C,eAAO,MAAM,cAAc,GAAI,SAAS,MAAM,KAAG,MAQhD,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAC3B,OAAO,MAAM,EACb,MAAM,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,KAC5C,IAgEF,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,OAAO,MAAM,KAAG,IAkBhD,CAAC;AAEF,eAAO,MAAM,WAAW,QAAO,IAO9B,CAAC;AAEF,eAAO,MAAM,WAAW,QAAO,IAE9B,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,SAAS,MAAM,KAAG,IAEzC,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,SAAS,MAAM,KAAG,IAE5C,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,SAAS,MAAM,KAAG,IAEzC,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,SAAS,MAAM,KAAG,IAE1C,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,SAAS,MAAM,KAAG,IAEzC,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,KAAG,MAE/C,CAAC;AAEF,eAAO,MAAM,aAAa,QAAO,MAyBhC,CAAC"}
|
package/dist/cli/ui.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.formatCommand = exports.logStep = exports.logError = exports.logWarn = exports.logSuccess = exports.logInfo = exports.printSpacer = exports.printBanner = exports.printGroupHeader = exports.printSummaryCard = exports.formatDuration = exports.nowMs = exports.startSpinner = exports.ui = void 0;
|
|
3
|
+
exports.getCliVersion = exports.formatCommand = exports.logStep = exports.logError = exports.logWarn = exports.logSuccess = exports.logInfo = exports.printSpacer = exports.printBanner = exports.printGroupHeader = exports.printSummaryCard = exports.formatDuration = exports.nowMs = exports.startSpinner = exports.ui = void 0;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
4
6
|
const shouldUseColor = () => {
|
|
5
7
|
return Boolean(process.stdout.isTTY);
|
|
6
8
|
};
|
|
@@ -26,7 +28,7 @@ exports.ui = {
|
|
|
26
28
|
white: (text) => paint("97", text),
|
|
27
29
|
};
|
|
28
30
|
const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
29
|
-
const ANSI_REGEX =
|
|
31
|
+
const ANSI_REGEX = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
30
32
|
const stripAnsi = (text) => text.replace(ANSI_REGEX, "");
|
|
31
33
|
const visibleLength = (text) => stripAnsi(text).length;
|
|
32
34
|
const getTerminalWidth = () => {
|
|
@@ -208,3 +210,25 @@ const formatCommand = (command) => {
|
|
|
208
210
|
return exports.ui.bold(command);
|
|
209
211
|
};
|
|
210
212
|
exports.formatCommand = formatCommand;
|
|
213
|
+
const getCliVersion = () => {
|
|
214
|
+
const packageCandidates = [
|
|
215
|
+
(0, node_path_1.join)(__dirname, "..", "..", "package.json"),
|
|
216
|
+
(0, node_path_1.join)(__dirname, "..", "package.json"),
|
|
217
|
+
];
|
|
218
|
+
for (const packagePath of packageCandidates) {
|
|
219
|
+
if (!(0, node_fs_1.existsSync)(packagePath)) {
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
try {
|
|
223
|
+
const packageJson = JSON.parse((0, node_fs_1.readFileSync)(packagePath, "utf-8"));
|
|
224
|
+
if (packageJson.version) {
|
|
225
|
+
return packageJson.version;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return "unknown";
|
|
233
|
+
};
|
|
234
|
+
exports.getCliVersion = getCliVersion;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AA0EA,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,MAuNH,CAAC"}
|
package/dist/commands/dev.js
CHANGED
|
@@ -5,21 +5,53 @@ const node_fs_1 = require("node:fs");
|
|
|
5
5
|
const node_path_1 = require("node:path");
|
|
6
6
|
const ui_1 = require("../cli/ui");
|
|
7
7
|
const paths_1 = require("../project/paths");
|
|
8
|
+
const generate_1 = require("./generate");
|
|
8
9
|
const serve_1 = require("./serve");
|
|
9
10
|
const WATCH_DEBOUNCE_MS = 150;
|
|
11
|
+
const GENERATED_SPEC_PATH = "fexapi/generated.api.json";
|
|
12
|
+
const GENERATED_SPEC_SUPPRESS_MS = 1200;
|
|
10
13
|
const normalizePath = (pathValue) => {
|
|
11
14
|
return pathValue.replace(/\\/g, "/").toLowerCase();
|
|
12
15
|
};
|
|
13
|
-
const
|
|
16
|
+
const getWatchReaction = (projectRoot, changedPath) => {
|
|
14
17
|
const relativePath = normalizePath((0, node_path_1.relative)(projectRoot, changedPath));
|
|
15
18
|
if (relativePath === "fexapi.config.js") {
|
|
16
|
-
return
|
|
19
|
+
return {
|
|
20
|
+
shouldRestart: true,
|
|
21
|
+
shouldRegenerate: false,
|
|
22
|
+
reason: "fexapi.config.js changed",
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
if (relativePath === "fexapi/schema.fexapi") {
|
|
26
|
+
return {
|
|
27
|
+
shouldRestart: true,
|
|
28
|
+
shouldRegenerate: true,
|
|
29
|
+
reason: "schema.fexapi changed",
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
if (relativePath.startsWith("fexapi/schemas/") &&
|
|
33
|
+
(relativePath.endsWith(".yaml") || relativePath.endsWith(".yml"))) {
|
|
34
|
+
return {
|
|
35
|
+
shouldRestart: true,
|
|
36
|
+
shouldRegenerate: true,
|
|
37
|
+
reason: "custom schema definition changed",
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
if (relativePath === GENERATED_SPEC_PATH) {
|
|
41
|
+
return {
|
|
42
|
+
shouldRestart: true,
|
|
43
|
+
shouldRegenerate: false,
|
|
44
|
+
reason: "generated.api.json changed",
|
|
45
|
+
};
|
|
17
46
|
}
|
|
18
47
|
if (relativePath.startsWith("fexapi/")) {
|
|
19
|
-
return
|
|
48
|
+
return {
|
|
49
|
+
shouldRestart: true,
|
|
50
|
+
shouldRegenerate: false,
|
|
51
|
+
reason: `file changed (${relativePath})`,
|
|
52
|
+
};
|
|
20
53
|
}
|
|
21
|
-
return
|
|
22
|
-
(relativePath.endsWith(".yaml") || relativePath.endsWith(".yml")));
|
|
54
|
+
return undefined;
|
|
23
55
|
};
|
|
24
56
|
const runDevCommand = ({ host, port, watchEnabled, logEnabled, }) => {
|
|
25
57
|
if (!watchEnabled) {
|
|
@@ -38,34 +70,87 @@ const runDevCommand = ({ host, port, watchEnabled, logEnabled, }) => {
|
|
|
38
70
|
let restartTimer;
|
|
39
71
|
let restartQueued = false;
|
|
40
72
|
let restartInProgress = false;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
73
|
+
let pendingReasons = new Set();
|
|
74
|
+
let pendingRegeneration = false;
|
|
75
|
+
let suppressGeneratedSpecUntilMs = 0;
|
|
76
|
+
const collectPendingWatchChanges = ({ reason, shouldRegenerate, }) => {
|
|
77
|
+
pendingReasons.add(reason);
|
|
78
|
+
if (shouldRegenerate) {
|
|
79
|
+
pendingRegeneration = true;
|
|
44
80
|
}
|
|
81
|
+
};
|
|
82
|
+
const restartServer = async ({ reason, shouldRegenerate, }) => {
|
|
45
83
|
if (restartInProgress) {
|
|
46
84
|
restartQueued = true;
|
|
85
|
+
collectPendingWatchChanges({ reason, shouldRegenerate });
|
|
47
86
|
return;
|
|
48
87
|
}
|
|
49
88
|
restartInProgress = true;
|
|
50
89
|
(0, ui_1.logInfo)(`[watch] change detected (${reason})`);
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
90
|
+
if (shouldRegenerate) {
|
|
91
|
+
(0, ui_1.printSpacer)();
|
|
92
|
+
(0, ui_1.logInfo)("[watch] regenerating generated.api.json from schema changes...");
|
|
93
|
+
const generationExitCode = (0, generate_1.generateFromSchema)();
|
|
94
|
+
if (generationExitCode !== 0) {
|
|
95
|
+
(0, ui_1.logError)("[watch] generation failed; keeping previous server state.");
|
|
96
|
+
(0, ui_1.logWarn)("Fix schema errors and save again. Watch mode will retry automatically.");
|
|
97
|
+
restartInProgress = false;
|
|
98
|
+
if (restartQueued) {
|
|
99
|
+
restartQueued = false;
|
|
100
|
+
const queuedReasons = Array.from(pendingReasons);
|
|
101
|
+
const queuedRegeneration = pendingRegeneration;
|
|
102
|
+
pendingReasons = new Set();
|
|
103
|
+
pendingRegeneration = false;
|
|
104
|
+
await restartServer({
|
|
105
|
+
reason: queuedReasons.join(", ") || "queued changes",
|
|
106
|
+
shouldRegenerate: queuedRegeneration,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
suppressGeneratedSpecUntilMs = Date.now() + GENERATED_SPEC_SUPPRESS_MS;
|
|
112
|
+
}
|
|
113
|
+
if (currentServer) {
|
|
114
|
+
await new Promise((resolve) => {
|
|
115
|
+
currentServer?.close(() => {
|
|
116
|
+
resolve();
|
|
117
|
+
});
|
|
54
118
|
});
|
|
55
|
-
}
|
|
119
|
+
}
|
|
56
120
|
currentServer = (0, serve_1.createProjectServer)({ host, port, logEnabled });
|
|
121
|
+
if (currentServer) {
|
|
122
|
+
(0, ui_1.logSuccess)("[watch] server reloaded");
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
(0, ui_1.logError)("[watch] server reload failed; waiting for next change to retry.");
|
|
126
|
+
}
|
|
57
127
|
restartInProgress = false;
|
|
58
128
|
if (restartQueued) {
|
|
59
129
|
restartQueued = false;
|
|
60
|
-
|
|
130
|
+
const queuedReasons = Array.from(pendingReasons);
|
|
131
|
+
const queuedRegeneration = pendingRegeneration;
|
|
132
|
+
pendingReasons = new Set();
|
|
133
|
+
pendingRegeneration = false;
|
|
134
|
+
await restartServer({
|
|
135
|
+
reason: queuedReasons.join(", ") || "queued changes",
|
|
136
|
+
shouldRegenerate: queuedRegeneration,
|
|
137
|
+
});
|
|
61
138
|
}
|
|
62
139
|
};
|
|
63
|
-
const scheduleRestart = (reason) => {
|
|
140
|
+
const scheduleRestart = ({ reason, shouldRegenerate, }) => {
|
|
141
|
+
collectPendingWatchChanges({ reason, shouldRegenerate });
|
|
64
142
|
if (restartTimer) {
|
|
65
143
|
clearTimeout(restartTimer);
|
|
66
144
|
}
|
|
67
145
|
restartTimer = setTimeout(() => {
|
|
68
|
-
|
|
146
|
+
const reasons = Array.from(pendingReasons);
|
|
147
|
+
const requiresRegeneration = pendingRegeneration;
|
|
148
|
+
pendingReasons = new Set();
|
|
149
|
+
pendingRegeneration = false;
|
|
150
|
+
void restartServer({
|
|
151
|
+
reason: reasons.join(", ") || reason,
|
|
152
|
+
shouldRegenerate: requiresRegeneration,
|
|
153
|
+
});
|
|
69
154
|
}, WATCH_DEBOUNCE_MS);
|
|
70
155
|
};
|
|
71
156
|
const activeWatchers = [];
|
|
@@ -76,12 +161,21 @@ const runDevCommand = ({ host, port, watchEnabled, logEnabled, }) => {
|
|
|
76
161
|
}
|
|
77
162
|
const watcher = (0, node_fs_1.watch)(watchTarget, { recursive: true }, (_event, file) => {
|
|
78
163
|
if (!file) {
|
|
79
|
-
scheduleRestart("unknown file");
|
|
164
|
+
scheduleRestart({ reason: "unknown file", shouldRegenerate: false });
|
|
80
165
|
return;
|
|
81
166
|
}
|
|
82
167
|
const changedPath = (0, node_path_1.join)(watchTarget, file.toString());
|
|
83
|
-
|
|
84
|
-
|
|
168
|
+
const normalizedRelativePath = normalizePath((0, node_path_1.relative)(projectRoot, changedPath));
|
|
169
|
+
if (normalizedRelativePath === GENERATED_SPEC_PATH &&
|
|
170
|
+
Date.now() < suppressGeneratedSpecUntilMs) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const watchReaction = getWatchReaction(projectRoot, changedPath);
|
|
174
|
+
if (watchReaction?.shouldRestart) {
|
|
175
|
+
scheduleRestart({
|
|
176
|
+
reason: watchReaction.reason,
|
|
177
|
+
shouldRegenerate: watchReaction.shouldRegenerate,
|
|
178
|
+
});
|
|
85
179
|
}
|
|
86
180
|
});
|
|
87
181
|
activeWatchers.push(watcher);
|
|
@@ -94,9 +188,11 @@ const runDevCommand = ({ host, port, watchEnabled, logEnabled, }) => {
|
|
|
94
188
|
for (const watcher of activeWatchers) {
|
|
95
189
|
watcher.close();
|
|
96
190
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
191
|
+
if (currentServer) {
|
|
192
|
+
await new Promise((resolve) => {
|
|
193
|
+
currentServer?.close(() => resolve());
|
|
194
|
+
});
|
|
195
|
+
}
|
|
100
196
|
process.exit(0);
|
|
101
197
|
};
|
|
102
198
|
process.on("SIGINT", () => {
|
|
@@ -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":"AA2JA,eAAO,MAAM,iBAAiB,GAAU,YAErC;IACD,KAAK,EAAE,OAAO,CAAC;CAChB,KAAG,OAAO,CAAC,MAAM,CAsHjB,CAAC"}
|
package/dist/commands/init.js
CHANGED
|
@@ -27,11 +27,10 @@ const askInitWizardQuestions = async () => {
|
|
|
27
27
|
return {
|
|
28
28
|
port: DEFAULT_INIT_PORT,
|
|
29
29
|
cors: true,
|
|
30
|
-
generateSampleSchemas: true,
|
|
31
30
|
};
|
|
32
31
|
}
|
|
33
32
|
const questionInterface = (0, promises_1.createInterface)({ input: node_process_1.stdin, output: node_process_1.stdout });
|
|
34
|
-
const totalSteps =
|
|
33
|
+
const totalSteps = 2;
|
|
35
34
|
const formatWizardPrompt = (step, question, hint, defaultValue) => {
|
|
36
35
|
return `${ui_1.ui.cyan("?")} ${ui_1.ui.bold(`[${step}/${totalSteps}]`)} ${ui_1.ui.white(question)} ${ui_1.ui.gray(`(${hint})`)} ${ui_1.ui.dim(`default: ${defaultValue}`)} ${ui_1.ui.gray("› ")}`;
|
|
37
36
|
};
|
|
@@ -69,90 +68,25 @@ const askInitWizardQuestions = async () => {
|
|
|
69
68
|
(0, ui_1.printSpacer)();
|
|
70
69
|
}
|
|
71
70
|
console.log(`${ui_1.ui.green("✓")} ${ui_1.ui.dim("CORS")}: ${cors ? ui_1.ui.green("enabled") : ui_1.ui.gray("disabled")}`);
|
|
72
|
-
let generateSampleSchemas = true;
|
|
73
|
-
while (true) {
|
|
74
|
-
const answer = await questionInterface.question(formatWizardPrompt(3, "Generate sample schemas?", "Y/n", "Y"));
|
|
75
|
-
const parsed = parseYesNo(answer, true);
|
|
76
|
-
if (parsed !== undefined) {
|
|
77
|
-
generateSampleSchemas = parsed;
|
|
78
|
-
break;
|
|
79
|
-
}
|
|
80
|
-
console.log(`${ui_1.ui.red("✕")} ${ui_1.ui.white("Please answer with Y/Yes or N/No.")}`);
|
|
81
|
-
(0, ui_1.printSpacer)();
|
|
82
|
-
}
|
|
83
|
-
console.log(`${ui_1.ui.green("✓")} ${ui_1.ui.dim("Sample schemas")}: ${generateSampleSchemas ? ui_1.ui.green("enabled") : ui_1.ui.gray("disabled")}`);
|
|
84
71
|
(0, ui_1.printSpacer)();
|
|
85
72
|
return {
|
|
86
73
|
port,
|
|
87
74
|
cors,
|
|
88
|
-
generateSampleSchemas,
|
|
89
75
|
};
|
|
90
76
|
}
|
|
91
77
|
finally {
|
|
92
78
|
questionInterface.close();
|
|
93
79
|
}
|
|
94
80
|
};
|
|
95
|
-
const getRuntimeConfigTemplate = ({ port, cors,
|
|
96
|
-
const routeSection = includeSampleRoutes
|
|
97
|
-
? [
|
|
98
|
-
" routes: {",
|
|
99
|
-
' "/users": { count: 10, schema: "user" },',
|
|
100
|
-
' "/posts": { count: 20, schema: "post" },',
|
|
101
|
-
" },",
|
|
102
|
-
]
|
|
103
|
-
: [];
|
|
81
|
+
const getRuntimeConfigTemplate = ({ port, cors, }) => {
|
|
104
82
|
return [
|
|
105
83
|
"module.exports = {",
|
|
106
84
|
` port: ${port},`,
|
|
107
85
|
` cors: ${cors},`,
|
|
108
86
|
" delay: 0,",
|
|
109
|
-
...routeSection,
|
|
110
87
|
"};",
|
|
111
88
|
].join("\n");
|
|
112
89
|
};
|
|
113
|
-
const SAMPLE_USER_SCHEMA = [
|
|
114
|
-
"id:",
|
|
115
|
-
" type: uuid",
|
|
116
|
-
"fullName:",
|
|
117
|
-
" type: name",
|
|
118
|
-
" faker: person.fullName",
|
|
119
|
-
"username:",
|
|
120
|
-
" type: string",
|
|
121
|
-
" faker: internet.username",
|
|
122
|
-
"email:",
|
|
123
|
-
" type: email",
|
|
124
|
-
" faker: internet.email",
|
|
125
|
-
"avatarUrl:",
|
|
126
|
-
" type: url",
|
|
127
|
-
" faker: image.avatar",
|
|
128
|
-
"bio:",
|
|
129
|
-
" type: string",
|
|
130
|
-
" faker: lorem.sentence",
|
|
131
|
-
"isActive:",
|
|
132
|
-
" type: boolean",
|
|
133
|
-
"joinedAt:",
|
|
134
|
-
" type: date",
|
|
135
|
-
].join("\n");
|
|
136
|
-
const SAMPLE_POST_SCHEMA = [
|
|
137
|
-
"id:",
|
|
138
|
-
" type: uuid",
|
|
139
|
-
"title:",
|
|
140
|
-
" type: string",
|
|
141
|
-
" faker: lorem.sentence",
|
|
142
|
-
"body:",
|
|
143
|
-
" type: string",
|
|
144
|
-
" faker: lorem.paragraphs",
|
|
145
|
-
"authorId:",
|
|
146
|
-
" type: uuid",
|
|
147
|
-
"published:",
|
|
148
|
-
" type: boolean",
|
|
149
|
-
"likes:",
|
|
150
|
-
" type: number",
|
|
151
|
-
" min: 0",
|
|
152
|
-
" max: 500",
|
|
153
|
-
"createdAt:",
|
|
154
|
-
" type: date",
|
|
155
|
-
].join("\n");
|
|
156
90
|
const initializeProject = async ({ force, }) => {
|
|
157
91
|
const initStartedAtMs = (0, ui_1.nowMs)();
|
|
158
92
|
const packageJsonPath = (0, paths_1.findClosestPackageJson)(process.cwd());
|
|
@@ -165,9 +99,6 @@ const initializeProject = async ({ force, }) => {
|
|
|
165
99
|
const fexapiDirectoryPath = (0, node_path_1.join)(projectRoot, "fexapi");
|
|
166
100
|
const schemaPath = (0, node_path_1.join)(fexapiDirectoryPath, "schema.fexapi");
|
|
167
101
|
const runtimeConfigPath = (0, node_path_1.join)(projectRoot, "fexapi.config.js");
|
|
168
|
-
const schemasDirectoryPath = (0, node_path_1.join)(projectRoot, "fexapi", "schemas");
|
|
169
|
-
const userSchemaPath = (0, node_path_1.join)(schemasDirectoryPath, "user.yaml");
|
|
170
|
-
const postSchemaPath = (0, node_path_1.join)(schemasDirectoryPath, "post.yaml");
|
|
171
102
|
const wizardAnswers = await askInitWizardQuestions();
|
|
172
103
|
(0, ui_1.printGroupHeader)("Init");
|
|
173
104
|
const initSpinner = (0, ui_1.startSpinner)("Scaffolding fexapi project files");
|
|
@@ -183,32 +114,8 @@ const initializeProject = async ({ force, }) => {
|
|
|
183
114
|
(0, node_fs_1.writeFileSync)(runtimeConfigPath, `${getRuntimeConfigTemplate({
|
|
184
115
|
port: wizardAnswers.port,
|
|
185
116
|
cors: wizardAnswers.cors,
|
|
186
|
-
includeSampleRoutes: wizardAnswers.generateSampleSchemas,
|
|
187
117
|
})}\n`, "utf-8");
|
|
188
118
|
}
|
|
189
|
-
let userSchemaStatus = "skipped";
|
|
190
|
-
let postSchemaStatus = "skipped";
|
|
191
|
-
if (wizardAnswers.generateSampleSchemas) {
|
|
192
|
-
(0, node_fs_1.mkdirSync)(schemasDirectoryPath, { recursive: true });
|
|
193
|
-
const userSchemaExists = (0, node_fs_1.existsSync)(userSchemaPath);
|
|
194
|
-
if (!userSchemaExists || force) {
|
|
195
|
-
initSpinner.update("Writing sample user schema");
|
|
196
|
-
(0, node_fs_1.writeFileSync)(userSchemaPath, `${SAMPLE_USER_SCHEMA}\n`, "utf-8");
|
|
197
|
-
userSchemaStatus = userSchemaExists ? "overwritten" : "created";
|
|
198
|
-
}
|
|
199
|
-
else {
|
|
200
|
-
userSchemaStatus = "exists";
|
|
201
|
-
}
|
|
202
|
-
const postSchemaExists = (0, node_fs_1.existsSync)(postSchemaPath);
|
|
203
|
-
if (!postSchemaExists || force) {
|
|
204
|
-
initSpinner.update("Writing sample post schema");
|
|
205
|
-
(0, node_fs_1.writeFileSync)(postSchemaPath, `${SAMPLE_POST_SCHEMA}\n`, "utf-8");
|
|
206
|
-
postSchemaStatus = postSchemaExists ? "overwritten" : "created";
|
|
207
|
-
}
|
|
208
|
-
else {
|
|
209
|
-
postSchemaStatus = "exists";
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
119
|
initSpinner.succeed("Project scaffolding complete");
|
|
213
120
|
(0, ui_1.logSuccess)(`Initialized fexapi in ${projectRoot}`);
|
|
214
121
|
(0, ui_1.logInfo)(`Detected framework: ${detectedProject.primaryFramework}`);
|
|
@@ -236,29 +143,6 @@ const initializeProject = async ({ force, }) => {
|
|
|
236
143
|
else {
|
|
237
144
|
(0, ui_1.logSuccess)(`Created ${runtimeConfigPath}`);
|
|
238
145
|
}
|
|
239
|
-
if (wizardAnswers.generateSampleSchemas) {
|
|
240
|
-
if (userSchemaStatus === "exists") {
|
|
241
|
-
(0, ui_1.logWarn)(`Exists ${userSchemaPath}`);
|
|
242
|
-
}
|
|
243
|
-
else if (userSchemaStatus === "overwritten") {
|
|
244
|
-
(0, ui_1.logSuccess)(`Overwritten ${userSchemaPath}`);
|
|
245
|
-
}
|
|
246
|
-
else if (userSchemaStatus === "created") {
|
|
247
|
-
(0, ui_1.logSuccess)(`Created ${userSchemaPath}`);
|
|
248
|
-
}
|
|
249
|
-
if (postSchemaStatus === "exists") {
|
|
250
|
-
(0, ui_1.logWarn)(`Exists ${postSchemaPath}`);
|
|
251
|
-
}
|
|
252
|
-
else if (postSchemaStatus === "overwritten") {
|
|
253
|
-
(0, ui_1.logSuccess)(`Overwritten ${postSchemaPath}`);
|
|
254
|
-
}
|
|
255
|
-
else if (postSchemaStatus === "created") {
|
|
256
|
-
(0, ui_1.logSuccess)(`Created ${postSchemaPath}`);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
else {
|
|
260
|
-
(0, ui_1.logWarn)("Sample schemas were skipped.");
|
|
261
|
-
}
|
|
262
146
|
if (detectedProject.primaryFramework === "unknown") {
|
|
263
147
|
(0, ui_1.logWarn)("No known framework dependency found. Update fexapi.config.js and schema.fexapi if needed.");
|
|
264
148
|
}
|
|
@@ -267,8 +151,6 @@ const initializeProject = async ({ force, }) => {
|
|
|
267
151
|
const createdFiles = [
|
|
268
152
|
!schemaExists || force,
|
|
269
153
|
!runtimeConfigExists || force,
|
|
270
|
-
userSchemaStatus === "created" || userSchemaStatus === "overwritten",
|
|
271
|
-
postSchemaStatus === "created" || postSchemaStatus === "overwritten",
|
|
272
154
|
].filter(Boolean).length;
|
|
273
155
|
(0, ui_1.printSummaryCard)("Init Summary", [
|
|
274
156
|
{
|
|
@@ -283,10 +165,6 @@ const initializeProject = async ({ force, }) => {
|
|
|
283
165
|
label: "cors",
|
|
284
166
|
value: wizardAnswers.cors ? "enabled" : "disabled",
|
|
285
167
|
},
|
|
286
|
-
{
|
|
287
|
-
label: "sample schemas",
|
|
288
|
-
value: wizardAnswers.generateSampleSchemas ? "enabled" : "disabled",
|
|
289
|
-
},
|
|
290
168
|
{
|
|
291
169
|
label: "files changed",
|
|
292
170
|
value: String(createdFiles),
|
package/dist/commands/serve.js
CHANGED
|
@@ -35,7 +35,7 @@ const createProjectServer = ({ host, port, logEnabled = false, }) => {
|
|
|
35
35
|
const more = overlappingPaths.length > 5
|
|
36
36
|
? ` (+${overlappingPaths.length - 5} more)`
|
|
37
37
|
: "";
|
|
38
|
-
(0, ui_1.
|
|
38
|
+
(0, ui_1.logInfo)(`Both schema and config define: ${sample}${more}. Schema routes take precedence; remove duplicates in fexapi.config.js to keep behavior clear.`);
|
|
39
39
|
}
|
|
40
40
|
if (Object.keys(schemaDefinitions).length > 0) {
|
|
41
41
|
(0, ui_1.logInfo)(`Loaded custom schemas from fexapi/schemas (${Object.keys(schemaDefinitions).length})`);
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,10 @@ const serve_1 = require("./commands/serve");
|
|
|
12
12
|
const args = process.argv.slice(2);
|
|
13
13
|
const [firstArg, ...restArgs] = args;
|
|
14
14
|
const main = async () => {
|
|
15
|
+
if (firstArg === "--version" || firstArg === "-v" || firstArg === "version") {
|
|
16
|
+
console.log((0, ui_1.getCliVersion)());
|
|
17
|
+
process.exit(0);
|
|
18
|
+
}
|
|
15
19
|
if (firstArg === "init") {
|
|
16
20
|
if (restArgs.includes("--help") || restArgs.includes("-h")) {
|
|
17
21
|
(0, ui_1.printBanner)();
|
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;AAGxB,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;AAgMF,eAAO,MAAM,WAAW,GAAI,0EAOzB,aAAkB,
|
|
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;AAGxB,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;AAgMF,eAAO,MAAM,WAAW,GAAI,0EAOzB,aAAkB,wFA0JpB,CAAC"}
|
package/dist/server.js
CHANGED
|
@@ -191,17 +191,6 @@ const startServer = ({ host = DEFAULT_HOST, port = DEFAULT_PORT, apiSpec, runtim
|
|
|
191
191
|
response.end();
|
|
192
192
|
return;
|
|
193
193
|
}
|
|
194
|
-
if (request.method === "GET") {
|
|
195
|
-
const configuredRoute = configuredRoutes[pathname];
|
|
196
|
-
if (configuredRoute) {
|
|
197
|
-
const count = getCountOverrideFromUrl(request.url) ?? configuredRoute.count;
|
|
198
|
-
const payloadKey = toCollectionKey(pathname);
|
|
199
|
-
sendJson(response, 200, {
|
|
200
|
-
[payloadKey]: Array.from({ length: count }, () => createRecordFromSchemaName(configuredRoute.schema, schemaDefinitions)),
|
|
201
|
-
}, { cors: corsEnabled, delay: responseDelay });
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
194
|
if (apiSpec) {
|
|
206
195
|
const matchedRoute = apiSpec.routes.find((route) => route.method === request.method && route.path === pathname);
|
|
207
196
|
if (matchedRoute) {
|
|
@@ -228,7 +217,9 @@ const startServer = ({ host = DEFAULT_HOST, port = DEFAULT_PORT, apiSpec, runtim
|
|
|
228
217
|
requestBody = JSON.parse(raw);
|
|
229
218
|
}
|
|
230
219
|
}
|
|
231
|
-
catch {
|
|
220
|
+
catch {
|
|
221
|
+
requestBody = {};
|
|
222
|
+
}
|
|
232
223
|
const generatedRecord = createRecordFromRoute(matchedRoute);
|
|
233
224
|
const merged = { ...generatedRecord, ...requestBody };
|
|
234
225
|
const statusCode = method === "POST" ? 201 : 200;
|
|
@@ -240,6 +231,17 @@ const startServer = ({ host = DEFAULT_HOST, port = DEFAULT_PORT, apiSpec, runtim
|
|
|
240
231
|
return;
|
|
241
232
|
}
|
|
242
233
|
}
|
|
234
|
+
if (request.method === "GET") {
|
|
235
|
+
const configuredRoute = configuredRoutes[pathname];
|
|
236
|
+
if (configuredRoute) {
|
|
237
|
+
const count = getCountOverrideFromUrl(request.url) ?? configuredRoute.count;
|
|
238
|
+
const payloadKey = toCollectionKey(pathname);
|
|
239
|
+
sendJson(response, 200, {
|
|
240
|
+
[payloadKey]: Array.from({ length: count }, () => createRecordFromSchemaName(configuredRoute.schema, schemaDefinitions)),
|
|
241
|
+
}, { cors: corsEnabled, delay: responseDelay });
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
243
245
|
sendJson(response, 404, {
|
|
244
246
|
message: "Route not found",
|
|
245
247
|
availableRoutes,
|