fexapi 0.1.2 → 0.1.3
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 +17 -3
- 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)
|
|
@@ -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.");
|