milkio 0.8.2 → 1.0.0-alpha.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/action/index.ts +19 -0
- package/command/index.ts +53 -0
- package/context/index.ts +18 -0
- package/exception/index.ts +45 -0
- package/execute/execute-id-generator.ts +7 -0
- package/execute/index.ts +160 -0
- package/index.ts +11 -36
- package/listener/index.ts +199 -0
- package/logger/index.ts +67 -0
- package/meta/index.ts +12 -0
- package/middleware/index.ts +19 -0
- package/package.json +9 -13
- package/stream/index.ts +19 -0
- package/test/index.ts +21 -0
- package/types/index.ts +74 -0
- package/utils/create-id.ts +3 -0
- package/world/index.ts +65 -0
- package/api-test/index.ts +0 -122
- package/c.ts +0 -50
- package/defines/define-api-test.ts +0 -69
- package/defines/define-api.ts +0 -11
- package/defines/define-command-handler.ts +0 -47
- package/defines/define-config.ts +0 -32
- package/defines/define-http-handler.ts +0 -291
- package/defines/define-middleware.ts +0 -9
- package/defines/define-use.ts +0 -12
- package/kernel/context.ts +0 -55
- package/kernel/execute.ts +0 -194
- package/kernel/fail.ts +0 -20
- package/kernel/logger.ts +0 -131
- package/kernel/meta.ts +0 -9
- package/kernel/middleware.ts +0 -51
- package/kernel/milkio.ts +0 -87
- package/kernel/runtime.ts +0 -11
- package/kernel/validate.ts +0 -16
- package/scripts/gen-insignificant.ts +0 -258
- package/scripts/gen-significant.ts +0 -186
- package/types.ts +0 -101
- package/utils/create-ulid.ts +0 -5
- package/utils/env-to-boolean.ts +0 -11
- package/utils/env-to-number.ts +0 -5
- package/utils/env-to-string.ts +0 -5
- package/utils/exec.ts +0 -26
- package/utils/handle-catch-error.ts +0 -52
- package/utils/header-to-plain-object.ts +0 -8
- package/utils/remove-dir.ts +0 -22
- package/utils/tson.ts +0 -3
package/action/index.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type $context, type $meta } from "..";
|
|
2
|
+
|
|
3
|
+
export const action = <ActionInitT extends ActionInit>(init: ActionInitT): Action<ActionInitT> => {
|
|
4
|
+
const action = init as unknown as Action<ActionInitT>;
|
|
5
|
+
action.$milkioType = "action";
|
|
6
|
+
if (action.meta === undefined) action.meta = {};
|
|
7
|
+
return action;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type ActionInit = {
|
|
11
|
+
meta?: $meta;
|
|
12
|
+
handler: (context: $context, params: any) => Promise<unknown>;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type Action<ActionInitT extends ActionInit> = {
|
|
16
|
+
$milkioType: "action";
|
|
17
|
+
meta: ActionInitT["meta"] extends undefined ? {} : ActionInitT["meta"];
|
|
18
|
+
handler: ActionInitT["handler"];
|
|
19
|
+
};
|
package/command/index.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type MilkioRuntimeInit, type MilkioInit, type GeneratedInit } from "..";
|
|
2
|
+
|
|
3
|
+
export const command = <CommandInitT extends CommandInit>(init: CommandInitT): Command<CommandInitT> => {
|
|
4
|
+
const command = init as unknown as Command<CommandInitT>;
|
|
5
|
+
command.$milkioType = "command";
|
|
6
|
+
return command;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export type CommandInit = {
|
|
10
|
+
handler: (commands: Array<string>, options: Record<string, string>) => Promise<unknown>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type Command<CommandInitT extends CommandInit> = {
|
|
14
|
+
$milkioType: "command";
|
|
15
|
+
handler: CommandInitT["handler"];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const __initCommander = <MilkioRuntime extends MilkioRuntimeInit<MilkioInit> = MilkioRuntimeInit<MilkioInit>>(generated: GeneratedInit, runtime: MilkioRuntime) => {
|
|
19
|
+
const commander = async (argv: Array<string>, options?: { onNotFound?: () => any }) => {
|
|
20
|
+
const params = {
|
|
21
|
+
path: "index",
|
|
22
|
+
commands: [] as Array<string>,
|
|
23
|
+
options: {} as Record<string, string | true>,
|
|
24
|
+
};
|
|
25
|
+
for (const v of argv.slice(3)) {
|
|
26
|
+
if (v.startsWith("--") && v.includes("=")) {
|
|
27
|
+
const vSplited = v.split("=");
|
|
28
|
+
params.options[vSplited[0].slice(2)] = vSplited.slice(1, vSplited.length).join("=");
|
|
29
|
+
} else if (v.startsWith("--")) {
|
|
30
|
+
params.options[v.slice(2)] = "1";
|
|
31
|
+
} else if (v.startsWith("-") && v.includes("=")) {
|
|
32
|
+
const vSplited = v.split("=");
|
|
33
|
+
params.options[vSplited[0].slice(1)] = vSplited.slice(1, vSplited.length).join("=");
|
|
34
|
+
} else if (v.startsWith("-")) {
|
|
35
|
+
params.options[v.slice(1)] = "1";
|
|
36
|
+
} else {
|
|
37
|
+
params.commands.push(v);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (argv.length === 2) params.path = `index`;
|
|
41
|
+
else params.path = `${argv[2] ?? "index"}`;
|
|
42
|
+
|
|
43
|
+
if (!(params.path in generated.commandSchema.commands)) {
|
|
44
|
+
if (options?.onNotFound) return await options.onNotFound();
|
|
45
|
+
console.log(`\x1B[44m UwU \x1B[0m \x1B[2mCommand not found:\x1B[0m \x1B[32m${params.path}\x1B[0m`);
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return await generated.commandSchema.commands[params.path].module.handler(params.commands, params.options);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return commander;
|
|
53
|
+
};
|
package/context/index.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type $types, type Logger } from "..";
|
|
2
|
+
|
|
3
|
+
export interface $context {
|
|
4
|
+
executeId: string;
|
|
5
|
+
path: string;
|
|
6
|
+
logger: Logger;
|
|
7
|
+
http?: ContextHttp<Record<any, any>>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type ContextHttp<ParamsParsed> = {
|
|
11
|
+
url: URL;
|
|
12
|
+
ip: string;
|
|
13
|
+
path: { string: keyof $types["generated"]["routeSchema"]["$types"]; array: Array<string> };
|
|
14
|
+
params: {
|
|
15
|
+
string: string;
|
|
16
|
+
parsed: ParamsParsed;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { TSON } from "@southern-aurora/tson";
|
|
2
|
+
import { type MilkioResponseReject, type Logger } from "..";
|
|
3
|
+
|
|
4
|
+
export interface $rejectCode {
|
|
5
|
+
FAIL: string;
|
|
6
|
+
REQUEST_FAIL: any;
|
|
7
|
+
NOT_DEVELOP_MODE: string;
|
|
8
|
+
REQUEST_TIMEOUT: { timeout: number; message: string };
|
|
9
|
+
NOT_FOUND: { path: string };
|
|
10
|
+
PARAMS_TYPE_INCORRECT: { path: string; expected: string; value: any; message: string } | null;
|
|
11
|
+
RESULTS_TYPE_INCORRECT: { path: string; expected: string; value: any; message: string } | null;
|
|
12
|
+
UNACCEPTABLE: { expected: string; message: string };
|
|
13
|
+
PARAMS_TYPE_NOT_SUPPORTED: { expected: string };
|
|
14
|
+
INTERNAL_SERVER_ERROR: undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function reject<Code extends keyof $rejectCode, RejectData extends $rejectCode[Code]>(code: Code, data: RejectData): MilkioRejectError<Code, RejectData> {
|
|
18
|
+
const error = { $milkioReject: true, code: code, data: data } as MilkioRejectError<Code, RejectData>;
|
|
19
|
+
Error.captureStackTrace(error);
|
|
20
|
+
return error;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type MilkioRejectError<Code extends keyof $rejectCode = keyof $rejectCode, RejectData extends $rejectCode[Code] = $rejectCode[Code]> = { code: Code; data: RejectData; stack: string; $milkioReject: true };
|
|
24
|
+
|
|
25
|
+
export function exceptionHandler(executeId: string, logger: Logger, error: MilkioRejectError<any, any> | any): MilkioResponseReject {
|
|
26
|
+
const name = error?.code ?? error?.name ?? error?.constructor?.name ?? "Unnamed Exception";
|
|
27
|
+
|
|
28
|
+
if (error?.$milkioReject === true && error?.code === "NOT_FOUND") {
|
|
29
|
+
logger.info(name, error?.data?.path ?? "Unknown path");
|
|
30
|
+
} else {
|
|
31
|
+
try {
|
|
32
|
+
const stack = error?.$milkioReject ? (error?.stack ?? "").split("\n").slice(2).join("\n") : error?.stack ?? "";
|
|
33
|
+
logger.error(name, `\n${TSON.stringify(error?.data)}`, `\n${stack}\n`);
|
|
34
|
+
} catch (_) {
|
|
35
|
+
logger.error(name, `\n${error?.toString()}`, `\n${error?.stack}\n`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let result: MilkioResponseReject;
|
|
40
|
+
|
|
41
|
+
if (error?.$milkioReject === true) result = { success: false, code: error.code, reject: error.data, executeId };
|
|
42
|
+
else result = { success: false, code: "INTERNAL_SERVER_ERROR", reject: undefined, executeId };
|
|
43
|
+
|
|
44
|
+
return result;
|
|
45
|
+
}
|
package/execute/index.ts
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { type IValidation } from "typia";
|
|
2
|
+
import { TSON } from "@southern-aurora/tson";
|
|
3
|
+
import { createId } from "../utils/create-id";
|
|
4
|
+
import { reject, type $context, type $meta, type ExecuteOptions, type Logger, type MilkioRuntimeInit, type Results, type GeneratedInit, type MilkioInit, createLogger, exceptionHandler, Ping } from "..";
|
|
5
|
+
|
|
6
|
+
export type MilkioHttpRequest = {
|
|
7
|
+
request: Request;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const __initExecuter = <MilkioRuntime extends MilkioRuntimeInit<MilkioRuntimeInit<MilkioInit>> = MilkioRuntimeInit<MilkioInit>>(generated: GeneratedInit, runtime: MilkioRuntime) => {
|
|
11
|
+
const __call = async (
|
|
12
|
+
routeSchema: any,
|
|
13
|
+
options: {
|
|
14
|
+
createdExecuteId: string;
|
|
15
|
+
createdLogger: Logger;
|
|
16
|
+
path: string;
|
|
17
|
+
headers: Record<string, string> | Headers;
|
|
18
|
+
mixinContext: Record<any, any> | undefined;
|
|
19
|
+
} & (
|
|
20
|
+
| {
|
|
21
|
+
params: Record<any, any>;
|
|
22
|
+
paramsType: "raw";
|
|
23
|
+
}
|
|
24
|
+
| {
|
|
25
|
+
params: string;
|
|
26
|
+
paramsType: "string";
|
|
27
|
+
}
|
|
28
|
+
),
|
|
29
|
+
): Promise<{ executeId: string; headers: Headers; params: Record<any, unknown>; results: Results<any>; context: $context; meta: Readonly<$meta>; type: "action" | "stream" }> => {
|
|
30
|
+
const executeId = options.createdExecuteId;
|
|
31
|
+
let headers: Headers;
|
|
32
|
+
if (!(options.headers instanceof Headers)) {
|
|
33
|
+
// @ts-ignore
|
|
34
|
+
headers = new Headers({
|
|
35
|
+
...options.headers,
|
|
36
|
+
});
|
|
37
|
+
} else {
|
|
38
|
+
headers = options.headers;
|
|
39
|
+
}
|
|
40
|
+
let params: Record<any, unknown>;
|
|
41
|
+
if (runtime.port.develop === "disabled") throw reject("NOT_DEVELOP_MODE", "This feature must be in developer mode to use. Usually entering developer mode requires using a cookbook to start milkio and accessing it through localhost.");
|
|
42
|
+
if (options.paramsType === "raw") {
|
|
43
|
+
params = options.params;
|
|
44
|
+
if (typeof params === "undefined") params = {};
|
|
45
|
+
} else {
|
|
46
|
+
if (options.params === "") params = {};
|
|
47
|
+
else {
|
|
48
|
+
try {
|
|
49
|
+
params = TSON.parse(options.params);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
throw reject("PARAMS_TYPE_NOT_SUPPORTED", { expected: "json" });
|
|
52
|
+
}
|
|
53
|
+
if (typeof params === "undefined") params = {};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (typeof params !== "object" || Array.isArray(params)) throw reject("PARAMS_TYPE_NOT_SUPPORTED", { expected: "json" });
|
|
57
|
+
if ("$milkioGenerateParams" in params && params.$milkioGenerateParams === "enable") {
|
|
58
|
+
delete params.$milkioGenerateParams;
|
|
59
|
+
let paramsRand = routeSchema.randomParams();
|
|
60
|
+
if (paramsRand === undefined || paramsRand === null) paramsRand = {};
|
|
61
|
+
params = { ...paramsRand, ...params };
|
|
62
|
+
options.createdLogger.debug("[milkio]", "🪄 generate params:", options.path, TSON.stringify(params));
|
|
63
|
+
}
|
|
64
|
+
if (options.mixinContext?.detail?.params?.string) options.mixinContext.detail.params.parsed = params; // listen でパースしたパラメータを渡す
|
|
65
|
+
const context = {
|
|
66
|
+
...(options.mixinContext ? options.mixinContext : {}),
|
|
67
|
+
path: options.path,
|
|
68
|
+
logger: options.createdLogger,
|
|
69
|
+
executeId: options.createdExecuteId,
|
|
70
|
+
} as unknown as $context;
|
|
71
|
+
|
|
72
|
+
const results: Results<unknown> = { value: undefined };
|
|
73
|
+
|
|
74
|
+
const module = await routeSchema.module();
|
|
75
|
+
let meta = (module.meta ? module.meta : {}) as unknown as Readonly<$meta>;
|
|
76
|
+
|
|
77
|
+
if (!meta.typeSafety || meta.typeSafety.includes("params")) {
|
|
78
|
+
const validation = routeSchema.validateParams(params) as IValidation<any>;
|
|
79
|
+
if (!validation.success) throw reject("PARAMS_TYPE_INCORRECT", { ...validation.errors[0], message: `The value '${validation.errors[0].path}' is '${validation.errors[0].value}', which does not meet '${validation.errors[0].expected}' requirements.` });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
results.value = await module.default.handler(context, params);
|
|
83
|
+
|
|
84
|
+
if (!meta.typeSafety || meta.typeSafety.includes("results")) {
|
|
85
|
+
const validation = routeSchema.validateResults(results.value) as IValidation<any>;
|
|
86
|
+
if (!validation.success) throw reject("RESULTS_TYPE_INCORRECT", { ...validation.errors[0], message: `The value '${validation.errors[0].path}' is '${validation.errors[0].value}', which does not meet '${validation.errors[0].expected}' requirements.` });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return { executeId, headers, params, results, context, meta, type: module.$milkioType };
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const execute = async (path: string, options?: ExecuteOptions): Promise<any> => {
|
|
93
|
+
if (!options) options = {};
|
|
94
|
+
const executeId = createId();
|
|
95
|
+
const logger = createLogger(runtime, executeId);
|
|
96
|
+
runtime.runtime.request.set(executeId, { logger: logger });
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
const routeSchema = generated.routeSchema.routes.get(path);
|
|
100
|
+
if (routeSchema === undefined) throw reject("NOT_FOUND", { path: path });
|
|
101
|
+
|
|
102
|
+
const executed = await __call(routeSchema, {
|
|
103
|
+
createdExecuteId: executeId,
|
|
104
|
+
createdLogger: logger,
|
|
105
|
+
path: path,
|
|
106
|
+
headers: options.headers ?? {},
|
|
107
|
+
mixinContext: {},
|
|
108
|
+
params: options.params ?? {},
|
|
109
|
+
paramsType: "raw",
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
if (routeSchema.type === "stream") {
|
|
113
|
+
// stream
|
|
114
|
+
return [
|
|
115
|
+
undefined,
|
|
116
|
+
(async function* () {
|
|
117
|
+
try {
|
|
118
|
+
for await (const result of executed.results.value) {
|
|
119
|
+
yield [null, result];
|
|
120
|
+
}
|
|
121
|
+
return undefined;
|
|
122
|
+
} catch (error) {
|
|
123
|
+
const reject = exceptionHandler(executeId, logger, error);
|
|
124
|
+
const result: any = {};
|
|
125
|
+
result[reject.code] = reject.reject;
|
|
126
|
+
|
|
127
|
+
yield [result, null];
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
})(),
|
|
131
|
+
{ executeId: executeId },
|
|
132
|
+
];
|
|
133
|
+
} else {
|
|
134
|
+
// action
|
|
135
|
+
return [null, executed.results.value, { executeId: executeId }];
|
|
136
|
+
}
|
|
137
|
+
} catch (error) {
|
|
138
|
+
const reject = exceptionHandler(executeId, logger, error);
|
|
139
|
+
const result: any = {};
|
|
140
|
+
result[reject.code] = reject.reject;
|
|
141
|
+
|
|
142
|
+
return [result, null, { executeId: executeId }];
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const ping = async (): Promise<Ping> => [
|
|
147
|
+
null,
|
|
148
|
+
{
|
|
149
|
+
connect: true,
|
|
150
|
+
delay: 0,
|
|
151
|
+
serverTimestamp: Date.now(),
|
|
152
|
+
},
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
__call,
|
|
157
|
+
execute,
|
|
158
|
+
ping,
|
|
159
|
+
};
|
|
160
|
+
};
|
package/index.ts
CHANGED
|
@@ -1,36 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
export * from "./
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export * from "./
|
|
6
|
-
export * from "./
|
|
7
|
-
export * from "./
|
|
8
|
-
export * from "./
|
|
9
|
-
export * from "./
|
|
10
|
-
export * from "./
|
|
11
|
-
|
|
12
|
-
// defines
|
|
13
|
-
export * from "./defines/define-use";
|
|
14
|
-
export * from "./defines/define-api";
|
|
15
|
-
export * from "./defines/define-config";
|
|
16
|
-
export * from "./defines/define-api-test";
|
|
17
|
-
export * from "./defines/define-middleware";
|
|
18
|
-
|
|
19
|
-
// kernel
|
|
20
|
-
export * from "./kernel/runtime";
|
|
21
|
-
export * from "./kernel/logger";
|
|
22
|
-
export * from "./kernel/fail";
|
|
23
|
-
export * from "./kernel/meta";
|
|
24
|
-
export * from "./kernel/context";
|
|
25
|
-
export * from "./kernel/validate";
|
|
26
|
-
export * from "./kernel/middleware";
|
|
27
|
-
|
|
28
|
-
// handler
|
|
29
|
-
export * from "./defines/define-http-handler";
|
|
30
|
-
export * from "./defines/define-command-handler";
|
|
31
|
-
|
|
32
|
-
// api test
|
|
33
|
-
export * from "./api-test/index";
|
|
34
|
-
|
|
35
|
-
// milkio
|
|
36
|
-
export * from "./kernel/milkio";
|
|
1
|
+
export * from "./types";
|
|
2
|
+
export * from "./world";
|
|
3
|
+
export * from "./command";
|
|
4
|
+
export * from "./action";
|
|
5
|
+
export * from "./stream";
|
|
6
|
+
export * from "./test";
|
|
7
|
+
export * from "./logger";
|
|
8
|
+
export * from "./context";
|
|
9
|
+
export * from "./meta";
|
|
10
|
+
export * from "./listener";
|
|
11
|
+
export * from "./exception";
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { TSON } from "@southern-aurora/tson";
|
|
2
|
+
import { type $context, type MilkioRuntimeInit, type Mixin, type GeneratedInit, type MilkioInit, type $types, createLogger, type ContextHttp, exceptionHandler, type MilkioResponseReject, type Results, type MilkioResponseSuccess, reject } from "..";
|
|
3
|
+
import { type __initExecuter } from "../execute";
|
|
4
|
+
import { createId } from "../utils/create-id";
|
|
5
|
+
|
|
6
|
+
export type MilkioHttpRequest = Request;
|
|
7
|
+
|
|
8
|
+
export type MilkioHttpResponse = Mixin<
|
|
9
|
+
ResponseInit,
|
|
10
|
+
{
|
|
11
|
+
body: string | Blob | FormData | URLSearchParams | ReadableStream<Uint8Array>;
|
|
12
|
+
status: number;
|
|
13
|
+
headers: Record<string, string>;
|
|
14
|
+
}
|
|
15
|
+
>;
|
|
16
|
+
|
|
17
|
+
export const __initListener = <MilkioRuntime extends MilkioRuntimeInit<MilkioRuntimeInit<MilkioInit>> = MilkioRuntimeInit<MilkioInit>>(generated: GeneratedInit, runtime: MilkioRuntime, executer: ReturnType<typeof __initExecuter>) => {
|
|
18
|
+
const port = runtime.port.app;
|
|
19
|
+
const fetch = async (request: MilkioHttpRequest): Promise<Response> => {
|
|
20
|
+
if (request.method === "OPTIONS") {
|
|
21
|
+
return new Response(undefined, {
|
|
22
|
+
headers: {
|
|
23
|
+
"Access-Control-Allow-Methods": runtime.corsAllowMethods ?? "*",
|
|
24
|
+
"Access-Control-Allow-Origin": runtime.corsAllowOrigin ?? "*",
|
|
25
|
+
"Access-Control-Allow-Headers": runtime.corsAllowHeaders ?? "*",
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (request.url.endsWith("/generate_204")) {
|
|
31
|
+
return new Response("", {
|
|
32
|
+
status: 204,
|
|
33
|
+
headers: {
|
|
34
|
+
"Access-Control-Allow-Methods": runtime.corsAllowMethods ?? "*",
|
|
35
|
+
"Access-Control-Allow-Origin": runtime.corsAllowOrigin ?? "*",
|
|
36
|
+
"Access-Control-Allow-Headers": runtime.corsAllowHeaders ?? "*",
|
|
37
|
+
"Cache-Control": "no-store",
|
|
38
|
+
"Content-Type": "text/plain; time=" + Date.now(),
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const executeId = runtime?.executeIdGenerator ? await runtime.executeIdGenerator(request) : createId();
|
|
44
|
+
const logger = createLogger(runtime, executeId);
|
|
45
|
+
runtime.runtime.request.set(executeId, { logger: logger });
|
|
46
|
+
const response: MilkioHttpResponse = {
|
|
47
|
+
body: "",
|
|
48
|
+
status: 200,
|
|
49
|
+
headers: {
|
|
50
|
+
"Access-Control-Allow-Methods": runtime.corsAllowMethods ?? "*",
|
|
51
|
+
"Access-Control-Allow-Origin": runtime.corsAllowOrigin ?? "*",
|
|
52
|
+
"Access-Control-Allow-Headers": runtime.corsAllowHeaders ?? "*",
|
|
53
|
+
"Cache-Control": "no-store",
|
|
54
|
+
"Content-Type": "application/json",
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const detail = (await (async () => {
|
|
60
|
+
const url = new URL(request.url);
|
|
61
|
+
let pathArray = url.pathname.substring(1).split("/");
|
|
62
|
+
if (runtime.ignorePathLevel !== undefined && runtime.ignorePathLevel !== 0) pathArray = pathArray.slice(runtime.ignorePathLevel);
|
|
63
|
+
const pathString = `/${pathArray.join("/")}`;
|
|
64
|
+
const ip = runtime.getRealIp ? runtime.getRealIp(request) : "::1";
|
|
65
|
+
const params = {
|
|
66
|
+
string: await request.text(),
|
|
67
|
+
parsed: undefined,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
url,
|
|
72
|
+
path: { string: pathString as keyof $types["generated"]["routeSchema"]["$types"], array: pathArray },
|
|
73
|
+
ip,
|
|
74
|
+
params,
|
|
75
|
+
} as ContextHttp<undefined>;
|
|
76
|
+
})())!;
|
|
77
|
+
|
|
78
|
+
if (!request.headers.get("Accept")?.startsWith("text/event-stream")) {
|
|
79
|
+
// action
|
|
80
|
+
const routeSchema = generated.routeSchema.routes.get(detail.path.string);
|
|
81
|
+
if (routeSchema === undefined) throw reject("NOT_FOUND", { path: detail.path.string as string });
|
|
82
|
+
if (routeSchema.type !== "action") throw reject("UNACCEPTABLE", { expected: "stream", message: `Not acceptable, the Accept in the request header should be "text/event-stream". If you are using the "@milkio/stargate" package, please add \`type: "stream"\` to the execute options.` });
|
|
83
|
+
|
|
84
|
+
const executed = await executer.__call(routeSchema, {
|
|
85
|
+
createdExecuteId: executeId,
|
|
86
|
+
createdLogger: logger,
|
|
87
|
+
path: detail.path.string as string,
|
|
88
|
+
headers: request.headers as Headers,
|
|
89
|
+
mixinContext: { detail },
|
|
90
|
+
params: detail.params.string,
|
|
91
|
+
paramsType: "string",
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
if (executed.results.value !== undefined) response.body = TSON.stringify({ success: true, data: executed.results.value, executeId } satisfies MilkioResponseSuccess<any>);
|
|
95
|
+
|
|
96
|
+
runtime.runtime.request.delete(executeId);
|
|
97
|
+
return new Response(response.body, response);
|
|
98
|
+
} else {
|
|
99
|
+
// stream
|
|
100
|
+
const routeSchema = generated.routeSchema.routes.get(detail.path.string);
|
|
101
|
+
if (routeSchema === undefined) throw reject("NOT_FOUND", { path: detail.path.string as string });
|
|
102
|
+
if (routeSchema.type !== "stream") throw reject("UNACCEPTABLE", { expected: "stream", message: `Not acceptable, the Accept in the request header should be "application/json". If you are using the "@milkio/stargate" package, please remove \`type: "stream"\` to the execute options.` });
|
|
103
|
+
|
|
104
|
+
const executed = await executer.__call(routeSchema, {
|
|
105
|
+
createdExecuteId: executeId,
|
|
106
|
+
createdLogger: logger,
|
|
107
|
+
path: detail.path.string as string,
|
|
108
|
+
headers: request.headers as Headers,
|
|
109
|
+
mixinContext: { detail },
|
|
110
|
+
params: detail.params.string,
|
|
111
|
+
paramsType: "string",
|
|
112
|
+
});
|
|
113
|
+
let stream: ReadableStream;
|
|
114
|
+
let control: ReadableStreamDirectController | ReadableStreamDefaultController;
|
|
115
|
+
|
|
116
|
+
if (typeof Bun !== "undefined") {
|
|
117
|
+
// @ts-ignore: bun
|
|
118
|
+
stream = new ReadableStream({
|
|
119
|
+
type: "direct",
|
|
120
|
+
async pull(controller: ReadableStreamDirectController) {
|
|
121
|
+
control = controller;
|
|
122
|
+
try {
|
|
123
|
+
controller.write(`data:@${JSON.stringify({ success: true, data: undefined, executeId } satisfies MilkioResponseSuccess<any>)}\n\n`);
|
|
124
|
+
for await (const value of executed.results.value) {
|
|
125
|
+
if (!request.signal.aborted) {
|
|
126
|
+
const result: string = JSON.stringify([null, TSON.encode(value)]);
|
|
127
|
+
controller.write(`data:${result}\n\n`);
|
|
128
|
+
} else {
|
|
129
|
+
executed.results.value.return(undefined);
|
|
130
|
+
controller.close();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
} catch (error) {
|
|
134
|
+
const exception = exceptionHandler(executeId, logger, error);
|
|
135
|
+
const result: any = {};
|
|
136
|
+
result[exception.code] = exception.reject;
|
|
137
|
+
controller.write(`data:${JSON.stringify([TSON.encode(result), null])}\n\n`);
|
|
138
|
+
}
|
|
139
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
140
|
+
controller.close();
|
|
141
|
+
},
|
|
142
|
+
cancel() {
|
|
143
|
+
control.close();
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
} else {
|
|
147
|
+
// node.js or others
|
|
148
|
+
stream = new ReadableStream({
|
|
149
|
+
async pull(controller) {
|
|
150
|
+
control = controller;
|
|
151
|
+
try {
|
|
152
|
+
controller.enqueue(`data:@${JSON.stringify({ success: true, data: undefined, executeId } satisfies MilkioResponseSuccess<any>)}\n\n`);
|
|
153
|
+
for await (const value of executed.results.value) {
|
|
154
|
+
if (!request.signal.aborted) {
|
|
155
|
+
const result: string = JSON.stringify([null, TSON.encode(value)]);
|
|
156
|
+
controller.enqueue(`data:${result}\n\n`);
|
|
157
|
+
} else {
|
|
158
|
+
executed.results.value.return(undefined);
|
|
159
|
+
controller.close();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
} catch (error) {
|
|
163
|
+
const exception = exceptionHandler(executeId, logger, error);
|
|
164
|
+
const result: any = {};
|
|
165
|
+
result[exception.code] = exception.reject;
|
|
166
|
+
controller.enqueue(`data:${JSON.stringify([TSON.encode(result), null])}\n\n`);
|
|
167
|
+
}
|
|
168
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
169
|
+
controller.close();
|
|
170
|
+
},
|
|
171
|
+
cancel() {
|
|
172
|
+
control.close();
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
response.body = stream;
|
|
178
|
+
response.headers["Content-Type"] = "text/event-stream";
|
|
179
|
+
response.headers["Cache-Control"] = "no-cache";
|
|
180
|
+
|
|
181
|
+
runtime.runtime.request.delete(executeId);
|
|
182
|
+
return new Response(response.body, response);
|
|
183
|
+
}
|
|
184
|
+
} catch (error) {
|
|
185
|
+
const results: Results<MilkioResponseReject> = {
|
|
186
|
+
value: exceptionHandler(executeId, logger, error),
|
|
187
|
+
};
|
|
188
|
+
if (results.value !== undefined) response.body = TSON.stringify(results.value);
|
|
189
|
+
|
|
190
|
+
runtime.runtime.request.delete(executeId);
|
|
191
|
+
return new Response(response.body, response);
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
port,
|
|
197
|
+
fetch,
|
|
198
|
+
};
|
|
199
|
+
};
|
package/logger/index.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { TSON } from "@southern-aurora/tson";
|
|
2
|
+
import { format } from "date-fns";
|
|
3
|
+
import { type MilkioInit, type MilkioRuntimeInit } from "..";
|
|
4
|
+
|
|
5
|
+
export type Log = [string /* executeId */, "[DEBUG]" | "[INFO]" | "[WARN]" | "[ERROR]", string, string, ...Array<unknown>];
|
|
6
|
+
|
|
7
|
+
export type Logger = {
|
|
8
|
+
_: {
|
|
9
|
+
logs: Array<Log>;
|
|
10
|
+
tags: Map<string, unknown>;
|
|
11
|
+
};
|
|
12
|
+
setTag: (key: string, value: unknown) => void;
|
|
13
|
+
setLog: (...log: Log) => void;
|
|
14
|
+
debug: (description: string, ...params: Array<unknown>) => Log;
|
|
15
|
+
info: (description: string, ...params: Array<unknown>) => Log;
|
|
16
|
+
warn: (description: string, ...params: Array<unknown>) => Log;
|
|
17
|
+
error: (description: string, ...params: Array<unknown>) => Log;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const createLogger = <MilkioRuntime extends MilkioRuntimeInit<MilkioRuntimeInit<MilkioInit>> = MilkioRuntimeInit<MilkioInit>>(runtime: MilkioRuntime, executeId: string): Logger => {
|
|
21
|
+
const logger = {} as Logger;
|
|
22
|
+
|
|
23
|
+
logger._ = {
|
|
24
|
+
logs: new Array(),
|
|
25
|
+
tags: new Map(),
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const __tagPush = (key: string, value: unknown): void => {
|
|
29
|
+
logger._.tags.set(key, value);
|
|
30
|
+
};
|
|
31
|
+
const __logPush = (log: Log): Log => {
|
|
32
|
+
logger._.logs.push([...log]);
|
|
33
|
+
if (runtime.port.develop !== "disabled") {
|
|
34
|
+
void (async () => {
|
|
35
|
+
try {
|
|
36
|
+
const response = await fetch(`http://localhost:${runtime.port.develop}/$action`, {
|
|
37
|
+
method: "POST",
|
|
38
|
+
headers: {
|
|
39
|
+
"Content-Type": "application/json",
|
|
40
|
+
},
|
|
41
|
+
body: TSON.stringify({
|
|
42
|
+
type: "milkio@logger",
|
|
43
|
+
log: log,
|
|
44
|
+
}),
|
|
45
|
+
});
|
|
46
|
+
if (!response.ok) console.log("[COOKBOOK]", await response.text());
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.log("[COOKBOOK]", error);
|
|
49
|
+
}
|
|
50
|
+
})();
|
|
51
|
+
}
|
|
52
|
+
console.log(...log);
|
|
53
|
+
return log;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
logger.setTag = (key: string, value: unknown) => __tagPush(key, value);
|
|
57
|
+
logger.setLog = (...log: Log) => __logPush(log);
|
|
58
|
+
|
|
59
|
+
const getNow = () => format(new Date(), "(yyyy-MM-dd hh:mm:ss)");
|
|
60
|
+
|
|
61
|
+
logger.debug = (description: string, ...params: Array<unknown>) => __logPush([executeId, "[DEBUG]", description, getNow(), ...params]);
|
|
62
|
+
logger.info = (description: string, ...params: Array<unknown>) => __logPush([executeId, "[INFO]", description, getNow(), ...params]);
|
|
63
|
+
logger.warn = (description: string, ...params: Array<unknown>) => __logPush([executeId, "[WARN]", description, getNow(), ...params]);
|
|
64
|
+
logger.error = (description: string, ...params: Array<unknown>) => __logPush([executeId, "[ERROR]", description, getNow(), ...params]);
|
|
65
|
+
|
|
66
|
+
return logger;
|
|
67
|
+
};
|
package/meta/index.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type $context, type $meta } from "..";
|
|
2
|
+
|
|
3
|
+
export const middleware = <MiddlewareInitT extends MiddlewareInit>(init: MiddlewareInitT): Middleware<MiddlewareInitT> => {
|
|
4
|
+
const middleware = init as unknown as Middleware<MiddlewareInitT>;
|
|
5
|
+
middleware.$milkioType = "middleware";
|
|
6
|
+
if (middleware.meta === undefined) middleware.meta = {};
|
|
7
|
+
return middleware;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type MiddlewareInit = {
|
|
11
|
+
meta?: $meta;
|
|
12
|
+
handler: (context: $context, params: any) => Promise<unknown>;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type Middleware<MiddlewareInitT extends MiddlewareInit> = {
|
|
16
|
+
$milkioType: "middleware";
|
|
17
|
+
meta: MiddlewareInitT["meta"] extends undefined ? {} : MiddlewareInitT["meta"];
|
|
18
|
+
handler: MiddlewareInitT["handler"];
|
|
19
|
+
};
|