fexapi 0.2.2 → 0.2.4

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":"AAQA,eAAO,MAAM,SAAS,YAiErB,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,7 @@ 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")}`);
16
19
  console.log(` ${(0, ui_1.formatCommand)("fexapi --help")}`);
17
20
  (0, ui_1.printSpacer)();
18
21
  console.log(ui_1.ui.bold("Examples"));
@@ -23,7 +26,7 @@ const printHelp = () => {
23
26
  console.log(` ${(0, ui_1.formatCommand)("fexapi dev --watch")}`);
24
27
  console.log(` ${(0, ui_1.formatCommand)("fexapi dev --watch --log")}`);
25
28
  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")}`);
29
+ console.log(` ${(0, ui_1.formatCommand)("fexapi serve --host localhost --port 5000")}`);
27
30
  console.log(` ${(0, ui_1.formatCommand)("fexapi --port 4000")}`);
28
31
  (0, ui_1.printSpacer)();
29
32
  console.log(ui_1.ui.bold("Package Manager Usage"));
@@ -32,13 +35,12 @@ const printHelp = () => {
32
35
  console.log(` ${(0, ui_1.formatCommand)("yarn dlx fexapi init")}`);
33
36
  (0, ui_1.printSpacer)();
34
37
  console.log(ui_1.ui.bold("fexapi init creates"));
35
- console.log(` ${ui_1.ui.dim("fexapi.config.json")}`);
36
38
  console.log(` ${ui_1.ui.dim("fexapi.config.js")}`);
37
39
  console.log(` ${ui_1.ui.dim("fexapi/schema.fexapi")}`);
38
40
  console.log(` ${ui_1.ui.dim("fexapi/schemas/*.yaml (optional, via wizard)")}`);
39
41
  (0, ui_1.printSpacer)();
40
42
  console.log(ui_1.ui.bold("Init wizard asks"));
41
- console.log(` ${ui_1.ui.dim("What port? (default: 3000)")}`);
43
+ console.log(` ${ui_1.ui.dim("What port? (default: 4000)")}`);
42
44
  console.log(` ${ui_1.ui.dim("Enable CORS? (Y/n)")}`);
43
45
  console.log(` ${ui_1.ui.dim("Generate sample schemas? (Y/n)")}`);
44
46
  (0, ui_1.printSpacer)();
@@ -49,6 +51,5 @@ const printHelp = () => {
49
51
  (0, ui_1.printSpacer)();
50
52
  console.log(ui_1.ui.bold("Generate output"));
51
53
  console.log(` ${ui_1.ui.dim("fexapi/generated.api.json")}`);
52
- console.log(` ${ui_1.ui.dim("fexapi/migrations/schema.json")}`);
53
54
  };
54
55
  exports.printHelp = printHelp;
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
@@ -1 +1 @@
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"}
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 = /\u001b\[[0-9;]*m/g;
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 = () => {
@@ -120,48 +122,57 @@ const printSummaryCard = (title, rows) => {
120
122
  const terminalWidth = getTerminalWidth();
121
123
  const compactMode = terminalWidth < 64;
122
124
  if (compactMode) {
123
- console.log(exports.ui.gray(`--- ${title} ---`));
125
+ console.log(exports.ui.gray(`+-- ${title} --+`));
124
126
  for (const row of rows) {
125
- console.log(`${exports.ui.dim(row.label)}: ${row.value}`);
127
+ console.log(`${exports.ui.dim(row.label)} ${exports.ui.gray("::")} ${styleCardValue(row.value)}`);
126
128
  }
127
- console.log(exports.ui.gray("---------------"));
129
+ console.log(exports.ui.gray("+----------------+"));
128
130
  return;
129
131
  }
130
132
  const safeRows = rows.map((row) => ({
131
133
  label: row.label,
132
134
  value: row.value,
133
135
  }));
134
- const cardWidth = terminalWidth - 2;
136
+ const maxCardWidth = 96;
137
+ const naturalInnerWidth = Math.max(36, visibleLength(title) + 2, ...safeRows.map((row) => visibleLength(row.label) + visibleLength(row.value) + 7));
138
+ const cardWidth = Math.max(40, Math.min(maxCardWidth, terminalWidth - 2, naturalInnerWidth + 2));
135
139
  const innerWidth = cardWidth - 2;
136
140
  const labelWidth = Math.min(20, Math.max(10, ...safeRows.map((row) => visibleLength(row.label))));
137
- const valueSpace = Math.max(8, innerWidth - 3 - labelWidth - 3);
141
+ const valueSpace = Math.max(8, innerWidth - 7 - labelWidth);
142
+ const renderBoxLine = (content) => {
143
+ const remaining = Math.max(0, innerWidth - 2 - visibleLength(content));
144
+ return `│ ${content}${" ".repeat(remaining)} │`;
145
+ };
138
146
  const topBorder = `┌${"─".repeat(innerWidth)}┐`;
139
147
  const divider = `├${"─".repeat(innerWidth)}┤`;
140
148
  const bottomBorder = `└${"─".repeat(innerWidth)}┘`;
141
149
  console.log(exports.ui.gray(topBorder));
142
150
  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} │`);
151
+ console.log(renderBoxLine(exports.ui.bold(exports.ui.cyan(renderedTitle))));
145
152
  console.log(exports.ui.gray(divider));
146
153
  for (const row of safeRows) {
147
154
  const rawValue = stripAnsi(row.value);
148
155
  const value = truncateText(rawValue, valueSpace);
149
156
  const label = row.label.padEnd(labelWidth, " ");
150
157
  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}│`);
158
+ console.log(renderBoxLine(`${exports.ui.dim(label)} ${exports.ui.gray("::")} ${styledValue}`));
153
159
  }
154
160
  console.log(exports.ui.gray(bottomBorder));
155
161
  };
156
162
  exports.printSummaryCard = printSummaryCard;
157
163
  const printGroupHeader = (title) => {
158
164
  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
+ const compactMode = terminalWidth < 64;
166
+ if (compactMode) {
167
+ console.log(exports.ui.gray(`-- ${title} --`));
168
+ return;
169
+ }
170
+ const innerWidth = terminalWidth - 2;
171
+ const renderedTitle = truncateText(title, Math.max(1, innerWidth - 6));
172
+ const rawPrefix = `── ${renderedTitle} `;
173
+ const fill = Math.max(0, innerWidth - visibleLength(rawPrefix));
174
+ const prefix = `${exports.ui.gray("── ")}${exports.ui.bold(exports.ui.cyan(renderedTitle))} `;
175
+ console.log(`${exports.ui.gray("┌")}${prefix}${exports.ui.gray("─".repeat(fill))}${exports.ui.gray("┐")}`);
165
176
  };
166
177
  exports.printGroupHeader = printGroupHeader;
167
178
  const printBanner = () => {
@@ -199,3 +210,25 @@ const formatCommand = (command) => {
199
210
  return exports.ui.bold(command);
200
211
  };
201
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":"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,MA6KrC,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,8 +21,6 @@ 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.");
@@ -67,52 +64,12 @@ const generateFromSchema = () => {
67
64
  port: parsed.schema.port,
68
65
  routes: parsed.schema.routes,
69
66
  };
70
- (0, node_fs_1.mkdirSync)(migrationsDirectoryPath, { recursive: true });
71
- const migrationPath = (0, node_path_1.join)(migrationsDirectoryPath, "schema.json");
72
- const migration = {
73
- migrationId: new Date().toISOString().replace(/[.:]/g, "-"),
74
- sourceSchema: "fexapi/schema.fexapi",
75
- createdAt: generated.generatedAt,
76
- port: parsed.schema.port,
77
- routes: parsed.schema.routes,
78
- };
79
67
  let generatedStatus = "cached";
80
- let migrationStatus = "cached";
81
68
  if (schemaChanged || !(0, node_fs_1.existsSync)(generatedPath)) {
82
69
  generationSpinner.update("Writing generated API spec");
83
70
  (0, node_fs_1.writeFileSync)(generatedPath, `${JSON.stringify(generated, null, 2)}\n`, "utf-8");
84
71
  generatedStatus = "changed";
85
72
  }
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
- }
91
- let existingConfig = {};
92
- if ((0, node_fs_1.existsSync)(configPath)) {
93
- try {
94
- existingConfig = JSON.parse((0, node_fs_1.readFileSync)(configPath, "utf-8"));
95
- }
96
- catch {
97
- existingConfig = {};
98
- }
99
- }
100
- const updatedConfig = {
101
- ...existingConfig,
102
- schemaPath: "fexapi/schema.fexapi",
103
- generatedPath: constants_1.GENERATED_SPEC_RELATIVE_PATH,
104
- ...(schemaChanged ? { lastGeneratedAt: new Date().toISOString() } : {}),
105
- };
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
73
  generationSpinner.succeed(`Generate complete (${schemaChanged ? "changed" : "cached"})`);
117
74
  (0, ui_1.printSpacer)();
118
75
  (0, ui_1.printGroupHeader)("Summary");
@@ -133,14 +90,6 @@ const generateFromSchema = () => {
133
90
  label: "generated.api.json",
134
91
  value: generatedStatus,
135
92
  },
136
- {
137
- label: "migration",
138
- value: migrationStatus,
139
- },
140
- {
141
- label: "config",
142
- value: configStatus,
143
- },
144
93
  {
145
94
  label: "time",
146
95
  value: (0, ui_1.formatDuration)(startedAtMs),
@@ -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,CA2MjB,CAAC"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAgOA,eAAO,MAAM,iBAAiB,GAAU,YAErC;IACD,KAAK,EAAE,OAAO,CAAC;CAChB,KAAG,OAAO,CAAC,MAAM,CA8KjB,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,
@@ -79,21 +92,12 @@ const askInitWizardQuestions = async () => {
79
92
  questionInterface.close();
80
93
  }
81
94
  };
82
- const getRuntimeConfigTemplate = ({ port, cors, includeSampleRoutes, }) => {
83
- const routeSection = includeSampleRoutes
84
- ? [
85
- " routes: {",
86
- ' "/users": { count: 10, schema: "user" },',
87
- ' "/posts": { count: 20, schema: "post" },',
88
- " },",
89
- ]
90
- : [];
95
+ const getRuntimeConfigTemplate = ({ port, cors, }) => {
91
96
  return [
92
97
  "module.exports = {",
93
98
  ` port: ${port},`,
94
99
  ` cors: ${cors},`,
95
100
  " delay: 0,",
96
- ...routeSection,
97
101
  "};",
98
102
  ].join("\n");
99
103
  };
@@ -151,7 +155,6 @@ const initializeProject = async ({ force, }) => {
151
155
  const detectedProject = (0, detect_1.detectProject)(packageJsonPath, projectRoot);
152
156
  const fexapiDirectoryPath = (0, node_path_1.join)(projectRoot, "fexapi");
153
157
  const schemaPath = (0, node_path_1.join)(fexapiDirectoryPath, "schema.fexapi");
154
- const configPath = (0, node_path_1.join)(projectRoot, "fexapi.config.json");
155
158
  const runtimeConfigPath = (0, node_path_1.join)(projectRoot, "fexapi.config.js");
156
159
  const schemasDirectoryPath = (0, node_path_1.join)(projectRoot, "fexapi", "schemas");
157
160
  const userSchemaPath = (0, node_path_1.join)(schemasDirectoryPath, "user.yaml");
@@ -160,23 +163,7 @@ const initializeProject = async ({ force, }) => {
160
163
  (0, ui_1.printGroupHeader)("Init");
161
164
  const initSpinner = (0, ui_1.startSpinner)("Scaffolding fexapi project files");
162
165
  (0, node_fs_1.mkdirSync)(fexapiDirectoryPath, { recursive: true });
163
- const configExists = (0, node_fs_1.existsSync)(configPath);
164
166
  const schemaExists = (0, node_fs_1.existsSync)(schemaPath);
165
- const config = {
166
- framework: detectedProject.primaryFramework,
167
- frameworks: detectedProject.frameworks,
168
- tooling: detectedProject.tooling,
169
- schemaPath: "fexapi/schema.fexapi",
170
- generatedPath: constants_1.GENERATED_SPEC_RELATIVE_PATH,
171
- defaultPort: wizardAnswers.port,
172
- corsEnabled: wizardAnswers.cors,
173
- sampleSchemasGenerated: wizardAnswers.generateSampleSchemas,
174
- createdAt: new Date().toISOString(),
175
- };
176
- if (!configExists || force) {
177
- initSpinner.update("Writing fexapi.config.json");
178
- (0, node_fs_1.writeFileSync)(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf-8");
179
- }
180
167
  if (!schemaExists || force) {
181
168
  initSpinner.update("Writing fexapi/schema.fexapi");
182
169
  (0, node_fs_1.writeFileSync)(schemaPath, `${(0, detect_1.getSchemaTemplate)(detectedProject.primaryFramework, wizardAnswers.port)}\n`, "utf-8");
@@ -187,7 +174,6 @@ const initializeProject = async ({ force, }) => {
187
174
  (0, node_fs_1.writeFileSync)(runtimeConfigPath, `${getRuntimeConfigTemplate({
188
175
  port: wizardAnswers.port,
189
176
  cors: wizardAnswers.cors,
190
- includeSampleRoutes: wizardAnswers.generateSampleSchemas,
191
177
  })}\n`, "utf-8");
192
178
  }
193
179
  let userSchemaStatus = "skipped";
@@ -222,15 +208,6 @@ const initializeProject = async ({ force, }) => {
222
208
  }
223
209
  (0, ui_1.printSpacer)();
224
210
  (0, ui_1.printGroupHeader)("Files");
225
- if (configExists && !force) {
226
- (0, ui_1.logWarn)(`Exists ${configPath}`);
227
- }
228
- else if (configExists && force) {
229
- (0, ui_1.logSuccess)(`Overwritten ${configPath}`);
230
- }
231
- else {
232
- (0, ui_1.logSuccess)(`Created ${configPath}`);
233
- }
234
211
  if (schemaExists && !force) {
235
212
  (0, ui_1.logWarn)(`Exists ${schemaPath}`);
236
213
  }
@@ -273,12 +250,11 @@ const initializeProject = async ({ force, }) => {
273
250
  (0, ui_1.logWarn)("Sample schemas were skipped.");
274
251
  }
275
252
  if (detectedProject.primaryFramework === "unknown") {
276
- (0, ui_1.logWarn)("No known framework dependency found. Update fexapi.config.json and schema.fexapi if needed.");
253
+ (0, ui_1.logWarn)("No known framework dependency found. Update fexapi.config.js and schema.fexapi if needed.");
277
254
  }
278
255
  (0, ui_1.printSpacer)();
279
256
  (0, ui_1.printGroupHeader)("Summary");
280
257
  const createdFiles = [
281
- !configExists || force,
282
258
  !schemaExists || force,
283
259
  !runtimeConfigExists || force,
284
260
  userSchemaStatus === "created" || userSchemaStatus === "overwritten",
@@ -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.logInfo)(`Both schema and config define: ${sample}${more}. Schema routes take precedence; remove duplicates in fexapi.config.js to keep behavior clear.`);
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
@@ -12,6 +12,12 @@ 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
+ (0, ui_1.printBanner)();
17
+ (0, ui_1.printSpacer)();
18
+ console.log((0, ui_1.getCliVersion)());
19
+ process.exit(0);
20
+ }
15
21
  if (firstArg === "init") {
16
22
  if (restArgs.includes("--help") || restArgs.includes("-h")) {
17
23
  (0, ui_1.printBanner)();
@@ -35,7 +41,7 @@ const main = async () => {
35
41
  (0, ui_1.printBanner)();
36
42
  (0, ui_1.printSpacer)();
37
43
  (0, ui_1.logInfo)(`Usage: ${(0, ui_1.formatCommand)("fexapi generate")}`);
38
- console.log("Reads fexapi/schema.fexapi and updates generated API artifacts + migration.");
44
+ console.log("Reads fexapi/schema.fexapi and updates generated API artifacts.");
39
45
  process.exit(0);
40
46
  }
41
47
  const generateOptions = (0, args_1.parseGenerateOptions)(restArgs);
@@ -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,wFAwJpB,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;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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fexapi",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
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",