milkio 0.2.13 → 0.2.15

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.
@@ -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 { hanldeCatchError } from "../utils/handle-catch-error";
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,53 +122,119 @@ 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
- if (rawbody === "") {
134
- params = undefined;
135
- } else {
136
- try {
137
- params = JSON.parse(rawbody);
138
- } catch (error) {
139
- const logger = useLogger(executeId);
140
- logger.log("TIP: body is not json, the content is not empty, but the content is not in a valid JSON format. The original content value can be retrieved via request.request.text()");
133
+ try {
134
+ if (rawbody) params = JSON.parse(rawbody);
135
+ else if (request.request.method === 'GET' && fullurl.searchParams.get('params')) {
136
+ params = JSON.parse(decodeURIComponent(fullurl.searchParams.get('params')!));
137
+ } else {
141
138
  params = undefined;
142
139
  }
140
+ } catch (error) {
141
+ const logger = useLogger(executeId);
142
+ logger.log("TIP: body is not json, the content is not empty, but the content is not in a valid JSON format. The original content value can be retrieved via request.request.text()");
143
+ params = undefined;
143
144
  }
144
145
 
145
146
  loggerPushTags(executeId, {
146
147
  params,
147
148
  });
148
149
 
149
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
150
- // @ts-ignore
151
- const result = await app._executeCoreToJson(pathstr, params, headers, {
152
- executeId,
153
- logger,
154
- detail,
155
- });
150
+ const resultsRaw = await app._call(mode, pathstr, params, headers, { executeId, logger, detail });
156
151
 
157
- // @ts-ignore
158
- // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression, @typescript-eslint/no-explicit-any
159
- if (!detail.response.body) detail.response.body = result;
152
+ let fn: any;
153
+ try {
154
+ fn = await schema.apiValidator.validate[pathstr]();
155
+ } catch (error) {
156
+ 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.");
157
+ }
160
158
 
161
- // before response middleware
162
- const middlewareResponse = {
163
- value: detail.response.body,
164
- };
165
- await MiddlewareEvent.handle("beforeHttpResponse", [middlewareResponse, detail]);
159
+ if (mode === "execute") {
160
+ const result: string = await fn.validateResults(TSON.encode(resultsRaw.$result));
161
+ if (!detail.response.body) detail.response.body = result;
166
162
 
167
- if (!detail.response.headers["Content-Type"]) detail.response.headers["Content-Type"] = "application/json";
168
- if (!detail.response.headers["Cache-Control"]) detail.response.headers["Cache-Control"] = "no-cache";
169
- if (!detail.response.body) detail.response.body = middlewareResponse.value;
163
+ // before response middleware
164
+ const middlewareResponse = {
165
+ value: detail.response.body,
166
+ };
167
+ await MiddlewareEvent.handle("beforeHttpResponse", [middlewareResponse, detail]);
168
+
169
+ if (!detail.response.headers["Content-Type"]) detail.response.headers["Content-Type"] = "application/json";
170
+ if (!detail.response.headers["Cache-Control"]) detail.response.headers["Cache-Control"] = "no-cache";
171
+ if (!detail.response.body) detail.response.body = middlewareResponse.value;
172
+ }
173
+
174
+ if (mode === "stream") {
175
+ const generator = (resultsRaw as any).$generator as AsyncGenerator;
176
+ let stream: ReadableStream;
177
+ let control: ReadableStreamDirectController | ReadableStreamDefaultController;
178
+ // SSE has a default timeout, which helps prevent memory leaks, especially when you are writing code with a while (true) loop
179
+
180
+ if (global?.Bun) {
181
+ // bun
182
+ stream = new ReadableStream({
183
+ type: "direct",
184
+ async pull(controller: ReadableStreamDirectController) {
185
+ control = controller;
186
+ try {
187
+ for await (const value of generator) {
188
+ if (!request.request.signal.aborted) {
189
+ const result: string = JSON.stringify(TSON.encode(value));
190
+ controller.write(`data:${result}\n\n`);
191
+ } else {
192
+ generator.return(undefined);
193
+ controller.close();
194
+ }
195
+ }
196
+ } catch (error) {
197
+ controller.close();
198
+ throw error;
199
+ }
200
+ controller.close();
201
+ },
202
+ cancel() {
203
+ control.close();
204
+ },
205
+ } as unknown as UnderlyingByteSource);
206
+ } else {
207
+ // node.js or others
208
+ stream = new ReadableStream({
209
+ async pull(controller) {
210
+ control = controller;
211
+ try {
212
+ for await (const value of generator) {
213
+ if (!request.request.signal.aborted) {
214
+ const result: string = JSON.stringify(TSON.encode(value));
215
+ controller.enqueue(`data:${result}\n\n`);
216
+ } else {
217
+ generator.return(undefined);
218
+ controller.close();
219
+ }
220
+ }
221
+ } catch (error) {
222
+ controller.close();
223
+ throw error;
224
+ }
225
+ controller.close();
226
+ },
227
+ cancel() {
228
+ control.close();
229
+ },
230
+ });
231
+ }
232
+ detail.response.headers["Content-Type"] = "text/event-stream";
233
+ detail.response.headers["Cache-Control"] = "no-cache";
234
+ detail.response.body = stream;
235
+ }
170
236
  } catch (error) {
171
- const result = hanldeCatchError(error, executeId);
237
+ const result = handleCatchError(error, executeId);
172
238
  if (!response.headers["Content-Type"]) response.headers["Content-Type"] = "application/json";
173
239
  if (!response.headers["Cache-Control"]) response.headers["Cache-Control"] = "no-cache";
174
240
  if (!response.body) response.body = TSON.stringify(result);
@@ -177,7 +243,6 @@ export function defineHttpHandler(app: MilkioApp, options: ExecuteHttpServerOpti
177
243
  loggerPushTags(executeId, {
178
244
  status: response.status,
179
245
  responseHeaders: response.headers,
180
- // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
181
246
  body: response.body?.toString() ?? "",
182
247
  timeout: new Date().getTime(),
183
248
  });
@@ -0,0 +1,148 @@
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
+ console.log(mode);
94
+
95
+ throw reject("BUSINESS_FAIL", `It looks like you used the wrong syntax, for this API you should use "client.${mode}(...)"`);
96
+ } catch (error: any) {
97
+ const errorResult = handleCatchError(error, executeId);
98
+
99
+ return { $type: "result", $result: errorResult };
100
+ }
101
+ }
102
+
103
+ export async function _execute<Path extends keyof (typeof schema)["apiMethodsTypeSchema"], Result extends Awaited<ReturnType<(typeof schema)["apiMethodsTypeSchema"][Path]["api"]["action"]>>>(
104
+ path: Path,
105
+ params: Parameters<(typeof schema)["apiMethodsTypeSchema"][Path]["api"]["action"]>[0] | string,
106
+ headersInit: Record<string, string> | Headers = {},
107
+ options?: ExecuteOptions,
108
+ ): Promise<ExecuteResult<Result>> {
109
+ const executeId = (options?.executeId ?? createUlid()) as ExecuteId;
110
+ const logger = useLogger(executeId);
111
+ runtime.execute.executeIds.add(executeId);
112
+
113
+ loggerPushTags(executeId, {
114
+ from: "execute",
115
+ executeId,
116
+ params,
117
+ path,
118
+ });
119
+
120
+ const result = await _call("execute", path, params, headersInit, {
121
+ ...options,
122
+ executeId,
123
+ logger,
124
+ onAfterHeaders: (headers) => {
125
+ loggerPushTags(executeId, {
126
+ headers: headerToPlainObject(headers),
127
+ });
128
+ },
129
+ });
130
+
131
+ loggerPushTags(executeId, { result: result.$result });
132
+ await loggerSubmit(executeId);
133
+ runtime.execute.executeIds.delete(executeId);
134
+
135
+ return result.$result as ExecuteResult<Result>;
136
+ }
137
+
138
+ 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> {
139
+ const resultsRaw = await _execute(path, params, headersInit, options);
140
+ let fn: any;
141
+ try {
142
+ fn = await schema.apiValidator.validate[path]();
143
+ } catch (error) {
144
+ 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.");
145
+ }
146
+ const results = await fn.validateResults(TSON.encode(resultsRaw));
147
+ return results;
148
+ }
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 type { Context } from "../../../src/context";
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
- _executeCore,
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
@@ -2,7 +2,7 @@
2
2
  "name": "milkio",
3
3
  "type": "module",
4
4
  "module": "index.ts",
5
- "version": "0.2.13",
5
+ "version": "0.2.15",
6
6
  "peerDependencies": {
7
7
  "typescript": "^5.4.2"
8
8
  },
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 hanldeCatchError(error: any, executeId: ExecuteId): ExecuteResult<any> {
6
+ export function handleCatchError(error: any, executeId: ExecuteId): ExecuteResult<any> {
7
7
  const logger = useLogger(executeId);
8
8
 
9
9
  if (configMilkio.debug === true) {