milkio 0.2.13 → 0.2.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/defines/define-api.ts +1 -4
- package/defines/define-http-handler.ts +87 -23
- package/kernel/execute.ts +146 -0
- package/kernel/milkio.ts +4 -205
- package/package.json +1 -1
- package/types.ts +46 -5
- package/utils/handle-catch-error.ts +1 -1
package/defines/define-api.ts
CHANGED
|
@@ -2,10 +2,7 @@ import type { Meta } from "../../../src/meta";
|
|
|
2
2
|
import type { Context } from "../../../src/context";
|
|
3
3
|
|
|
4
4
|
export function defineApi<ApiT extends Api>(api: ApiT): ApiT & { isApi: true } {
|
|
5
|
-
return {
|
|
6
|
-
...api,
|
|
7
|
-
isApi: true,
|
|
8
|
-
};
|
|
5
|
+
return { ...api, isApi: true };
|
|
9
6
|
}
|
|
10
7
|
|
|
11
8
|
export type Api = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { loggerPushTags, loggerSubmit, useLogger, runtime, MiddlewareEvent } from "..";
|
|
1
|
+
import { loggerPushTags, loggerSubmit, useLogger, runtime, MiddlewareEvent, reject } from "..";
|
|
2
2
|
import type { ExecuteId, MilkioApp, Mixin } from "..";
|
|
3
|
-
import {
|
|
3
|
+
import { handleCatchError } from "../utils/handle-catch-error";
|
|
4
4
|
import { routerHandler } from "../../../src/router";
|
|
5
5
|
import schema from "../../../generated/api-schema";
|
|
6
6
|
import { failCode } from "../../../src/fail-code";
|
|
@@ -122,13 +122,13 @@ export function defineHttpHandler(app: MilkioApp, options: ExecuteHttpServerOpti
|
|
|
122
122
|
// execute api
|
|
123
123
|
// after request middleware
|
|
124
124
|
await MiddlewareEvent.handle("afterHttpRequest", [headers, detail]);
|
|
125
|
+
const mode = headers.get("Accept") === "text/event-stream" ? "stream" : "execute";
|
|
125
126
|
|
|
126
127
|
const rawbody = await request.request.text();
|
|
127
128
|
loggerPushTags(executeId, {
|
|
128
129
|
body: rawbody || "no body",
|
|
129
130
|
});
|
|
130
131
|
|
|
131
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
132
132
|
let params: any;
|
|
133
133
|
if (rawbody === "") {
|
|
134
134
|
params = undefined;
|
|
@@ -146,29 +146,94 @@ export function defineHttpHandler(app: MilkioApp, options: ExecuteHttpServerOpti
|
|
|
146
146
|
params,
|
|
147
147
|
});
|
|
148
148
|
|
|
149
|
-
|
|
150
|
-
// @ts-ignore
|
|
151
|
-
const result = await app._executeCoreToJson(pathstr, params, headers, {
|
|
152
|
-
executeId,
|
|
153
|
-
logger,
|
|
154
|
-
detail,
|
|
155
|
-
});
|
|
149
|
+
const resultsRaw = await app._call(mode, pathstr, params, headers, { executeId, logger, detail });
|
|
156
150
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
151
|
+
let fn: any;
|
|
152
|
+
try {
|
|
153
|
+
fn = await schema.apiValidator.validate[pathstr]();
|
|
154
|
+
} catch (error) {
|
|
155
|
+
throw reject("BUSINESS_FAIL", "This is the new API, which takes effect after restarting the server or saving any changes. It will be fixed in the future.");
|
|
156
|
+
}
|
|
160
157
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
158
|
+
if (mode === "execute") {
|
|
159
|
+
const result: string = await fn.validateResults(TSON.encode(resultsRaw.$result));
|
|
160
|
+
if (!detail.response.body) detail.response.body = result;
|
|
161
|
+
|
|
162
|
+
// before response middleware
|
|
163
|
+
const middlewareResponse = {
|
|
164
|
+
value: detail.response.body,
|
|
165
|
+
};
|
|
166
|
+
await MiddlewareEvent.handle("beforeHttpResponse", [middlewareResponse, detail]);
|
|
166
167
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
168
|
+
if (!detail.response.headers["Content-Type"]) detail.response.headers["Content-Type"] = "application/json";
|
|
169
|
+
if (!detail.response.headers["Cache-Control"]) detail.response.headers["Cache-Control"] = "no-cache";
|
|
170
|
+
if (!detail.response.body) detail.response.body = middlewareResponse.value;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (mode === "stream") {
|
|
174
|
+
const generator = (resultsRaw as any).$generator as AsyncGenerator;
|
|
175
|
+
let stream: ReadableStream;
|
|
176
|
+
let control: ReadableStreamDirectController | ReadableStreamDefaultController;
|
|
177
|
+
// SSE has a default timeout, which helps prevent memory leaks, especially when you are writing code with a while (true) loop
|
|
178
|
+
|
|
179
|
+
if (global?.Bun) {
|
|
180
|
+
// bun
|
|
181
|
+
stream = new ReadableStream({
|
|
182
|
+
type: "direct",
|
|
183
|
+
async pull(controller: ReadableStreamDirectController) {
|
|
184
|
+
control = controller;
|
|
185
|
+
try {
|
|
186
|
+
for await (const value of generator) {
|
|
187
|
+
if (!request.request.signal.aborted) {
|
|
188
|
+
const result: string = JSON.stringify(TSON.encode(value));
|
|
189
|
+
controller.write(`data:${result}\n\n`);
|
|
190
|
+
} else {
|
|
191
|
+
generator.return(undefined);
|
|
192
|
+
controller.close();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
} catch (error) {
|
|
196
|
+
controller.close();
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
controller.close();
|
|
200
|
+
},
|
|
201
|
+
cancel() {
|
|
202
|
+
control.close();
|
|
203
|
+
},
|
|
204
|
+
} as unknown as UnderlyingByteSource);
|
|
205
|
+
} else {
|
|
206
|
+
// node.js or others
|
|
207
|
+
stream = new ReadableStream({
|
|
208
|
+
async pull(controller) {
|
|
209
|
+
control = controller;
|
|
210
|
+
try {
|
|
211
|
+
for await (const value of generator) {
|
|
212
|
+
if (!request.request.signal.aborted) {
|
|
213
|
+
const result: string = JSON.stringify(TSON.encode(value));
|
|
214
|
+
controller.enqueue(`data:${result}\n\n`);
|
|
215
|
+
} else {
|
|
216
|
+
generator.return(undefined);
|
|
217
|
+
controller.close();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
} catch (error) {
|
|
221
|
+
controller.close();
|
|
222
|
+
throw error;
|
|
223
|
+
}
|
|
224
|
+
controller.close();
|
|
225
|
+
},
|
|
226
|
+
cancel() {
|
|
227
|
+
control.close();
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
detail.response.headers["Content-Type"] = "text/event-stream";
|
|
232
|
+
detail.response.headers["Cache-Control"] = "no-cache";
|
|
233
|
+
detail.response.body = stream;
|
|
234
|
+
}
|
|
170
235
|
} catch (error) {
|
|
171
|
-
const result =
|
|
236
|
+
const result = handleCatchError(error, executeId);
|
|
172
237
|
if (!response.headers["Content-Type"]) response.headers["Content-Type"] = "application/json";
|
|
173
238
|
if (!response.headers["Cache-Control"]) response.headers["Cache-Control"] = "no-cache";
|
|
174
239
|
if (!response.body) response.body = TSON.stringify(result);
|
|
@@ -177,7 +242,6 @@ export function defineHttpHandler(app: MilkioApp, options: ExecuteHttpServerOpti
|
|
|
177
242
|
loggerPushTags(executeId, {
|
|
178
243
|
status: response.status,
|
|
179
244
|
responseHeaders: response.headers,
|
|
180
|
-
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
181
245
|
body: response.body?.toString() ?? "",
|
|
182
246
|
timeout: new Date().getTime(),
|
|
183
247
|
});
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import type { Context } from "../../../src/context";
|
|
2
|
+
import { failCode } from "../../../src/fail-code";
|
|
3
|
+
import schema from "../../../generated/api-schema";
|
|
4
|
+
import { type ExecuteId, type ExecuteOptions, type ExecuteResult, createUlid, useLogger, runtime, loggerPushTags, headerToPlainObject, loggerSubmit, type ExecuteCoreOptions, TSON, MiddlewareEvent, reject, _validate } from "..";
|
|
5
|
+
import { handleCatchError } from "../utils/handle-catch-error";
|
|
6
|
+
|
|
7
|
+
const apis = new Map<string, any>();
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* call is a low-level API that is useful only when you want to execute the Milkio Api without using execute or httpServer.
|
|
11
|
+
* It only does the most basic thing internally, which is calling the API. The external handling of functions such as making executeId, logging, middleware, etc., are all handled externally.
|
|
12
|
+
* Both execute and httpServer essentially call call.
|
|
13
|
+
*/
|
|
14
|
+
export async function _call(
|
|
15
|
+
mode: "execute" | "stream",
|
|
16
|
+
path: string,
|
|
17
|
+
params: unknown | string,
|
|
18
|
+
headersInit: Record<string, string> | Headers = {},
|
|
19
|
+
options: ExecuteCoreOptions,
|
|
20
|
+
): Promise<{ $type: "result"; $result: ExecuteResult<unknown> } | { $type: "stream"; $result: ExecuteResult<unknown>; $generator: AsyncGenerator }> {
|
|
21
|
+
const executeId = options.executeId as ExecuteId;
|
|
22
|
+
|
|
23
|
+
params = TSON.decode(params);
|
|
24
|
+
|
|
25
|
+
if (!(path in schema.apiMethodsSchema)) {
|
|
26
|
+
const result = {
|
|
27
|
+
executeId,
|
|
28
|
+
success: false,
|
|
29
|
+
fail: {
|
|
30
|
+
code: "NOT_FOUND",
|
|
31
|
+
message: failCode.NOT_FOUND(),
|
|
32
|
+
data: undefined,
|
|
33
|
+
},
|
|
34
|
+
} as ExecuteResult<unknown>;
|
|
35
|
+
|
|
36
|
+
return { $type: "result", $result: result };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let headers: Headers;
|
|
40
|
+
if (!(headersInit instanceof Headers)) {
|
|
41
|
+
// @ts-ignore
|
|
42
|
+
headers = new Headers({
|
|
43
|
+
...headersInit,
|
|
44
|
+
});
|
|
45
|
+
} else {
|
|
46
|
+
headers = headersInit;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (options?.onAfterHeaders) {
|
|
50
|
+
await options.onAfterHeaders(headers);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const context: Context = {
|
|
54
|
+
executeId,
|
|
55
|
+
path: path as string,
|
|
56
|
+
headers,
|
|
57
|
+
logger: options.logger,
|
|
58
|
+
detail: options?.detail ?? {},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
let result: { value: unknown };
|
|
62
|
+
try {
|
|
63
|
+
// before execute middleware
|
|
64
|
+
await MiddlewareEvent.handle("beforeExecute", [context]);
|
|
65
|
+
|
|
66
|
+
let fn: any;
|
|
67
|
+
// check type
|
|
68
|
+
try {
|
|
69
|
+
// @ts-ignore
|
|
70
|
+
fn = await schema.apiValidator.validate[path]();
|
|
71
|
+
} catch (error) {
|
|
72
|
+
throw reject("BUSINESS_FAIL", "This is the new API, which takes effect after restarting the server or saving any changes. It will be fixed in the future.");
|
|
73
|
+
}
|
|
74
|
+
params = _validate(await fn.validateParams(params));
|
|
75
|
+
|
|
76
|
+
// execute api
|
|
77
|
+
let api: any;
|
|
78
|
+
if (apis.has(path as string)) api = apis.get(path as string);
|
|
79
|
+
else {
|
|
80
|
+
// @ts-ignore
|
|
81
|
+
api = schema.apiMethodsSchema[path as string]();
|
|
82
|
+
apis.set(path as string, api);
|
|
83
|
+
}
|
|
84
|
+
const apiModuleAwaited = await api.module;
|
|
85
|
+
const apiMethod = apiModuleAwaited.api.action;
|
|
86
|
+
// @ts-ignore
|
|
87
|
+
result = { value: await apiMethod(params, context) };
|
|
88
|
+
// after execute middleware
|
|
89
|
+
await MiddlewareEvent.handle("afterExecute", [context, result]);
|
|
90
|
+
|
|
91
|
+
if (mode === "execute" && !(result.value as AsyncGenerator)[Symbol.asyncIterator]) return { $type: "result", $result: { executeId, success: true, data: result.value } };
|
|
92
|
+
if (mode === "stream" && (result.value as AsyncGenerator)[Symbol.asyncIterator]) return { $type: "stream", $result: { executeId, success: true, data: "$" }, $generator: result.value as AsyncGenerator };
|
|
93
|
+
throw reject("BUSINESS_FAIL", `It looks like you used the wrong syntax, for this API you should use "client.${mode}(...)"`);
|
|
94
|
+
} catch (error: any) {
|
|
95
|
+
const errorResult = handleCatchError(error, executeId);
|
|
96
|
+
|
|
97
|
+
return { $type: "result", $result: errorResult };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export async function _execute<Path extends keyof (typeof schema)["apiMethodsTypeSchema"], Result extends Awaited<ReturnType<(typeof schema)["apiMethodsTypeSchema"][Path]["api"]["action"]>>>(
|
|
102
|
+
path: Path,
|
|
103
|
+
params: Parameters<(typeof schema)["apiMethodsTypeSchema"][Path]["api"]["action"]>[0] | string,
|
|
104
|
+
headersInit: Record<string, string> | Headers = {},
|
|
105
|
+
options?: ExecuteOptions,
|
|
106
|
+
): Promise<ExecuteResult<Result>> {
|
|
107
|
+
const executeId = (options?.executeId ?? createUlid()) as ExecuteId;
|
|
108
|
+
const logger = useLogger(executeId);
|
|
109
|
+
runtime.execute.executeIds.add(executeId);
|
|
110
|
+
|
|
111
|
+
loggerPushTags(executeId, {
|
|
112
|
+
from: "execute",
|
|
113
|
+
executeId,
|
|
114
|
+
params,
|
|
115
|
+
path,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const result = await _call("execute", path, params, headersInit, {
|
|
119
|
+
...options,
|
|
120
|
+
executeId,
|
|
121
|
+
logger,
|
|
122
|
+
onAfterHeaders: (headers) => {
|
|
123
|
+
loggerPushTags(executeId, {
|
|
124
|
+
headers: headerToPlainObject(headers),
|
|
125
|
+
});
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
loggerPushTags(executeId, { result: result.$result });
|
|
130
|
+
await loggerSubmit(executeId);
|
|
131
|
+
runtime.execute.executeIds.delete(executeId);
|
|
132
|
+
|
|
133
|
+
return result.$result as ExecuteResult<Result>;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export async function _executeToJson<Path extends keyof (typeof schema)["apiMethodsTypeSchema"]>(path: Path, params: Parameters<(typeof schema)["apiMethodsTypeSchema"][Path]["api"]["action"]>[0] | string, headersInit: Record<string, string> | Headers = {}, options?: ExecuteOptions): Promise<string> {
|
|
137
|
+
const resultsRaw = await _execute(path, params, headersInit, options);
|
|
138
|
+
let fn: any;
|
|
139
|
+
try {
|
|
140
|
+
fn = await schema.apiValidator.validate[path]();
|
|
141
|
+
} catch (error) {
|
|
142
|
+
throw reject("BUSINESS_FAIL", "This is the new API, which takes effect after restarting the server or saving any changes. It will be fixed in the future.");
|
|
143
|
+
}
|
|
144
|
+
const results = await fn.validateResults(TSON.encode(resultsRaw));
|
|
145
|
+
return results;
|
|
146
|
+
}
|
package/kernel/milkio.ts
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import { type MiddlewareOptions, _middlewares, MiddlewareEvent } from "./middleware";
|
|
2
2
|
import schema from "../../../generated/api-schema";
|
|
3
|
-
import
|
|
4
|
-
import { failCode } from "../../../src/fail-code";
|
|
5
|
-
import type { MilkioContext } from "./context";
|
|
6
|
-
import { headerToPlainObject } from "../utils/header-to-plain-object";
|
|
7
|
-
import { type Mixin, type ExecuteId, type Fail, type FailEnumerates, loggerPushTags, loggerSubmit, runtime, TSON, type Logger, useLogger, reject } from "..";
|
|
8
|
-
import { hanldeCatchError } from "../utils/handle-catch-error";
|
|
3
|
+
import { runtime } from "..";
|
|
9
4
|
import { createUlid } from "../utils/create-ulid";
|
|
10
5
|
import { _validate } from "./validate";
|
|
6
|
+
import { _execute, _call, _executeToJson } from "./execute";
|
|
11
7
|
|
|
12
8
|
export type MilkioAppOptions = {
|
|
13
9
|
/**
|
|
@@ -45,11 +41,10 @@ export async function createMilkioApp(MilkioAppOptions: MilkioAppOptions = {}) {
|
|
|
45
41
|
}
|
|
46
42
|
|
|
47
43
|
const MilkioApp = {
|
|
44
|
+
randParams: _randParams,
|
|
48
45
|
execute: _execute,
|
|
49
46
|
executeToJson: _executeToJson,
|
|
50
|
-
|
|
51
|
-
_executeCoreToJson,
|
|
52
|
-
randParams: _randParams,
|
|
47
|
+
_call,
|
|
53
48
|
};
|
|
54
49
|
|
|
55
50
|
if (MilkioAppOptions.bootstraps) {
|
|
@@ -86,202 +81,6 @@ export async function createMilkioApp(MilkioAppOptions: MilkioAppOptions = {}) {
|
|
|
86
81
|
return MilkioApp;
|
|
87
82
|
}
|
|
88
83
|
|
|
89
|
-
async function _execute<Path extends keyof (typeof schema)["apiMethodsTypeSchema"], Result extends Awaited<ReturnType<(typeof schema)["apiMethodsTypeSchema"][Path]["api"]["action"]>>>(
|
|
90
|
-
path: Path,
|
|
91
|
-
params: Parameters<(typeof schema)["apiMethodsTypeSchema"][Path]["api"]["action"]>[0] | string,
|
|
92
|
-
headersInit: Record<string, string> | Headers = {},
|
|
93
|
-
options?: ExecuteOptions,
|
|
94
|
-
): Promise<ExecuteResult<Result>> {
|
|
95
|
-
const executeId = (options?.executeId ?? createUlid()) as ExecuteId;
|
|
96
|
-
const logger = useLogger(executeId);
|
|
97
|
-
runtime.execute.executeIds.add(executeId);
|
|
98
|
-
|
|
99
|
-
loggerPushTags(executeId, {
|
|
100
|
-
from: "execute",
|
|
101
|
-
executeId,
|
|
102
|
-
params,
|
|
103
|
-
path,
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
const result: any = await _executeCore(path, params, headersInit, {
|
|
107
|
-
...options,
|
|
108
|
-
executeId,
|
|
109
|
-
logger,
|
|
110
|
-
onAfterHeaders: (headers) => {
|
|
111
|
-
loggerPushTags(executeId, {
|
|
112
|
-
headers: headerToPlainObject(headers),
|
|
113
|
-
});
|
|
114
|
-
},
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
loggerPushTags(executeId, { result });
|
|
118
|
-
await loggerSubmit(executeId);
|
|
119
|
-
runtime.execute.executeIds.delete(executeId);
|
|
120
|
-
|
|
121
|
-
return result;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* executeCore is a low-level API that is useful only when you want to execute the Milkio Api without using execute or httpServer.
|
|
126
|
-
* It only does the most basic thing internally, which is calling the API. The external handling of functions such as making executeId, logging, middleware, etc., are all handled externally.
|
|
127
|
-
* Both execute and httpServer essentially call executeCore.
|
|
128
|
-
*/
|
|
129
|
-
async function _executeCore<Path extends keyof (typeof schema)["apiMethodsTypeSchema"], Result extends Awaited<ReturnType<(typeof schema)["apiMethodsTypeSchema"][Path]["api"]["action"]>>>(
|
|
130
|
-
path: Path,
|
|
131
|
-
params: Parameters<(typeof schema)["apiMethodsTypeSchema"][Path]["api"]["action"]>[0] | string,
|
|
132
|
-
headersInit: Record<string, string> | Headers = {},
|
|
133
|
-
options: ExecuteCoreOptions,
|
|
134
|
-
): Promise<ExecuteResult<Result>> {
|
|
135
|
-
const executeId = options.executeId as ExecuteId;
|
|
136
|
-
|
|
137
|
-
params = TSON.decode(params);
|
|
138
|
-
|
|
139
|
-
if (!(path in schema.apiMethodsSchema)) {
|
|
140
|
-
const result = {
|
|
141
|
-
executeId,
|
|
142
|
-
success: false,
|
|
143
|
-
fail: {
|
|
144
|
-
code: "NOT_FOUND",
|
|
145
|
-
message: failCode.NOT_FOUND(),
|
|
146
|
-
data: undefined,
|
|
147
|
-
},
|
|
148
|
-
} satisfies ExecuteResult<Result>;
|
|
149
|
-
|
|
150
|
-
return result;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
let headers: Headers;
|
|
154
|
-
if (!(headersInit instanceof Headers)) {
|
|
155
|
-
// @ts-ignore
|
|
156
|
-
headers = new Headers({
|
|
157
|
-
...headersInit,
|
|
158
|
-
});
|
|
159
|
-
} else {
|
|
160
|
-
headers = headersInit;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (options?.onAfterHeaders) {
|
|
164
|
-
await options.onAfterHeaders(headers);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const context: Context = {
|
|
168
|
-
executeId,
|
|
169
|
-
path,
|
|
170
|
-
headers,
|
|
171
|
-
logger: options.logger,
|
|
172
|
-
detail: options?.detail ?? {},
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
let result: { value: Result };
|
|
176
|
-
try {
|
|
177
|
-
// before execute middleware
|
|
178
|
-
await MiddlewareEvent.handle("beforeExecute", [context]);
|
|
179
|
-
|
|
180
|
-
let fn: any;
|
|
181
|
-
// check type
|
|
182
|
-
// @ts-ignore
|
|
183
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
184
|
-
try {
|
|
185
|
-
fn = await schema.apiValidator.validate[path]();
|
|
186
|
-
} catch (error) {
|
|
187
|
-
throw reject("BUSINESS_FAIL", "This is the new API, which takes effect after restarting the server or saving any changes. It will be fixed in the future.");
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
params = _validate(await fn.validateParams(params));
|
|
191
|
-
|
|
192
|
-
// execute api
|
|
193
|
-
let api: any;
|
|
194
|
-
if (apis.has(path)) api = apis.get(path);
|
|
195
|
-
else {
|
|
196
|
-
// @ts-ignore
|
|
197
|
-
api = schema.apiMethodsSchema[path]();
|
|
198
|
-
apis.set(path, api);
|
|
199
|
-
}
|
|
200
|
-
const apiModuleAwaited = await api.module;
|
|
201
|
-
|
|
202
|
-
const apiMethod = apiModuleAwaited.api.action;
|
|
203
|
-
|
|
204
|
-
// @ts-ignore
|
|
205
|
-
result = { value: await apiMethod(params, context) };
|
|
206
|
-
|
|
207
|
-
// after execute middleware
|
|
208
|
-
await MiddlewareEvent.handle("afterExecute", [context, result]);
|
|
209
|
-
} catch (error: any) {
|
|
210
|
-
const errorResult = hanldeCatchError(error, executeId);
|
|
211
|
-
|
|
212
|
-
return errorResult;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return {
|
|
216
|
-
executeId,
|
|
217
|
-
success: true,
|
|
218
|
-
data: result.value,
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
async function _executeToJson<Path extends keyof (typeof schema)["apiMethodsTypeSchema"]>(path: Path, params: Parameters<(typeof schema)["apiMethodsTypeSchema"][Path]["api"]["action"]>[0] | string, headersInit: Record<string, string> | Headers = {}, options?: ExecuteOptions): Promise<string> {
|
|
223
|
-
const resultsRaw = await _execute(path, params, headersInit, options);
|
|
224
|
-
let fn: any;
|
|
225
|
-
try {
|
|
226
|
-
fn = await schema.apiValidator.validate[path]();
|
|
227
|
-
} catch (error) {
|
|
228
|
-
throw reject("BUSINESS_FAIL", "This is the new API, which takes effect after restarting the server or saving any changes. It will be fixed in the future.");
|
|
229
|
-
}
|
|
230
|
-
const results = await fn.validateResults(TSON.encode(resultsRaw));
|
|
231
|
-
return results;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
async function _executeCoreToJson<Path extends keyof (typeof schema)["apiMethodsTypeSchema"]>(path: Path, params: Parameters<(typeof schema)["apiMethodsTypeSchema"][Path]["api"]["action"]>[0] | string, headersInit: Record<string, string> | Headers = {}, options: ExecuteCoreOptions): Promise<string> {
|
|
235
|
-
const resultsRaw = await _executeCore(path, params, headersInit, options);
|
|
236
|
-
let fn: any;
|
|
237
|
-
try {
|
|
238
|
-
fn = await schema.apiValidator.validate[path]();
|
|
239
|
-
} catch (error) {
|
|
240
|
-
throw reject("BUSINESS_FAIL", "This is the new API, which takes effect after restarting the server or saving any changes. It will be fixed in the future.");
|
|
241
|
-
}
|
|
242
|
-
const results = await fn.validateResults(TSON.encode(resultsRaw));
|
|
243
|
-
return results;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
84
|
export async function _randParams<Path extends keyof (typeof schema)["apiMethodsTypeSchema"]>(path: Path): Promise<Parameters<(typeof schema)["apiMethodsTypeSchema"][Path]["api"]["action"]>[0]> {
|
|
247
85
|
return await (await schema.apiValidator.validate[path]()).randParams();
|
|
248
86
|
}
|
|
249
|
-
|
|
250
|
-
const apis = new Map<string, any>();
|
|
251
|
-
|
|
252
|
-
export type ExecuteResult<Result> = ExecuteResultSuccess<Result> | ExecuteResultFail;
|
|
253
|
-
|
|
254
|
-
export type ExecuteResultSuccess<Result> = {
|
|
255
|
-
executeId: ExecuteId;
|
|
256
|
-
success: true;
|
|
257
|
-
data: Result;
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
export type ExecuteResultFail<FailT extends Fail<keyof FailEnumerates> = Fail<keyof FailEnumerates>> = {
|
|
261
|
-
executeId: ExecuteId;
|
|
262
|
-
success: false;
|
|
263
|
-
fail: FailT;
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
export type ExecuteOptions = {
|
|
267
|
-
/**
|
|
268
|
-
* The executeId of the request
|
|
269
|
-
* executeId may be generated by the serverless provider, if not, a random string will be generated instead
|
|
270
|
-
*/
|
|
271
|
-
executeId?: string;
|
|
272
|
-
/**
|
|
273
|
-
* Additional information about the request
|
|
274
|
-
* These are usually only fully implemented when called by an Http server
|
|
275
|
-
* During testing or when calling between microservices, some or all of the values may be undefined
|
|
276
|
-
*/
|
|
277
|
-
detail?: MilkioContext["detail"];
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
export type ExecuteCoreOptions = Mixin<
|
|
281
|
-
ExecuteOptions,
|
|
282
|
-
{
|
|
283
|
-
executeId: string;
|
|
284
|
-
logger: Logger;
|
|
285
|
-
onAfterHeaders?: (headers: Headers) => void | Promise<void>;
|
|
286
|
-
}
|
|
287
|
-
>;
|
package/package.json
CHANGED
package/types.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import type { createMilkioApp } from ".";
|
|
1
|
+
import type { Logger, MilkioContext, createMilkioApp } from ".";
|
|
2
2
|
import type { failCode } from "../../src/fail-code";
|
|
3
3
|
|
|
4
|
+
export type Override<P, S> = Omit<P, keyof S> & S;
|
|
5
|
+
|
|
6
|
+
export type Mixin<T, U> = U & Omit<T, keyof U>;
|
|
7
|
+
|
|
8
|
+
export type GeneratorGeneric<T> = T extends AsyncGenerator<infer I> ? I : never;
|
|
9
|
+
|
|
4
10
|
export type MilkioApp = Awaited<ReturnType<typeof createMilkioApp>>;
|
|
5
11
|
|
|
6
12
|
export type ExecuteId = string | "global";
|
|
@@ -33,10 +39,6 @@ export type CookbookItem = {
|
|
|
33
39
|
}>;
|
|
34
40
|
};
|
|
35
41
|
|
|
36
|
-
export type Override<P, S> = Omit<P, keyof S> & S;
|
|
37
|
-
|
|
38
|
-
export type Mixin<T, U> = U & Omit<T, keyof U>;
|
|
39
|
-
|
|
40
42
|
export type MilkioConfig = {
|
|
41
43
|
generate?: {
|
|
42
44
|
significant?: Array<string>;
|
|
@@ -47,3 +49,42 @@ export type MilkioConfig = {
|
|
|
47
49
|
commands?: Array<{ name?: string; script?: string; icon?: string }>;
|
|
48
50
|
};
|
|
49
51
|
};
|
|
52
|
+
|
|
53
|
+
export type ExecuteResult<Result> = ExecuteResultSuccess<Result> | ExecuteResultFail;
|
|
54
|
+
|
|
55
|
+
export type ExecuteResultSuccess<Result> = {
|
|
56
|
+
executeId: ExecuteId;
|
|
57
|
+
success: true;
|
|
58
|
+
data: Result;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export type ExecuteResultFail<FailT extends Fail<keyof FailEnumerates> = Fail<keyof FailEnumerates>> = {
|
|
62
|
+
executeId: ExecuteId;
|
|
63
|
+
success: false;
|
|
64
|
+
fail: FailT;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export type ExecuteOptions = {
|
|
68
|
+
/**
|
|
69
|
+
* The executeId of the request
|
|
70
|
+
* executeId may be generated by the serverless provider, if not, a random string will be generated instead
|
|
71
|
+
*/
|
|
72
|
+
executeId?: string;
|
|
73
|
+
/**
|
|
74
|
+
* Additional information about the request
|
|
75
|
+
* These are usually only fully implemented when called by an Http server
|
|
76
|
+
* During testing or when calling between microservices, some or all of the values may be undefined
|
|
77
|
+
*/
|
|
78
|
+
detail?: MilkioContext["detail"];
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export type ExecuteCoreOptions = Mixin<
|
|
82
|
+
ExecuteOptions,
|
|
83
|
+
{
|
|
84
|
+
executeId: string;
|
|
85
|
+
logger: Logger;
|
|
86
|
+
onAfterHeaders?: (headers: Headers) => void | Promise<void>;
|
|
87
|
+
}
|
|
88
|
+
>;
|
|
89
|
+
|
|
90
|
+
export type MilkioEvent<Result> = Awaited<GeneratorGeneric<ExecuteResultSuccess<Result>["data"] extends { $type: any } ? ExecuteResultSuccess<Result>["data"]["$type"] : never>>;
|
|
@@ -3,7 +3,7 @@ import { useLogger, type ExecuteId, type ExecuteResult } from "..";
|
|
|
3
3
|
import { configMilkio } from "../../../src/config/milkio";
|
|
4
4
|
|
|
5
5
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6
|
-
export function
|
|
6
|
+
export function handleCatchError(error: any, executeId: ExecuteId): ExecuteResult<any> {
|
|
7
7
|
const logger = useLogger(executeId);
|
|
8
8
|
|
|
9
9
|
if (configMilkio.debug === true) {
|