milkio 1.0.0-alpha.2 → 1.0.0-alpha.20

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/config/index.ts +48 -0
  22. package/context/index.ts +8 -2
  23. package/events/index.ts +47 -0
  24. package/exception/index.ts +0 -1
  25. package/execute/execute-id-generator.ts +0 -0
  26. package/execute/index.ts +30 -79
  27. package/index.ts +5 -1
  28. package/listener/index.ts +50 -31
  29. package/logger/index.ts +39 -28
  30. package/meta/index.ts +1 -1
  31. package/package.json +2 -2
  32. package/step/index.ts +36 -0
  33. package/stream/index.ts +0 -0
  34. package/types/index.ts +10 -16
  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 +48 -26
  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
@@ -0,0 +1,48 @@
1
+ import { type $types, type GeneratedInit } from "../types";
2
+
3
+ export const config = <ConfigDefaultT extends ConfigDefault, ConfigEnvironmentsT extends ConfigEnvironments<ConfigDefaultT>>(def: ConfigDefaultT, envs: ConfigEnvironmentsT = {} as Record<any, any>): [ConfigDefaultT, ConfigEnvironmentsT] => {
4
+ return [def, envs];
5
+ };
6
+
7
+ export const getConfig = async <Namespace extends keyof $types["generated"]["configSchema"]>(generated: GeneratedInit, env: Record<any, any>, envMode: string, namespace: Namespace) => {
8
+ if (generated.configSchema[namespace][1] && envMode in generated.configSchema[namespace][1]) {
9
+ return {
10
+ ...(await generated.configSchema[namespace][0](env)),
11
+ ...(await generated.configSchema[namespace][1][envMode](env)),
12
+ };
13
+ } else {
14
+ return {
15
+ ...(await generated.configSchema[namespace][0](env)),
16
+ };
17
+ }
18
+ };
19
+
20
+ export type ConfigDefault = (env: Record<string, string>) => Promise<Record<string, unknown>> | Record<string, unknown>;
21
+
22
+ export type ConfigEnvironments<T extends ConfigDefault> = {
23
+ [key: string]: (env: Record<string, string>) => Partial<Awaited<ReturnType<T>>> | Promise<Partial<Awaited<ReturnType<T>>>>;
24
+ };
25
+
26
+ export function envToString(value: string | number | undefined, defaultValue: string) {
27
+ if (value === undefined) return defaultValue;
28
+
29
+ return `${value}`;
30
+ }
31
+
32
+ export function envToNumber(value: string | undefined, defaultValue: number) {
33
+ if (value === undefined) return defaultValue;
34
+
35
+ return Number.parseInt(value, 10);
36
+ }
37
+
38
+ export function envToBoolean(value: string | number | undefined, defaultValue: boolean) {
39
+ if (value === "true") return true;
40
+
41
+ if (value === "false") return false;
42
+
43
+ if (value === "") return false;
44
+
45
+ if (undefined === value) return defaultValue;
46
+
47
+ return Boolean(value);
48
+ }
package/context/index.ts CHANGED
@@ -1,13 +1,17 @@
1
- import { type MilkioHttpRequest, type MilkioHttpResponse, type $types, type Logger } from "..";
1
+ import { type MilkioHttpRequest, type MilkioHttpResponse, type $types, type Logger, type Steps, type Mixin, type ExecuteOptions, type Action } from "..";
2
2
 
3
3
  export interface $context {
4
4
  executeId: string;
5
+ environment: string;
5
6
  path: string;
6
7
  logger: Logger;
7
8
  http?: ContextHttp<Record<any, any>>;
9
+ step: Steps<{}>["step"];
10
+ getConfig: <Namespace extends keyof $types["generated"]["configSchema"]>(namespace: Namespace) => Promise<Readonly<Awaited<ReturnType<$types["generated"]["configSchema"][Namespace][0]>>>>;
11
+ call: <Module extends Promise<{ default: Action<any> }>>(module: Module, params: Parameters<Awaited<Module>["default"]["handler"]>[1]) => Promise<ReturnType<Awaited<Module>["default"]["handler"]>>;
8
12
  }
9
13
 
10
- export type ContextHttp<ParamsParsed> = {
14
+ export type ContextHttp<ParamsParsed = any> = {
11
15
  url: URL;
12
16
  ip: string;
13
17
  path: { string: keyof $types["generated"]["routeSchema"]["$types"]; array: Array<string> };
@@ -18,3 +22,5 @@ export type ContextHttp<ParamsParsed> = {
18
22
  request: MilkioHttpRequest;
19
23
  response: MilkioHttpResponse;
20
24
  };
25
+
26
+ 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,14 +1,12 @@
1
1
  import { type IValidation } from "typia";
2
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
- };
3
+ import { reject, type $context, type $meta, type Logger, type MilkioRuntimeInit, type Results, type GeneratedInit, type MilkioInit, createStep, getConfig } from "..";
4
+ import { headersToJSON } from "../utils/headers-to-json";
9
5
 
10
6
  export const __initExecuter = <MilkioRuntime extends MilkioRuntimeInit<MilkioRuntimeInit<MilkioInit>> = MilkioRuntimeInit<MilkioInit>>(generated: GeneratedInit, runtime: MilkioRuntime) => {
11
- const __call = async (
7
+ const __execute = async (
8
+ env: Record<any, any> | undefined,
9
+ envMode: string | undefined,
12
10
  routeSchema: any,
13
11
  options: {
14
12
  createdExecuteId: string;
@@ -27,7 +25,9 @@ export const __initExecuter = <MilkioRuntime extends MilkioRuntimeInit<MilkioRun
27
25
  }
28
26
  ),
29
27
  ): 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;
28
+ const executeId: string = options.createdExecuteId;
29
+ env = env ?? {};
30
+ envMode = envMode ?? "production";
31
31
  let headers: Headers;
32
32
  if (!(options.headers instanceof Headers)) {
33
33
  // @ts-ignore
@@ -37,6 +37,8 @@ export const __initExecuter = <MilkioRuntime extends MilkioRuntimeInit<MilkioRun
37
37
  } else {
38
38
  headers = options.headers;
39
39
  }
40
+ if (!("toJSON" in headers)) (headers as any).toJSON = () => headersToJSON(headers);
41
+
40
42
  let params: Record<any, unknown>;
41
43
  if (options.paramsType === "raw") {
42
44
  params = options.params;
@@ -54,107 +56,56 @@ export const __initExecuter = <MilkioRuntime extends MilkioRuntimeInit<MilkioRun
54
56
  }
55
57
  if (typeof params !== "object" || Array.isArray(params)) throw reject("PARAMS_TYPE_NOT_SUPPORTED", { expected: "json" });
56
58
  if ("$milkioGenerateParams" in params && params.$milkioGenerateParams === "enable") {
57
- 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.");
59
+ if (!runtime.develop) throw reject("NOT_DEVELOP_MODE", "This feature must be in cookbook to use.");
58
60
  delete params.$milkioGenerateParams;
59
61
  let paramsRand = routeSchema.randomParams();
60
62
  if (paramsRand === undefined || paramsRand === null) paramsRand = {};
61
63
  params = { ...paramsRand, ...params };
62
- options.createdLogger.debug("[milkio]", "🪄 generate params:", options.path, TSON.stringify(params));
63
64
  }
64
65
  if (options.mixinContext?.http?.params?.string) options.mixinContext.http.params.parsed = params; // listen でパースしたパラメータを渡す
65
66
  const context = {
66
67
  ...(options.mixinContext ? options.mixinContext : {}),
68
+ envMode,
67
69
  path: options.path,
68
70
  logger: options.createdLogger,
69
71
  executeId: options.createdExecuteId,
72
+ getConfig: (namespace: string) => getConfig(generated, env, envMode, namespace),
73
+ call: (module: any, options: any) => __call(context, module, options),
74
+ step: createStep(),
70
75
  } as unknown as $context;
71
-
72
76
  const results: Results<unknown> = { value: undefined };
73
77
 
78
+ if (runtime.develop) {
79
+ options.createdLogger.request(`headers - ${TSON.stringify(headers.toJSON())}`, `\nparams - ${TSON.stringify(params)}`);
80
+ }
81
+
74
82
  const module = await routeSchema.module();
75
83
  let meta = (module.meta ? module.meta : {}) as unknown as Readonly<$meta>;
76
84
 
77
- if (!meta.typeSafety || meta.typeSafety.includes("params")) {
85
+ if (!meta.typeSafety || meta.typeSafety === true) {
78
86
  const validation = routeSchema.validateParams(params) as IValidation<any>;
79
87
  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
88
  }
81
89
 
90
+ await runtime.emit("milkio:executeBefore", { executeId: options.createdExecuteId, logger: options.createdLogger, path: options.path, context });
91
+
82
92
  results.value = await module.default.handler(context, params);
93
+ if (results.value === undefined || results.value === null || results.value === "") results.value = {};
94
+ else if (Array.isArray(results.value)) throw reject("FAIL", "The return type of the handler must be an object, which is currently an array.");
95
+ else if (typeof results.value !== "object") throw reject("FAIL", "The return type of the handler must be an object, which is currently a primitive type.");
83
96
 
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
- }
97
+ await runtime.emit("milkio:executeAfter", { executeId: options.createdExecuteId, logger: options.createdLogger, path: options.path, context, results });
88
98
 
89
99
  return { executeId, headers, params, results, context, meta, type: module.$milkioType };
90
100
  };
91
101
 
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
- }
102
+ const __call = async (context: $context, module: { default: any }, params?: any): Promise<any> => {
103
+ const moduleAwaited = await module;
104
+ return await moduleAwaited.default.handler(context, params);
144
105
  };
145
106
 
146
- const ping = async (): Promise<Ping> => [
147
- null,
148
- {
149
- connect: true,
150
- delay: 0,
151
- serverTimestamp: Date.now(),
152
- },
153
- ];
154
-
155
107
  return {
156
108
  __call,
157
- execute,
158
- ping,
109
+ __execute,
159
110
  };
160
111
  };
package/index.ts CHANGED
@@ -1,11 +1,15 @@
1
+ export * from "@southern-aurora/tson";
1
2
  export * from "./types";
3
+ export * from "./config";
4
+ export * from "./execute";
5
+ export * from "./events";
2
6
  export * from "./world";
3
7
  export * from "./command";
4
8
  export * from "./action";
5
9
  export * from "./stream";
6
- export * from "./test";
7
10
  export * from "./logger";
8
11
  export * from "./context";
9
12
  export * from "./meta";
13
+ export * from "./step";
10
14
  export * from "./listener";
11
15
  export * from "./exception";
package/listener/index.ts CHANGED
@@ -15,41 +15,50 @@ 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;
19
- const fetch = async (request: MilkioHttpRequest): Promise<Response> => {
20
- if (request.method === "OPTIONS") {
18
+ const port = runtime.port;
19
+ const fetch = async (options: {
20
+ request: MilkioHttpRequest;
21
+ envMode?: string;
22
+ env?: Record<any, any>;
23
+ }): Promise<Response> => {
24
+ if (options.request.method === "OPTIONS") {
21
25
  return new Response(undefined, {
22
26
  headers: {
23
- "Access-Control-Allow-Methods": runtime.corsAllowMethods ?? "*",
24
- "Access-Control-Allow-Origin": runtime.corsAllowOrigin ?? "*",
25
- "Access-Control-Allow-Headers": runtime.corsAllowHeaders ?? "*",
27
+ "Access-Control-Allow-Methods": runtime.cors?.corsAllowMethods ?? "*",
28
+ "Access-Control-Allow-Origin": runtime.cors?.corsAllowOrigin ?? "*",
29
+ "Access-Control-Allow-Headers": runtime.cors?.corsAllowHeaders ?? "*",
26
30
  },
27
31
  });
28
32
  }
29
33
 
30
- if (request.url.endsWith("/generate_204")) {
34
+ if (options.request.url.endsWith("/generate_204")) {
31
35
  return new Response("", {
32
36
  status: 204,
33
37
  headers: {
34
- "Access-Control-Allow-Methods": runtime.corsAllowMethods ?? "*",
35
- "Access-Control-Allow-Origin": runtime.corsAllowOrigin ?? "*",
36
- "Access-Control-Allow-Headers": runtime.corsAllowHeaders ?? "*",
38
+ "Access-Control-Allow-Methods": runtime.cors?.corsAllowMethods ?? "*",
39
+ "Access-Control-Allow-Origin": runtime.cors?.corsAllowOrigin ?? "*",
40
+ "Access-Control-Allow-Headers": runtime.cors?.corsAllowHeaders ?? "*",
37
41
  "Cache-Control": "no-store",
38
42
  "Content-Type": "text/plain; time=" + Date.now(),
39
43
  },
40
44
  });
41
45
  }
42
46
 
43
- const executeId = runtime?.executeIdGenerator ? await runtime.executeIdGenerator(request) : createId();
44
- const logger = createLogger(runtime, executeId);
47
+ const url = new URL(options.request.url);
48
+ let pathArray = url.pathname.substring(1).split("/");
49
+ if (runtime.ignorePathLevel !== undefined && runtime.ignorePathLevel !== 0) pathArray = pathArray.slice(runtime.ignorePathLevel);
50
+ const pathString = `/${pathArray.join("/")}`;
51
+
52
+ const executeId = runtime?.executeId ? await runtime.executeId(options.request) : createId();
53
+ const logger = createLogger(runtime, pathString, executeId);
45
54
  runtime.runtime.request.set(executeId, { logger: logger });
46
55
  const response: MilkioHttpResponse = {
47
56
  body: "",
48
57
  status: 200,
49
58
  headers: {
50
- "Access-Control-Allow-Methods": runtime.corsAllowMethods ?? "*",
51
- "Access-Control-Allow-Origin": runtime.corsAllowOrigin ?? "*",
52
- "Access-Control-Allow-Headers": runtime.corsAllowHeaders ?? "*",
59
+ "Access-Control-Allow-Methods": runtime.cors?.corsAllowMethods ?? "*",
60
+ "Access-Control-Allow-Origin": runtime.cors?.corsAllowOrigin ?? "*",
61
+ "Access-Control-Allow-Headers": runtime.cors?.corsAllowHeaders ?? "*",
53
62
  "Cache-Control": "no-store",
54
63
  "Content-Type": "application/json",
55
64
  },
@@ -57,13 +66,9 @@ export const __initListener = <MilkioRuntime extends MilkioRuntimeInit<MilkioRun
57
66
 
58
67
  try {
59
68
  const http = (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";
69
+ const ip = runtime.realIp ? runtime.realIp(options.request) : "::1";
65
70
  const params = {
66
- string: await request.text(),
71
+ string: await options.request.text(),
67
72
  parsed: undefined,
68
73
  };
69
74
 
@@ -72,22 +77,32 @@ export const __initListener = <MilkioRuntime extends MilkioRuntimeInit<MilkioRun
72
77
  ip,
73
78
  path: { string: pathString as keyof $types["generated"]["routeSchema"]["$types"], array: pathArray },
74
79
  params,
75
- request,
80
+ request: options.request,
76
81
  response,
77
- } as ContextHttp<undefined>;
82
+ } as ContextHttp;
78
83
  })())!;
79
84
 
80
- if (!request.headers.get("Accept")?.startsWith("text/event-stream")) {
85
+ await runtime.emit("milkio:httpRequest", { executeId, logger, path: http.path.string as string, http });
86
+
87
+ if (!runtime.develop && ((http.path.string as string).includes("__") || (http.path.string as string).startsWith("/$call/"))) {
88
+ await runtime.emit("milkio:httpNotFound", { executeId, logger, path: http.path.string as string, http });
89
+ throw reject("NOT_FOUND", { path: http.path.string as string });
90
+ }
91
+
92
+ if (!options.request.headers.get("Accept")?.startsWith("text/event-stream")) {
81
93
  // action
82
94
  const routeSchema = generated.routeSchema.routes.get(http.path.string);
83
- if (routeSchema === undefined) throw reject("NOT_FOUND", { path: http.path.string as string });
95
+ if (routeSchema === undefined) {
96
+ await runtime.emit("milkio:httpNotFound", { executeId, logger, path: http.path.string as string, http });
97
+ throw reject("NOT_FOUND", { path: http.path.string as string });
98
+ }
84
99
  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.` });
85
100
 
86
- const executed = await executer.__call(routeSchema, {
101
+ const executed = await executer.__execute(options.env, options.envMode, routeSchema, {
87
102
  createdExecuteId: executeId,
88
103
  createdLogger: logger,
89
104
  path: http.path.string as string,
90
- headers: request.headers as Headers,
105
+ headers: options.request.headers as Headers,
91
106
  mixinContext: { http },
92
107
  params: http.params.string,
93
108
  paramsType: "string",
@@ -95,6 +110,8 @@ export const __initListener = <MilkioRuntime extends MilkioRuntimeInit<MilkioRun
95
110
 
96
111
  if (executed.results.value !== undefined) response.body = TSON.stringify({ success: true, data: executed.results.value, executeId } satisfies MilkioResponseSuccess<any>);
97
112
 
113
+ await runtime.emit("milkio:httpResponse", { executeId, logger, path: http.path.string as string, http, context: executed.context });
114
+
98
115
  runtime.runtime.request.delete(executeId);
99
116
  return new Response(response.body, response);
100
117
  } else {
@@ -103,11 +120,11 @@ export const __initListener = <MilkioRuntime extends MilkioRuntimeInit<MilkioRun
103
120
  if (routeSchema === undefined) throw reject("NOT_FOUND", { path: http.path.string as string });
104
121
  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.` });
105
122
 
106
- const executed = await executer.__call(routeSchema, {
123
+ const executed = await executer.__execute(options.env, options.envMode, routeSchema, {
107
124
  createdExecuteId: executeId,
108
125
  createdLogger: logger,
109
126
  path: http.path.string as string,
110
- headers: request.headers as Headers,
127
+ headers: options.request.headers as Headers,
111
128
  mixinContext: { http },
112
129
  params: http.params.string,
113
130
  paramsType: "string",
@@ -124,7 +141,7 @@ export const __initListener = <MilkioRuntime extends MilkioRuntimeInit<MilkioRun
124
141
  try {
125
142
  controller.write(`data:@${JSON.stringify({ success: true, data: undefined, executeId } satisfies MilkioResponseSuccess<any>)}\n\n`);
126
143
  for await (const value of executed.results.value) {
127
- if (!request.signal.aborted) {
144
+ if (!options.request.signal.aborted) {
128
145
  const result: string = JSON.stringify([null, TSON.encode(value)]);
129
146
  controller.write(`data:${result}\n\n`);
130
147
  } else {
@@ -153,7 +170,7 @@ export const __initListener = <MilkioRuntime extends MilkioRuntimeInit<MilkioRun
153
170
  try {
154
171
  controller.enqueue(`data:@${JSON.stringify({ success: true, data: undefined, executeId } satisfies MilkioResponseSuccess<any>)}\n\n`);
155
172
  for await (const value of executed.results.value) {
156
- if (!request.signal.aborted) {
173
+ if (!options.request.signal.aborted) {
157
174
  const result: string = JSON.stringify([null, TSON.encode(value)]);
158
175
  controller.enqueue(`data:${result}\n\n`);
159
176
  } else {
@@ -180,6 +197,8 @@ export const __initListener = <MilkioRuntime extends MilkioRuntimeInit<MilkioRun
180
197
  response.headers["Content-Type"] = "text/event-stream";
181
198
  response.headers["Cache-Control"] = "no-cache";
182
199
 
200
+ await runtime.emit("milkio:httpResponse", { executeId, logger, path: http.path.string as string, http, context: executed.context });
201
+
183
202
  runtime.runtime.request.delete(executeId);
184
203
  return new Response(response.body, response);
185
204
  }
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.2",
5
+ "version": "1.0.0-alpha.20",
6
6
  "peerDependencies": {
7
7
  "typescript": "^5.4.2"
8
8
  },
@@ -10,7 +10,7 @@
10
10
  "dependencies": {
11
11
  "@paralleldrive/cuid2": "^2.2.2",
12
12
  "@poech/camel-hump-under": "^1.1.0",
13
- "@southern-aurora/tson": "^2.0.2",
13
+ "@southern-aurora/tson": "*",
14
14
  "chalk": "^5.3.0",
15
15
  "date-fns": "^4.1.0",
16
16
  "typia": "6.9.0"
package/step/index.ts ADDED
@@ -0,0 +1,36 @@
1
+ export type Steps<StageT extends Record<any, any>> = {
2
+ step: StepFunction<StageT>;
3
+ run: () => Promise<Remove_<StageT>>;
4
+ };
5
+
6
+ type Remove_<T> = {
7
+ [K in keyof T as K extends `_${string}` ? never : K]: T[K];
8
+ };
9
+
10
+ type ToEmptyObject<T> = T extends undefined | null | never ? {} : T extends object ? T : {};
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
@@ -18,6 +26,7 @@ export type ExecuteId = string | "global";
18
26
  export type GeneratedInit = {
19
27
  routeSchema: any;
20
28
  commandSchema: any;
29
+ configSchema: any;
21
30
  testSchema: any;
22
31
  };
23
32
 
@@ -48,21 +57,6 @@ export type Ping =
48
57
  },
49
58
  ];
50
59
 
51
- export type Execute = <Path extends keyof $types["generated"]["routeSchema"]["$types"]>(
52
- path: Path,
53
- options?: Mixin<
54
- ExecuteOptions,
55
- {
56
- headers?: Record<string, string>;
57
- params?: $types["generated"]["routeSchema"]["$types"][Path]["params"];
58
- }
59
- >,
60
- ) => $types["generated"]["routeSchema"]["$types"][Path]["🐣"] extends boolean
61
- ? // action
62
- Promise<[Partial<$rejectCode>, null, ExecuteResultsOption] | [null, ExecuteActionResults<Path>, ExecuteResultsOption]>
63
- : // stream
64
- Promise<[Partial<$rejectCode>, null, ExecuteResultsOption] | [null, AsyncGenerator<[Partial<$rejectCode>, null] | [null, ExecuteStreamResults<Path>], null>, ExecuteResultsOption]>;
65
-
66
60
  export type ExecuteResultsOption = { executeId: string };
67
61
 
68
62
  export type ExecuteActionResults<Path extends keyof Generated["routeSchema"]["$types"], Generated extends $types["generated"] = $types["generated"]> = Generated["routeSchema"]["$types"][Path]["result"];
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 Ping, type LoggerSubmittingHandler, type LoggerInsertingHandler, type $context } 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,30 @@ export const createWorld = async <CookbookOptions extends MilkioInit>(generated:
45
58
  // Initialize the app
46
59
  const app = {
47
60
  _: _,
48
- _executer: executer,
49
- execute: executer.execute,
50
- ping: executer.ping,
61
+ // event manager
62
+ on: eventManager.on,
63
+ off: eventManager.off,
64
+ emit: eventManager.emit,
65
+ // commander
51
66
  commander,
67
+ // listener
52
68
  listener,
53
69
  };
54
70
 
55
- return app as MilkioWorld<CookbookOptions>;
71
+ runtime.app = app;
72
+
73
+ return app as MilkioWorld<MilkioOptions>;
56
74
  };
57
75
 
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>>>>;
63
- execute: Execute;
76
+ export type MilkioWorld<MilkioOptions extends MilkioInit = MilkioInit> = {
77
+ _: MilkioRuntimeInit<MilkioOptions>;
78
+ // event manager
79
+ on: Awaited<ReturnType<typeof __initEventManager>>["on"];
80
+ off: Awaited<ReturnType<typeof __initEventManager>>["off"];
81
+ emit: Awaited<ReturnType<typeof __initEventManager>>["emit"];
64
82
  ping: (options?: { timeout?: number }) => Promise<Ping>;
83
+ // commander
84
+ commander: Awaited<ReturnType<typeof __initCommander<MilkioRuntimeInit<MilkioOptions>>>>;
85
+ // listener
86
+ listener: Awaited<ReturnType<typeof __initListener<MilkioRuntimeInit<MilkioOptions>>>>;
65
87
  };
@@ -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 = {};