milkio 0.8.1 → 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.
Files changed (47) hide show
  1. package/action/index.ts +19 -0
  2. package/command/index.ts +53 -0
  3. package/context/index.ts +18 -0
  4. package/exception/index.ts +45 -0
  5. package/execute/execute-id-generator.ts +7 -0
  6. package/execute/index.ts +160 -0
  7. package/index.ts +11 -36
  8. package/listener/index.ts +199 -0
  9. package/logger/index.ts +67 -0
  10. package/meta/index.ts +12 -0
  11. package/middleware/index.ts +19 -0
  12. package/package.json +9 -13
  13. package/stream/index.ts +19 -0
  14. package/test/index.ts +21 -0
  15. package/types/index.ts +74 -0
  16. package/utils/create-id.ts +3 -0
  17. package/world/index.ts +65 -0
  18. package/api-test/index.ts +0 -122
  19. package/c.ts +0 -50
  20. package/defines/define-api-test.ts +0 -69
  21. package/defines/define-api.ts +0 -11
  22. package/defines/define-command-handler.ts +0 -47
  23. package/defines/define-config.ts +0 -32
  24. package/defines/define-http-handler.ts +0 -291
  25. package/defines/define-middleware.ts +0 -9
  26. package/defines/define-use.ts +0 -12
  27. package/kernel/context.ts +0 -55
  28. package/kernel/execute.ts +0 -194
  29. package/kernel/fail.ts +0 -20
  30. package/kernel/logger.ts +0 -131
  31. package/kernel/meta.ts +0 -9
  32. package/kernel/middleware.ts +0 -51
  33. package/kernel/milkio.ts +0 -87
  34. package/kernel/runtime.ts +0 -11
  35. package/kernel/validate.ts +0 -16
  36. package/scripts/gen-insignificant.ts +0 -266
  37. package/scripts/gen-significant.ts +0 -186
  38. package/types.ts +0 -101
  39. package/utils/create-ulid.ts +0 -5
  40. package/utils/env-to-boolean.ts +0 -11
  41. package/utils/env-to-number.ts +0 -5
  42. package/utils/env-to-string.ts +0 -5
  43. package/utils/exec.ts +0 -26
  44. package/utils/handle-catch-error.ts +0 -52
  45. package/utils/header-to-plain-object.ts +0 -8
  46. package/utils/remove-dir.ts +0 -22
  47. package/utils/tson.ts +0 -3
@@ -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
+ };
@@ -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
+ };
@@ -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
+ }
@@ -0,0 +1,7 @@
1
+ import { createId } from "../utils/create-id";
2
+
3
+ export type ExecuteIdGenerator = (request?: Request) => string | Promise<string>;
4
+
5
+ export const defineDefaultExecuteIdGenerator = () => {
6
+ return createId;
7
+ };
@@ -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
- // types
2
- export * from "./types";
3
-
4
- // utils
5
- export * from "./utils/tson";
6
- export * from "./utils/create-ulid";
7
- export * from "./utils/env-to-string";
8
- export * from "./utils/env-to-number";
9
- export * from "./utils/env-to-boolean";
10
- export * from "./utils/header-to-plain-object";
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
+ };
@@ -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,12 @@
1
+ export interface $meta {
2
+ /**
3
+ * type safety
4
+ * @default ["params","results"]
5
+ */
6
+ typeSafety?: Array<"params" | "results">;
7
+ /**
8
+ * methods
9
+ * @default ["POST"]
10
+ */
11
+ methods?: Array<"GET" | "POST" | "PUT" | "DELETE" | "PATCH" | string>;
12
+ }
@@ -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
+ };