milkio 1.0.0-alpha.0 → 1.0.0-alpha.10

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 (40) hide show
  1. package/.co.toml +0 -0
  2. package/.publish/publish.json +0 -0
  3. package/.publish/releases/0.0.0.md +0 -0
  4. package/.publish/releases/0.1.0.md +0 -0
  5. package/.publish/releases/0.2.0.md +0 -0
  6. package/.publish/releases/0.3.0.md +0 -0
  7. package/.publish/releases/0.4.0.md +0 -0
  8. package/.publish/releases/0.5.0.md +0 -0
  9. package/.publish/releases/0.6.0.md +0 -0
  10. package/.publish/releases/0.8.0.md +0 -0
  11. package/.publish/releases-github/0.0.0.md +0 -0
  12. package/.publish/releases-github/0.1.0.md +0 -0
  13. package/.publish/releases-github/0.2.0.md +0 -0
  14. package/.publish/releases-github/0.3.0.md +0 -0
  15. package/.publish/releases-github/0.4.0.md +0 -0
  16. package/.publish/releases-github/0.5.0.md +0 -0
  17. package/.publish/releases-github/0.6.0.md +0 -0
  18. package/.publish/releases-github/0.8.0.md +0 -0
  19. package/action/index.ts +0 -0
  20. package/command/index.ts +0 -0
  21. package/context/index.ts +8 -2
  22. package/events/index.ts +47 -0
  23. package/exception/index.ts +0 -1
  24. package/execute/execute-id-generator.ts +0 -0
  25. package/execute/index.ts +34 -25
  26. package/index.ts +4 -1
  27. package/listener/index.ts +42 -30
  28. package/logger/index.ts +39 -28
  29. package/meta/index.ts +1 -1
  30. package/package.json +1 -1
  31. package/step/index.ts +36 -0
  32. package/stream/index.ts +0 -0
  33. package/types/index.ts +9 -1
  34. package/use/index.ts +12 -0
  35. package/utils/create-id.ts +0 -0
  36. package/utils/headers-to-json.ts +7 -0
  37. package/utils/send-cookbook-event.ts +26 -0
  38. package/world/index.ts +51 -22
  39. package/middleware/index.ts +0 -19
  40. package/test/index.ts +0 -21
package/.co.toml CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/action/index.ts CHANGED
File without changes
package/command/index.ts CHANGED
File without changes
package/context/index.ts CHANGED
@@ -1,13 +1,15 @@
1
- import { type $types, type Logger } from "..";
1
+ import { type MilkioHttpRequest, type MilkioHttpResponse, type $types, type Logger, type Steps, type Execute } from "..";
2
2
 
3
3
  export interface $context {
4
4
  executeId: string;
5
5
  path: string;
6
6
  logger: Logger;
7
7
  http?: ContextHttp<Record<any, any>>;
8
+ step: Steps<{}>["step"];
9
+ execute: Execute;
8
10
  }
9
11
 
10
- export type ContextHttp<ParamsParsed> = {
12
+ export type ContextHttp<ParamsParsed = any> = {
11
13
  url: URL;
12
14
  ip: string;
13
15
  path: { string: keyof $types["generated"]["routeSchema"]["$types"]; array: Array<string> };
@@ -15,4 +17,8 @@ export type ContextHttp<ParamsParsed> = {
15
17
  string: string;
16
18
  parsed: ParamsParsed;
17
19
  };
20
+ request: MilkioHttpRequest;
21
+ response: MilkioHttpResponse;
18
22
  };
23
+
24
+ export type ContextCreatedHandler = (context: $context) => Promise<void> | void;
@@ -0,0 +1,47 @@
1
+ import { type $context, type ContextHttp, type Results, type Logger } from "..";
2
+
3
+ export interface $events {
4
+ "milkio:httpRequest": { executeId: string; path: string; logger: Logger; http: ContextHttp<Record<string, any>> };
5
+ "milkio:httpResponse": { executeId: string; path: string; logger: Logger; http: ContextHttp<Record<string, any>>; context: $context };
6
+ "milkio:httpNotFound": { executeId: string; path: string; logger: Logger; http: ContextHttp<Record<string, any>> };
7
+ "milkio:executeBefore": { executeId: string; path: string; logger: Logger; context: $context };
8
+ "milkio:executeAfter": { executeId: string; path: string; logger: Logger; context: $context; results: Results<any> };
9
+ }
10
+
11
+ export const __initEventManager = () => {
12
+ const handlers = new Map<(event: any) => void, string>();
13
+ const indexed = new Map<string, Set<(event: any) => void>>();
14
+
15
+ const eventManager = {
16
+ on: <Key extends keyof $events, Handler extends (event: $events[Key]) => void>(key: Key, handler: Handler) => {
17
+ handlers.set(handler, key as string);
18
+ if (indexed.has(key as string) === false) {
19
+ indexed.set(key as string, new Set());
20
+ }
21
+ const set = indexed.get(key as string)!;
22
+ set.add(handler);
23
+ handlers.set(handler, key as string);
24
+
25
+ return () => {
26
+ handlers.delete(handler);
27
+ set.delete(handler);
28
+ };
29
+ },
30
+ off: <Key extends keyof $events, Handler extends (event: $events[Key]) => void>(key: Key, handler: Handler) => {
31
+ const set = indexed.get(key as string);
32
+ if (!set) return;
33
+ handlers.delete(handler);
34
+ set.delete(handler);
35
+ },
36
+ emit: async <Key extends keyof $events, Value extends $events[Key]>(key: Key, value: Value): Promise<void> => {
37
+ const h = indexed.get(key as string);
38
+ if (h) {
39
+ for (const handler of h) {
40
+ await handler(value);
41
+ }
42
+ }
43
+ },
44
+ };
45
+
46
+ return eventManager;
47
+ };
@@ -8,7 +8,6 @@ export interface $rejectCode {
8
8
  REQUEST_TIMEOUT: { timeout: number; message: string };
9
9
  NOT_FOUND: { path: string };
10
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
11
  UNACCEPTABLE: { expected: string; message: string };
13
12
  PARAMS_TYPE_NOT_SUPPORTED: { expected: string };
14
13
  INTERNAL_SERVER_ERROR: undefined;
File without changes
package/execute/index.ts CHANGED
@@ -1,11 +1,8 @@
1
1
  import { type IValidation } from "typia";
2
2
  import { TSON } from "@southern-aurora/tson";
3
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
- };
4
+ import { reject, type $context, type $meta, type ExecuteOptions, type Logger, type MilkioRuntimeInit, type Results, type GeneratedInit, type MilkioInit, createLogger, exceptionHandler, Ping, createStep } from "..";
5
+ import { headersToJSON } from "../utils/headers-to-json";
9
6
 
10
7
  export const __initExecuter = <MilkioRuntime extends MilkioRuntimeInit<MilkioRuntimeInit<MilkioInit>> = MilkioRuntimeInit<MilkioInit>>(generated: GeneratedInit, runtime: MilkioRuntime) => {
11
8
  const __call = async (
@@ -16,6 +13,7 @@ export const __initExecuter = <MilkioRuntime extends MilkioRuntimeInit<MilkioRun
16
13
  path: string;
17
14
  headers: Record<string, string> | Headers;
18
15
  mixinContext: Record<any, any> | undefined;
16
+ extendContext?: $context;
19
17
  } & (
20
18
  | {
21
19
  params: Record<any, any>;
@@ -37,8 +35,9 @@ export const __initExecuter = <MilkioRuntime extends MilkioRuntimeInit<MilkioRun
37
35
  } else {
38
36
  headers = options.headers;
39
37
  }
38
+ if (!("toJSON" in headers)) (headers as any).toJSON = () => headersToJSON(headers);
39
+
40
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
41
  if (options.paramsType === "raw") {
43
42
  params = options.params;
44
43
  if (typeof params === "undefined") params = {};
@@ -55,49 +54,58 @@ export const __initExecuter = <MilkioRuntime extends MilkioRuntimeInit<MilkioRun
55
54
  }
56
55
  if (typeof params !== "object" || Array.isArray(params)) throw reject("PARAMS_TYPE_NOT_SUPPORTED", { expected: "json" });
57
56
  if ("$milkioGenerateParams" in params && params.$milkioGenerateParams === "enable") {
57
+ if (!runtime.develop) throw reject("NOT_DEVELOP_MODE", "This feature must be in cookbook to use.");
58
58
  delete params.$milkioGenerateParams;
59
59
  let paramsRand = routeSchema.randomParams();
60
60
  if (paramsRand === undefined || paramsRand === null) paramsRand = {};
61
61
  params = { ...paramsRand, ...params };
62
- options.createdLogger.debug("[milkio]", "🪄 generate params:", options.path, TSON.stringify(params));
63
62
  }
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
-
63
+ if (options.mixinContext?.http?.params?.string) options.mixinContext.http.params.parsed = params; // listen でパースしたパラメータを渡す
64
+ const context =
65
+ options.extendContext ??
66
+ ({
67
+ ...(options.mixinContext ? options.mixinContext : {}),
68
+ path: options.path,
69
+ logger: options.createdLogger,
70
+ executeId: options.createdExecuteId,
71
+ execute: (path: any, options: any) => runtime.runtime.app.execute(path, options, context),
72
+ step: createStep(),
73
+ } as unknown as $context);
72
74
  const results: Results<unknown> = { value: undefined };
73
75
 
76
+ if (runtime.develop) {
77
+ options.createdLogger.request(`headers - ${TSON.stringify(headers.toJSON())}`, `\nparams - ${TSON.stringify(params)}`);
78
+ }
79
+
74
80
  const module = await routeSchema.module();
75
81
  let meta = (module.meta ? module.meta : {}) as unknown as Readonly<$meta>;
76
82
 
77
- if (!meta.typeSafety || meta.typeSafety.includes("params")) {
83
+ if (!meta.typeSafety || meta.typeSafety === true) {
78
84
  const validation = routeSchema.validateParams(params) as IValidation<any>;
79
85
  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
86
  }
81
87
 
88
+ if (!options.extendContext) await runtime.emit("milkio:executeBefore", { executeId: options.createdExecuteId, logger: options.createdLogger, path: options.path, context });
89
+
82
90
  results.value = await module.default.handler(context, params);
83
91
 
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
- }
92
+ if (!options.extendContext) await runtime.emit("milkio:executeAfter", { executeId: options.createdExecuteId, logger: options.createdLogger, path: options.path, context, results });
88
93
 
89
94
  return { executeId, headers, params, results, context, meta, type: module.$milkioType };
90
95
  };
91
96
 
92
- const execute = async (path: string, options?: ExecuteOptions): Promise<any> => {
97
+ const execute = async (path: string, options?: ExecuteOptions, context?: $context): Promise<any> => {
93
98
  if (!options) options = {};
94
- const executeId = createId();
95
- const logger = createLogger(runtime, executeId);
96
- runtime.runtime.request.set(executeId, { logger: logger });
99
+
100
+ const executeId = context?.executeId ?? createId();
101
+ const logger = context?.logger ?? createLogger(runtime, path, executeId);
102
+ if (!context?.logger) runtime.runtime.request.set(executeId, { logger: logger });
97
103
 
98
104
  try {
99
105
  const routeSchema = generated.routeSchema.routes.get(path);
100
- if (routeSchema === undefined) throw reject("NOT_FOUND", { path: path });
106
+ if (routeSchema === undefined) {
107
+ throw reject("NOT_FOUND", { path: path });
108
+ }
101
109
 
102
110
  const executed = await __call(routeSchema, {
103
111
  createdExecuteId: executeId,
@@ -107,6 +115,7 @@ export const __initExecuter = <MilkioRuntime extends MilkioRuntimeInit<MilkioRun
107
115
  mixinContext: {},
108
116
  params: options.params ?? {},
109
117
  paramsType: "raw",
118
+ extendContext: context,
110
119
  });
111
120
 
112
121
  if (routeSchema.type === "stream") {
package/index.ts CHANGED
@@ -1,11 +1,14 @@
1
1
  export * from "./types";
2
+ export * from "./use";
3
+ export * from "./execute";
4
+ export * from "./events";
2
5
  export * from "./world";
3
6
  export * from "./command";
4
7
  export * from "./action";
5
8
  export * from "./stream";
6
- export * from "./test";
7
9
  export * from "./logger";
8
10
  export * from "./context";
9
11
  export * from "./meta";
12
+ export * from "./step";
10
13
  export * from "./listener";
11
14
  export * from "./exception";
package/listener/index.ts CHANGED
@@ -15,14 +15,14 @@ export type MilkioHttpResponse = Mixin<
15
15
  >;
16
16
 
17
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;
18
+ const port = runtime.port;
19
19
  const fetch = async (request: MilkioHttpRequest): Promise<Response> => {
20
20
  if (request.method === "OPTIONS") {
21
21
  return new Response(undefined, {
22
22
  headers: {
23
- "Access-Control-Allow-Methods": runtime.corsAllowMethods ?? "*",
24
- "Access-Control-Allow-Origin": runtime.corsAllowOrigin ?? "*",
25
- "Access-Control-Allow-Headers": runtime.corsAllowHeaders ?? "*",
23
+ "Access-Control-Allow-Methods": runtime.cors?.corsAllowMethods ?? "*",
24
+ "Access-Control-Allow-Origin": runtime.cors?.corsAllowOrigin ?? "*",
25
+ "Access-Control-Allow-Headers": runtime.cors?.corsAllowHeaders ?? "*",
26
26
  },
27
27
  });
28
28
  }
@@ -31,37 +31,38 @@ export const __initListener = <MilkioRuntime extends MilkioRuntimeInit<MilkioRun
31
31
  return new Response("", {
32
32
  status: 204,
33
33
  headers: {
34
- "Access-Control-Allow-Methods": runtime.corsAllowMethods ?? "*",
35
- "Access-Control-Allow-Origin": runtime.corsAllowOrigin ?? "*",
36
- "Access-Control-Allow-Headers": runtime.corsAllowHeaders ?? "*",
34
+ "Access-Control-Allow-Methods": runtime.cors?.corsAllowMethods ?? "*",
35
+ "Access-Control-Allow-Origin": runtime.cors?.corsAllowOrigin ?? "*",
36
+ "Access-Control-Allow-Headers": runtime.cors?.corsAllowHeaders ?? "*",
37
37
  "Cache-Control": "no-store",
38
38
  "Content-Type": "text/plain; time=" + Date.now(),
39
39
  },
40
40
  });
41
41
  }
42
42
 
43
- const executeId = runtime?.executeIdGenerator ? await runtime.executeIdGenerator(request) : createId();
44
- const logger = createLogger(runtime, executeId);
43
+ const url = new URL(request.url);
44
+ let pathArray = url.pathname.substring(1).split("/");
45
+ if (runtime.ignorePathLevel !== undefined && runtime.ignorePathLevel !== 0) pathArray = pathArray.slice(runtime.ignorePathLevel);
46
+ const pathString = `/${pathArray.join("/")}`;
47
+
48
+ const executeId = runtime?.executeId ? await runtime.executeId(request) : createId();
49
+ const logger = createLogger(runtime, pathString, executeId);
45
50
  runtime.runtime.request.set(executeId, { logger: logger });
46
51
  const response: MilkioHttpResponse = {
47
52
  body: "",
48
53
  status: 200,
49
54
  headers: {
50
- "Access-Control-Allow-Methods": runtime.corsAllowMethods ?? "*",
51
- "Access-Control-Allow-Origin": runtime.corsAllowOrigin ?? "*",
52
- "Access-Control-Allow-Headers": runtime.corsAllowHeaders ?? "*",
55
+ "Access-Control-Allow-Methods": runtime.cors?.corsAllowMethods ?? "*",
56
+ "Access-Control-Allow-Origin": runtime.cors?.corsAllowOrigin ?? "*",
57
+ "Access-Control-Allow-Headers": runtime.cors?.corsAllowHeaders ?? "*",
53
58
  "Cache-Control": "no-store",
54
59
  "Content-Type": "application/json",
55
60
  },
56
61
  };
57
62
 
58
63
  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";
64
+ const http = (await (async () => {
65
+ const ip = runtime.realIp ? runtime.realIp(request) : "::1";
65
66
  const params = {
66
67
  string: await request.text(),
67
68
  parsed: undefined,
@@ -69,45 +70,54 @@ export const __initListener = <MilkioRuntime extends MilkioRuntimeInit<MilkioRun
69
70
 
70
71
  return {
71
72
  url,
72
- path: { string: pathString as keyof $types["generated"]["routeSchema"]["$types"], array: pathArray },
73
73
  ip,
74
+ path: { string: pathString as keyof $types["generated"]["routeSchema"]["$types"], array: pathArray },
74
75
  params,
75
- } as ContextHttp<undefined>;
76
+ request,
77
+ response,
78
+ } as ContextHttp;
76
79
  })())!;
77
80
 
81
+ await runtime.emit("milkio:httpRequest", { executeId, logger, path: http.path.string as string, http });
82
+
78
83
  if (!request.headers.get("Accept")?.startsWith("text/event-stream")) {
79
84
  // 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 });
85
+ const routeSchema = generated.routeSchema.routes.get(http.path.string);
86
+ if (routeSchema === undefined) {
87
+ await runtime.emit("milkio:httpNotFound", { executeId, logger, path: http.path.string as string, http });
88
+ throw reject("NOT_FOUND", { path: http.path.string as string });
89
+ }
82
90
  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
91
 
84
92
  const executed = await executer.__call(routeSchema, {
85
93
  createdExecuteId: executeId,
86
94
  createdLogger: logger,
87
- path: detail.path.string as string,
95
+ path: http.path.string as string,
88
96
  headers: request.headers as Headers,
89
- mixinContext: { detail },
90
- params: detail.params.string,
97
+ mixinContext: { http },
98
+ params: http.params.string,
91
99
  paramsType: "string",
92
100
  });
93
101
 
94
102
  if (executed.results.value !== undefined) response.body = TSON.stringify({ success: true, data: executed.results.value, executeId } satisfies MilkioResponseSuccess<any>);
95
103
 
104
+ await runtime.emit("milkio:httpResponse", { executeId, logger, path: http.path.string as string, http, context: executed.context });
105
+
96
106
  runtime.runtime.request.delete(executeId);
97
107
  return new Response(response.body, response);
98
108
  } else {
99
109
  // 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 });
110
+ const routeSchema = generated.routeSchema.routes.get(http.path.string);
111
+ if (routeSchema === undefined) throw reject("NOT_FOUND", { path: http.path.string as string });
102
112
  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
113
 
104
114
  const executed = await executer.__call(routeSchema, {
105
115
  createdExecuteId: executeId,
106
116
  createdLogger: logger,
107
- path: detail.path.string as string,
117
+ path: http.path.string as string,
108
118
  headers: request.headers as Headers,
109
- mixinContext: { detail },
110
- params: detail.params.string,
119
+ mixinContext: { http },
120
+ params: http.params.string,
111
121
  paramsType: "string",
112
122
  });
113
123
  let stream: ReadableStream;
@@ -178,6 +188,8 @@ export const __initListener = <MilkioRuntime extends MilkioRuntimeInit<MilkioRun
178
188
  response.headers["Content-Type"] = "text/event-stream";
179
189
  response.headers["Cache-Control"] = "no-cache";
180
190
 
191
+ await runtime.emit("milkio:httpResponse", { executeId, logger, path: http.path.string as string, http, context: executed.context });
192
+
181
193
  runtime.runtime.request.delete(executeId);
182
194
  return new Response(response.body, response);
183
195
  }
package/logger/index.ts CHANGED
@@ -1,13 +1,14 @@
1
- import { TSON } from "@southern-aurora/tson";
2
1
  import { format } from "date-fns";
3
- import { type MilkioInit, type MilkioRuntimeInit } from "..";
2
+ import { type $context, type MilkioInit, type MilkioRuntimeInit } from "..";
3
+ import { sendCookbookEvent } from "../utils/send-cookbook-event";
4
4
 
5
- export type Log = [string /* executeId */, "[DEBUG]" | "[INFO]" | "[WARN]" | "[ERROR]", string, string, ...Array<unknown>];
5
+ export type Log = ["[DEBUG]" | "[INFO]" | "[WARN]" | "[ERROR]" | "[REQUEST]" | "[RESPONSE]", string /* executeId */, string, string, string, ...Array<unknown>];
6
6
 
7
7
  export type Logger = {
8
8
  _: {
9
9
  logs: Array<Log>;
10
10
  tags: Map<string, unknown>;
11
+ submit: (context: $context) => Promise<void> | void;
11
12
  };
12
13
  setTag: (key: string, value: unknown) => void;
13
14
  setLog: (...log: Log) => void;
@@ -15,53 +16,63 @@ export type Logger = {
15
16
  info: (description: string, ...params: Array<unknown>) => Log;
16
17
  warn: (description: string, ...params: Array<unknown>) => Log;
17
18
  error: (description: string, ...params: Array<unknown>) => Log;
19
+ request: (description: string, ...params: Array<unknown>) => Log;
20
+ response: (description: string, ...params: Array<unknown>) => Log;
18
21
  };
19
22
 
20
- export const createLogger = <MilkioRuntime extends MilkioRuntimeInit<MilkioRuntimeInit<MilkioInit>> = MilkioRuntimeInit<MilkioInit>>(runtime: MilkioRuntime, executeId: string): Logger => {
23
+ export type LoggerInsertingHandler = (log: Log) => boolean;
24
+
25
+ export type LoggerSubmittingHandler = (context: $context, logs: Array<Log>, tags: Map<string, unknown>) => Promise<void> | void;
26
+
27
+ export const createLogger = <MilkioRuntime extends MilkioRuntimeInit<MilkioRuntimeInit<MilkioInit>> = MilkioRuntimeInit<MilkioInit>>(runtime: MilkioRuntime, path: string, executeId: string): Logger => {
21
28
  const logger = {} as Logger;
22
29
 
23
30
  logger._ = {
24
31
  logs: new Array(),
25
32
  tags: new Map(),
33
+ submit: (context: $context) => {
34
+ if (!runtime.onLoggerSubmitting) return;
35
+ return runtime.onLoggerSubmitting(context, logger._.logs, logger._.tags);
36
+ },
26
37
  };
27
38
 
28
39
  const __tagPush = (key: string, value: unknown): void => {
29
40
  logger._.tags.set(key, value);
30
41
  };
31
42
  const __logPush = (log: Log): Log => {
43
+ const inserting = runtime.onLoggerInserting
44
+ ? runtime.onLoggerInserting
45
+ : (log: Log) => {
46
+ log = [...log];
47
+ log[0] = `\n${log[0]}` as any;
48
+ console.log(...log);
49
+ return true;
50
+ };
51
+
52
+ if (!inserting(log)) return log;
53
+
32
54
  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
- })();
55
+ if (runtime.cookbook) {
56
+ void sendCookbookEvent(runtime, {
57
+ type: "milkio@logger",
58
+ log: log,
59
+ });
51
60
  }
52
- console.log(...log);
61
+
53
62
  return log;
54
63
  };
55
64
 
56
65
  logger.setTag = (key: string, value: unknown) => __tagPush(key, value);
57
66
  logger.setLog = (...log: Log) => __logPush(log);
58
67
 
59
- const getNow = () => format(new Date(), "(yyyy-MM-dd hh:mm:ss)");
68
+ const getNow = () => `${format(new Date(), "(yyyy-MM-dd hh:mm:ss)")}`;
60
69
 
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]);
70
+ logger.debug = (description: string, ...params: Array<unknown>) => __logPush(["[DEBUG]", path, executeId, getNow(), `\n${description}`, ...params]);
71
+ logger.info = (description: string, ...params: Array<unknown>) => __logPush(["[INFO]", path, executeId, getNow(), `\n${description}`, ...params]);
72
+ logger.warn = (description: string, ...params: Array<unknown>) => __logPush(["[WARN]", path, executeId, getNow(), `\n${description}`, ...params]);
73
+ logger.error = (description: string, ...params: Array<unknown>) => __logPush(["[ERROR]", path, executeId, getNow(), `\n${description}`, ...params]);
74
+ logger.request = (description: string, ...params: Array<unknown>) => __logPush(["[REQUEST]", path, executeId, getNow(), `\n${description}`, ...params]);
75
+ logger.response = (description: string, ...params: Array<unknown>) => __logPush(["[RESPONSE]", path, executeId, getNow(), `\n${description}`, ...params]);
65
76
 
66
77
  return logger;
67
78
  };
package/meta/index.ts CHANGED
@@ -3,7 +3,7 @@ export interface $meta {
3
3
  * type safety
4
4
  * @default ["params","results"]
5
5
  */
6
- typeSafety?: Array<"params" | "results">;
6
+ typeSafety?: boolean;
7
7
  /**
8
8
  * methods
9
9
  * @default ["POST"]
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "milkio",
3
3
  "type": "module",
4
4
  "module": "index.ts",
5
- "version": "1.0.0-alpha.0",
5
+ "version": "1.0.0-alpha.10",
6
6
  "peerDependencies": {
7
7
  "typescript": "^5.4.2"
8
8
  },
package/step/index.ts ADDED
@@ -0,0 +1,36 @@
1
+ import type { ToEmptyObject } from "..";
2
+
3
+ export type Steps<StageT extends Record<any, any>> = {
4
+ step: StepFunction<StageT>;
5
+ run: () => Promise<Remove_<StageT>>;
6
+ };
7
+
8
+ type Remove_<T> = {
9
+ [K in keyof T as K extends `_${string}` ? never : K]: T[K];
10
+ };
11
+
12
+ export type StepFunction<StageT extends Record<any, any>> = <HandlerT extends (stage: Readonly<StageT>) => Record<any, any> | Promise<Record<any, any>>>(handler: HandlerT) => Steps<Awaited<StageT> & ToEmptyObject<Awaited<ReturnType<HandlerT>>>>;
13
+
14
+ export const createStep = (): Steps<{}>["step"] => {
15
+ const stepController = {
16
+ $milkioType: "step",
17
+ _steps: [] as Array<(stage: any) => Promise<any>>,
18
+ step(handler: (stage: any) => Promise<any>) {
19
+ stepController._steps.push(handler);
20
+ return stepController;
21
+ },
22
+ async run() {
23
+ let stage = {};
24
+ for (const step of stepController._steps) {
25
+ stage = { ...stage, ...(await step(stage)) };
26
+ }
27
+ let result: Record<any, any> = {};
28
+ for (const key in stage) {
29
+ const value = (stage as any)[key];
30
+ if (!key.startsWith("_")) result[key] = value;
31
+ }
32
+ return result;
33
+ },
34
+ };
35
+ return stepController.step as any as Steps<{}>["step"];
36
+ };
package/stream/index.ts CHANGED
File without changes
package/types/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type $rejectCode } from "..";
1
+ import { type $context, type $rejectCode } from "..";
2
2
 
3
3
  export interface $types {
4
4
  [key: string]: Record<any, any>;
@@ -10,6 +10,14 @@ export type Mixin<T, U> = U & Omit<T, keyof U>;
10
10
 
11
11
  export type GeneratorGeneric<T> = T extends AsyncGenerator<infer I> ? I : never;
12
12
 
13
+ export type DBCreate<T, KO extends keyof T | never = never, TO extends Omit<T, KO> = Omit<T, KO>> = {
14
+ [K in keyof TO]?: TO[K] extends null ? undefined : Exclude<TO[K], null>;
15
+ };
16
+
17
+ export type DBUpdate<T, KO extends keyof T | never = never, TO extends Partial<Omit<T, KO>> = Partial<Omit<T, KO>>> = {
18
+ [K in keyof TO]?: TO[K] extends null ? undefined : Exclude<TO[K], null>;
19
+ };
20
+
13
21
  export type ExecuteId = string | "global";
14
22
 
15
23
  // DON'T TRY TO WRITE A MORE DETAILED TYPE FOR THIS TYPE
package/use/index.ts ADDED
@@ -0,0 +1,12 @@
1
+ export const defineUse = <CreatorFn extends () => Promise<unknown> | unknown>(creatorFn: CreatorFn): (() => Promise<Awaited<ReturnType<CreatorFn>>>) => {
2
+ let use: any | undefined;
3
+
4
+ const getUse = async () => {
5
+ if (use === undefined) {
6
+ use = await creatorFn();
7
+ }
8
+ return use;
9
+ };
10
+
11
+ return getUse;
12
+ };
File without changes
@@ -0,0 +1,7 @@
1
+ export function headersToJSON(headers: Headers) {
2
+ const json: Record<string, string> = {};
3
+ for (const [key, value] of (headers as Headers).entries()) {
4
+ json[key] = value;
5
+ }
6
+ return json;
7
+ }
@@ -0,0 +1,26 @@
1
+ import type { Log, MilkioInit, MilkioRuntimeInit } from "..";
2
+ import { TSON } from "@southern-aurora/tson";
3
+
4
+ type CookbookEvent = {
5
+ type: "milkio@logger";
6
+ log: Log;
7
+ };
8
+
9
+ export const sendCookbookEvent = async (runtime: MilkioRuntimeInit<MilkioInit>, event: CookbookEvent) => {
10
+ try {
11
+ const response = await fetch(`http://localhost:${runtime.cookbook.cookbookPort}/$action`, {
12
+ method: "POST",
13
+ headers: {
14
+ "Content-Type": "application/json",
15
+ },
16
+ body: TSON.stringify(event),
17
+ });
18
+ if (!response.ok) {
19
+ console.log("[COOKBOOK]", await response.text());
20
+ console.log("[COOKBOOK]", "Is Cookbook closed? There is an abnormality in the communication with Cookbook.");
21
+ }
22
+ } catch (error) {
23
+ console.log("[COOKBOOK]", error);
24
+ console.log("[COOKBOOK]", "Is Cookbook closed? There is an abnormality in the communication with Cookbook.");
25
+ }
26
+ };
package/world/index.ts CHANGED
@@ -1,41 +1,54 @@
1
- import { __initCommander, __initListener, ExecuteId, type Logger, type Mixin, type GeneratedInit, $types, Execute, Ping } from "..";
2
- import { __initExecuter } from "../execute";
1
+ import { __initCommander, __initListener, __initExecuter, __initEventManager, type ExecuteId, type Logger, type Mixin, type GeneratedInit, type Execute, type Ping, type ContextCreatedHandler, LoggerSubmittingHandler, LoggerInsertingHandler } from "..";
3
2
  import { defineDefaultExecuteIdGenerator } from "../execute/execute-id-generator";
4
3
 
5
4
  export type MilkioInit = {
6
- port: {
7
- app: number;
8
- develop: number | "disabled";
5
+ port: number;
6
+ develop: boolean;
7
+ cookbook: {
8
+ cookbookPort: number;
9
+ };
10
+ cors?: {
11
+ corsAllowMethods?: string;
12
+ corsAllowHeaders?: string;
13
+ corsAllowOrigin?: string;
9
14
  };
10
- getRealIp?: (request: Request) => string;
11
- executeIdGenerator?: (request: Request) => string | Promise<string>;
12
- corsAllowMethods?: string;
13
- corsAllowHeaders?: string;
14
- corsAllowOrigin?: string;
15
15
  ignorePathLevel?: number;
16
+ realIp?: (request: Request) => string;
17
+ executeId?: (request: Request) => string | Promise<string>;
18
+ onLoggerSubmitting?: LoggerSubmittingHandler;
19
+ onLoggerInserting?: LoggerInsertingHandler;
16
20
  };
17
21
 
18
22
  export type MilkioRuntimeInit<T extends MilkioInit> = Mixin<
19
23
  T,
20
24
  {
21
- executeIdGenerator: (request: Request) => string | Promise<string>;
25
+ executeId: (request: Request) => string | Promise<string>;
22
26
  runtime: {
23
27
  request: Map<ExecuteId, { logger: Logger }>;
28
+ app: any;
24
29
  };
30
+ on: Awaited<ReturnType<typeof __initEventManager>>["on"];
31
+ off: Awaited<ReturnType<typeof __initEventManager>>["off"];
32
+ emit: Awaited<ReturnType<typeof __initEventManager>>["emit"];
25
33
  }
26
34
  >;
27
35
 
28
- export const createWorld = async <CookbookOptions extends MilkioInit>(generated: GeneratedInit, options: CookbookOptions): Promise<MilkioWorld<CookbookOptions>> => {
29
- const executeIdGenerator = options.executeIdGenerator ?? defineDefaultExecuteIdGenerator();
36
+ export const createWorld = async <MilkioOptions extends MilkioInit>(generated: GeneratedInit, options: MilkioOptions): Promise<MilkioWorld<MilkioOptions>> => {
37
+ const executeId = options.executeId ?? defineDefaultExecuteIdGenerator();
30
38
 
31
39
  const runtime = {
32
40
  request: new Map(),
33
- } as MilkioRuntimeInit<CookbookOptions>["runtime"];
41
+ } as MilkioRuntimeInit<MilkioOptions>["runtime"];
42
+
43
+ const eventManager = __initEventManager();
34
44
 
35
- const _: MilkioRuntimeInit<CookbookOptions> = {
45
+ const _: MilkioRuntimeInit<MilkioOptions> = {
36
46
  ...options,
37
- executeIdGenerator,
47
+ executeId,
38
48
  runtime,
49
+ on: eventManager.on,
50
+ off: eventManager.off,
51
+ emit: eventManager.emit,
39
52
  };
40
53
 
41
54
  const executer = __initExecuter(generated, _);
@@ -45,21 +58,37 @@ export const createWorld = async <CookbookOptions extends MilkioInit>(generated:
45
58
  // Initialize the app
46
59
  const app = {
47
60
  _: _,
61
+ // event manager
62
+ on: eventManager.on,
63
+ off: eventManager.off,
64
+ emit: eventManager.emit,
65
+ // executer
48
66
  _executer: executer,
49
67
  execute: executer.execute,
50
68
  ping: executer.ping,
69
+ // commander
51
70
  commander,
71
+ // listener
52
72
  listener,
53
73
  };
54
74
 
55
- return app as MilkioWorld<CookbookOptions>;
75
+ runtime.app = app;
76
+
77
+ return app as MilkioWorld<MilkioOptions>;
56
78
  };
57
79
 
58
- export type MilkioWorld<CookbookOptions extends MilkioInit = MilkioInit> = {
59
- _: MilkioRuntimeInit<CookbookOptions>;
60
- _executer: Awaited<ReturnType<typeof __initExecuter<MilkioRuntimeInit<CookbookOptions>>>>;
61
- commander: Awaited<ReturnType<typeof __initCommander<MilkioRuntimeInit<CookbookOptions>>>>;
62
- listener: Awaited<ReturnType<typeof __initListener<MilkioRuntimeInit<CookbookOptions>>>>;
80
+ export type MilkioWorld<MilkioOptions extends MilkioInit = MilkioInit> = {
81
+ _: MilkioRuntimeInit<MilkioOptions>;
82
+ // event manager
83
+ on: Awaited<ReturnType<typeof __initEventManager>>["on"];
84
+ off: Awaited<ReturnType<typeof __initEventManager>>["off"];
85
+ emit: Awaited<ReturnType<typeof __initEventManager>>["emit"];
86
+ // executer
87
+ _executer: Awaited<ReturnType<typeof __initExecuter<MilkioRuntimeInit<MilkioOptions>>>>;
63
88
  execute: Execute;
64
89
  ping: (options?: { timeout?: number }) => Promise<Ping>;
90
+ // commander
91
+ commander: Awaited<ReturnType<typeof __initCommander<MilkioRuntimeInit<MilkioOptions>>>>;
92
+ // listener
93
+ listener: Awaited<ReturnType<typeof __initListener<MilkioRuntimeInit<MilkioOptions>>>>;
65
94
  };
@@ -1,19 +0,0 @@
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
- };
package/test/index.ts DELETED
@@ -1,21 +0,0 @@
1
- import { type context, type meta } from "..";
2
-
3
- export const test = <TestInitT extends TestInit>(init: TestInitT): Test<TestInitT> => {
4
- const test = init as unknown as Test<TestInitT>;
5
- test.$milkioType = "test";
6
- if (test.name === undefined) test.name = {};
7
- return test;
8
- };
9
-
10
- export type TestInit = {
11
- name: meta;
12
- handler: (test: TestTools) => Promise<unknown>;
13
- };
14
-
15
- export type Test<TestInitT extends TestInit> = {
16
- $milkioType: "test";
17
- name: TestInitT["name"] extends undefined ? {} : TestInitT["name"];
18
- handler: TestInitT["handler"];
19
- };
20
-
21
- export type TestTools = {};