fexapi 0.2.0 → 0.2.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/dist/cli/args.js +2 -2
- package/dist/cli/ui.d.ts +17 -0
- package/dist/cli/ui.d.ts.map +1 -1
- package/dist/cli/ui.js +153 -7
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +87 -19
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +46 -1
- package/dist/server.js +1 -1
- package/package.json +1 -1
package/dist/cli/args.js
CHANGED
|
@@ -90,7 +90,7 @@ const parseServeOptions = (serveArgs) => {
|
|
|
90
90
|
if (positionalArgs.length > 0) {
|
|
91
91
|
return { error: `Unexpected argument(s): ${positionalArgs.join(", ")}` };
|
|
92
92
|
}
|
|
93
|
-
const host = hostValue ?? "
|
|
93
|
+
const host = hostValue ?? "localhost";
|
|
94
94
|
const port = portValue ? Number(portValue) : undefined;
|
|
95
95
|
if (port !== undefined &&
|
|
96
96
|
(!Number.isInteger(port) || port < 1 || port > 65535)) {
|
|
@@ -150,7 +150,7 @@ const parseDevOptions = (devArgs) => {
|
|
|
150
150
|
if (positionalArgs.length > 0) {
|
|
151
151
|
return { error: `Unexpected argument(s): ${positionalArgs.join(", ")}` };
|
|
152
152
|
}
|
|
153
|
-
const host = hostValue ?? "
|
|
153
|
+
const host = hostValue ?? "localhost";
|
|
154
154
|
const port = portValue ? Number(portValue) : undefined;
|
|
155
155
|
if (port !== undefined &&
|
|
156
156
|
(!Number.isInteger(port) || port < 1 || port > 65535)) {
|
package/dist/cli/ui.d.ts
CHANGED
|
@@ -2,12 +2,28 @@ export declare const ui: {
|
|
|
2
2
|
bold: (text: string) => string;
|
|
3
3
|
dim: (text: string) => string;
|
|
4
4
|
cyan: (text: string) => string;
|
|
5
|
+
magenta: (text: string) => string;
|
|
5
6
|
blue: (text: string) => string;
|
|
6
7
|
green: (text: string) => string;
|
|
7
8
|
yellow: (text: string) => string;
|
|
8
9
|
red: (text: string) => string;
|
|
9
10
|
gray: (text: string) => string;
|
|
11
|
+
white: (text: string) => string;
|
|
10
12
|
};
|
|
13
|
+
type SpinnerController = {
|
|
14
|
+
update: (text: string) => void;
|
|
15
|
+
succeed: (text: string) => void;
|
|
16
|
+
fail: (text: string) => void;
|
|
17
|
+
stop: () => void;
|
|
18
|
+
};
|
|
19
|
+
export declare const startSpinner: (initialText: string) => SpinnerController;
|
|
20
|
+
export declare const nowMs: () => number;
|
|
21
|
+
export declare const formatDuration: (startMs: number) => string;
|
|
22
|
+
export declare const printSummaryCard: (title: string, rows: Array<{
|
|
23
|
+
label: string;
|
|
24
|
+
value: string;
|
|
25
|
+
}>) => void;
|
|
26
|
+
export declare const printGroupHeader: (title: string) => void;
|
|
11
27
|
export declare const printBanner: () => void;
|
|
12
28
|
export declare const printSpacer: () => void;
|
|
13
29
|
export declare const logInfo: (message: string) => void;
|
|
@@ -16,4 +32,5 @@ export declare const logWarn: (message: string) => void;
|
|
|
16
32
|
export declare const logError: (message: string) => void;
|
|
17
33
|
export declare const logStep: (message: string) => void;
|
|
18
34
|
export declare const formatCommand: (command: string) => string;
|
|
35
|
+
export {};
|
|
19
36
|
//# 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":"AAgBA,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,IAqDF,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,OAAO,MAAM,KAAG,IAQhD,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"}
|
package/dist/cli/ui.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
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.ui = void 0;
|
|
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;
|
|
4
4
|
const shouldUseColor = () => {
|
|
5
5
|
return Boolean(process.stdout.isTTY);
|
|
6
6
|
};
|
|
7
7
|
const colorEnabled = shouldUseColor();
|
|
8
|
+
const interactive = Boolean(process.stdout.isTTY);
|
|
9
|
+
const DEFAULT_TERMINAL_WIDTH = 80;
|
|
8
10
|
const paint = (code, text) => {
|
|
9
11
|
if (!colorEnabled) {
|
|
10
12
|
return text;
|
|
@@ -15,14 +17,158 @@ exports.ui = {
|
|
|
15
17
|
bold: (text) => paint("1", text),
|
|
16
18
|
dim: (text) => paint("2", text),
|
|
17
19
|
cyan: (text) => paint("36", text),
|
|
20
|
+
magenta: (text) => paint("35", text),
|
|
18
21
|
blue: (text) => paint("94", text),
|
|
19
22
|
green: (text) => paint("32", text),
|
|
20
23
|
yellow: (text) => paint("33", text),
|
|
21
24
|
red: (text) => paint("31", text),
|
|
22
25
|
gray: (text) => paint("90", text),
|
|
26
|
+
white: (text) => paint("97", text),
|
|
23
27
|
};
|
|
28
|
+
const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
29
|
+
const ANSI_REGEX = /\u001b\[[0-9;]*m/g;
|
|
30
|
+
const stripAnsi = (text) => text.replace(ANSI_REGEX, "");
|
|
31
|
+
const visibleLength = (text) => stripAnsi(text).length;
|
|
32
|
+
const getTerminalWidth = () => {
|
|
33
|
+
const columns = process.stdout.columns;
|
|
34
|
+
if (!columns || Number.isNaN(columns)) {
|
|
35
|
+
return DEFAULT_TERMINAL_WIDTH;
|
|
36
|
+
}
|
|
37
|
+
return Math.max(40, columns);
|
|
38
|
+
};
|
|
39
|
+
const truncateText = (text, maxLength) => {
|
|
40
|
+
if (text.length <= maxLength) {
|
|
41
|
+
return text;
|
|
42
|
+
}
|
|
43
|
+
if (maxLength <= 1) {
|
|
44
|
+
return text.slice(0, maxLength);
|
|
45
|
+
}
|
|
46
|
+
return `${text.slice(0, maxLength - 1)}…`;
|
|
47
|
+
};
|
|
48
|
+
const styleCardValue = (value) => {
|
|
49
|
+
const normalized = value.trim().toLowerCase();
|
|
50
|
+
if (normalized === "changed" ||
|
|
51
|
+
normalized === "enabled" ||
|
|
52
|
+
normalized === "running") {
|
|
53
|
+
return exports.ui.green(exports.ui.bold(value));
|
|
54
|
+
}
|
|
55
|
+
if (normalized === "cached" || normalized === "disabled") {
|
|
56
|
+
return exports.ui.gray(value);
|
|
57
|
+
}
|
|
58
|
+
if (normalized === "stopped" || normalized === "failed") {
|
|
59
|
+
return exports.ui.red(exports.ui.bold(value));
|
|
60
|
+
}
|
|
61
|
+
return exports.ui.white(exports.ui.bold(value));
|
|
62
|
+
};
|
|
63
|
+
const clearCurrentLine = () => {
|
|
64
|
+
if (!interactive) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
process.stdout.write("\r\u001b[2K");
|
|
68
|
+
};
|
|
69
|
+
const startSpinner = (initialText) => {
|
|
70
|
+
if (!interactive) {
|
|
71
|
+
(0, exports.logStep)(initialText);
|
|
72
|
+
return {
|
|
73
|
+
update: (text) => (0, exports.logStep)(text),
|
|
74
|
+
succeed: (text) => (0, exports.logSuccess)(text),
|
|
75
|
+
fail: (text) => (0, exports.logError)(text),
|
|
76
|
+
stop: () => undefined,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
let text = initialText;
|
|
80
|
+
let frameIndex = 0;
|
|
81
|
+
const render = () => {
|
|
82
|
+
const frame = SPINNER_FRAMES[frameIndex % SPINNER_FRAMES.length] ?? "-";
|
|
83
|
+
frameIndex += 1;
|
|
84
|
+
process.stdout.write(`\r${exports.ui.cyan(frame)} ${exports.ui.white(exports.ui.bold(text))}`);
|
|
85
|
+
};
|
|
86
|
+
render();
|
|
87
|
+
const timer = setInterval(render, 80);
|
|
88
|
+
return {
|
|
89
|
+
update: (nextText) => {
|
|
90
|
+
text = nextText;
|
|
91
|
+
},
|
|
92
|
+
succeed: (finalText) => {
|
|
93
|
+
clearInterval(timer);
|
|
94
|
+
clearCurrentLine();
|
|
95
|
+
console.log(`${exports.ui.green("✓")} ${exports.ui.white(finalText)}`);
|
|
96
|
+
},
|
|
97
|
+
fail: (finalText) => {
|
|
98
|
+
clearInterval(timer);
|
|
99
|
+
clearCurrentLine();
|
|
100
|
+
console.log(`${exports.ui.red("✕")} ${exports.ui.white(finalText)}`);
|
|
101
|
+
},
|
|
102
|
+
stop: () => {
|
|
103
|
+
clearInterval(timer);
|
|
104
|
+
clearCurrentLine();
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
exports.startSpinner = startSpinner;
|
|
109
|
+
const nowMs = () => Date.now();
|
|
110
|
+
exports.nowMs = nowMs;
|
|
111
|
+
const formatDuration = (startMs) => {
|
|
112
|
+
const elapsedMs = Date.now() - startMs;
|
|
113
|
+
if (elapsedMs < 1000) {
|
|
114
|
+
return `${elapsedMs}ms`;
|
|
115
|
+
}
|
|
116
|
+
return `${(elapsedMs / 1000).toFixed(2)}s`;
|
|
117
|
+
};
|
|
118
|
+
exports.formatDuration = formatDuration;
|
|
119
|
+
const printSummaryCard = (title, rows) => {
|
|
120
|
+
const terminalWidth = getTerminalWidth();
|
|
121
|
+
const compactMode = terminalWidth < 64;
|
|
122
|
+
if (compactMode) {
|
|
123
|
+
console.log(exports.ui.gray(`--- ${title} ---`));
|
|
124
|
+
for (const row of rows) {
|
|
125
|
+
console.log(`${exports.ui.dim(row.label)}: ${row.value}`);
|
|
126
|
+
}
|
|
127
|
+
console.log(exports.ui.gray("---------------"));
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const safeRows = rows.map((row) => ({
|
|
131
|
+
label: row.label,
|
|
132
|
+
value: row.value,
|
|
133
|
+
}));
|
|
134
|
+
const cardWidth = terminalWidth - 2;
|
|
135
|
+
const innerWidth = cardWidth - 2;
|
|
136
|
+
const labelWidth = Math.min(20, Math.max(10, ...safeRows.map((row) => visibleLength(row.label))));
|
|
137
|
+
const valueSpace = Math.max(8, innerWidth - 3 - labelWidth - 3);
|
|
138
|
+
const topBorder = `┌${"─".repeat(innerWidth)}┐`;
|
|
139
|
+
const divider = `├${"─".repeat(innerWidth)}┤`;
|
|
140
|
+
const bottomBorder = `└${"─".repeat(innerWidth)}┘`;
|
|
141
|
+
console.log(exports.ui.gray(topBorder));
|
|
142
|
+
const renderedTitle = truncateText(title, innerWidth - 2);
|
|
143
|
+
const titlePadding = " ".repeat(Math.max(0, innerWidth - 2 - visibleLength(renderedTitle)));
|
|
144
|
+
console.log(`│ ${exports.ui.bold(exports.ui.cyan(renderedTitle))}${titlePadding} │`);
|
|
145
|
+
console.log(exports.ui.gray(divider));
|
|
146
|
+
for (const row of safeRows) {
|
|
147
|
+
const rawValue = stripAnsi(row.value);
|
|
148
|
+
const value = truncateText(rawValue, valueSpace);
|
|
149
|
+
const label = row.label.padEnd(labelWidth, " ");
|
|
150
|
+
const styledValue = styleCardValue(value);
|
|
151
|
+
const spaces = " ".repeat(Math.max(1, innerWidth - 3 - visibleLength(label) - 3 - visibleLength(value)));
|
|
152
|
+
console.log(`│ ${exports.ui.dim(label)} ${exports.ui.gray("::")} ${styledValue}${spaces}│`);
|
|
153
|
+
}
|
|
154
|
+
console.log(exports.ui.gray(bottomBorder));
|
|
155
|
+
};
|
|
156
|
+
exports.printSummaryCard = printSummaryCard;
|
|
157
|
+
const printGroupHeader = (title) => {
|
|
158
|
+
const terminalWidth = getTerminalWidth();
|
|
159
|
+
const marker = exports.ui.gray("──");
|
|
160
|
+
const text = ` ${exports.ui.bold(title)} `;
|
|
161
|
+
const lineLength = Math.max(0, terminalWidth - visibleLength(title) - 4);
|
|
162
|
+
const left = marker;
|
|
163
|
+
const right = exports.ui.gray("─".repeat(Math.max(0, lineLength - 2)));
|
|
164
|
+
console.log(`${left}${text}${right}`);
|
|
165
|
+
};
|
|
166
|
+
exports.printGroupHeader = printGroupHeader;
|
|
24
167
|
const printBanner = () => {
|
|
25
|
-
console.log(exports.ui.bold(exports.ui.cyan("fexapi")) +
|
|
168
|
+
console.log(exports.ui.bold(exports.ui.cyan("fexapi")) +
|
|
169
|
+
exports.ui.gray(" ") +
|
|
170
|
+
exports.ui.magenta("mock") +
|
|
171
|
+
exports.ui.gray(" api toolkit"));
|
|
26
172
|
};
|
|
27
173
|
exports.printBanner = printBanner;
|
|
28
174
|
const printSpacer = () => {
|
|
@@ -30,23 +176,23 @@ const printSpacer = () => {
|
|
|
30
176
|
};
|
|
31
177
|
exports.printSpacer = printSpacer;
|
|
32
178
|
const logInfo = (message) => {
|
|
33
|
-
console.log(`${exports.ui.blue("info")} ${message}`);
|
|
179
|
+
console.log(`${exports.ui.blue("•")} ${exports.ui.blue("info")} ${exports.ui.white(message)}`);
|
|
34
180
|
};
|
|
35
181
|
exports.logInfo = logInfo;
|
|
36
182
|
const logSuccess = (message) => {
|
|
37
|
-
console.log(`${exports.ui.green("ok ")} ${message}`);
|
|
183
|
+
console.log(`${exports.ui.green("✓")} ${exports.ui.green("ok ")} ${exports.ui.white(message)}`);
|
|
38
184
|
};
|
|
39
185
|
exports.logSuccess = logSuccess;
|
|
40
186
|
const logWarn = (message) => {
|
|
41
|
-
console.log(`${exports.ui.yellow("warn")} ${message}`);
|
|
187
|
+
console.log(`${exports.ui.yellow("!")} ${exports.ui.yellow("warn")} ${exports.ui.white(message)}`);
|
|
42
188
|
};
|
|
43
189
|
exports.logWarn = logWarn;
|
|
44
190
|
const logError = (message) => {
|
|
45
|
-
console.error(`${exports.ui.red("err ")} ${message}`);
|
|
191
|
+
console.error(`${exports.ui.red("✕")} ${exports.ui.red("err ")} ${exports.ui.white(message)}`);
|
|
46
192
|
};
|
|
47
193
|
exports.logError = logError;
|
|
48
194
|
const logStep = (message) => {
|
|
49
|
-
console.log(`${exports.ui.cyan("
|
|
195
|
+
console.log(`${exports.ui.cyan("›")} ${exports.ui.white(message)}`);
|
|
50
196
|
};
|
|
51
197
|
exports.logStep = logStep;
|
|
52
198
|
const formatCommand = (command) => {
|
|
@@ -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":"AAyBA,eAAO,MAAM,kBAAkB,QAAO,MA6KrC,CAAC"}
|
|
@@ -7,7 +7,14 @@ const constants_1 = require("../constants");
|
|
|
7
7
|
const ui_1 = require("../cli/ui");
|
|
8
8
|
const paths_1 = require("../project/paths");
|
|
9
9
|
const schema_1 = require("../schema");
|
|
10
|
+
const createRouteSignature = (value) => {
|
|
11
|
+
return JSON.stringify({
|
|
12
|
+
port: value.port,
|
|
13
|
+
routes: value.routes,
|
|
14
|
+
});
|
|
15
|
+
};
|
|
10
16
|
const generateFromSchema = () => {
|
|
17
|
+
const startedAtMs = (0, ui_1.nowMs)();
|
|
11
18
|
const projectRoot = (0, paths_1.resolveProjectRoot)();
|
|
12
19
|
if (!projectRoot) {
|
|
13
20
|
(0, ui_1.logError)("Could not find package.json in this directory or parent directories.");
|
|
@@ -19,18 +26,41 @@ const generateFromSchema = () => {
|
|
|
19
26
|
const configPath = (0, node_path_1.join)(projectRoot, "fexapi.config.json");
|
|
20
27
|
if (!(0, node_fs_1.existsSync)(schemaPath)) {
|
|
21
28
|
(0, ui_1.logError)(`Schema file not found: ${schemaPath}`);
|
|
22
|
-
(0, ui_1.
|
|
29
|
+
(0, ui_1.logError)("Run `fexapi init` first.");
|
|
23
30
|
return 1;
|
|
24
31
|
}
|
|
32
|
+
(0, ui_1.printGroupHeader)("Generate");
|
|
33
|
+
const generationSpinner = (0, ui_1.startSpinner)("Reading schema");
|
|
25
34
|
const schemaText = (0, node_fs_1.readFileSync)(schemaPath, "utf-8");
|
|
35
|
+
generationSpinner.update("Parsing schema routes");
|
|
26
36
|
const parsed = (0, schema_1.parseFexapiSchema)(schemaText);
|
|
27
37
|
if (parsed.errors.length > 0 || !parsed.schema) {
|
|
38
|
+
generationSpinner.fail("Schema parsing failed");
|
|
28
39
|
(0, ui_1.logError)("Failed to generate API from schema.fexapi");
|
|
29
40
|
for (const error of parsed.errors) {
|
|
30
41
|
(0, ui_1.logError)(`- ${error}`);
|
|
31
42
|
}
|
|
32
43
|
return 1;
|
|
33
44
|
}
|
|
45
|
+
generationSpinner.update("Resolving cache state");
|
|
46
|
+
const previousGenerated = (0, node_fs_1.existsSync)(generatedPath)
|
|
47
|
+
? (() => {
|
|
48
|
+
try {
|
|
49
|
+
return JSON.parse((0, node_fs_1.readFileSync)(generatedPath, "utf-8"));
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
})()
|
|
55
|
+
: undefined;
|
|
56
|
+
const nextSignature = createRouteSignature({
|
|
57
|
+
port: parsed.schema.port,
|
|
58
|
+
routes: parsed.schema.routes,
|
|
59
|
+
});
|
|
60
|
+
const previousSignature = previousGenerated
|
|
61
|
+
? createRouteSignature(previousGenerated)
|
|
62
|
+
: undefined;
|
|
63
|
+
const schemaChanged = nextSignature !== previousSignature;
|
|
34
64
|
const generated = {
|
|
35
65
|
schemaVersion: 1,
|
|
36
66
|
generatedAt: new Date().toISOString(),
|
|
@@ -38,25 +68,26 @@ const generateFromSchema = () => {
|
|
|
38
68
|
routes: parsed.schema.routes,
|
|
39
69
|
};
|
|
40
70
|
(0, node_fs_1.mkdirSync)(migrationsDirectoryPath, { recursive: true });
|
|
41
|
-
const existingMigrationFiles = (0, node_fs_1.readdirSync)(migrationsDirectoryPath, {
|
|
42
|
-
withFileTypes: true,
|
|
43
|
-
})
|
|
44
|
-
.filter((entry) => entry.isFile() && entry.name.endsWith(".json"))
|
|
45
|
-
.map((entry) => (0, node_path_1.join)(migrationsDirectoryPath, entry.name));
|
|
46
|
-
for (const migrationFilePath of existingMigrationFiles) {
|
|
47
|
-
(0, node_fs_1.unlinkSync)(migrationFilePath);
|
|
48
|
-
}
|
|
49
|
-
const migrationId = new Date().toISOString().replace(/[.:]/g, "-");
|
|
50
71
|
const migrationPath = (0, node_path_1.join)(migrationsDirectoryPath, "schema.json");
|
|
51
72
|
const migration = {
|
|
52
|
-
migrationId,
|
|
73
|
+
migrationId: new Date().toISOString().replace(/[.:]/g, "-"),
|
|
53
74
|
sourceSchema: "fexapi/schema.fexapi",
|
|
54
75
|
createdAt: generated.generatedAt,
|
|
55
76
|
port: parsed.schema.port,
|
|
56
77
|
routes: parsed.schema.routes,
|
|
57
78
|
};
|
|
58
|
-
|
|
59
|
-
|
|
79
|
+
let generatedStatus = "cached";
|
|
80
|
+
let migrationStatus = "cached";
|
|
81
|
+
if (schemaChanged || !(0, node_fs_1.existsSync)(generatedPath)) {
|
|
82
|
+
generationSpinner.update("Writing generated API spec");
|
|
83
|
+
(0, node_fs_1.writeFileSync)(generatedPath, `${JSON.stringify(generated, null, 2)}\n`, "utf-8");
|
|
84
|
+
generatedStatus = "changed";
|
|
85
|
+
}
|
|
86
|
+
if (schemaChanged || !(0, node_fs_1.existsSync)(migrationPath)) {
|
|
87
|
+
generationSpinner.update("Updating migration snapshot");
|
|
88
|
+
(0, node_fs_1.writeFileSync)(migrationPath, `${JSON.stringify(migration, null, 2)}\n`, "utf-8");
|
|
89
|
+
migrationStatus = "changed";
|
|
90
|
+
}
|
|
60
91
|
let existingConfig = {};
|
|
61
92
|
if ((0, node_fs_1.existsSync)(configPath)) {
|
|
62
93
|
try {
|
|
@@ -70,14 +101,51 @@ const generateFromSchema = () => {
|
|
|
70
101
|
...existingConfig,
|
|
71
102
|
schemaPath: "fexapi/schema.fexapi",
|
|
72
103
|
generatedPath: constants_1.GENERATED_SPEC_RELATIVE_PATH,
|
|
73
|
-
lastGeneratedAt: new Date().toISOString(),
|
|
104
|
+
...(schemaChanged ? { lastGeneratedAt: new Date().toISOString() } : {}),
|
|
74
105
|
};
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
(0,
|
|
106
|
+
generationSpinner.update("Syncing project config");
|
|
107
|
+
const nextConfigText = `${JSON.stringify(updatedConfig, null, 2)}\n`;
|
|
108
|
+
const previousConfigText = (0, node_fs_1.existsSync)(configPath)
|
|
109
|
+
? (0, node_fs_1.readFileSync)(configPath, "utf-8")
|
|
110
|
+
: undefined;
|
|
111
|
+
let configStatus = "cached";
|
|
112
|
+
if (previousConfigText !== nextConfigText) {
|
|
113
|
+
(0, node_fs_1.writeFileSync)(configPath, nextConfigText, "utf-8");
|
|
114
|
+
configStatus = "changed";
|
|
115
|
+
}
|
|
116
|
+
generationSpinner.succeed(`Generate complete (${schemaChanged ? "changed" : "cached"})`);
|
|
78
117
|
(0, ui_1.printSpacer)();
|
|
79
|
-
(0, ui_1.
|
|
80
|
-
(0, ui_1.
|
|
118
|
+
(0, ui_1.printGroupHeader)("Summary");
|
|
119
|
+
(0, ui_1.printSummaryCard)("Generate Summary", [
|
|
120
|
+
{
|
|
121
|
+
label: "routes",
|
|
122
|
+
value: String(parsed.schema.routes.length),
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
label: "port",
|
|
126
|
+
value: String(parsed.schema.port),
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
label: "schema source",
|
|
130
|
+
value: "fexapi/schema.fexapi",
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
label: "generated.api.json",
|
|
134
|
+
value: generatedStatus,
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
label: "migration",
|
|
138
|
+
value: migrationStatus,
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
label: "config",
|
|
142
|
+
value: configStatus,
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
label: "time",
|
|
146
|
+
value: (0, ui_1.formatDuration)(startedAtMs),
|
|
147
|
+
},
|
|
148
|
+
]);
|
|
81
149
|
return 0;
|
|
82
150
|
};
|
|
83
151
|
exports.generateFromSchema = generateFromSchema;
|
|
@@ -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":"AAuMA,eAAO,MAAM,iBAAiB,GAAU,YAErC;IACD,KAAK,EAAE,OAAO,CAAC;CAChB,KAAG,OAAO,CAAC,MAAM,CA2MjB,CAAC"}
|
package/dist/commands/init.js
CHANGED
|
@@ -9,7 +9,7 @@ const constants_1 = require("../constants");
|
|
|
9
9
|
const ui_1 = require("../cli/ui");
|
|
10
10
|
const detect_1 = require("../project/detect");
|
|
11
11
|
const paths_1 = require("../project/paths");
|
|
12
|
-
const DEFAULT_INIT_PORT =
|
|
12
|
+
const DEFAULT_INIT_PORT = 4000;
|
|
13
13
|
const parseYesNo = (value, defaultValue) => {
|
|
14
14
|
const normalized = value.trim().toLowerCase();
|
|
15
15
|
if (!normalized) {
|
|
@@ -141,6 +141,7 @@ const SAMPLE_POST_SCHEMA = [
|
|
|
141
141
|
" type: date",
|
|
142
142
|
].join("\n");
|
|
143
143
|
const initializeProject = async ({ force, }) => {
|
|
144
|
+
const initStartedAtMs = (0, ui_1.nowMs)();
|
|
144
145
|
const packageJsonPath = (0, paths_1.findClosestPackageJson)(process.cwd());
|
|
145
146
|
if (!packageJsonPath) {
|
|
146
147
|
(0, ui_1.logError)("Could not find package.json in this directory or parent directories.");
|
|
@@ -156,6 +157,8 @@ const initializeProject = async ({ force, }) => {
|
|
|
156
157
|
const userSchemaPath = (0, node_path_1.join)(schemasDirectoryPath, "user.yaml");
|
|
157
158
|
const postSchemaPath = (0, node_path_1.join)(schemasDirectoryPath, "post.yaml");
|
|
158
159
|
const wizardAnswers = await askInitWizardQuestions();
|
|
160
|
+
(0, ui_1.printGroupHeader)("Init");
|
|
161
|
+
const initSpinner = (0, ui_1.startSpinner)("Scaffolding fexapi project files");
|
|
159
162
|
(0, node_fs_1.mkdirSync)(fexapiDirectoryPath, { recursive: true });
|
|
160
163
|
const configExists = (0, node_fs_1.existsSync)(configPath);
|
|
161
164
|
const schemaExists = (0, node_fs_1.existsSync)(schemaPath);
|
|
@@ -171,13 +174,16 @@ const initializeProject = async ({ force, }) => {
|
|
|
171
174
|
createdAt: new Date().toISOString(),
|
|
172
175
|
};
|
|
173
176
|
if (!configExists || force) {
|
|
177
|
+
initSpinner.update("Writing fexapi.config.json");
|
|
174
178
|
(0, node_fs_1.writeFileSync)(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf-8");
|
|
175
179
|
}
|
|
176
180
|
if (!schemaExists || force) {
|
|
181
|
+
initSpinner.update("Writing fexapi/schema.fexapi");
|
|
177
182
|
(0, node_fs_1.writeFileSync)(schemaPath, `${(0, detect_1.getSchemaTemplate)(detectedProject.primaryFramework, wizardAnswers.port)}\n`, "utf-8");
|
|
178
183
|
}
|
|
179
184
|
const runtimeConfigExists = (0, node_fs_1.existsSync)(runtimeConfigPath);
|
|
180
185
|
if (!runtimeConfigExists || force) {
|
|
186
|
+
initSpinner.update("Writing fexapi.config.js");
|
|
181
187
|
(0, node_fs_1.writeFileSync)(runtimeConfigPath, `${getRuntimeConfigTemplate({
|
|
182
188
|
port: wizardAnswers.port,
|
|
183
189
|
cors: wizardAnswers.cors,
|
|
@@ -190,6 +196,7 @@ const initializeProject = async ({ force, }) => {
|
|
|
190
196
|
(0, node_fs_1.mkdirSync)(schemasDirectoryPath, { recursive: true });
|
|
191
197
|
const userSchemaExists = (0, node_fs_1.existsSync)(userSchemaPath);
|
|
192
198
|
if (!userSchemaExists || force) {
|
|
199
|
+
initSpinner.update("Writing sample user schema");
|
|
193
200
|
(0, node_fs_1.writeFileSync)(userSchemaPath, `${SAMPLE_USER_SCHEMA}\n`, "utf-8");
|
|
194
201
|
userSchemaStatus = userSchemaExists ? "overwritten" : "created";
|
|
195
202
|
}
|
|
@@ -198,6 +205,7 @@ const initializeProject = async ({ force, }) => {
|
|
|
198
205
|
}
|
|
199
206
|
const postSchemaExists = (0, node_fs_1.existsSync)(postSchemaPath);
|
|
200
207
|
if (!postSchemaExists || force) {
|
|
208
|
+
initSpinner.update("Writing sample post schema");
|
|
201
209
|
(0, node_fs_1.writeFileSync)(postSchemaPath, `${SAMPLE_POST_SCHEMA}\n`, "utf-8");
|
|
202
210
|
postSchemaStatus = postSchemaExists ? "overwritten" : "created";
|
|
203
211
|
}
|
|
@@ -205,6 +213,7 @@ const initializeProject = async ({ force, }) => {
|
|
|
205
213
|
postSchemaStatus = "exists";
|
|
206
214
|
}
|
|
207
215
|
}
|
|
216
|
+
initSpinner.succeed("Project scaffolding complete");
|
|
208
217
|
(0, ui_1.logSuccess)(`Initialized fexapi in ${projectRoot}`);
|
|
209
218
|
(0, ui_1.logInfo)(`Detected framework: ${detectedProject.primaryFramework}`);
|
|
210
219
|
(0, ui_1.logInfo)(`Detected frameworks: ${detectedProject.frameworks.join(", ")}`);
|
|
@@ -212,6 +221,7 @@ const initializeProject = async ({ force, }) => {
|
|
|
212
221
|
(0, ui_1.logInfo)(`Detected tooling: ${detectedProject.tooling.join(", ")}`);
|
|
213
222
|
}
|
|
214
223
|
(0, ui_1.printSpacer)();
|
|
224
|
+
(0, ui_1.printGroupHeader)("Files");
|
|
215
225
|
if (configExists && !force) {
|
|
216
226
|
(0, ui_1.logWarn)(`Exists ${configPath}`);
|
|
217
227
|
}
|
|
@@ -266,6 +276,41 @@ const initializeProject = async ({ force, }) => {
|
|
|
266
276
|
(0, ui_1.logWarn)("No known framework dependency found. Update fexapi.config.json and schema.fexapi if needed.");
|
|
267
277
|
}
|
|
268
278
|
(0, ui_1.printSpacer)();
|
|
279
|
+
(0, ui_1.printGroupHeader)("Summary");
|
|
280
|
+
const createdFiles = [
|
|
281
|
+
!configExists || force,
|
|
282
|
+
!schemaExists || force,
|
|
283
|
+
!runtimeConfigExists || force,
|
|
284
|
+
userSchemaStatus === "created" || userSchemaStatus === "overwritten",
|
|
285
|
+
postSchemaStatus === "created" || postSchemaStatus === "overwritten",
|
|
286
|
+
].filter(Boolean).length;
|
|
287
|
+
(0, ui_1.printSummaryCard)("Init Summary", [
|
|
288
|
+
{
|
|
289
|
+
label: "framework",
|
|
290
|
+
value: detectedProject.primaryFramework,
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
label: "port",
|
|
294
|
+
value: String(wizardAnswers.port),
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
label: "cors",
|
|
298
|
+
value: wizardAnswers.cors ? "enabled" : "disabled",
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
label: "sample schemas",
|
|
302
|
+
value: wizardAnswers.generateSampleSchemas ? "enabled" : "disabled",
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
label: "files changed",
|
|
306
|
+
value: String(createdFiles),
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
label: "time",
|
|
310
|
+
value: (0, ui_1.formatDuration)(initStartedAtMs),
|
|
311
|
+
},
|
|
312
|
+
]);
|
|
313
|
+
(0, ui_1.printSpacer)();
|
|
269
314
|
(0, ui_1.logInfo)(`Next: ${(0, ui_1.formatCommand)("fexapi generate")} then ${(0, ui_1.formatCommand)("fexapi serve")}`);
|
|
270
315
|
return 0;
|
|
271
316
|
};
|
package/dist/server.js
CHANGED
|
@@ -4,7 +4,7 @@ exports.startServer = void 0;
|
|
|
4
4
|
const faker_1 = require("@faker-js/faker");
|
|
5
5
|
const node_http_1 = require("node:http");
|
|
6
6
|
const ui_1 = require("./cli/ui");
|
|
7
|
-
const DEFAULT_HOST = "
|
|
7
|
+
const DEFAULT_HOST = "localhost";
|
|
8
8
|
const DEFAULT_PORT = 4000;
|
|
9
9
|
const sendJson = (response, statusCode, payload, options) => {
|
|
10
10
|
const send = () => {
|