fexapi 0.2.1 → 0.2.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.
@@ -1 +1 @@
1
- {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../src/cli/help.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,SAAS,YAgErB,CAAC"}
1
+ {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../src/cli/help.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,SAAS,YA8DrB,CAAC"}
package/dist/cli/help.js CHANGED
@@ -23,7 +23,7 @@ const printHelp = () => {
23
23
  console.log(` ${(0, ui_1.formatCommand)("fexapi dev --watch")}`);
24
24
  console.log(` ${(0, ui_1.formatCommand)("fexapi dev --watch --log")}`);
25
25
  console.log(` ${(0, ui_1.formatCommand)("fexapi serve --log")}`);
26
- console.log(` ${(0, ui_1.formatCommand)("fexapi serve --host 127.0.0.1 --port 5000")}`);
26
+ console.log(` ${(0, ui_1.formatCommand)("fexapi serve --host localhost --port 5000")}`);
27
27
  console.log(` ${(0, ui_1.formatCommand)("fexapi --port 4000")}`);
28
28
  (0, ui_1.printSpacer)();
29
29
  console.log(ui_1.ui.bold("Package Manager Usage"));
@@ -32,13 +32,12 @@ const printHelp = () => {
32
32
  console.log(` ${(0, ui_1.formatCommand)("yarn dlx fexapi init")}`);
33
33
  (0, ui_1.printSpacer)();
34
34
  console.log(ui_1.ui.bold("fexapi init creates"));
35
- console.log(` ${ui_1.ui.dim("fexapi.config.json")}`);
36
35
  console.log(` ${ui_1.ui.dim("fexapi.config.js")}`);
37
36
  console.log(` ${ui_1.ui.dim("fexapi/schema.fexapi")}`);
38
37
  console.log(` ${ui_1.ui.dim("fexapi/schemas/*.yaml (optional, via wizard)")}`);
39
38
  (0, ui_1.printSpacer)();
40
39
  console.log(ui_1.ui.bold("Init wizard asks"));
41
- console.log(` ${ui_1.ui.dim("What port? (default: 3000)")}`);
40
+ console.log(` ${ui_1.ui.dim("What port? (default: 4000)")}`);
42
41
  console.log(` ${ui_1.ui.dim("Enable CORS? (Y/n)")}`);
43
42
  console.log(` ${ui_1.ui.dim("Generate sample schemas? (Y/n)")}`);
44
43
  (0, ui_1.printSpacer)();
@@ -49,6 +48,5 @@ const printHelp = () => {
49
48
  (0, ui_1.printSpacer)();
50
49
  console.log(ui_1.ui.bold("Generate output"));
51
50
  console.log(` ${ui_1.ui.dim("fexapi/generated.api.json")}`);
52
- console.log(` ${ui_1.ui.dim("fexapi/migrations/schema.json")}`);
53
51
  };
54
52
  exports.printHelp = printHelp;
package/dist/cli/ui.d.ts CHANGED
@@ -8,6 +8,7 @@ export declare const ui: {
8
8
  yellow: (text: string) => string;
9
9
  red: (text: string) => string;
10
10
  gray: (text: string) => string;
11
+ white: (text: string) => string;
11
12
  };
12
13
  type SpinnerController = {
13
14
  update: (text: string) => void;
@@ -22,6 +23,7 @@ export declare const printSummaryCard: (title: string, rows: Array<{
22
23
  label: string;
23
24
  value: string;
24
25
  }>) => void;
26
+ export declare const printGroupHeader: (title: string) => void;
25
27
  export declare const printBanner: () => void;
26
28
  export declare const printSpacer: () => void;
27
29
  export declare const logInfo: (message: string) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../src/cli/ui.ts"],"names":[],"mappings":"AAeA,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;CAC7B,CAAC;AAYF,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,IAyBF,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"}
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,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"}
package/dist/cli/ui.js CHANGED
@@ -1,11 +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.printSummaryCard = exports.formatDuration = exports.nowMs = exports.startSpinner = 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
8
  const interactive = Boolean(process.stdout.isTTY);
9
+ const DEFAULT_TERMINAL_WIDTH = 80;
9
10
  const paint = (code, text) => {
10
11
  if (!colorEnabled) {
11
12
  return text;
@@ -22,8 +23,43 @@ exports.ui = {
22
23
  yellow: (text) => paint("33", text),
23
24
  red: (text) => paint("31", text),
24
25
  gray: (text) => paint("90", text),
26
+ white: (text) => paint("97", text),
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));
25
62
  };
26
- const SPINNER_FRAMES = ["-", "\\", "|", "/"];
27
63
  const clearCurrentLine = () => {
28
64
  if (!interactive) {
29
65
  return;
@@ -45,7 +81,7 @@ const startSpinner = (initialText) => {
45
81
  const render = () => {
46
82
  const frame = SPINNER_FRAMES[frameIndex % SPINNER_FRAMES.length] ?? "-";
47
83
  frameIndex += 1;
48
- process.stdout.write(`\r${exports.ui.cyan(frame)} ${exports.ui.bold(text)}`);
84
+ process.stdout.write(`\r${exports.ui.cyan(frame)} ${exports.ui.white(exports.ui.bold(text))}`);
49
85
  };
50
86
  render();
51
87
  const timer = setInterval(render, 80);
@@ -56,12 +92,12 @@ const startSpinner = (initialText) => {
56
92
  succeed: (finalText) => {
57
93
  clearInterval(timer);
58
94
  clearCurrentLine();
59
- console.log(`${exports.ui.green("ok")} ${finalText}`);
95
+ console.log(`${exports.ui.green("")} ${exports.ui.white(finalText)}`);
60
96
  },
61
97
  fail: (finalText) => {
62
98
  clearInterval(timer);
63
99
  clearCurrentLine();
64
- console.log(`${exports.ui.red("err")} ${finalText}`);
100
+ console.log(`${exports.ui.red("")} ${exports.ui.white(finalText)}`);
65
101
  },
66
102
  stop: () => {
67
103
  clearInterval(timer);
@@ -81,25 +117,62 @@ const formatDuration = (startMs) => {
81
117
  };
82
118
  exports.formatDuration = formatDuration;
83
119
  const printSummaryCard = (title, rows) => {
84
- const contentRows = [{ label: "", value: title }, ...rows];
85
- const maxLabel = Math.max(...contentRows.map((row) => row.label.length));
86
- const maxValue = Math.max(...contentRows.map((row) => row.value.length));
87
- const cardWidth = Math.max(40, maxLabel + maxValue + 7);
88
- const border = `+${"-".repeat(cardWidth - 2)}+`;
89
- console.log(exports.ui.gray(border));
90
- const titleText = exports.ui.bold(exports.ui.cyan(title));
91
- const titleLine = `| ${titleText}${" ".repeat(Math.max(0, cardWidth - 4 - title.length))} |`;
92
- console.log(titleLine);
93
- console.log(exports.ui.gray(`|${"-".repeat(cardWidth - 2)}|`));
94
- for (const row of rows) {
95
- const label = row.label.padEnd(maxLabel, " ");
96
- const value = row.value;
97
- const spaces = " ".repeat(Math.max(1, cardWidth - 4 - label.length - 3 - value.length));
98
- console.log(`| ${exports.ui.dim(label)} : ${exports.ui.bold(value)}${spaces}|`);
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)} ${exports.ui.gray("::")} ${styleCardValue(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 maxCardWidth = 96;
135
+ const naturalInnerWidth = Math.max(36, visibleLength(title) + 2, ...safeRows.map((row) => visibleLength(row.label) + visibleLength(row.value) + 7));
136
+ const cardWidth = Math.max(40, Math.min(maxCardWidth, terminalWidth - 2, naturalInnerWidth + 2));
137
+ const innerWidth = cardWidth - 2;
138
+ const labelWidth = Math.min(20, Math.max(10, ...safeRows.map((row) => visibleLength(row.label))));
139
+ const valueSpace = Math.max(8, innerWidth - 7 - labelWidth);
140
+ const renderBoxLine = (content) => {
141
+ const remaining = Math.max(0, innerWidth - 2 - visibleLength(content));
142
+ return `│ ${content}${" ".repeat(remaining)} │`;
143
+ };
144
+ const topBorder = `┌${"─".repeat(innerWidth)}┐`;
145
+ const divider = `├${"─".repeat(innerWidth)}┤`;
146
+ const bottomBorder = `└${"─".repeat(innerWidth)}┘`;
147
+ console.log(exports.ui.gray(topBorder));
148
+ const renderedTitle = truncateText(title, innerWidth - 2);
149
+ console.log(renderBoxLine(exports.ui.bold(exports.ui.cyan(renderedTitle))));
150
+ console.log(exports.ui.gray(divider));
151
+ for (const row of safeRows) {
152
+ const rawValue = stripAnsi(row.value);
153
+ const value = truncateText(rawValue, valueSpace);
154
+ const label = row.label.padEnd(labelWidth, " ");
155
+ const styledValue = styleCardValue(value);
156
+ console.log(renderBoxLine(`${exports.ui.dim(label)} ${exports.ui.gray("::")} ${styledValue}`));
99
157
  }
100
- console.log(exports.ui.gray(border));
158
+ console.log(exports.ui.gray(bottomBorder));
101
159
  };
102
160
  exports.printSummaryCard = printSummaryCard;
161
+ const printGroupHeader = (title) => {
162
+ const terminalWidth = getTerminalWidth();
163
+ const compactMode = terminalWidth < 64;
164
+ if (compactMode) {
165
+ console.log(exports.ui.gray(`-- ${title} --`));
166
+ return;
167
+ }
168
+ const innerWidth = terminalWidth - 2;
169
+ const renderedTitle = truncateText(title, Math.max(1, innerWidth - 6));
170
+ const rawPrefix = `── ${renderedTitle} `;
171
+ const fill = Math.max(0, innerWidth - visibleLength(rawPrefix));
172
+ const prefix = `${exports.ui.gray("── ")}${exports.ui.bold(exports.ui.cyan(renderedTitle))} `;
173
+ console.log(`${exports.ui.gray("┌")}${prefix}${exports.ui.gray("─".repeat(fill))}${exports.ui.gray("┐")}`);
174
+ };
175
+ exports.printGroupHeader = printGroupHeader;
103
176
  const printBanner = () => {
104
177
  console.log(exports.ui.bold(exports.ui.cyan("fexapi")) +
105
178
  exports.ui.gray(" ") +
@@ -112,23 +185,23 @@ const printSpacer = () => {
112
185
  };
113
186
  exports.printSpacer = printSpacer;
114
187
  const logInfo = (message) => {
115
- console.log(`${exports.ui.blue("info")} ${message}`);
188
+ console.log(`${exports.ui.blue("•")} ${exports.ui.blue("info")} ${exports.ui.white(message)}`);
116
189
  };
117
190
  exports.logInfo = logInfo;
118
191
  const logSuccess = (message) => {
119
- console.log(`${exports.ui.green("ok ")} ${message}`);
192
+ console.log(`${exports.ui.green("✓")} ${exports.ui.green("ok ")} ${exports.ui.white(message)}`);
120
193
  };
121
194
  exports.logSuccess = logSuccess;
122
195
  const logWarn = (message) => {
123
- console.log(`${exports.ui.yellow("warn")} ${message}`);
196
+ console.log(`${exports.ui.yellow("!")} ${exports.ui.yellow("warn")} ${exports.ui.white(message)}`);
124
197
  };
125
198
  exports.logWarn = logWarn;
126
199
  const logError = (message) => {
127
- console.error(`${exports.ui.red("err ")} ${message}`);
200
+ console.error(`${exports.ui.red("✕")} ${exports.ui.red("err ")} ${exports.ui.white(message)}`);
128
201
  };
129
202
  exports.logError = logError;
130
203
  const logStep = (message) => {
131
- console.log(`${exports.ui.cyan("->")} ${message}`);
204
+ console.log(`${exports.ui.cyan("")} ${exports.ui.white(message)}`);
132
205
  };
133
206
  exports.logStep = logStep;
134
207
  const formatCommand = (command) => {
@@ -1 +1 @@
1
- {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAiCA,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,MAmHH,CAAC"}
1
+ {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AA8BA,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,MAmHH,CAAC"}
@@ -12,8 +12,7 @@ const normalizePath = (pathValue) => {
12
12
  };
13
13
  const isWatchedPath = (projectRoot, changedPath) => {
14
14
  const relativePath = normalizePath((0, node_path_1.relative)(projectRoot, changedPath));
15
- if (relativePath === "fexapi.config.js" ||
16
- relativePath === "fexapi.config.json") {
15
+ if (relativePath === "fexapi.config.js") {
17
16
  return true;
18
17
  }
19
18
  if (relativePath.startsWith("fexapi/")) {
@@ -1 +1 @@
1
- {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AAyBA,eAAO,MAAM,kBAAkB,QAAO,MA8KrC,CAAC"}
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AAwBA,eAAO,MAAM,kBAAkB,QAAO,MA8GrC,CAAC"}
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.generateFromSchema = void 0;
4
4
  const node_fs_1 = require("node:fs");
5
5
  const node_path_1 = require("node:path");
6
- const constants_1 = require("../constants");
7
6
  const ui_1 = require("../cli/ui");
8
7
  const paths_1 = require("../project/paths");
9
8
  const schema_1 = require("../schema");
@@ -22,13 +21,12 @@ const generateFromSchema = () => {
22
21
  }
23
22
  const schemaPath = (0, node_path_1.join)(projectRoot, "fexapi", "schema.fexapi");
24
23
  const generatedPath = (0, node_path_1.join)(projectRoot, "fexapi", "generated.api.json");
25
- const migrationsDirectoryPath = (0, node_path_1.join)(projectRoot, "fexapi", "migrations");
26
- const configPath = (0, node_path_1.join)(projectRoot, "fexapi.config.json");
27
24
  if (!(0, node_fs_1.existsSync)(schemaPath)) {
28
25
  (0, ui_1.logError)(`Schema file not found: ${schemaPath}`);
29
26
  (0, ui_1.logError)("Run `fexapi init` first.");
30
27
  return 1;
31
28
  }
29
+ (0, ui_1.printGroupHeader)("Generate");
32
30
  const generationSpinner = (0, ui_1.startSpinner)("Reading schema");
33
31
  const schemaText = (0, node_fs_1.readFileSync)(schemaPath, "utf-8");
34
32
  generationSpinner.update("Parsing schema routes");
@@ -66,62 +64,23 @@ const generateFromSchema = () => {
66
64
  port: parsed.schema.port,
67
65
  routes: parsed.schema.routes,
68
66
  };
69
- (0, node_fs_1.mkdirSync)(migrationsDirectoryPath, { recursive: true });
70
- const migrationPath = (0, node_path_1.join)(migrationsDirectoryPath, "schema.json");
71
- const migration = {
72
- migrationId: new Date().toISOString().replace(/[.:]/g, "-"),
73
- sourceSchema: "fexapi/schema.fexapi",
74
- createdAt: generated.generatedAt,
75
- port: parsed.schema.port,
76
- routes: parsed.schema.routes,
77
- };
78
67
  let generatedStatus = "cached";
79
- let migrationStatus = "cached";
80
68
  if (schemaChanged || !(0, node_fs_1.existsSync)(generatedPath)) {
81
69
  generationSpinner.update("Writing generated API spec");
82
70
  (0, node_fs_1.writeFileSync)(generatedPath, `${JSON.stringify(generated, null, 2)}\n`, "utf-8");
83
71
  generatedStatus = "changed";
84
72
  }
85
- if (schemaChanged || !(0, node_fs_1.existsSync)(migrationPath)) {
86
- generationSpinner.update("Updating migration snapshot");
87
- (0, node_fs_1.writeFileSync)(migrationPath, `${JSON.stringify(migration, null, 2)}\n`, "utf-8");
88
- migrationStatus = "changed";
89
- }
90
- let existingConfig = {};
91
- if ((0, node_fs_1.existsSync)(configPath)) {
92
- try {
93
- existingConfig = JSON.parse((0, node_fs_1.readFileSync)(configPath, "utf-8"));
94
- }
95
- catch {
96
- existingConfig = {};
97
- }
98
- }
99
- const updatedConfig = {
100
- ...existingConfig,
101
- schemaPath: "fexapi/schema.fexapi",
102
- generatedPath: constants_1.GENERATED_SPEC_RELATIVE_PATH,
103
- ...(schemaChanged ? { lastGeneratedAt: new Date().toISOString() } : {}),
104
- };
105
- generationSpinner.update("Syncing project config");
106
- const nextConfigText = `${JSON.stringify(updatedConfig, null, 2)}\n`;
107
- const previousConfigText = (0, node_fs_1.existsSync)(configPath)
108
- ? (0, node_fs_1.readFileSync)(configPath, "utf-8")
109
- : undefined;
110
- let configStatus = "cached";
111
- if (previousConfigText !== nextConfigText) {
112
- (0, node_fs_1.writeFileSync)(configPath, nextConfigText, "utf-8");
113
- configStatus = "changed";
114
- }
115
73
  generationSpinner.succeed(`Generate complete (${schemaChanged ? "changed" : "cached"})`);
116
74
  (0, ui_1.printSpacer)();
75
+ (0, ui_1.printGroupHeader)("Summary");
117
76
  (0, ui_1.printSummaryCard)("Generate Summary", [
118
77
  {
119
78
  label: "routes",
120
- value: ui_1.ui.cyan(String(parsed.schema.routes.length)),
79
+ value: String(parsed.schema.routes.length),
121
80
  },
122
81
  {
123
82
  label: "port",
124
- value: ui_1.ui.cyan(String(parsed.schema.port)),
83
+ value: String(parsed.schema.port),
125
84
  },
126
85
  {
127
86
  label: "schema source",
@@ -129,19 +88,11 @@ const generateFromSchema = () => {
129
88
  },
130
89
  {
131
90
  label: "generated.api.json",
132
- value: generatedStatus === "changed" ? ui_1.ui.green("changed") : ui_1.ui.gray("cached"),
133
- },
134
- {
135
- label: "migration",
136
- value: migrationStatus === "changed" ? ui_1.ui.green("changed") : ui_1.ui.gray("cached"),
137
- },
138
- {
139
- label: "config",
140
- value: configStatus === "changed" ? ui_1.ui.green("changed") : ui_1.ui.gray("cached"),
91
+ value: generatedStatus,
141
92
  },
142
93
  {
143
94
  label: "time",
144
- value: ui_1.ui.bold((0, ui_1.formatDuration)(startedAtMs)),
95
+ value: (0, ui_1.formatDuration)(startedAtMs),
145
96
  },
146
97
  ]);
147
98
  return 0;
@@ -1 +1 @@
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,CA0MjB,CAAC"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AA4OA,eAAO,MAAM,iBAAiB,GAAU,YAErC;IACD,KAAK,EAAE,OAAO,CAAC;CAChB,KAAG,OAAO,CAAC,MAAM,CA+KjB,CAAC"}
@@ -5,7 +5,6 @@ const node_fs_1 = require("node:fs");
5
5
  const node_path_1 = require("node:path");
6
6
  const promises_1 = require("node:readline/promises");
7
7
  const node_process_1 = require("node:process");
8
- const constants_1 = require("../constants");
9
8
  const ui_1 = require("../cli/ui");
10
9
  const detect_1 = require("../project/detect");
11
10
  const paths_1 = require("../project/paths");
@@ -32,10 +31,18 @@ const askInitWizardQuestions = async () => {
32
31
  };
33
32
  }
34
33
  const questionInterface = (0, promises_1.createInterface)({ input: node_process_1.stdin, output: node_process_1.stdout });
34
+ const totalSteps = 3;
35
+ const formatWizardPrompt = (step, question, hint, defaultValue) => {
36
+ 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
+ };
35
38
  try {
39
+ (0, ui_1.printSpacer)();
40
+ (0, ui_1.printGroupHeader)("Setup Wizard");
41
+ console.log(ui_1.ui.dim("Press Enter to accept defaults."));
42
+ (0, ui_1.printSpacer)();
36
43
  let port = DEFAULT_INIT_PORT;
37
44
  while (true) {
38
- const answer = await questionInterface.question(`What port? (default: ${DEFAULT_INIT_PORT}) `);
45
+ const answer = await questionInterface.question(formatWizardPrompt(1, "What port should FexAPI use?", "1-65535", String(DEFAULT_INIT_PORT)));
39
46
  if (!answer.trim()) {
40
47
  break;
41
48
  }
@@ -46,29 +53,35 @@ const askInitWizardQuestions = async () => {
46
53
  port = parsedPort;
47
54
  break;
48
55
  }
49
- console.log("Please enter a valid port (1-65535).\n");
56
+ console.log(`${ui_1.ui.red("✕")} ${ui_1.ui.white("Please enter a valid port between 1 and 65535.")}`);
57
+ (0, ui_1.printSpacer)();
50
58
  }
59
+ console.log(`${ui_1.ui.green("✓")} ${ui_1.ui.dim("Port")}: ${ui_1.ui.bold(String(port))}`);
51
60
  let cors = true;
52
61
  while (true) {
53
- const answer = await questionInterface.question("Enable CORS? (Y/n) ");
62
+ const answer = await questionInterface.question(formatWizardPrompt(2, "Enable CORS?", "Y/n", "Y"));
54
63
  const parsed = parseYesNo(answer, true);
55
64
  if (parsed !== undefined) {
56
65
  cors = parsed;
57
66
  break;
58
67
  }
59
- console.log("Please answer with Y/Yes or N/No.\n");
68
+ console.log(`${ui_1.ui.red("✕")} ${ui_1.ui.white("Please answer with Y/Yes or N/No.")}`);
69
+ (0, ui_1.printSpacer)();
60
70
  }
71
+ console.log(`${ui_1.ui.green("✓")} ${ui_1.ui.dim("CORS")}: ${cors ? ui_1.ui.green("enabled") : ui_1.ui.gray("disabled")}`);
61
72
  let generateSampleSchemas = true;
62
73
  while (true) {
63
- const answer = await questionInterface.question("Generate sample schemas? (Y/n) ");
74
+ const answer = await questionInterface.question(formatWizardPrompt(3, "Generate sample schemas?", "Y/n", "Y"));
64
75
  const parsed = parseYesNo(answer, true);
65
76
  if (parsed !== undefined) {
66
77
  generateSampleSchemas = parsed;
67
78
  break;
68
79
  }
69
- console.log("Please answer with Y/Yes or N/No.\n");
80
+ console.log(`${ui_1.ui.red("✕")} ${ui_1.ui.white("Please answer with Y/Yes or N/No.")}`);
81
+ (0, ui_1.printSpacer)();
70
82
  }
71
- console.log("");
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
+ (0, ui_1.printSpacer)();
72
85
  return {
73
86
  port,
74
87
  cors,
@@ -151,31 +164,15 @@ const initializeProject = async ({ force, }) => {
151
164
  const detectedProject = (0, detect_1.detectProject)(packageJsonPath, projectRoot);
152
165
  const fexapiDirectoryPath = (0, node_path_1.join)(projectRoot, "fexapi");
153
166
  const schemaPath = (0, node_path_1.join)(fexapiDirectoryPath, "schema.fexapi");
154
- const configPath = (0, node_path_1.join)(projectRoot, "fexapi.config.json");
155
167
  const runtimeConfigPath = (0, node_path_1.join)(projectRoot, "fexapi.config.js");
156
168
  const schemasDirectoryPath = (0, node_path_1.join)(projectRoot, "fexapi", "schemas");
157
169
  const userSchemaPath = (0, node_path_1.join)(schemasDirectoryPath, "user.yaml");
158
170
  const postSchemaPath = (0, node_path_1.join)(schemasDirectoryPath, "post.yaml");
159
171
  const wizardAnswers = await askInitWizardQuestions();
172
+ (0, ui_1.printGroupHeader)("Init");
160
173
  const initSpinner = (0, ui_1.startSpinner)("Scaffolding fexapi project files");
161
174
  (0, node_fs_1.mkdirSync)(fexapiDirectoryPath, { recursive: true });
162
- const configExists = (0, node_fs_1.existsSync)(configPath);
163
175
  const schemaExists = (0, node_fs_1.existsSync)(schemaPath);
164
- const config = {
165
- framework: detectedProject.primaryFramework,
166
- frameworks: detectedProject.frameworks,
167
- tooling: detectedProject.tooling,
168
- schemaPath: "fexapi/schema.fexapi",
169
- generatedPath: constants_1.GENERATED_SPEC_RELATIVE_PATH,
170
- defaultPort: wizardAnswers.port,
171
- corsEnabled: wizardAnswers.cors,
172
- sampleSchemasGenerated: wizardAnswers.generateSampleSchemas,
173
- createdAt: new Date().toISOString(),
174
- };
175
- if (!configExists || force) {
176
- initSpinner.update("Writing fexapi.config.json");
177
- (0, node_fs_1.writeFileSync)(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf-8");
178
- }
179
176
  if (!schemaExists || force) {
180
177
  initSpinner.update("Writing fexapi/schema.fexapi");
181
178
  (0, node_fs_1.writeFileSync)(schemaPath, `${(0, detect_1.getSchemaTemplate)(detectedProject.primaryFramework, wizardAnswers.port)}\n`, "utf-8");
@@ -220,15 +217,7 @@ const initializeProject = async ({ force, }) => {
220
217
  (0, ui_1.logInfo)(`Detected tooling: ${detectedProject.tooling.join(", ")}`);
221
218
  }
222
219
  (0, ui_1.printSpacer)();
223
- if (configExists && !force) {
224
- (0, ui_1.logWarn)(`Exists ${configPath}`);
225
- }
226
- else if (configExists && force) {
227
- (0, ui_1.logSuccess)(`Overwritten ${configPath}`);
228
- }
229
- else {
230
- (0, ui_1.logSuccess)(`Created ${configPath}`);
231
- }
220
+ (0, ui_1.printGroupHeader)("Files");
232
221
  if (schemaExists && !force) {
233
222
  (0, ui_1.logWarn)(`Exists ${schemaPath}`);
234
223
  }
@@ -271,11 +260,11 @@ const initializeProject = async ({ force, }) => {
271
260
  (0, ui_1.logWarn)("Sample schemas were skipped.");
272
261
  }
273
262
  if (detectedProject.primaryFramework === "unknown") {
274
- (0, ui_1.logWarn)("No known framework dependency found. Update fexapi.config.json and schema.fexapi if needed.");
263
+ (0, ui_1.logWarn)("No known framework dependency found. Update fexapi.config.js and schema.fexapi if needed.");
275
264
  }
276
265
  (0, ui_1.printSpacer)();
266
+ (0, ui_1.printGroupHeader)("Summary");
277
267
  const createdFiles = [
278
- !configExists || force,
279
268
  !schemaExists || force,
280
269
  !runtimeConfigExists || force,
281
270
  userSchemaStatus === "created" || userSchemaStatus === "overwritten",
@@ -288,25 +277,23 @@ const initializeProject = async ({ force, }) => {
288
277
  },
289
278
  {
290
279
  label: "port",
291
- value: ui_1.ui.cyan(String(wizardAnswers.port)),
280
+ value: String(wizardAnswers.port),
292
281
  },
293
282
  {
294
283
  label: "cors",
295
- value: wizardAnswers.cors ? ui_1.ui.green("enabled") : ui_1.ui.gray("disabled"),
284
+ value: wizardAnswers.cors ? "enabled" : "disabled",
296
285
  },
297
286
  {
298
287
  label: "sample schemas",
299
- value: wizardAnswers.generateSampleSchemas
300
- ? ui_1.ui.green("enabled")
301
- : ui_1.ui.gray("disabled"),
288
+ value: wizardAnswers.generateSampleSchemas ? "enabled" : "disabled",
302
289
  },
303
290
  {
304
291
  label: "files changed",
305
- value: ui_1.ui.bold(String(createdFiles)),
292
+ value: String(createdFiles),
306
293
  },
307
294
  {
308
295
  label: "time",
309
- value: ui_1.ui.bold((0, ui_1.formatDuration)(initStartedAtMs)),
296
+ value: (0, ui_1.formatDuration)(initStartedAtMs),
310
297
  },
311
298
  ]);
312
299
  (0, ui_1.printSpacer)();
@@ -1 +1 @@
1
- {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AASxC,eAAO,MAAM,mBAAmB,GAAI,6BAIjC;IACD,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,KAAG,MAAM,GAAG,SA2DZ,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,6BAI1B;IACD,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,KAAG,MAsBH,CAAC"}
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AASxC,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,SA+EZ,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"}
@@ -24,9 +24,19 @@ const createProjectServer = ({ host, port, logEnabled = false, }) => {
24
24
  ? (0, generated_spec_1.loadGeneratedApiSpec)(projectRoot)
25
25
  : undefined;
26
26
  const effectivePort = port ?? runtimeConfig?.port ?? generatedSpec?.port ?? 4000;
27
+ const configuredRoutePaths = Object.keys(runtimeConfig?.routes ?? {});
28
+ const generatedRoutePaths = new Set((generatedSpec?.routes ?? []).map((route) => route.path));
29
+ const overlappingPaths = configuredRoutePaths.filter((path) => generatedRoutePaths.has(path));
27
30
  if (runtimeConfig?.routes && Object.keys(runtimeConfig.routes).length > 0) {
28
31
  (0, ui_1.logInfo)(`Using routes from fexapi.config.js (${Object.keys(runtimeConfig.routes).length})`);
29
32
  }
33
+ if (overlappingPaths.length > 0) {
34
+ const sample = overlappingPaths.slice(0, 5).join(", ");
35
+ const more = overlappingPaths.length > 5
36
+ ? ` (+${overlappingPaths.length - 5} more)`
37
+ : "";
38
+ (0, ui_1.logWarn)(`Config routes override generated schema routes for: ${sample}${more}. Remove or edit fexapi.config.js routes if you want schema.fexapi changes to appear.`);
39
+ }
30
40
  if (Object.keys(schemaDefinitions).length > 0) {
31
41
  (0, ui_1.logInfo)(`Loaded custom schemas from fexapi/schemas (${Object.keys(schemaDefinitions).length})`);
32
42
  }
package/dist/index.js CHANGED
@@ -35,7 +35,7 @@ const main = async () => {
35
35
  (0, ui_1.printBanner)();
36
36
  (0, ui_1.printSpacer)();
37
37
  (0, ui_1.logInfo)(`Usage: ${(0, ui_1.formatCommand)("fexapi generate")}`);
38
- console.log("Reads fexapi/schema.fexapi and updates generated API artifacts + migration.");
38
+ console.log("Reads fexapi/schema.fexapi and updates generated API artifacts.");
39
39
  process.exit(0);
40
40
  }
41
41
  const generateOptions = (0, args_1.parseGenerateOptions)(restArgs);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fexapi",
3
- "version": "0.2.1",
3
+ "version": "0.2.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",