milkio 0.1.2 → 0.2.0
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/api-test/index.ts +65 -65
- package/c.ts +37 -35
- package/defines/define-api-test.ts +24 -17
- package/defines/define-api.ts +9 -9
- package/defines/define-command-handler.ts +41 -41
- package/defines/define-http-handler.ts +181 -181
- package/defines/define-middleware.ts +6 -6
- package/defines/define-use.ts +10 -10
- package/index.ts +22 -22
- package/kernel/context.ts +17 -17
- package/kernel/fail.ts +13 -13
- package/kernel/logger.ts +96 -96
- package/kernel/meta.ts +6 -6
- package/kernel/middleware.ts +32 -32
- package/kernel/milkio.ts +250 -224
- package/kernel/runtime.ts +9 -9
- package/kernel/validate.ts +9 -9
- package/package.json +1 -1
- package/scripts/gen-insignificant.ts +239 -239
- package/scripts/gen-significant.ts +118 -120
- package/utils/create-ulid.ts +3 -3
- package/utils/env-to-boolean.ts +5 -5
- package/utils/env-to-number.ts +2 -2
- package/utils/env-to-string.ts +2 -2
- package/utils/exec.ts +18 -18
- package/utils/handle-catch-error.ts +37 -37
- package/utils/header-to-plain-object.ts +7 -7
- package/utils/remove-dir.ts +19 -19
- package/utils/tson.ts +2 -2
- package/.co.toml +0 -2
|
@@ -1,264 +1,264 @@
|
|
|
1
1
|
/* eslint-disable no-console, @typescript-eslint/no-dynamic-delete */
|
|
2
2
|
|
|
3
|
-
import { $ } from "bun"
|
|
4
|
-
import { join } from "node:path"
|
|
5
|
-
import { cwd } from "node:process"
|
|
6
|
-
import { existsSync } from "node:fs"
|
|
7
|
-
import { writeFile, readFile, copyFile } from "node:fs/promises"
|
|
8
|
-
import { MilkioConfig, TSON, type Cookbook } from ".."
|
|
3
|
+
import { $ } from "bun";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { cwd } from "node:process";
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
import { writeFile, readFile, copyFile } from "node:fs/promises";
|
|
8
|
+
import { type MilkioConfig, TSON, type Cookbook } from "..";
|
|
9
9
|
|
|
10
|
-
export default async () =>
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
export default async () => {
|
|
11
|
+
const schema = await import("../../../generated/api-schema");
|
|
12
|
+
const paths = Object.keys(schema.default.apiMethodsSchema);
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
console.log("");
|
|
15
|
+
console.time(`Cookbook Stage`);
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
const cookbook: Cookbook = {};
|
|
18
|
+
for (const path of paths) {
|
|
19
|
+
// const module = await import(/* @vite-ignore */ join(`../../../src/apps/${path}`));
|
|
20
|
+
const code = (await readFile(join(cwd(), `./src/apps/${path}.ts`))).toString();
|
|
21
|
+
const codeLines = code.split("\n");
|
|
22
|
+
let title;
|
|
23
|
+
let desc;
|
|
24
|
+
const descRaw = /\n\/\*\*\n[\s\S]+?\*\//.exec(code)?.[0] ?? "";
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
if (descRaw) {
|
|
27
|
+
const descRawLines = descRaw.split("\n");
|
|
28
|
+
if (descRawLines.at(0)?.trim() === "") descRawLines.shift();
|
|
29
|
+
if (descRawLines.at(-1)?.trim() === "") descRawLines.pop();
|
|
30
|
+
let first = true;
|
|
31
|
+
for (let index = 0; index < descRawLines.length; index++) {
|
|
32
|
+
const descRawLine = descRawLines[index].replace(/^[/ ]+?[*]*/, "").replace(/[*]*\/$/, "");
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
if (!descRawLine) continue;
|
|
35
|
+
if (first) {
|
|
36
|
+
title = descRawLine.replace(/#/g, "").trim();
|
|
37
|
+
// Originally the title was in the first line, desc is the rest of it, now desc contains complete markdown content.
|
|
38
|
+
// continue;
|
|
39
|
+
}
|
|
40
|
+
first = false;
|
|
41
|
+
desc = `${desc ?? ""}\n${descRawLine.trim()}`;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
45
|
+
let apiParams = /action\([\s\S]+?\)/.exec(code)?.[0] ?? ""; // The intention of the following code is to extract the parameter part of the action.
|
|
46
|
+
apiParams = /\([\s\S]*,/.exec(apiParams)?.[0] ?? "";
|
|
47
|
+
apiParams = apiParams.slice(0, -1);
|
|
48
|
+
apiParams = apiParams.slice(/[\s\S]+?:/.exec(apiParams)?.[0].length);
|
|
49
|
+
const apiParamsLines = apiParams.split("\n"); // The intention of the following code is to remove extra spaces, which will make the code look more beautiful.
|
|
50
|
+
if (apiParamsLines.at(-1)?.trim() === "") apiParamsLines.pop();
|
|
51
|
+
if (apiParamsLines.at(-1)?.trim() === "") apiParamsLines.pop();
|
|
52
|
+
let spaceNumber = 0;
|
|
53
|
+
for (const char of apiParamsLines.at(-1) ?? "") {
|
|
54
|
+
if (char === " ") spaceNumber++;
|
|
55
|
+
else break;
|
|
56
|
+
}
|
|
57
|
+
for (let index = 0; index < apiParamsLines.length; index++) {
|
|
58
|
+
const line = apiParamsLines[index];
|
|
59
|
+
let spaceNumberForThisLine = 0;
|
|
60
|
+
for (const char of line) {
|
|
61
|
+
if (char === " ") spaceNumberForThisLine++;
|
|
62
|
+
else break;
|
|
63
|
+
}
|
|
64
|
+
if (spaceNumberForThisLine >= spaceNumber) {
|
|
65
|
+
apiParamsLines[index] = line.slice(spaceNumber);
|
|
66
|
+
} else {
|
|
67
|
+
apiParamsLines[index] = line.slice(spaceNumberForThisLine);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
apiParams = apiParamsLines.join("\n");
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
72
|
+
// Find the code for the API testing section.
|
|
73
|
+
const apiTestsCodeChars = [];
|
|
74
|
+
let apiTestsStartIndex = undefined as undefined | number;
|
|
75
|
+
let semicolonMatch = 0;
|
|
76
|
+
let semicolonMax = 0;
|
|
77
|
+
for (let index = 0; index < codeLines.length; index++) {
|
|
78
|
+
const codeLine = codeLines[index];
|
|
79
|
+
if (apiTestsStartIndex === undefined && !codeLine.includes("defineApiTest(")) continue;
|
|
80
|
+
if (apiTestsStartIndex === undefined) apiTestsStartIndex = index;
|
|
81
|
+
const codeChars = codeLine.split("");
|
|
82
|
+
for (const codeChar of codeChars) {
|
|
83
|
+
if (codeChar === "[") {
|
|
84
|
+
semicolonMatch++;
|
|
85
|
+
semicolonMax++;
|
|
86
|
+
}
|
|
87
|
+
if (semicolonMatch !== 0) apiTestsCodeChars.push(codeChar);
|
|
88
|
+
if (codeChar === "]") semicolonMatch--;
|
|
89
|
+
}
|
|
90
|
+
if (semicolonMatch === 0 && semicolonMax >= 1) {
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
apiTestsCodeChars.push("\n");
|
|
94
|
+
}
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
96
|
+
// Find the code for each API test case.
|
|
97
|
+
const apiCaseCodes: Array<string> = [];
|
|
98
|
+
let currentApiCaseCode = undefined as undefined | Array<string>;
|
|
99
|
+
let apiTestCaseStartIndex = undefined as undefined | number;
|
|
100
|
+
let apiTestCaseMatch = 0;
|
|
101
|
+
for (let index = 0; index < apiTestsCodeChars.length; index++) {
|
|
102
|
+
const apiTestsCodeChar = apiTestsCodeChars[index];
|
|
103
|
+
if (apiTestCaseStartIndex === undefined && apiTestsCodeChar === "{") {
|
|
104
|
+
currentApiCaseCode = [];
|
|
105
|
+
apiTestCaseStartIndex = index;
|
|
106
|
+
}
|
|
107
|
+
if (apiTestsCodeChar === "{") {
|
|
108
|
+
apiTestCaseMatch++;
|
|
109
|
+
}
|
|
110
110
|
|
|
111
|
-
|
|
111
|
+
if (apiTestCaseMatch !== 0) currentApiCaseCode!.push(apiTestsCodeChar);
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
113
|
+
if (apiTestsCodeChar === "}") {
|
|
114
|
+
apiTestCaseMatch--;
|
|
115
|
+
if (apiTestCaseMatch === 0) {
|
|
116
|
+
apiCaseCodes.push(currentApiCaseCode!.join(""));
|
|
117
|
+
currentApiCaseCode = undefined;
|
|
118
|
+
apiTestCaseStartIndex = undefined;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
122
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
123
|
+
const apiCases: Array<{
|
|
124
|
+
name: string;
|
|
125
|
+
handler: string;
|
|
126
|
+
}> = [];
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
128
|
+
for (let index = 0; index < apiCaseCodes.length; index++) {
|
|
129
|
+
const code = apiCaseCodes[index];
|
|
130
|
+
const name = /name:[\s\S]+?,/.exec(code)?.[0]?.slice(5, -1)?.trim().slice(1, -1) ?? "";
|
|
131
|
+
const handlerChars = /handler:[\s\S]*/.exec(code)?.[0]?.split("") ?? [];
|
|
132
|
+
let handler = ""; // Find the main code of the handler.
|
|
133
|
+
let handlerStartIndex = undefined as undefined | number;
|
|
134
|
+
let handlerMatch = 0;
|
|
135
|
+
for (let index = 0; index < handlerChars.length; index++) {
|
|
136
|
+
const handlerChar = handlerChars[index];
|
|
137
|
+
if (handlerStartIndex !== undefined && handlerChar === "{") handlerStartIndex = index;
|
|
138
|
+
if (handlerChar === "{") handlerMatch++;
|
|
139
|
+
if (handlerMatch !== 0) handler = handler + handlerChar;
|
|
140
|
+
if (handlerChar === "}") handlerMatch--;
|
|
141
|
+
if (handlerStartIndex !== undefined && handlerMatch === 0) break;
|
|
142
|
+
}
|
|
143
|
+
handler = handler.slice(1, -1);
|
|
144
144
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
145
|
+
const handlerLines = handler.split("\n"); // The intention of the following code is to remove extra spaces, which will make the code look more beautiful.
|
|
146
|
+
if (handlerLines.at(-1)?.trim() === "") handlerLines.pop();
|
|
147
|
+
if (handlerLines.at(-1)?.trim() === "") handlerLines.pop();
|
|
148
|
+
if (handlerLines.at(0)?.trim() === "") handlerLines.shift();
|
|
149
|
+
if (handlerLines.at(0)?.trim() === "") handlerLines.shift();
|
|
150
|
+
let spaceNumber = 0;
|
|
151
|
+
for (const char of handlerLines.at(-1) ?? "") {
|
|
152
|
+
if (char === " ") spaceNumber++;
|
|
153
|
+
else break;
|
|
154
|
+
}
|
|
155
|
+
for (let index = 0; index < handlerLines.length; index++) {
|
|
156
|
+
const line = handlerLines[index];
|
|
157
|
+
let spaceNumberForThisLine = 0;
|
|
158
|
+
for (const char of line) {
|
|
159
|
+
if (char === " ") spaceNumberForThisLine++;
|
|
160
|
+
else break;
|
|
161
|
+
}
|
|
162
|
+
if (spaceNumberForThisLine >= spaceNumber) {
|
|
163
|
+
handlerLines[index] = line.slice(spaceNumber);
|
|
164
|
+
} else {
|
|
165
|
+
handlerLines[index] = line.slice(spaceNumberForThisLine);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
handler = handlerLines.join("\n");
|
|
169
169
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
170
|
+
apiCases.push({
|
|
171
|
+
name,
|
|
172
|
+
handler,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
175
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
176
|
+
// This value has been deprecated because TypeScript types can already replace it well
|
|
177
|
+
// let paramsSchema;
|
|
178
|
+
// try {
|
|
179
|
+
// const moduleGenerated = await import(/* @vite-ignore */ `../../../generated/products/apps/${path}`);
|
|
180
|
+
// paramsSchema = moduleGenerated.paramsSchema.schemas[0]?.properties?.data;
|
|
181
|
+
// } catch (error) {}
|
|
182
182
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
183
|
+
cookbook[path] = {
|
|
184
|
+
title,
|
|
185
|
+
desc,
|
|
186
|
+
params: apiParams,
|
|
187
|
+
cases: apiCases,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
190
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
191
|
+
/**
|
|
192
|
+
* -- indexes
|
|
193
|
+
*/
|
|
194
194
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
195
|
+
const indexes: Record<string, Array<string>> = {};
|
|
196
|
+
const folderIndexes: Record<string, Array<string>> = {};
|
|
197
|
+
indexes["(root)"] = [];
|
|
198
|
+
folderIndexes["(root)"] = [];
|
|
199
|
+
for (const path in cookbook) {
|
|
200
|
+
if (!path.includes("/")) indexes["(root)"].push(path);
|
|
201
|
+
}
|
|
202
|
+
for (const path in cookbook) {
|
|
203
|
+
const dirnames = path.split("/");
|
|
204
|
+
for (let index = 0; index < dirnames.length - 1; index++) {
|
|
205
|
+
const dirpath = dirnames.slice(0, index + 1).join("/");
|
|
206
|
+
if (!indexes[dirpath]) indexes[dirpath] = [];
|
|
207
|
+
if (!folderIndexes[dirpath]) folderIndexes[dirpath] = [];
|
|
208
|
+
if (index + 1 === dirnames.length - 1) {
|
|
209
|
+
indexes[dirpath].push(path);
|
|
210
|
+
} else {
|
|
211
|
+
const childDirpath = dirnames.slice(0, index + 2).join("/");
|
|
212
|
+
if (folderIndexes[dirpath].includes(childDirpath)) continue;
|
|
213
|
+
folderIndexes[dirpath].push(childDirpath);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
for (const path in folderIndexes) {
|
|
218
|
+
if (path.includes("/") || path === "(root)") continue;
|
|
219
|
+
folderIndexes["(root)"].push(path);
|
|
220
|
+
}
|
|
221
221
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
222
|
+
const readme = (await readFile(join(cwd(), "src", "apps", "README.md"))).toString();
|
|
223
|
+
Object.keys(indexes).forEach((key) => indexes[key].length === 0 && delete indexes[key]);
|
|
224
|
+
const generatedAt = new Date();
|
|
225
225
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
226
|
+
await writeFile(
|
|
227
|
+
join(cwd(), `./generated/cookbook.json`),
|
|
228
|
+
TSON.stringify({
|
|
229
|
+
cookbook,
|
|
230
|
+
readme,
|
|
231
|
+
indexes,
|
|
232
|
+
folderIndexes,
|
|
233
|
+
generatedAt,
|
|
234
|
+
}),
|
|
235
|
+
);
|
|
236
236
|
|
|
237
|
-
|
|
238
|
-
|
|
237
|
+
console.timeEnd(`Cookbook Stage`);
|
|
238
|
+
console.log(``);
|
|
239
239
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
240
|
+
console.time(`Client Stage`);
|
|
241
|
+
try {
|
|
242
|
+
await $`bun run ./node_modules/typescript/bin/tsc --outDir "./packages/client/project"`.quiet();
|
|
243
|
+
} catch (error) {}
|
|
244
|
+
await Bun.build({
|
|
245
|
+
entrypoints: ["./packages/client/index.ts"],
|
|
246
|
+
outdir: "./packages/client/dist",
|
|
247
|
+
target: "browser",
|
|
248
|
+
minify: true,
|
|
249
|
+
});
|
|
250
|
+
await copyFile(join(cwd(), "src", "fail-code.ts"), join(cwd(), "packages", "client", "project", "src", "fail-code.ts"));
|
|
251
|
+
console.timeEnd(`Client Stage`);
|
|
252
|
+
console.log(``);
|
|
253
253
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
}
|
|
254
|
+
if (!existsSync(join(cwd(), "milkio.toml"))) return;
|
|
255
|
+
const milkioConfig = (await import(join(cwd(), "milkio.toml"))).default as MilkioConfig;
|
|
256
|
+
if (!milkioConfig?.generate?.significant) return;
|
|
257
|
+
let i = 0;
|
|
258
|
+
for (const command of milkioConfig.generate.significant) {
|
|
259
|
+
++i;
|
|
260
|
+
console.time(`Insignificant Stage (LINE ${i})`);
|
|
261
|
+
await $`${{ raw: command }}`;
|
|
262
|
+
console.timeEnd(`Insignificant Stage (LINE ${i})`);
|
|
263
|
+
}
|
|
264
|
+
};
|