fexapi 0.1.2 → 0.1.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.
- package/README.md +27 -3
- package/dist/cli/args.d.ts +3 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +9 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +2 -0
- package/dist/commands/format.d.ts +2 -0
- package/dist/commands/format.d.ts.map +1 -0
- package/dist/commands/format.js +56 -0
- package/dist/index.js +16 -0
- package/dist/project/detect.d.ts.map +1 -1
- package/dist/project/detect.js +16 -3
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +77 -37
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -37,15 +37,29 @@ Creates:
|
|
|
37
37
|
|
|
38
38
|
### 2) Edit schema file
|
|
39
39
|
|
|
40
|
-
`fexapi/schema.fexapi`
|
|
40
|
+
`fexapi/schema.fexapi` supports readable multi-line route fields (single-line still works):
|
|
41
41
|
|
|
42
42
|
```txt
|
|
43
43
|
# Server
|
|
44
44
|
port: 4100
|
|
45
45
|
|
|
46
46
|
# Routes
|
|
47
|
-
GET /users:
|
|
48
|
-
|
|
47
|
+
GET /users:
|
|
48
|
+
id:uuid
|
|
49
|
+
name:name
|
|
50
|
+
email:email
|
|
51
|
+
age:number
|
|
52
|
+
phone:phone
|
|
53
|
+
pic:url
|
|
54
|
+
courseName:string
|
|
55
|
+
|
|
56
|
+
GET /courses:
|
|
57
|
+
id:uuid
|
|
58
|
+
courseName:string
|
|
59
|
+
mentor:name
|
|
60
|
+
|
|
61
|
+
# one-line format is also valid:
|
|
62
|
+
# GET /users: id:uuid,name:name,email:email
|
|
49
63
|
```
|
|
50
64
|
|
|
51
65
|
### 3) Generate artifacts (updates migration)
|
|
@@ -59,6 +73,16 @@ Generates:
|
|
|
59
73
|
- `fexapi/generated.api.json`
|
|
60
74
|
- `fexapi/migrations/schema.json`
|
|
61
75
|
|
|
76
|
+
### 3a) Format schema (optional)
|
|
77
|
+
|
|
78
|
+
Auto-format your schema to use readable multi-line field formatting:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npx fexapi format
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
This rewrites `fexapi/schema.fexapi` with each field on its own indented line for better readability.
|
|
85
|
+
|
|
62
86
|
### 4) Start server
|
|
63
87
|
|
|
64
88
|
```bash
|
package/dist/cli/args.d.ts
CHANGED
|
@@ -6,6 +6,9 @@ export declare const parseInitOptions: (initArgs: string[]) => {
|
|
|
6
6
|
export declare const parseGenerateOptions: (generateArgs: string[]) => {
|
|
7
7
|
error?: string;
|
|
8
8
|
};
|
|
9
|
+
export declare const parseFormatOptions: (formatArgs: string[]) => {
|
|
10
|
+
error?: string;
|
|
11
|
+
};
|
|
9
12
|
export declare const parseServeOptions: (serveArgs: string[]) => {
|
|
10
13
|
host: string;
|
|
11
14
|
port?: number;
|
package/dist/cli/args.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,GAC3B,UAAU,MAAM,EAAE,KACjB;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAWtC,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,cAAc,MAAM,EAAE,KACrB;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAQlB,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,WAAW,MAAM,EAAE,KAClB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAkDxE,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,SAAS,MAAM,EAAE,KAEf;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,GAC3E;IAAE,KAAK,EAAE,MAAM,CAAA;CAwDlB,CAAC"}
|
|
1
|
+
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,GAC3B,UAAU,MAAM,EAAE,KACjB;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAWtC,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,cAAc,MAAM,EAAE,KACrB;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAQlB,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,YAAY,MAAM,EAAE,KACnB;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAQlB,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,WAAW,MAAM,EAAE,KAClB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAkDxE,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,SAAS,MAAM,EAAE,KAEf;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,GAC3E;IAAE,KAAK,EAAE,MAAM,CAAA;CAwDlB,CAAC"}
|
package/dist/cli/args.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parseDevOptions = exports.parseServeOptions = exports.parseGenerateOptions = exports.parseInitOptions = void 0;
|
|
3
|
+
exports.parseDevOptions = exports.parseServeOptions = exports.parseFormatOptions = exports.parseGenerateOptions = exports.parseInitOptions = void 0;
|
|
4
4
|
const parseInitOptions = (initArgs) => {
|
|
5
5
|
const validFlags = new Set(["--force"]);
|
|
6
6
|
const invalidFlags = initArgs.filter((value) => value.startsWith("-") && !validFlags.has(value));
|
|
@@ -18,6 +18,14 @@ const parseGenerateOptions = (generateArgs) => {
|
|
|
18
18
|
return {};
|
|
19
19
|
};
|
|
20
20
|
exports.parseGenerateOptions = parseGenerateOptions;
|
|
21
|
+
const parseFormatOptions = (formatArgs) => {
|
|
22
|
+
const invalidFlags = formatArgs.filter((value) => value.startsWith("-"));
|
|
23
|
+
if (invalidFlags.length > 0) {
|
|
24
|
+
return { error: `Unknown option(s): ${invalidFlags.join(", ")}` };
|
|
25
|
+
}
|
|
26
|
+
return {};
|
|
27
|
+
};
|
|
28
|
+
exports.parseFormatOptions = parseFormatOptions;
|
|
21
29
|
const parseServeOptions = (serveArgs) => {
|
|
22
30
|
const getFlagValue = (flagName) => {
|
|
23
31
|
const index = serveArgs.indexOf(flagName);
|
package/dist/cli/help.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../src/cli/help.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS,
|
|
1
|
+
{"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../src/cli/help.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS,YAkDrB,CAAC"}
|
package/dist/cli/help.js
CHANGED
|
@@ -7,6 +7,7 @@ const printHelp = () => {
|
|
|
7
7
|
console.log("Usage:");
|
|
8
8
|
console.log(" fexapi init [--force]");
|
|
9
9
|
console.log(" fexapi generate");
|
|
10
|
+
console.log(" fexapi format");
|
|
10
11
|
console.log(" fexapi dev [--watch] [--host <host>] [--port <number>] [--log]");
|
|
11
12
|
console.log(" fexapi serve [--host <host>] [--port <number>] [--log]");
|
|
12
13
|
console.log(" fexapi run [--host <host>] [--port <number>] [--log]");
|
|
@@ -17,6 +18,7 @@ const printHelp = () => {
|
|
|
17
18
|
console.log(" fexapi init");
|
|
18
19
|
console.log(" fexapi init --force");
|
|
19
20
|
console.log(" fexapi generate");
|
|
21
|
+
console.log(" fexapi format");
|
|
20
22
|
console.log(" fexapi dev --watch");
|
|
21
23
|
console.log(" fexapi dev --watch --log");
|
|
22
24
|
console.log(" fexapi serve --log");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/commands/format.ts"],"names":[],"mappings":"AAmCA,eAAO,MAAM,YAAY,QAAO,MAuC/B,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatSchema = void 0;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
const schema_1 = require("../schema");
|
|
7
|
+
const paths_1 = require("../project/paths");
|
|
8
|
+
const formatSchemaToMultiline = (schemaText) => {
|
|
9
|
+
const parsed = (0, schema_1.parseFexapiSchema)(schemaText);
|
|
10
|
+
if (parsed.errors.length > 0 ||
|
|
11
|
+
!parsed.schema ||
|
|
12
|
+
parsed.schema.routes.length === 0) {
|
|
13
|
+
return schemaText;
|
|
14
|
+
}
|
|
15
|
+
const lines = [];
|
|
16
|
+
lines.push("# Server");
|
|
17
|
+
lines.push(`port: ${parsed.schema.port}`);
|
|
18
|
+
lines.push("");
|
|
19
|
+
lines.push("# Routes");
|
|
20
|
+
for (const route of parsed.schema.routes) {
|
|
21
|
+
lines.push(`${route.method} ${route.path}:`);
|
|
22
|
+
for (const field of route.fields) {
|
|
23
|
+
lines.push(` ${field.name}:${field.type}`);
|
|
24
|
+
}
|
|
25
|
+
lines.push("");
|
|
26
|
+
}
|
|
27
|
+
return lines.join("\n").trimEnd() + "\n";
|
|
28
|
+
};
|
|
29
|
+
const formatSchema = () => {
|
|
30
|
+
const projectRoot = (0, paths_1.resolveProjectRoot)();
|
|
31
|
+
if (!projectRoot) {
|
|
32
|
+
console.error("Could not find package.json in this directory or parent directories.");
|
|
33
|
+
return 1;
|
|
34
|
+
}
|
|
35
|
+
const schemaPath = (0, node_path_1.join)(projectRoot, "fexapi", "schema.fexapi");
|
|
36
|
+
if (!(0, node_fs_1.existsSync)(schemaPath)) {
|
|
37
|
+
console.error(`Schema file not found: ${schemaPath}`);
|
|
38
|
+
console.error("Run `fexapi init` first.");
|
|
39
|
+
return 1;
|
|
40
|
+
}
|
|
41
|
+
const schemaText = (0, node_fs_1.readFileSync)(schemaPath, "utf-8");
|
|
42
|
+
const parsed = (0, schema_1.parseFexapiSchema)(schemaText);
|
|
43
|
+
if (parsed.errors.length > 0 || !parsed.schema) {
|
|
44
|
+
console.error("Failed to parse schema.fexapi");
|
|
45
|
+
for (const error of parsed.errors) {
|
|
46
|
+
console.error(`- ${error}`);
|
|
47
|
+
}
|
|
48
|
+
return 1;
|
|
49
|
+
}
|
|
50
|
+
const formatted = formatSchemaToMultiline(schemaText);
|
|
51
|
+
(0, node_fs_1.writeFileSync)(schemaPath, formatted, "utf-8");
|
|
52
|
+
console.log(`Formatted ${schemaPath}`);
|
|
53
|
+
console.log(`Routes: ${parsed.schema.routes.length} | Port: ${parsed.schema.port}`);
|
|
54
|
+
return 0;
|
|
55
|
+
};
|
|
56
|
+
exports.formatSchema = formatSchema;
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
4
4
|
const args_1 = require("./cli/args");
|
|
5
5
|
const dev_1 = require("./commands/dev");
|
|
6
6
|
const help_1 = require("./cli/help");
|
|
7
|
+
const format_1 = require("./commands/format");
|
|
7
8
|
const generate_1 = require("./commands/generate");
|
|
8
9
|
const init_1 = require("./commands/init");
|
|
9
10
|
const serve_1 = require("./commands/serve");
|
|
@@ -41,6 +42,21 @@ const main = async () => {
|
|
|
41
42
|
}
|
|
42
43
|
process.exit((0, generate_1.generateFromSchema)());
|
|
43
44
|
}
|
|
45
|
+
else if (firstArg === "format") {
|
|
46
|
+
if (restArgs.includes("--help") || restArgs.includes("-h")) {
|
|
47
|
+
console.log("Usage: fexapi format");
|
|
48
|
+
console.log("Reformats fexapi/schema.fexapi to use readable multi-line field formatting.");
|
|
49
|
+
process.exit(0);
|
|
50
|
+
}
|
|
51
|
+
const formatOptions = (0, args_1.parseFormatOptions)(restArgs);
|
|
52
|
+
if (formatOptions.error) {
|
|
53
|
+
console.error(formatOptions.error);
|
|
54
|
+
console.log("");
|
|
55
|
+
console.log("Usage: fexapi format");
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
process.exit((0, format_1.formatSchema)());
|
|
59
|
+
}
|
|
44
60
|
else if (firstArg === "dev") {
|
|
45
61
|
if (restArgs.includes("--help") || restArgs.includes("-h")) {
|
|
46
62
|
console.log("Usage: fexapi dev [--watch] [--host <host>] [--port <number>] [--log]");
|
|
@@ -1 +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,GAC5B,WAAW,kBAAkB,EAC7B,aAAW,KACV,
|
|
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,GAC5B,WAAW,kBAAkB,EAC7B,aAAW,KACV,MA+BF,CAAC"}
|
package/dist/project/detect.js
CHANGED
|
@@ -105,9 +105,22 @@ const getSchemaTemplate = (framework, port = 4000) => {
|
|
|
105
105
|
`port: ${port}`,
|
|
106
106
|
"",
|
|
107
107
|
"# Routes",
|
|
108
|
-
"# Format: METHOD /endpoint: field:type,field:type",
|
|
109
|
-
"
|
|
110
|
-
"
|
|
108
|
+
"# Format (single-line): METHOD /endpoint: field:type,field:type",
|
|
109
|
+
"# Format (multi-line):",
|
|
110
|
+
"# METHOD /endpoint:",
|
|
111
|
+
"# field:type",
|
|
112
|
+
"# field:type",
|
|
113
|
+
"GET /users:",
|
|
114
|
+
" id:uuid",
|
|
115
|
+
" fullName:name",
|
|
116
|
+
" username:string",
|
|
117
|
+
" email:email",
|
|
118
|
+
" avatarUrl:url",
|
|
119
|
+
"GET /posts:",
|
|
120
|
+
" id:uuid",
|
|
121
|
+
" title:string",
|
|
122
|
+
" body:string",
|
|
123
|
+
" createdAt:date",
|
|
111
124
|
].join("\n");
|
|
112
125
|
};
|
|
113
126
|
exports.getSchemaTemplate = getSchemaTemplate;
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GACvB,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,MAAM,GACN,MAAM,GACN,OAAO,GACP,KAAK,GACL,MAAM,GACN,OAAO,CAAC;AAEZ,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,eAAe,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB,CAAC;
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GACvB,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,MAAM,GACN,MAAM,GACN,OAAO,GACP,KAAK,GACL,MAAM,GACN,OAAO,CAAC;AAEZ,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,eAAe,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB,CAAC;AAqHF,eAAO,MAAM,iBAAiB,GAC5B,YAAY,MAAM,KACjB;IAAE,MAAM,CAAC,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAmG3C,CAAC"}
|
package/dist/schema.js
CHANGED
|
@@ -14,7 +14,27 @@ const VALID_TYPES = [
|
|
|
14
14
|
];
|
|
15
15
|
const DEFAULT_PORT = 4000;
|
|
16
16
|
const ROUTE_FORMAT_ERROR_MESSAGE = "Invalid route definition. Expected format: " +
|
|
17
|
-
"METHOD /endpoint: field:type,field:type";
|
|
17
|
+
"METHOD /endpoint: field:type,field:type (or multiline fields under METHOD /endpoint:)";
|
|
18
|
+
const isPortLine = (line) => /^port\s*:/i.test(line.trim());
|
|
19
|
+
const parseRouteHeader = (line) => {
|
|
20
|
+
const separatorIndex = line.indexOf(":");
|
|
21
|
+
if (separatorIndex === -1) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
const rawLeft = line.slice(0, separatorIndex);
|
|
25
|
+
const rawFields = line.slice(separatorIndex + 1);
|
|
26
|
+
const [rawMethod, rawPath] = rawLeft.trim().split(/\s+/, 2);
|
|
27
|
+
const method = rawMethod?.toUpperCase();
|
|
28
|
+
const path = rawPath?.trim();
|
|
29
|
+
if (!method || !path || !path.startsWith("/")) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
method,
|
|
34
|
+
path,
|
|
35
|
+
inlineFields: rawFields,
|
|
36
|
+
};
|
|
37
|
+
};
|
|
18
38
|
const parseField = (rawField) => {
|
|
19
39
|
const [rawName, rawType] = rawField.split(":");
|
|
20
40
|
const name = rawName?.trim();
|
|
@@ -35,38 +55,23 @@ const parseField = (rawField) => {
|
|
|
35
55
|
type: type,
|
|
36
56
|
};
|
|
37
57
|
};
|
|
38
|
-
const parseRoute = (
|
|
39
|
-
const separatorIndex = line.indexOf(":");
|
|
40
|
-
if (separatorIndex === -1) {
|
|
41
|
-
return { error: ROUTE_FORMAT_ERROR_MESSAGE };
|
|
42
|
-
}
|
|
43
|
-
const rawLeft = line.slice(0, separatorIndex);
|
|
44
|
-
const rawFields = line.slice(separatorIndex + 1);
|
|
45
|
-
if (!rawLeft || !rawFields) {
|
|
46
|
-
return { error: ROUTE_FORMAT_ERROR_MESSAGE };
|
|
47
|
-
}
|
|
48
|
-
const [rawMethod, rawPath] = rawLeft.trim().split(/\s+/, 2);
|
|
49
|
-
const method = rawMethod?.toUpperCase();
|
|
50
|
-
const path = rawPath?.trim();
|
|
58
|
+
const parseRoute = ({ method, path, rawFields, }) => {
|
|
51
59
|
if (!method || !path) {
|
|
52
|
-
return {
|
|
53
|
-
error: "Invalid route definition. Missing METHOD or /endpoint before ':'.",
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
if (!path.startsWith("/")) {
|
|
57
|
-
return { error: `Route path must start with '/': ${path}` };
|
|
60
|
+
return { error: ROUTE_FORMAT_ERROR_MESSAGE };
|
|
58
61
|
}
|
|
59
62
|
const fields = [];
|
|
60
|
-
for (const
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
63
|
+
for (const rawFieldLine of rawFields) {
|
|
64
|
+
for (const part of rawFieldLine.split(",")) {
|
|
65
|
+
const trimmedPart = part.trim();
|
|
66
|
+
if (!trimmedPart) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const parsedField = parseField(trimmedPart);
|
|
70
|
+
if ("error" in parsedField) {
|
|
71
|
+
return { error: parsedField.error };
|
|
72
|
+
}
|
|
73
|
+
fields.push(parsedField);
|
|
68
74
|
}
|
|
69
|
-
fields.push(parsedField);
|
|
70
75
|
}
|
|
71
76
|
if (fields.length === 0) {
|
|
72
77
|
return { error: `Route ${method} ${path} has no valid fields.` };
|
|
@@ -77,13 +82,15 @@ const parseFexapiSchema = (schemaText) => {
|
|
|
77
82
|
let port = DEFAULT_PORT;
|
|
78
83
|
const routes = [];
|
|
79
84
|
const errors = [];
|
|
80
|
-
const lines = schemaText
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
const lines = schemaText.split(/\r?\n/);
|
|
86
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
87
|
+
const rawLine = lines[index] ?? "";
|
|
88
|
+
const trimmedLine = rawLine.trim();
|
|
89
|
+
if (!trimmedLine || trimmedLine.startsWith("#")) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (isPortLine(trimmedLine)) {
|
|
93
|
+
const rawPort = trimmedLine.slice(trimmedLine.indexOf(":") + 1).trim();
|
|
87
94
|
const parsedPort = Number(rawPort);
|
|
88
95
|
if (!Number.isInteger(parsedPort) ||
|
|
89
96
|
parsedPort < 1 ||
|
|
@@ -95,12 +102,45 @@ const parseFexapiSchema = (schemaText) => {
|
|
|
95
102
|
}
|
|
96
103
|
continue;
|
|
97
104
|
}
|
|
98
|
-
const
|
|
105
|
+
const header = parseRouteHeader(trimmedLine);
|
|
106
|
+
if (!header) {
|
|
107
|
+
errors.push(ROUTE_FORMAT_ERROR_MESSAGE);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const rawFields = [];
|
|
111
|
+
if (header.inlineFields.trim()) {
|
|
112
|
+
rawFields.push(header.inlineFields);
|
|
113
|
+
}
|
|
114
|
+
let lookaheadIndex = index + 1;
|
|
115
|
+
while (lookaheadIndex < lines.length) {
|
|
116
|
+
const lookaheadRawLine = lines[lookaheadIndex] ?? "";
|
|
117
|
+
const lookaheadTrimmedLine = lookaheadRawLine.trim();
|
|
118
|
+
if (!lookaheadTrimmedLine || lookaheadTrimmedLine.startsWith("#")) {
|
|
119
|
+
lookaheadIndex += 1;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (isPortLine(lookaheadTrimmedLine) ||
|
|
123
|
+
parseRouteHeader(lookaheadTrimmedLine)) {
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
if (/^\s+/.test(lookaheadRawLine)) {
|
|
127
|
+
rawFields.push(lookaheadTrimmedLine.replace(/^-+\s*/, ""));
|
|
128
|
+
lookaheadIndex += 1;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
const parsedRoute = parseRoute({
|
|
134
|
+
method: header.method,
|
|
135
|
+
path: header.path,
|
|
136
|
+
rawFields,
|
|
137
|
+
});
|
|
99
138
|
if ("error" in parsedRoute) {
|
|
100
139
|
errors.push(parsedRoute.error);
|
|
101
140
|
continue;
|
|
102
141
|
}
|
|
103
142
|
routes.push(parsedRoute);
|
|
143
|
+
index = lookaheadIndex - 1;
|
|
104
144
|
}
|
|
105
145
|
if (routes.length === 0) {
|
|
106
146
|
errors.push("No routes defined in schema.fexapi.");
|