fexapi 0.1.0 → 0.1.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.
Files changed (45) hide show
  1. package/README.md +115 -1
  2. package/dist/cli/args.d.ts +15 -0
  3. package/dist/cli/args.d.ts.map +1 -0
  4. package/dist/cli/args.js +53 -0
  5. package/dist/cli/help.d.ts +2 -0
  6. package/dist/cli/help.d.ts.map +1 -0
  7. package/dist/cli/help.js +40 -0
  8. package/dist/commands/generate.d.ts +2 -0
  9. package/dist/commands/generate.d.ts.map +1 -0
  10. package/dist/commands/generate.js +73 -0
  11. package/dist/commands/init.d.ts +4 -0
  12. package/dist/commands/init.d.ts.map +1 -0
  13. package/dist/commands/init.js +66 -0
  14. package/dist/commands/serve.d.ts +5 -0
  15. package/dist/commands/serve.d.ts.map +1 -0
  16. package/dist/commands/serve.js +56 -0
  17. package/dist/config/generated-spec.d.ts +3 -0
  18. package/dist/config/generated-spec.d.ts.map +1 -0
  19. package/dist/config/generated-spec.js +26 -0
  20. package/dist/config/runtime-config.d.ts +3 -0
  21. package/dist/config/runtime-config.d.ts.map +1 -0
  22. package/dist/config/runtime-config.js +97 -0
  23. package/dist/config/schema-definitions.d.ts +3 -0
  24. package/dist/config/schema-definitions.d.ts.map +1 -0
  25. package/dist/config/schema-definitions.js +90 -0
  26. package/dist/constants.d.ts +2 -0
  27. package/dist/constants.d.ts.map +1 -0
  28. package/dist/constants.js +4 -0
  29. package/dist/index.js +23 -386
  30. package/dist/project/detect.d.ts +4 -0
  31. package/dist/project/detect.d.ts.map +1 -0
  32. package/dist/project/detect.js +113 -0
  33. package/dist/project/paths.d.ts +3 -0
  34. package/dist/project/paths.d.ts.map +1 -0
  35. package/dist/project/paths.js +28 -0
  36. package/dist/server.d.ts +4 -1
  37. package/dist/server.d.ts.map +1 -1
  38. package/dist/server.js +119 -40
  39. package/dist/types/config.d.ts +20 -0
  40. package/dist/types/config.d.ts.map +1 -0
  41. package/dist/types/config.js +2 -0
  42. package/dist/types/project.d.ts +7 -0
  43. package/dist/types/project.d.ts.map +1 -0
  44. package/dist/types/project.js +2 -0
  45. package/package.json +53 -51
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loadSchemaDefinitions = void 0;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ const yaml_1 = require("yaml");
7
+ const VALID_TYPES = new Set([
8
+ "number",
9
+ "string",
10
+ "boolean",
11
+ "date",
12
+ "uuid",
13
+ "email",
14
+ "url",
15
+ "name",
16
+ "phone",
17
+ ]);
18
+ const isRecord = (value) => {
19
+ return typeof value === "object" && value !== null && !Array.isArray(value);
20
+ };
21
+ const parseSchemaDefinition = (schemaName, rawValue) => {
22
+ if (!isRecord(rawValue)) {
23
+ console.error(`Invalid schemas/${schemaName}.yaml: expected root object of field definitions.`);
24
+ return undefined;
25
+ }
26
+ const result = {};
27
+ for (const [fieldName, rawFieldConfig] of Object.entries(rawValue)) {
28
+ if (!isRecord(rawFieldConfig)) {
29
+ console.error(`Invalid schemas/${schemaName}.yaml field \`${fieldName}\`: expected object with type/faker/min/max.`);
30
+ continue;
31
+ }
32
+ const rawType = rawFieldConfig.type;
33
+ if (typeof rawType !== "string") {
34
+ console.error(`Invalid schemas/${schemaName}.yaml field \`${fieldName}\`: missing string \`type\`.`);
35
+ continue;
36
+ }
37
+ const normalizedType = rawType.trim().toLowerCase();
38
+ if (!VALID_TYPES.has(normalizedType)) {
39
+ console.error(`Invalid schemas/${schemaName}.yaml field \`${fieldName}\`: unknown type \`${rawType}\`.`);
40
+ continue;
41
+ }
42
+ const minValue = rawFieldConfig.min;
43
+ const maxValue = rawFieldConfig.max;
44
+ result[fieldName] = {
45
+ type: normalizedType,
46
+ faker: typeof rawFieldConfig.faker === "string"
47
+ ? rawFieldConfig.faker.trim()
48
+ : undefined,
49
+ min: typeof minValue === "number" && Number.isFinite(minValue)
50
+ ? minValue
51
+ : undefined,
52
+ max: typeof maxValue === "number" && Number.isFinite(maxValue)
53
+ ? maxValue
54
+ : undefined,
55
+ };
56
+ }
57
+ return Object.keys(result).length > 0 ? result : undefined;
58
+ };
59
+ const loadSchemaDefinitions = (projectRoot) => {
60
+ const schemasDirectoryPath = (0, node_path_1.join)(projectRoot, "schemas");
61
+ if (!(0, node_fs_1.existsSync)(schemasDirectoryPath)) {
62
+ return {};
63
+ }
64
+ const result = {};
65
+ for (const entry of (0, node_fs_1.readdirSync)(schemasDirectoryPath)) {
66
+ const schemaPath = (0, node_path_1.join)(schemasDirectoryPath, entry);
67
+ if (!(0, node_fs_1.statSync)(schemaPath).isFile()) {
68
+ continue;
69
+ }
70
+ const extension = (0, node_path_1.extname)(entry).toLowerCase();
71
+ if (extension !== ".yaml" && extension !== ".yml") {
72
+ continue;
73
+ }
74
+ const schemaName = (0, node_path_1.basename)(entry, extension).toLowerCase();
75
+ try {
76
+ const source = (0, node_fs_1.readFileSync)(schemaPath, "utf-8");
77
+ const parsed = (0, yaml_1.parse)(source);
78
+ const definition = parseSchemaDefinition(schemaName, parsed);
79
+ if (definition) {
80
+ result[schemaName] = definition;
81
+ }
82
+ }
83
+ catch (error) {
84
+ const message = error instanceof Error ? error.message : String(error);
85
+ console.error(`Failed to parse ${schemaPath}: ${message}`);
86
+ }
87
+ }
88
+ return result;
89
+ };
90
+ exports.loadSchemaDefinitions = loadSchemaDefinitions;
@@ -0,0 +1,2 @@
1
+ export declare const GENERATED_SPEC_RELATIVE_PATH = "fexapi/generated.api.json";
2
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,4BAA4B,8BAA8B,CAAC"}
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GENERATED_SPEC_RELATIVE_PATH = void 0;
4
+ exports.GENERATED_SPEC_RELATIVE_PATH = "fexapi/generated.api.json";
package/dist/index.js CHANGED
@@ -1,353 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- const node_fs_1 = require("node:fs");
5
- const node_path_1 = require("node:path");
6
- const schema_1 = require("./schema");
7
- const server_1 = require("./server");
4
+ const args_1 = require("./cli/args");
5
+ const help_1 = require("./cli/help");
6
+ const generate_1 = require("./commands/generate");
7
+ const init_1 = require("./commands/init");
8
+ const serve_1 = require("./commands/serve");
8
9
  const args = process.argv.slice(2);
9
- const GENERATED_SPEC_RELATIVE_PATH = "fexapi/generated.api.json";
10
- const printHelp = () => {
11
- console.log("fexapi-cli");
12
- console.log("");
13
- console.log("Usage:");
14
- console.log(" fexapi init [--force]");
15
- console.log(" fexapi generate");
16
- console.log(" fexapi serve [--host <host>] [--port <number>]");
17
- console.log(" fexapi [--host <host>] [--port <number>]");
18
- console.log(" fexapi --help");
19
- console.log("");
20
- console.log("Examples:");
21
- console.log(" fexapi init");
22
- console.log(" fexapi init --force");
23
- console.log(" fexapi generate");
24
- console.log(" fexapi serve --host 127.0.0.1 --port 5000");
25
- console.log(" fexapi --port 4000");
26
- console.log("");
27
- console.log("Package manager usage:");
28
- console.log(" npx fexapi init");
29
- console.log(" pnpm dlx fexapi init");
30
- console.log(" yarn dlx fexapi init");
31
- console.log("");
32
- console.log("`fexapi init` creates:");
33
- console.log(" fexapi.config.json");
34
- console.log(" fexapi/schema.fexapi");
35
- console.log("");
36
- console.log("Then run:");
37
- console.log(" fexapi generate");
38
- console.log(" fexapi serve");
39
- };
40
- const findClosestPackageJson = (startDirectory) => {
41
- let currentDirectory = startDirectory;
42
- while (true) {
43
- const candidate = (0, node_path_1.join)(currentDirectory, "package.json");
44
- if ((0, node_fs_1.existsSync)(candidate)) {
45
- return candidate;
46
- }
47
- const parentDirectory = (0, node_path_1.dirname)(currentDirectory);
48
- if (parentDirectory === currentDirectory) {
49
- return undefined;
50
- }
51
- currentDirectory = parentDirectory;
52
- }
53
- };
54
- const readDependencyNames = (packageJsonPath) => {
55
- const packageJsonText = (0, node_fs_1.readFileSync)(packageJsonPath, "utf-8");
56
- const packageJson = JSON.parse(packageJsonText);
57
- const dependencies = (packageJson.dependencies ?? {});
58
- const devDependencies = (packageJson.devDependencies ?? {});
59
- return new Set([
60
- ...Object.keys(dependencies),
61
- ...Object.keys(devDependencies),
62
- ]);
63
- };
64
- const readWorkspaceDependencyNames = (projectRoot) => {
65
- const result = new Set();
66
- const rootsToScan = [
67
- (0, node_path_1.join)(projectRoot, "apps"),
68
- (0, node_path_1.join)(projectRoot, "packages"),
69
- ];
70
- for (const rootPath of rootsToScan) {
71
- if (!(0, node_fs_1.existsSync)(rootPath)) {
72
- continue;
73
- }
74
- for (const entry of (0, node_fs_1.readdirSync)(rootPath)) {
75
- const entryPath = (0, node_path_1.join)(rootPath, entry);
76
- if (!(0, node_fs_1.statSync)(entryPath).isDirectory()) {
77
- continue;
78
- }
79
- const packageJsonPath = (0, node_path_1.join)(entryPath, "package.json");
80
- if (!(0, node_fs_1.existsSync)(packageJsonPath)) {
81
- continue;
82
- }
83
- const dependencyNames = readDependencyNames(packageJsonPath);
84
- for (const dependencyName of dependencyNames) {
85
- result.add(dependencyName);
86
- }
87
- }
88
- }
89
- return result;
90
- };
91
- const detectProject = (packageJsonPath, projectRoot) => {
92
- const dependencyNames = readDependencyNames(packageJsonPath);
93
- const frameworks = new Set();
94
- const tooling = new Set();
95
- if (dependencyNames.has("turbo") ||
96
- (0, node_fs_1.existsSync)((0, node_path_1.join)(projectRoot, "turbo.json"))) {
97
- tooling.add("turborepo");
98
- }
99
- if (dependencyNames.has("nx") || (0, node_fs_1.existsSync)((0, node_path_1.join)(projectRoot, "nx.json"))) {
100
- tooling.add("nx");
101
- }
102
- if ((0, node_fs_1.existsSync)((0, node_path_1.join)(projectRoot, "pnpm-workspace.yaml"))) {
103
- tooling.add("pnpm-workspace");
104
- }
105
- if (tooling.has("turborepo") || tooling.has("pnpm-workspace")) {
106
- const workspaceDeps = readWorkspaceDependencyNames(projectRoot);
107
- for (const dependencyName of workspaceDeps) {
108
- dependencyNames.add(dependencyName);
109
- }
110
- }
111
- if (dependencyNames.has("next")) {
112
- frameworks.add("nextjs");
113
- }
114
- if (dependencyNames.has("react") || dependencyNames.has("react-dom")) {
115
- frameworks.add("reactjs");
116
- }
117
- if (dependencyNames.has("vue")) {
118
- frameworks.add("vue");
119
- }
120
- if (dependencyNames.has("nuxt")) {
121
- frameworks.add("nuxt");
122
- }
123
- if (dependencyNames.has("svelte")) {
124
- frameworks.add("svelte");
125
- }
126
- if (dependencyNames.has("@sveltejs/kit")) {
127
- frameworks.add("sveltekit");
128
- }
129
- if (dependencyNames.has("@angular/core")) {
130
- frameworks.add("angular");
131
- }
132
- if (dependencyNames.has("solid-js")) {
133
- frameworks.add("solid");
134
- }
135
- if (dependencyNames.has("@remix-run/react") ||
136
- dependencyNames.has("@remix-run/node")) {
137
- frameworks.add("remix");
138
- }
139
- if (dependencyNames.has("astro")) {
140
- frameworks.add("astro");
141
- }
142
- if (dependencyNames.has("vite")) {
143
- tooling.add("vite");
144
- }
145
- const frameworkList = Array.from(frameworks);
146
- const primaryFramework = frameworkList[0] ?? "unknown";
147
- return {
148
- primaryFramework,
149
- frameworks: frameworkList.length > 0 ? frameworkList : ["unknown"],
150
- tooling: Array.from(tooling),
151
- };
152
- };
153
- const getSchemaTemplate = (framework) => {
154
- const frameworkHint = framework === "nextjs"
155
- ? "# Framework: Next.js"
156
- : framework === "reactjs"
157
- ? "# Framework: React"
158
- : "# Framework: unknown";
159
- return [
160
- frameworkHint,
161
- "# Server",
162
- "port: 4000",
163
- "",
164
- "# Routes",
165
- "# Format: METHOD /endpoint: field:type,field:type",
166
- "GET /users: id:uuid,fullName:name,username:string,email:email,avatarUrl:url",
167
- "GET /posts: id:uuid,title:string,body:string,createdAt:date",
168
- ].join("\n");
169
- };
170
- const parseInitOptions = (initArgs) => {
171
- const validFlags = new Set(["--force"]);
172
- const invalidFlags = initArgs.filter((value) => value.startsWith("-") && !validFlags.has(value));
173
- if (invalidFlags.length > 0) {
174
- return { error: `Unknown option(s): ${invalidFlags.join(", ")}` };
175
- }
176
- return { force: initArgs.includes("--force") };
177
- };
178
- const parseGenerateOptions = (generateArgs) => {
179
- const invalidFlags = generateArgs.filter((value) => value.startsWith("-"));
180
- if (invalidFlags.length > 0) {
181
- return { error: `Unknown option(s): ${invalidFlags.join(", ")}` };
182
- }
183
- return {};
184
- };
185
- const resolveProjectRoot = () => {
186
- const packageJsonPath = findClosestPackageJson(process.cwd());
187
- if (!packageJsonPath) {
188
- return undefined;
189
- }
190
- return (0, node_path_1.dirname)(packageJsonPath);
191
- };
192
- const initializeProject = ({ force }) => {
193
- const packageJsonPath = findClosestPackageJson(process.cwd());
194
- if (!packageJsonPath) {
195
- console.error("Could not find package.json in this directory or parent directories.");
196
- return 1;
197
- }
198
- const projectRoot = (0, node_path_1.dirname)(packageJsonPath);
199
- const detectedProject = detectProject(packageJsonPath, projectRoot);
200
- const fexapiDirectoryPath = (0, node_path_1.join)(projectRoot, "fexapi");
201
- const schemaPath = (0, node_path_1.join)(fexapiDirectoryPath, "schema.fexapi");
202
- const configPath = (0, node_path_1.join)(projectRoot, "fexapi.config.json");
203
- (0, node_fs_1.mkdirSync)(fexapiDirectoryPath, { recursive: true });
204
- const configExists = (0, node_fs_1.existsSync)(configPath);
205
- const schemaExists = (0, node_fs_1.existsSync)(schemaPath);
206
- const config = {
207
- framework: detectedProject.primaryFramework,
208
- frameworks: detectedProject.frameworks,
209
- tooling: detectedProject.tooling,
210
- schemaPath: "fexapi/schema.fexapi",
211
- generatedPath: GENERATED_SPEC_RELATIVE_PATH,
212
- createdAt: new Date().toISOString(),
213
- };
214
- if (!configExists || force) {
215
- (0, node_fs_1.writeFileSync)(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf-8");
216
- }
217
- if (!schemaExists || force) {
218
- (0, node_fs_1.writeFileSync)(schemaPath, `${getSchemaTemplate(detectedProject.primaryFramework)}\n`, "utf-8");
219
- }
220
- console.log(`Initialized Fexapi in ${projectRoot}`);
221
- console.log(`Detected framework: ${detectedProject.primaryFramework}`);
222
- console.log(`Detected frameworks: ${detectedProject.frameworks.join(", ")}`);
223
- if (detectedProject.tooling.length > 0) {
224
- console.log(`Detected tooling: ${detectedProject.tooling.join(", ")}`);
225
- }
226
- if (configExists && !force) {
227
- console.log(`Exists ${configPath}`);
228
- }
229
- else if (configExists && force) {
230
- console.log(`Overwritten ${configPath}`);
231
- }
232
- else {
233
- console.log(`Created ${configPath}`);
234
- }
235
- if (schemaExists && !force) {
236
- console.log(`Exists ${schemaPath}`);
237
- }
238
- else if (schemaExists && force) {
239
- console.log(`Overwritten ${schemaPath}`);
240
- }
241
- else {
242
- console.log(`Created ${schemaPath}`);
243
- }
244
- if (detectedProject.primaryFramework === "unknown") {
245
- console.log("No known framework dependency found. Update fexapi.config.json and schema.fexapi if needed.");
246
- }
247
- return 0;
248
- };
249
- const generateFromSchema = () => {
250
- const projectRoot = resolveProjectRoot();
251
- if (!projectRoot) {
252
- console.error("Could not find package.json in this directory or parent directories.");
253
- return 1;
254
- }
255
- const schemaPath = (0, node_path_1.join)(projectRoot, "fexapi", "schema.fexapi");
256
- const generatedPath = (0, node_path_1.join)(projectRoot, "fexapi", "generated.api.json");
257
- const configPath = (0, node_path_1.join)(projectRoot, "fexapi.config.json");
258
- if (!(0, node_fs_1.existsSync)(schemaPath)) {
259
- console.error(`Schema file not found: ${schemaPath}`);
260
- console.error("Run `fexapi init` first.");
261
- return 1;
262
- }
263
- const schemaText = (0, node_fs_1.readFileSync)(schemaPath, "utf-8");
264
- const parsed = (0, schema_1.parseFexapiSchema)(schemaText);
265
- if (parsed.errors.length > 0 || !parsed.schema) {
266
- console.error("Failed to generate API from schema.fexapi");
267
- for (const error of parsed.errors) {
268
- console.error(`- ${error}`);
269
- }
270
- return 1;
271
- }
272
- const generated = {
273
- schemaVersion: 1,
274
- generatedAt: new Date().toISOString(),
275
- port: parsed.schema.port,
276
- routes: parsed.schema.routes,
277
- };
278
- (0, node_fs_1.writeFileSync)(generatedPath, `${JSON.stringify(generated, null, 2)}\n`, "utf-8");
279
- let existingConfig = {};
280
- if ((0, node_fs_1.existsSync)(configPath)) {
281
- try {
282
- existingConfig = JSON.parse((0, node_fs_1.readFileSync)(configPath, "utf-8"));
283
- }
284
- catch {
285
- existingConfig = {};
286
- }
287
- }
288
- const updatedConfig = {
289
- ...existingConfig,
290
- schemaPath: "fexapi/schema.fexapi",
291
- generatedPath: GENERATED_SPEC_RELATIVE_PATH,
292
- lastGeneratedAt: new Date().toISOString(),
293
- };
294
- (0, node_fs_1.writeFileSync)(configPath, `${JSON.stringify(updatedConfig, null, 2)}\n`, "utf-8");
295
- console.log(`Generated API spec at ${generatedPath}`);
296
- console.log(`Routes generated: ${parsed.schema.routes.length}`);
297
- console.log(`Configured server port: ${parsed.schema.port}`);
298
- return 0;
299
- };
300
- const parseServeOptions = (serveArgs) => {
301
- const getFlagValue = (flagName) => {
302
- const index = serveArgs.indexOf(flagName);
303
- if (index === -1) {
304
- return undefined;
305
- }
306
- const value = serveArgs[index + 1];
307
- if (!value || value.startsWith("-")) {
308
- return { error: `Missing value for ${flagName}` };
309
- }
310
- return value;
311
- };
312
- const unknownFlags = serveArgs.filter((value) => value.startsWith("-") && value !== "--host" && value !== "--port");
313
- if (unknownFlags.length > 0) {
314
- return { error: `Unknown option(s): ${unknownFlags.join(", ")}` };
315
- }
316
- const hostValue = getFlagValue("--host");
317
- if (hostValue && typeof hostValue !== "string") {
318
- return hostValue;
319
- }
320
- const portValue = getFlagValue("--port");
321
- if (portValue && typeof portValue !== "string") {
322
- return portValue;
323
- }
324
- const host = hostValue ?? "127.0.0.1";
325
- const port = portValue ? Number(portValue) : undefined;
326
- if (port !== undefined &&
327
- (!Number.isInteger(port) || port < 1 || port > 65535)) {
328
- return { error: `Invalid port: ${portValue ?? ""}`.trim() };
329
- }
330
- return { host, port };
331
- };
332
- const loadGeneratedApiSpec = (projectRoot) => {
333
- const generatedPath = (0, node_path_1.join)(projectRoot, GENERATED_SPEC_RELATIVE_PATH);
334
- if (!(0, node_fs_1.existsSync)(generatedPath)) {
335
- return undefined;
336
- }
337
- try {
338
- const parsed = JSON.parse((0, node_fs_1.readFileSync)(generatedPath, "utf-8"));
339
- if (typeof parsed.port !== "number" || !Array.isArray(parsed.routes)) {
340
- return undefined;
341
- }
342
- return {
343
- port: parsed.port,
344
- routes: parsed.routes,
345
- };
346
- }
347
- catch {
348
- return undefined;
349
- }
350
- };
351
10
  const [firstArg, ...restArgs] = args;
352
11
  if (firstArg === "init") {
353
12
  if (restArgs.includes("--help") || restArgs.includes("-h")) {
@@ -356,80 +15,58 @@ if (firstArg === "init") {
356
15
  console.log("Use --force to overwrite existing files.");
357
16
  process.exit(0);
358
17
  }
359
- const initOptions = parseInitOptions(restArgs);
18
+ const initOptions = (0, args_1.parseInitOptions)(restArgs);
360
19
  if ("error" in initOptions) {
361
20
  console.error(initOptions.error);
362
21
  console.log("");
363
22
  console.log("Usage: fexapi init [--force]");
364
23
  process.exit(1);
365
24
  }
366
- const exitCode = initializeProject({ force: initOptions.force });
367
- process.exit(exitCode);
25
+ process.exit((0, init_1.initializeProject)({ force: initOptions.force }));
368
26
  }
369
27
  else if (firstArg === "generate") {
370
28
  if (restArgs.includes("--help") || restArgs.includes("-h")) {
371
29
  console.log("Usage: fexapi generate");
372
- console.log("Reads fexapi/schema.fexapi and creates fexapi/generated.api.json.");
30
+ console.log("Reads fexapi/schema.fexapi and creates generated API artifacts + migrations.");
373
31
  process.exit(0);
374
32
  }
375
- const generateOptions = parseGenerateOptions(restArgs);
33
+ const generateOptions = (0, args_1.parseGenerateOptions)(restArgs);
376
34
  if (generateOptions.error) {
377
35
  console.error(generateOptions.error);
378
36
  console.log("");
379
37
  console.log("Usage: fexapi generate");
380
38
  process.exit(1);
381
39
  }
382
- const exitCode = generateFromSchema();
383
- process.exit(exitCode);
40
+ process.exit((0, generate_1.generateFromSchema)());
384
41
  }
385
- else if (!firstArg || firstArg === "serve" || firstArg.startsWith("-")) {
386
- const serveArgs = firstArg === "serve" ? restArgs : args;
42
+ else if (!firstArg ||
43
+ firstArg === "serve" ||
44
+ firstArg === "run" ||
45
+ firstArg.startsWith("-")) {
46
+ const serveArgs = firstArg === "serve" || firstArg === "run" ? restArgs : args;
387
47
  if (serveArgs.includes("--help") || serveArgs.includes("-h")) {
388
- printHelp();
48
+ (0, help_1.printHelp)();
389
49
  process.exit(0);
390
50
  }
391
- const options = parseServeOptions(serveArgs);
51
+ const options = (0, args_1.parseServeOptions)(serveArgs);
392
52
  if ("error" in options) {
393
53
  console.error(options.error);
394
54
  console.log("");
395
- printHelp();
55
+ (0, help_1.printHelp)();
396
56
  process.exit(1);
397
57
  }
398
- const projectRoot = resolveProjectRoot();
399
- const generatedSpec = projectRoot
400
- ? loadGeneratedApiSpec(projectRoot)
401
- : undefined;
402
- const effectivePort = options.port ?? generatedSpec?.port ?? 4000;
403
- if (generatedSpec) {
404
- console.log(`Using generated schema routes (${generatedSpec.routes.length}) from ${GENERATED_SPEC_RELATIVE_PATH}`);
405
- }
406
- else {
407
- console.log("No generated schema found. Run `fexapi generate` to serve schema-defined endpoints.");
58
+ const exitCode = (0, serve_1.serveProject)(options);
59
+ if (exitCode !== 0) {
60
+ process.exit(exitCode);
408
61
  }
409
- const server = (0, server_1.startServer)({
410
- host: options.host,
411
- port: effectivePort,
412
- apiSpec: generatedSpec,
413
- });
414
- const shutdown = () => {
415
- server.close((error) => {
416
- if (error) {
417
- console.error("Error while shutting down server", error);
418
- process.exit(1);
419
- }
420
- process.exit(0);
421
- });
422
- };
423
- process.on("SIGINT", shutdown);
424
- process.on("SIGTERM", shutdown);
425
62
  }
426
63
  else if (firstArg === "help") {
427
- printHelp();
64
+ (0, help_1.printHelp)();
428
65
  process.exit(0);
429
66
  }
430
67
  else {
431
68
  console.error(`Unknown command: ${firstArg}`);
432
69
  console.log("");
433
- printHelp();
70
+ (0, help_1.printHelp)();
434
71
  process.exit(1);
435
72
  }
@@ -0,0 +1,4 @@
1
+ import type { DetectedProject, SupportedFramework } from "../types/project";
2
+ export declare const detectProject: (packageJsonPath: string, projectRoot: string) => DetectedProject;
3
+ export declare const getSchemaTemplate: (framework: SupportedFramework) => string;
4
+ //# sourceMappingURL=detect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../src/project/detect.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAyD5E,eAAO,MAAM,aAAa,GACxB,iBAAiB,MAAM,EACvB,aAAa,MAAM,KAClB,eAwDF,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,WAAW,kBAAkB,KAAG,MAkBjE,CAAC"}
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSchemaTemplate = exports.detectProject = void 0;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ const readDependencyNames = (packageJsonPath) => {
7
+ const packageJsonText = (0, node_fs_1.readFileSync)(packageJsonPath, "utf-8");
8
+ const packageJson = JSON.parse(packageJsonText);
9
+ const dependencies = (packageJson.dependencies ?? {});
10
+ const devDependencies = (packageJson.devDependencies ?? {});
11
+ return new Set([
12
+ ...Object.keys(dependencies),
13
+ ...Object.keys(devDependencies),
14
+ ]);
15
+ };
16
+ const readWorkspaceDependencyNames = (projectRoot) => {
17
+ const result = new Set();
18
+ const rootsToScan = [
19
+ (0, node_path_1.join)(projectRoot, "apps"),
20
+ (0, node_path_1.join)(projectRoot, "packages"),
21
+ ];
22
+ for (const rootPath of rootsToScan) {
23
+ if (!(0, node_fs_1.existsSync)(rootPath)) {
24
+ continue;
25
+ }
26
+ for (const entry of (0, node_fs_1.readdirSync)(rootPath)) {
27
+ const entryPath = (0, node_path_1.join)(rootPath, entry);
28
+ if (!(0, node_fs_1.statSync)(entryPath).isDirectory()) {
29
+ continue;
30
+ }
31
+ const packageJsonPath = (0, node_path_1.join)(entryPath, "package.json");
32
+ if (!(0, node_fs_1.existsSync)(packageJsonPath)) {
33
+ continue;
34
+ }
35
+ const dependencyNames = readDependencyNames(packageJsonPath);
36
+ for (const dependencyName of dependencyNames) {
37
+ result.add(dependencyName);
38
+ }
39
+ }
40
+ }
41
+ return result;
42
+ };
43
+ const detectProject = (packageJsonPath, projectRoot) => {
44
+ const dependencyNames = readDependencyNames(packageJsonPath);
45
+ const frameworks = new Set();
46
+ const tooling = new Set();
47
+ if (dependencyNames.has("turbo") ||
48
+ (0, node_fs_1.existsSync)((0, node_path_1.join)(projectRoot, "turbo.json"))) {
49
+ tooling.add("turborepo");
50
+ }
51
+ if (dependencyNames.has("nx") || (0, node_fs_1.existsSync)((0, node_path_1.join)(projectRoot, "nx.json"))) {
52
+ tooling.add("nx");
53
+ }
54
+ if ((0, node_fs_1.existsSync)((0, node_path_1.join)(projectRoot, "pnpm-workspace.yaml"))) {
55
+ tooling.add("pnpm-workspace");
56
+ }
57
+ if (tooling.has("turborepo") || tooling.has("pnpm-workspace")) {
58
+ const workspaceDeps = readWorkspaceDependencyNames(projectRoot);
59
+ for (const dependencyName of workspaceDeps) {
60
+ dependencyNames.add(dependencyName);
61
+ }
62
+ }
63
+ if (dependencyNames.has("next"))
64
+ frameworks.add("nextjs");
65
+ if (dependencyNames.has("react") || dependencyNames.has("react-dom"))
66
+ frameworks.add("reactjs");
67
+ if (dependencyNames.has("vue"))
68
+ frameworks.add("vue");
69
+ if (dependencyNames.has("nuxt"))
70
+ frameworks.add("nuxt");
71
+ if (dependencyNames.has("svelte"))
72
+ frameworks.add("svelte");
73
+ if (dependencyNames.has("@sveltejs/kit"))
74
+ frameworks.add("sveltekit");
75
+ if (dependencyNames.has("@angular/core"))
76
+ frameworks.add("angular");
77
+ if (dependencyNames.has("solid-js"))
78
+ frameworks.add("solid");
79
+ if (dependencyNames.has("@remix-run/react") ||
80
+ dependencyNames.has("@remix-run/node"))
81
+ frameworks.add("remix");
82
+ if (dependencyNames.has("astro"))
83
+ frameworks.add("astro");
84
+ if (dependencyNames.has("vite")) {
85
+ tooling.add("vite");
86
+ }
87
+ const frameworkList = Array.from(frameworks);
88
+ const primaryFramework = frameworkList[0] ?? "unknown";
89
+ return {
90
+ primaryFramework,
91
+ frameworks: frameworkList.length > 0 ? frameworkList : ["unknown"],
92
+ tooling: Array.from(tooling),
93
+ };
94
+ };
95
+ exports.detectProject = detectProject;
96
+ const getSchemaTemplate = (framework) => {
97
+ const frameworkHint = framework === "nextjs"
98
+ ? "# Framework: Next.js"
99
+ : framework === "reactjs"
100
+ ? "# Framework: React"
101
+ : "# Framework: unknown";
102
+ return [
103
+ frameworkHint,
104
+ "# Server",
105
+ "port: 4000",
106
+ "",
107
+ "# Routes",
108
+ "# Format: METHOD /endpoint: field:type,field:type",
109
+ "GET /users: id:uuid,fullName:name,username:string,email:email,avatarUrl:url",
110
+ "GET /posts: id:uuid,title:string,body:string,createdAt:date",
111
+ ].join("\n");
112
+ };
113
+ exports.getSchemaTemplate = getSchemaTemplate;