fexapi 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +142 -1
- package/dist/cli/args.d.ts +24 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +98 -0
- package/dist/cli/help.d.ts +2 -0
- package/dist/cli/help.d.ts.map +1 -0
- package/dist/cli/help.js +51 -0
- package/dist/commands/dev.d.ts +7 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +114 -0
- package/dist/commands/generate.d.ts +2 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +81 -0
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +250 -0
- package/dist/commands/serve.d.ts +12 -0
- package/dist/commands/serve.d.ts.map +1 -0
- package/dist/commands/serve.js +68 -0
- package/dist/config/generated-spec.d.ts +3 -0
- package/dist/config/generated-spec.d.ts.map +1 -0
- package/dist/config/generated-spec.js +26 -0
- package/dist/config/runtime-config.d.ts +3 -0
- package/dist/config/runtime-config.d.ts.map +1 -0
- package/dist/config/runtime-config.js +97 -0
- package/dist/config/schema-definitions.d.ts +3 -0
- package/dist/config/schema-definitions.d.ts.map +1 -0
- package/dist/config/schema-definitions.js +90 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +4 -0
- package/dist/index.js +74 -411
- package/dist/project/detect.d.ts +4 -0
- package/dist/project/detect.d.ts.map +1 -0
- package/dist/project/detect.js +113 -0
- package/dist/project/paths.d.ts +3 -0
- package/dist/project/paths.d.ts.map +1 -0
- package/dist/project/paths.js +28 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +4 -6
- package/dist/server.d.ts +5 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +123 -43
- package/dist/types/config.d.ts +20 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +2 -0
- package/dist/types/project.d.ts +7 -0
- package/dist/types/project.d.ts.map +1 -0
- package/dist/types/project.js +2 -0
- package/package.json +56 -51
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.initializeProject = void 0;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
const promises_1 = require("node:readline/promises");
|
|
7
|
+
const node_process_1 = require("node:process");
|
|
8
|
+
const constants_1 = require("../constants");
|
|
9
|
+
const detect_1 = require("../project/detect");
|
|
10
|
+
const paths_1 = require("../project/paths");
|
|
11
|
+
const DEFAULT_INIT_PORT = 3000;
|
|
12
|
+
const parseYesNo = (value, defaultValue) => {
|
|
13
|
+
const normalized = value.trim().toLowerCase();
|
|
14
|
+
if (!normalized) {
|
|
15
|
+
return defaultValue;
|
|
16
|
+
}
|
|
17
|
+
if (normalized === "y" || normalized === "yes") {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
if (normalized === "n" || normalized === "no") {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
return undefined;
|
|
24
|
+
};
|
|
25
|
+
const askInitWizardQuestions = async () => {
|
|
26
|
+
if (!node_process_1.stdin.isTTY || !node_process_1.stdout.isTTY) {
|
|
27
|
+
return {
|
|
28
|
+
port: DEFAULT_INIT_PORT,
|
|
29
|
+
cors: true,
|
|
30
|
+
generateSampleSchemas: true,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const questionInterface = (0, promises_1.createInterface)({ input: node_process_1.stdin, output: node_process_1.stdout });
|
|
34
|
+
try {
|
|
35
|
+
let port = DEFAULT_INIT_PORT;
|
|
36
|
+
while (true) {
|
|
37
|
+
const answer = await questionInterface.question(`What port? (default: ${DEFAULT_INIT_PORT}) `);
|
|
38
|
+
if (!answer.trim()) {
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
const parsedPort = Number(answer.trim());
|
|
42
|
+
if (Number.isInteger(parsedPort) &&
|
|
43
|
+
parsedPort >= 1 &&
|
|
44
|
+
parsedPort <= 65535) {
|
|
45
|
+
port = parsedPort;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
console.log("Please enter a valid port (1-65535).\n");
|
|
49
|
+
}
|
|
50
|
+
let cors = true;
|
|
51
|
+
while (true) {
|
|
52
|
+
const answer = await questionInterface.question("Enable CORS? (Y/n) ");
|
|
53
|
+
const parsed = parseYesNo(answer, true);
|
|
54
|
+
if (parsed !== undefined) {
|
|
55
|
+
cors = parsed;
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
console.log("Please answer with Y/Yes or N/No.\n");
|
|
59
|
+
}
|
|
60
|
+
let generateSampleSchemas = true;
|
|
61
|
+
while (true) {
|
|
62
|
+
const answer = await questionInterface.question("Generate sample schemas? (Y/n) ");
|
|
63
|
+
const parsed = parseYesNo(answer, true);
|
|
64
|
+
if (parsed !== undefined) {
|
|
65
|
+
generateSampleSchemas = parsed;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
console.log("Please answer with Y/Yes or N/No.\n");
|
|
69
|
+
}
|
|
70
|
+
console.log("");
|
|
71
|
+
return {
|
|
72
|
+
port,
|
|
73
|
+
cors,
|
|
74
|
+
generateSampleSchemas,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
questionInterface.close();
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const getRuntimeConfigTemplate = ({ port, cors, includeSampleRoutes, }) => {
|
|
82
|
+
const routeSection = includeSampleRoutes
|
|
83
|
+
? [
|
|
84
|
+
" routes: {",
|
|
85
|
+
' "/users": { count: 25, schema: "user" },',
|
|
86
|
+
' "/posts": { count: 40, schema: "post" },',
|
|
87
|
+
" },",
|
|
88
|
+
]
|
|
89
|
+
: [];
|
|
90
|
+
return [
|
|
91
|
+
"module.exports = {",
|
|
92
|
+
` port: ${port},`,
|
|
93
|
+
` cors: ${cors},`,
|
|
94
|
+
" delay: 0,",
|
|
95
|
+
...routeSection,
|
|
96
|
+
"};",
|
|
97
|
+
].join("\n");
|
|
98
|
+
};
|
|
99
|
+
const SAMPLE_USER_SCHEMA = [
|
|
100
|
+
"id:",
|
|
101
|
+
" type: uuid",
|
|
102
|
+
"fullName:",
|
|
103
|
+
" type: name",
|
|
104
|
+
" faker: person.fullName",
|
|
105
|
+
"email:",
|
|
106
|
+
" type: email",
|
|
107
|
+
" faker: internet.email",
|
|
108
|
+
"avatarUrl:",
|
|
109
|
+
" type: url",
|
|
110
|
+
" faker: image.avatar",
|
|
111
|
+
].join("\n");
|
|
112
|
+
const SAMPLE_POST_SCHEMA = [
|
|
113
|
+
"id:",
|
|
114
|
+
" type: uuid",
|
|
115
|
+
"title:",
|
|
116
|
+
" type: string",
|
|
117
|
+
" faker: lorem.sentence",
|
|
118
|
+
"body:",
|
|
119
|
+
" type: string",
|
|
120
|
+
" faker: lorem.paragraph",
|
|
121
|
+
"createdAt:",
|
|
122
|
+
" type: date",
|
|
123
|
+
].join("\n");
|
|
124
|
+
const initializeProject = async ({ force, }) => {
|
|
125
|
+
const packageJsonPath = (0, paths_1.findClosestPackageJson)(process.cwd());
|
|
126
|
+
if (!packageJsonPath) {
|
|
127
|
+
console.error("Could not find package.json in this directory or parent directories.");
|
|
128
|
+
return 1;
|
|
129
|
+
}
|
|
130
|
+
const projectRoot = (0, node_path_1.dirname)(packageJsonPath);
|
|
131
|
+
const detectedProject = (0, detect_1.detectProject)(packageJsonPath, projectRoot);
|
|
132
|
+
const fexapiDirectoryPath = (0, node_path_1.join)(projectRoot, "fexapi");
|
|
133
|
+
const schemaPath = (0, node_path_1.join)(fexapiDirectoryPath, "schema.fexapi");
|
|
134
|
+
const configPath = (0, node_path_1.join)(projectRoot, "fexapi.config.json");
|
|
135
|
+
const runtimeConfigPath = (0, node_path_1.join)(projectRoot, "fexapi.config.js");
|
|
136
|
+
const schemasDirectoryPath = (0, node_path_1.join)(projectRoot, "schemas");
|
|
137
|
+
const userSchemaPath = (0, node_path_1.join)(schemasDirectoryPath, "user.yaml");
|
|
138
|
+
const postSchemaPath = (0, node_path_1.join)(schemasDirectoryPath, "post.yaml");
|
|
139
|
+
const wizardAnswers = await askInitWizardQuestions();
|
|
140
|
+
(0, node_fs_1.mkdirSync)(fexapiDirectoryPath, { recursive: true });
|
|
141
|
+
const configExists = (0, node_fs_1.existsSync)(configPath);
|
|
142
|
+
const schemaExists = (0, node_fs_1.existsSync)(schemaPath);
|
|
143
|
+
const config = {
|
|
144
|
+
framework: detectedProject.primaryFramework,
|
|
145
|
+
frameworks: detectedProject.frameworks,
|
|
146
|
+
tooling: detectedProject.tooling,
|
|
147
|
+
schemaPath: "fexapi/schema.fexapi",
|
|
148
|
+
generatedPath: constants_1.GENERATED_SPEC_RELATIVE_PATH,
|
|
149
|
+
defaultPort: wizardAnswers.port,
|
|
150
|
+
corsEnabled: wizardAnswers.cors,
|
|
151
|
+
sampleSchemasGenerated: wizardAnswers.generateSampleSchemas,
|
|
152
|
+
createdAt: new Date().toISOString(),
|
|
153
|
+
};
|
|
154
|
+
if (!configExists || force) {
|
|
155
|
+
(0, node_fs_1.writeFileSync)(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf-8");
|
|
156
|
+
}
|
|
157
|
+
if (!schemaExists || force) {
|
|
158
|
+
(0, node_fs_1.writeFileSync)(schemaPath, `${(0, detect_1.getSchemaTemplate)(detectedProject.primaryFramework, wizardAnswers.port)}\n`, "utf-8");
|
|
159
|
+
}
|
|
160
|
+
const runtimeConfigExists = (0, node_fs_1.existsSync)(runtimeConfigPath);
|
|
161
|
+
if (!runtimeConfigExists || force) {
|
|
162
|
+
(0, node_fs_1.writeFileSync)(runtimeConfigPath, `${getRuntimeConfigTemplate({
|
|
163
|
+
port: wizardAnswers.port,
|
|
164
|
+
cors: wizardAnswers.cors,
|
|
165
|
+
includeSampleRoutes: wizardAnswers.generateSampleSchemas,
|
|
166
|
+
})}\n`, "utf-8");
|
|
167
|
+
}
|
|
168
|
+
let userSchemaStatus = "skipped";
|
|
169
|
+
let postSchemaStatus = "skipped";
|
|
170
|
+
if (wizardAnswers.generateSampleSchemas) {
|
|
171
|
+
(0, node_fs_1.mkdirSync)(schemasDirectoryPath, { recursive: true });
|
|
172
|
+
const userSchemaExists = (0, node_fs_1.existsSync)(userSchemaPath);
|
|
173
|
+
if (!userSchemaExists || force) {
|
|
174
|
+
(0, node_fs_1.writeFileSync)(userSchemaPath, `${SAMPLE_USER_SCHEMA}\n`, "utf-8");
|
|
175
|
+
userSchemaStatus = userSchemaExists ? "overwritten" : "created";
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
userSchemaStatus = "exists";
|
|
179
|
+
}
|
|
180
|
+
const postSchemaExists = (0, node_fs_1.existsSync)(postSchemaPath);
|
|
181
|
+
if (!postSchemaExists || force) {
|
|
182
|
+
(0, node_fs_1.writeFileSync)(postSchemaPath, `${SAMPLE_POST_SCHEMA}\n`, "utf-8");
|
|
183
|
+
postSchemaStatus = postSchemaExists ? "overwritten" : "created";
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
postSchemaStatus = "exists";
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
console.log(`Initialized Fexapi in ${projectRoot}`);
|
|
190
|
+
console.log(`Detected framework: ${detectedProject.primaryFramework}`);
|
|
191
|
+
console.log(`Detected frameworks: ${detectedProject.frameworks.join(", ")}`);
|
|
192
|
+
if (detectedProject.tooling.length > 0) {
|
|
193
|
+
console.log(`Detected tooling: ${detectedProject.tooling.join(", ")}`);
|
|
194
|
+
}
|
|
195
|
+
if (configExists && !force) {
|
|
196
|
+
console.log(`Exists ${configPath}`);
|
|
197
|
+
}
|
|
198
|
+
else if (configExists && force) {
|
|
199
|
+
console.log(`Overwritten ${configPath}`);
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
console.log(`Created ${configPath}`);
|
|
203
|
+
}
|
|
204
|
+
if (schemaExists && !force) {
|
|
205
|
+
console.log(`Exists ${schemaPath}`);
|
|
206
|
+
}
|
|
207
|
+
else if (schemaExists && force) {
|
|
208
|
+
console.log(`Overwritten ${schemaPath}`);
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
console.log(`Created ${schemaPath}`);
|
|
212
|
+
}
|
|
213
|
+
if (runtimeConfigExists && !force) {
|
|
214
|
+
console.log(`Exists ${runtimeConfigPath}`);
|
|
215
|
+
}
|
|
216
|
+
else if (runtimeConfigExists && force) {
|
|
217
|
+
console.log(`Overwritten ${runtimeConfigPath}`);
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
console.log(`Created ${runtimeConfigPath}`);
|
|
221
|
+
}
|
|
222
|
+
if (wizardAnswers.generateSampleSchemas) {
|
|
223
|
+
if (userSchemaStatus === "exists") {
|
|
224
|
+
console.log(`Exists ${userSchemaPath}`);
|
|
225
|
+
}
|
|
226
|
+
else if (userSchemaStatus === "overwritten") {
|
|
227
|
+
console.log(`Overwritten ${userSchemaPath}`);
|
|
228
|
+
}
|
|
229
|
+
else if (userSchemaStatus === "created") {
|
|
230
|
+
console.log(`Created ${userSchemaPath}`);
|
|
231
|
+
}
|
|
232
|
+
if (postSchemaStatus === "exists") {
|
|
233
|
+
console.log(`Exists ${postSchemaPath}`);
|
|
234
|
+
}
|
|
235
|
+
else if (postSchemaStatus === "overwritten") {
|
|
236
|
+
console.log(`Overwritten ${postSchemaPath}`);
|
|
237
|
+
}
|
|
238
|
+
else if (postSchemaStatus === "created") {
|
|
239
|
+
console.log(`Created ${postSchemaPath}`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
console.log("Sample schemas were skipped.");
|
|
244
|
+
}
|
|
245
|
+
if (detectedProject.primaryFramework === "unknown") {
|
|
246
|
+
console.log("No known framework dependency found. Update fexapi.config.json and schema.fexapi if needed.");
|
|
247
|
+
}
|
|
248
|
+
return 0;
|
|
249
|
+
};
|
|
250
|
+
exports.initializeProject = initializeProject;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Server } from "node:http";
|
|
2
|
+
export declare const createProjectServer: ({ host, port, logEnabled, }: {
|
|
3
|
+
host: string;
|
|
4
|
+
port?: number;
|
|
5
|
+
logEnabled?: boolean;
|
|
6
|
+
}) => Server | undefined;
|
|
7
|
+
export declare const serveProject: ({ host, port, logEnabled, }: {
|
|
8
|
+
host: string;
|
|
9
|
+
port?: number;
|
|
10
|
+
logEnabled?: boolean;
|
|
11
|
+
}) => number;
|
|
12
|
+
//# sourceMappingURL=serve.d.ts.map
|
|
@@ -0,0 +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;AAQxC,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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.serveProject = exports.createProjectServer = void 0;
|
|
4
|
+
const constants_1 = require("../constants");
|
|
5
|
+
const generated_spec_1 = require("../config/generated-spec");
|
|
6
|
+
const runtime_config_1 = require("../config/runtime-config");
|
|
7
|
+
const schema_definitions_1 = require("../config/schema-definitions");
|
|
8
|
+
const paths_1 = require("../project/paths");
|
|
9
|
+
const server_1 = require("../server");
|
|
10
|
+
const createProjectServer = ({ host, port, logEnabled = false, }) => {
|
|
11
|
+
const projectRoot = (0, paths_1.resolveProjectRoot)();
|
|
12
|
+
if (!projectRoot) {
|
|
13
|
+
console.error("Could not find package.json in this directory or parent directories.");
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
const runtimeConfig = projectRoot
|
|
17
|
+
? (0, runtime_config_1.loadFexapiRuntimeConfig)(projectRoot)
|
|
18
|
+
: undefined;
|
|
19
|
+
const schemaDefinitions = projectRoot
|
|
20
|
+
? (0, schema_definitions_1.loadSchemaDefinitions)(projectRoot)
|
|
21
|
+
: {};
|
|
22
|
+
const generatedSpec = projectRoot
|
|
23
|
+
? (0, generated_spec_1.loadGeneratedApiSpec)(projectRoot)
|
|
24
|
+
: undefined;
|
|
25
|
+
const effectivePort = port ?? runtimeConfig?.port ?? generatedSpec?.port ?? 4000;
|
|
26
|
+
if (runtimeConfig?.routes && Object.keys(runtimeConfig.routes).length > 0) {
|
|
27
|
+
console.log(`Using routes from fexapi.config.js (${Object.keys(runtimeConfig.routes).length})`);
|
|
28
|
+
}
|
|
29
|
+
if (Object.keys(schemaDefinitions).length > 0) {
|
|
30
|
+
console.log(`Loaded custom schemas from /schemas (${Object.keys(schemaDefinitions).length})`);
|
|
31
|
+
}
|
|
32
|
+
if (generatedSpec &&
|
|
33
|
+
!(runtimeConfig?.routes && Object.keys(runtimeConfig.routes).length > 0)) {
|
|
34
|
+
console.log(`Using generated schema routes (${generatedSpec.routes.length}) from ${constants_1.GENERATED_SPEC_RELATIVE_PATH}`);
|
|
35
|
+
}
|
|
36
|
+
else if (!runtimeConfig?.routes ||
|
|
37
|
+
Object.keys(runtimeConfig.routes).length === 0) {
|
|
38
|
+
console.log("No generated schema found. Run `fexapi generate` to serve schema-defined endpoints.");
|
|
39
|
+
}
|
|
40
|
+
return (0, server_1.startServer)({
|
|
41
|
+
host,
|
|
42
|
+
port: effectivePort,
|
|
43
|
+
apiSpec: generatedSpec,
|
|
44
|
+
runtimeConfig,
|
|
45
|
+
schemaDefinitions,
|
|
46
|
+
logRequests: logEnabled,
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
exports.createProjectServer = createProjectServer;
|
|
50
|
+
const serveProject = ({ host, port, logEnabled, }) => {
|
|
51
|
+
const server = (0, exports.createProjectServer)({ host, port, logEnabled });
|
|
52
|
+
if (!server) {
|
|
53
|
+
return 1;
|
|
54
|
+
}
|
|
55
|
+
const shutdown = () => {
|
|
56
|
+
server.close((error) => {
|
|
57
|
+
if (error) {
|
|
58
|
+
console.error("Error while shutting down server", error);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
process.exit(0);
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
process.on("SIGINT", shutdown);
|
|
65
|
+
process.on("SIGTERM", shutdown);
|
|
66
|
+
return 0;
|
|
67
|
+
};
|
|
68
|
+
exports.serveProject = serveProject;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generated-spec.d.ts","sourceRoot":"","sources":["../../src/config/generated-spec.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAElD,eAAO,MAAM,oBAAoB,GAC/B,aAAa,MAAM,KAClB,gBAAgB,GAAG,SAwBrB,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadGeneratedApiSpec = void 0;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
const constants_1 = require("../constants");
|
|
7
|
+
const loadGeneratedApiSpec = (projectRoot) => {
|
|
8
|
+
const generatedPath = (0, node_path_1.join)(projectRoot, constants_1.GENERATED_SPEC_RELATIVE_PATH);
|
|
9
|
+
if (!(0, node_fs_1.existsSync)(generatedPath)) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
const parsed = JSON.parse((0, node_fs_1.readFileSync)(generatedPath, "utf-8"));
|
|
14
|
+
if (typeof parsed.port !== "number" || !Array.isArray(parsed.routes)) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
port: parsed.port,
|
|
19
|
+
routes: parsed.routes,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
exports.loadGeneratedApiSpec = loadGeneratedApiSpec;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-config.d.ts","sourceRoot":"","sources":["../../src/config/runtime-config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAqB,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAU9E,eAAO,MAAM,uBAAuB,GAClC,aAAa,MAAM,KAClB,mBAAmB,GAAG,SA8GxB,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadFexapiRuntimeConfig = void 0;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
const isRecord = (value) => {
|
|
7
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8
|
+
};
|
|
9
|
+
const normalizePath = (value) => {
|
|
10
|
+
return value.startsWith("/") ? value : `/${value}`;
|
|
11
|
+
};
|
|
12
|
+
const loadFexapiRuntimeConfig = (projectRoot) => {
|
|
13
|
+
const configPath = (0, node_path_1.join)(projectRoot, "fexapi.config.js");
|
|
14
|
+
if (!(0, node_fs_1.existsSync)(configPath)) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const source = (0, node_fs_1.readFileSync)(configPath, "utf-8");
|
|
19
|
+
const moduleShim = { exports: {} };
|
|
20
|
+
const exportsShim = {};
|
|
21
|
+
const executeModule = new Function("module", "exports", source);
|
|
22
|
+
executeModule(moduleShim, exportsShim);
|
|
23
|
+
const rawConfig = moduleShim.exports;
|
|
24
|
+
if (!isRecord(rawConfig)) {
|
|
25
|
+
console.error("Invalid fexapi.config.js: expected an object export.");
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
const normalizedConfig = {};
|
|
29
|
+
if (rawConfig.port !== undefined) {
|
|
30
|
+
if (typeof rawConfig.port !== "number" ||
|
|
31
|
+
!Number.isInteger(rawConfig.port) ||
|
|
32
|
+
rawConfig.port < 1 ||
|
|
33
|
+
rawConfig.port > 65535) {
|
|
34
|
+
console.error("Invalid fexapi.config.js: `port` must be an integer between 1 and 65535.");
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
normalizedConfig.port = rawConfig.port;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (rawConfig.cors !== undefined) {
|
|
41
|
+
if (typeof rawConfig.cors !== "boolean") {
|
|
42
|
+
console.error("Invalid fexapi.config.js: `cors` must be true or false.");
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
normalizedConfig.cors = rawConfig.cors;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (rawConfig.delay !== undefined) {
|
|
49
|
+
if (typeof rawConfig.delay !== "number" ||
|
|
50
|
+
!Number.isFinite(rawConfig.delay) ||
|
|
51
|
+
rawConfig.delay < 0) {
|
|
52
|
+
console.error("Invalid fexapi.config.js: `delay` must be a non-negative number.");
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
normalizedConfig.delay = Math.floor(rawConfig.delay);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (rawConfig.routes !== undefined) {
|
|
59
|
+
if (!isRecord(rawConfig.routes)) {
|
|
60
|
+
console.error("Invalid fexapi.config.js: `routes` must be an object.");
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
const normalizedRoutes = {};
|
|
64
|
+
for (const [pathKey, routeValue] of Object.entries(rawConfig.routes)) {
|
|
65
|
+
if (!isRecord(routeValue)) {
|
|
66
|
+
console.error(`Invalid fexapi.config.js route at ${pathKey}: expected an object with count and schema.`);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const schemaValue = routeValue.schema;
|
|
70
|
+
if (typeof schemaValue !== "string" ||
|
|
71
|
+
schemaValue.trim().length === 0) {
|
|
72
|
+
console.error(`Invalid fexapi.config.js route at ${pathKey}: schema must be a non-empty string.`);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const countValue = routeValue.count;
|
|
76
|
+
const normalizedCount = typeof countValue === "number" &&
|
|
77
|
+
Number.isInteger(countValue) &&
|
|
78
|
+
countValue > 0
|
|
79
|
+
? countValue
|
|
80
|
+
: 10;
|
|
81
|
+
normalizedRoutes[normalizePath(pathKey)] = {
|
|
82
|
+
count: normalizedCount,
|
|
83
|
+
schema: schemaValue.trim(),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
normalizedConfig.routes = normalizedRoutes;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return normalizedConfig;
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
93
|
+
console.error(`Failed to load fexapi.config.js: ${message}`);
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
exports.loadFexapiRuntimeConfig = loadFexapiRuntimeConfig;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-definitions.d.ts","sourceRoot":"","sources":["../../src/config/schema-definitions.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAGV,uBAAuB,EACxB,MAAM,iBAAiB,CAAC;AA8EzB,eAAO,MAAM,qBAAqB,GAChC,aAAa,MAAM,KAClB,uBAoCF,CAAC"}
|
|
@@ -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 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,4BAA4B,8BAA8B,CAAC"}
|