fexapi 0.2.0 → 0.2.1

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 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 ?? "127.0.0.1";
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 ?? "127.0.0.1";
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,26 @@ 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;
10
11
  };
12
+ type SpinnerController = {
13
+ update: (text: string) => void;
14
+ succeed: (text: string) => void;
15
+ fail: (text: string) => void;
16
+ stop: () => void;
17
+ };
18
+ export declare const startSpinner: (initialText: string) => SpinnerController;
19
+ export declare const nowMs: () => number;
20
+ export declare const formatDuration: (startMs: number) => string;
21
+ export declare const printSummaryCard: (title: string, rows: Array<{
22
+ label: string;
23
+ value: string;
24
+ }>) => void;
11
25
  export declare const printBanner: () => void;
12
26
  export declare const printSpacer: () => void;
13
27
  export declare const logInfo: (message: string) => void;
@@ -16,4 +30,5 @@ export declare const logWarn: (message: string) => void;
16
30
  export declare const logError: (message: string) => void;
17
31
  export declare const logStep: (message: string) => void;
18
32
  export declare const formatCommand: (command: string) => string;
33
+ export {};
19
34
  //# sourceMappingURL=ui.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../src/cli/ui.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,EAAE;iBACA,MAAM,KAAG,MAAM;gBAChB,MAAM,KAAG,MAAM;iBACd,MAAM,KAAG,MAAM;iBACf,MAAM,KAAG,MAAM;kBACd,MAAM,KAAG,MAAM;mBACd,MAAM,KAAG,MAAM;gBAClB,MAAM,KAAG,MAAM;iBACd,MAAM,KAAG,MAAM;CAC7B,CAAC;AAEF,eAAO,MAAM,WAAW,QAAO,IAE9B,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":"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"}
package/dist/cli/ui.js CHANGED
@@ -1,10 +1,11 @@
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.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);
8
9
  const paint = (code, text) => {
9
10
  if (!colorEnabled) {
10
11
  return text;
@@ -15,14 +16,95 @@ exports.ui = {
15
16
  bold: (text) => paint("1", text),
16
17
  dim: (text) => paint("2", text),
17
18
  cyan: (text) => paint("36", text),
19
+ magenta: (text) => paint("35", text),
18
20
  blue: (text) => paint("94", text),
19
21
  green: (text) => paint("32", text),
20
22
  yellow: (text) => paint("33", text),
21
23
  red: (text) => paint("31", text),
22
24
  gray: (text) => paint("90", text),
23
25
  };
26
+ const SPINNER_FRAMES = ["-", "\\", "|", "/"];
27
+ const clearCurrentLine = () => {
28
+ if (!interactive) {
29
+ return;
30
+ }
31
+ process.stdout.write("\r\u001b[2K");
32
+ };
33
+ const startSpinner = (initialText) => {
34
+ if (!interactive) {
35
+ (0, exports.logStep)(initialText);
36
+ return {
37
+ update: (text) => (0, exports.logStep)(text),
38
+ succeed: (text) => (0, exports.logSuccess)(text),
39
+ fail: (text) => (0, exports.logError)(text),
40
+ stop: () => undefined,
41
+ };
42
+ }
43
+ let text = initialText;
44
+ let frameIndex = 0;
45
+ const render = () => {
46
+ const frame = SPINNER_FRAMES[frameIndex % SPINNER_FRAMES.length] ?? "-";
47
+ frameIndex += 1;
48
+ process.stdout.write(`\r${exports.ui.cyan(frame)} ${exports.ui.bold(text)}`);
49
+ };
50
+ render();
51
+ const timer = setInterval(render, 80);
52
+ return {
53
+ update: (nextText) => {
54
+ text = nextText;
55
+ },
56
+ succeed: (finalText) => {
57
+ clearInterval(timer);
58
+ clearCurrentLine();
59
+ console.log(`${exports.ui.green("ok")} ${finalText}`);
60
+ },
61
+ fail: (finalText) => {
62
+ clearInterval(timer);
63
+ clearCurrentLine();
64
+ console.log(`${exports.ui.red("err")} ${finalText}`);
65
+ },
66
+ stop: () => {
67
+ clearInterval(timer);
68
+ clearCurrentLine();
69
+ },
70
+ };
71
+ };
72
+ exports.startSpinner = startSpinner;
73
+ const nowMs = () => Date.now();
74
+ exports.nowMs = nowMs;
75
+ const formatDuration = (startMs) => {
76
+ const elapsedMs = Date.now() - startMs;
77
+ if (elapsedMs < 1000) {
78
+ return `${elapsedMs}ms`;
79
+ }
80
+ return `${(elapsedMs / 1000).toFixed(2)}s`;
81
+ };
82
+ exports.formatDuration = formatDuration;
83
+ 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}|`);
99
+ }
100
+ console.log(exports.ui.gray(border));
101
+ };
102
+ exports.printSummaryCard = printSummaryCard;
24
103
  const printBanner = () => {
25
- console.log(exports.ui.bold(exports.ui.cyan("fexapi")) + exports.ui.gray(" mock api toolkit"));
104
+ console.log(exports.ui.bold(exports.ui.cyan("fexapi")) +
105
+ exports.ui.gray(" ") +
106
+ exports.ui.magenta("mock") +
107
+ exports.ui.gray(" api toolkit"));
26
108
  };
27
109
  exports.printBanner = printBanner;
28
110
  const printSpacer = () => {
@@ -1 +1 @@
1
- {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,kBAAkB,QAAO,MA0GrC,CAAC"}
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AAyBA,eAAO,MAAM,kBAAkB,QAAO,MA8KrC,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,40 @@ 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.logInfo)("Run `fexapi init` first.");
29
+ (0, ui_1.logError)("Run `fexapi init` first.");
23
30
  return 1;
24
31
  }
32
+ const generationSpinner = (0, ui_1.startSpinner)("Reading schema");
25
33
  const schemaText = (0, node_fs_1.readFileSync)(schemaPath, "utf-8");
34
+ generationSpinner.update("Parsing schema routes");
26
35
  const parsed = (0, schema_1.parseFexapiSchema)(schemaText);
27
36
  if (parsed.errors.length > 0 || !parsed.schema) {
37
+ generationSpinner.fail("Schema parsing failed");
28
38
  (0, ui_1.logError)("Failed to generate API from schema.fexapi");
29
39
  for (const error of parsed.errors) {
30
40
  (0, ui_1.logError)(`- ${error}`);
31
41
  }
32
42
  return 1;
33
43
  }
44
+ generationSpinner.update("Resolving cache state");
45
+ const previousGenerated = (0, node_fs_1.existsSync)(generatedPath)
46
+ ? (() => {
47
+ try {
48
+ return JSON.parse((0, node_fs_1.readFileSync)(generatedPath, "utf-8"));
49
+ }
50
+ catch {
51
+ return undefined;
52
+ }
53
+ })()
54
+ : undefined;
55
+ const nextSignature = createRouteSignature({
56
+ port: parsed.schema.port,
57
+ routes: parsed.schema.routes,
58
+ });
59
+ const previousSignature = previousGenerated
60
+ ? createRouteSignature(previousGenerated)
61
+ : undefined;
62
+ const schemaChanged = nextSignature !== previousSignature;
34
63
  const generated = {
35
64
  schemaVersion: 1,
36
65
  generatedAt: new Date().toISOString(),
@@ -38,25 +67,26 @@ const generateFromSchema = () => {
38
67
  routes: parsed.schema.routes,
39
68
  };
40
69
  (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
70
  const migrationPath = (0, node_path_1.join)(migrationsDirectoryPath, "schema.json");
51
71
  const migration = {
52
- migrationId,
72
+ migrationId: new Date().toISOString().replace(/[.:]/g, "-"),
53
73
  sourceSchema: "fexapi/schema.fexapi",
54
74
  createdAt: generated.generatedAt,
55
75
  port: parsed.schema.port,
56
76
  routes: parsed.schema.routes,
57
77
  };
58
- (0, node_fs_1.writeFileSync)(generatedPath, `${JSON.stringify(generated, null, 2)}\n`, "utf-8");
59
- (0, node_fs_1.writeFileSync)(migrationPath, `${JSON.stringify(migration, null, 2)}\n`, "utf-8");
78
+ let generatedStatus = "cached";
79
+ let migrationStatus = "cached";
80
+ if (schemaChanged || !(0, node_fs_1.existsSync)(generatedPath)) {
81
+ generationSpinner.update("Writing generated API spec");
82
+ (0, node_fs_1.writeFileSync)(generatedPath, `${JSON.stringify(generated, null, 2)}\n`, "utf-8");
83
+ generatedStatus = "changed";
84
+ }
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
+ }
60
90
  let existingConfig = {};
61
91
  if ((0, node_fs_1.existsSync)(configPath)) {
62
92
  try {
@@ -70,14 +100,50 @@ const generateFromSchema = () => {
70
100
  ...existingConfig,
71
101
  schemaPath: "fexapi/schema.fexapi",
72
102
  generatedPath: constants_1.GENERATED_SPEC_RELATIVE_PATH,
73
- lastGeneratedAt: new Date().toISOString(),
103
+ ...(schemaChanged ? { lastGeneratedAt: new Date().toISOString() } : {}),
74
104
  };
75
- (0, node_fs_1.writeFileSync)(configPath, `${JSON.stringify(updatedConfig, null, 2)}\n`, "utf-8");
76
- (0, ui_1.logSuccess)(`Generated API spec at ${generatedPath}`);
77
- (0, ui_1.logSuccess)(`Migration updated at ${migrationPath}`);
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
+ generationSpinner.succeed(`Generate complete (${schemaChanged ? "changed" : "cached"})`);
78
116
  (0, ui_1.printSpacer)();
79
- (0, ui_1.logInfo)(`Routes generated: ${parsed.schema.routes.length}`);
80
- (0, ui_1.logInfo)(`Configured server port: ${parsed.schema.port}`);
117
+ (0, ui_1.printSummaryCard)("Generate Summary", [
118
+ {
119
+ label: "routes",
120
+ value: ui_1.ui.cyan(String(parsed.schema.routes.length)),
121
+ },
122
+ {
123
+ label: "port",
124
+ value: ui_1.ui.cyan(String(parsed.schema.port)),
125
+ },
126
+ {
127
+ label: "schema source",
128
+ value: "fexapi/schema.fexapi",
129
+ },
130
+ {
131
+ 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"),
141
+ },
142
+ {
143
+ label: "time",
144
+ value: ui_1.ui.bold((0, ui_1.formatDuration)(startedAtMs)),
145
+ },
146
+ ]);
81
147
  return 0;
82
148
  };
83
149
  exports.generateFromSchema = generateFromSchema;
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAkMA,eAAO,MAAM,iBAAiB,GAAU,YAErC;IACD,KAAK,EAAE,OAAO,CAAC;CAChB,KAAG,OAAO,CAAC,MAAM,CA0JjB,CAAC"}
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"}
@@ -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 = 3000;
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,7 @@ 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
+ const initSpinner = (0, ui_1.startSpinner)("Scaffolding fexapi project files");
159
161
  (0, node_fs_1.mkdirSync)(fexapiDirectoryPath, { recursive: true });
160
162
  const configExists = (0, node_fs_1.existsSync)(configPath);
161
163
  const schemaExists = (0, node_fs_1.existsSync)(schemaPath);
@@ -171,13 +173,16 @@ const initializeProject = async ({ force, }) => {
171
173
  createdAt: new Date().toISOString(),
172
174
  };
173
175
  if (!configExists || force) {
176
+ initSpinner.update("Writing fexapi.config.json");
174
177
  (0, node_fs_1.writeFileSync)(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf-8");
175
178
  }
176
179
  if (!schemaExists || force) {
180
+ initSpinner.update("Writing fexapi/schema.fexapi");
177
181
  (0, node_fs_1.writeFileSync)(schemaPath, `${(0, detect_1.getSchemaTemplate)(detectedProject.primaryFramework, wizardAnswers.port)}\n`, "utf-8");
178
182
  }
179
183
  const runtimeConfigExists = (0, node_fs_1.existsSync)(runtimeConfigPath);
180
184
  if (!runtimeConfigExists || force) {
185
+ initSpinner.update("Writing fexapi.config.js");
181
186
  (0, node_fs_1.writeFileSync)(runtimeConfigPath, `${getRuntimeConfigTemplate({
182
187
  port: wizardAnswers.port,
183
188
  cors: wizardAnswers.cors,
@@ -190,6 +195,7 @@ const initializeProject = async ({ force, }) => {
190
195
  (0, node_fs_1.mkdirSync)(schemasDirectoryPath, { recursive: true });
191
196
  const userSchemaExists = (0, node_fs_1.existsSync)(userSchemaPath);
192
197
  if (!userSchemaExists || force) {
198
+ initSpinner.update("Writing sample user schema");
193
199
  (0, node_fs_1.writeFileSync)(userSchemaPath, `${SAMPLE_USER_SCHEMA}\n`, "utf-8");
194
200
  userSchemaStatus = userSchemaExists ? "overwritten" : "created";
195
201
  }
@@ -198,6 +204,7 @@ const initializeProject = async ({ force, }) => {
198
204
  }
199
205
  const postSchemaExists = (0, node_fs_1.existsSync)(postSchemaPath);
200
206
  if (!postSchemaExists || force) {
207
+ initSpinner.update("Writing sample post schema");
201
208
  (0, node_fs_1.writeFileSync)(postSchemaPath, `${SAMPLE_POST_SCHEMA}\n`, "utf-8");
202
209
  postSchemaStatus = postSchemaExists ? "overwritten" : "created";
203
210
  }
@@ -205,6 +212,7 @@ const initializeProject = async ({ force, }) => {
205
212
  postSchemaStatus = "exists";
206
213
  }
207
214
  }
215
+ initSpinner.succeed("Project scaffolding complete");
208
216
  (0, ui_1.logSuccess)(`Initialized fexapi in ${projectRoot}`);
209
217
  (0, ui_1.logInfo)(`Detected framework: ${detectedProject.primaryFramework}`);
210
218
  (0, ui_1.logInfo)(`Detected frameworks: ${detectedProject.frameworks.join(", ")}`);
@@ -266,6 +274,42 @@ const initializeProject = async ({ force, }) => {
266
274
  (0, ui_1.logWarn)("No known framework dependency found. Update fexapi.config.json and schema.fexapi if needed.");
267
275
  }
268
276
  (0, ui_1.printSpacer)();
277
+ const createdFiles = [
278
+ !configExists || force,
279
+ !schemaExists || force,
280
+ !runtimeConfigExists || force,
281
+ userSchemaStatus === "created" || userSchemaStatus === "overwritten",
282
+ postSchemaStatus === "created" || postSchemaStatus === "overwritten",
283
+ ].filter(Boolean).length;
284
+ (0, ui_1.printSummaryCard)("Init Summary", [
285
+ {
286
+ label: "framework",
287
+ value: detectedProject.primaryFramework,
288
+ },
289
+ {
290
+ label: "port",
291
+ value: ui_1.ui.cyan(String(wizardAnswers.port)),
292
+ },
293
+ {
294
+ label: "cors",
295
+ value: wizardAnswers.cors ? ui_1.ui.green("enabled") : ui_1.ui.gray("disabled"),
296
+ },
297
+ {
298
+ label: "sample schemas",
299
+ value: wizardAnswers.generateSampleSchemas
300
+ ? ui_1.ui.green("enabled")
301
+ : ui_1.ui.gray("disabled"),
302
+ },
303
+ {
304
+ label: "files changed",
305
+ value: ui_1.ui.bold(String(createdFiles)),
306
+ },
307
+ {
308
+ label: "time",
309
+ value: ui_1.ui.bold((0, ui_1.formatDuration)(initStartedAtMs)),
310
+ },
311
+ ]);
312
+ (0, ui_1.printSpacer)();
269
313
  (0, ui_1.logInfo)(`Next: ${(0, ui_1.formatCommand)("fexapi generate")} then ${(0, ui_1.formatCommand)("fexapi serve")}`);
270
314
  return 0;
271
315
  };
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 = "127.0.0.1";
7
+ const DEFAULT_HOST = "localhost";
8
8
  const DEFAULT_PORT = 4000;
9
9
  const sendJson = (response, statusCode, payload, options) => {
10
10
  const send = () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fexapi",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
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",