@milkio/stargate 1.0.0-alpha.92 → 1.0.0-alpha.94
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/index.ts +557 -377
- package/package.json +1 -1
package/index.ts
CHANGED
|
@@ -1,112 +1,112 @@
|
|
|
1
|
-
import { TSON } from
|
|
1
|
+
import { TSON } from "@southern-aurora/tson";
|
|
2
2
|
|
|
3
3
|
export type MilkioStargateOptions = {
|
|
4
|
-
baseUrl: string | (() => string) | (() => Promise<string>)
|
|
5
|
-
timeout?: number
|
|
6
|
-
fetch?: typeof fetch
|
|
7
|
-
abort?: typeof AbortController
|
|
8
|
-
}
|
|
4
|
+
baseUrl: string | (() => string) | (() => Promise<string>);
|
|
5
|
+
timeout?: number;
|
|
6
|
+
fetch?: typeof fetch;
|
|
7
|
+
abort?: typeof AbortController;
|
|
8
|
+
};
|
|
9
9
|
|
|
10
|
-
export type Mixin<T, U> = U & Omit<T, keyof U
|
|
10
|
+
export type Mixin<T, U> = U & Omit<T, keyof U>;
|
|
11
11
|
|
|
12
12
|
export type ExecuteOptions = {
|
|
13
|
-
params?: Record<any, any
|
|
14
|
-
headers?: Record<string, string
|
|
15
|
-
timeout?: number
|
|
16
|
-
type?:
|
|
17
|
-
baseUrl?: string | (() => string) | (() => Promise<string>)
|
|
18
|
-
}
|
|
13
|
+
params?: Record<any, any>;
|
|
14
|
+
headers?: Record<string, string>;
|
|
15
|
+
timeout?: number;
|
|
16
|
+
type?: "action" | "stream";
|
|
17
|
+
baseUrl?: string | (() => string) | (() => Promise<string>);
|
|
18
|
+
};
|
|
19
19
|
|
|
20
|
-
export type ExecuteResultsOption = { executeId: string }
|
|
20
|
+
export type ExecuteResultsOption = { executeId: string };
|
|
21
21
|
|
|
22
22
|
export type Ping =
|
|
23
23
|
| [
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
{
|
|
25
|
+
connect: false;
|
|
26
|
+
delay: number;
|
|
27
|
+
error: any;
|
|
28
|
+
},
|
|
29
|
+
null,
|
|
30
|
+
]
|
|
31
31
|
| [
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
export async function createStargate<Generated extends { routeSchema: any
|
|
40
|
-
const $fetch = stargateOptions.fetch ?? fetch
|
|
41
|
-
const $abort = stargateOptions.abort ?? AbortController
|
|
32
|
+
null,
|
|
33
|
+
{
|
|
34
|
+
connect: true;
|
|
35
|
+
delay: number;
|
|
36
|
+
serverTimestamp: number;
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
export async function createStargate<Generated extends { routeSchema: any; rejectCode: any }>(stargateOptions: MilkioStargateOptions) {
|
|
40
|
+
const $fetch = stargateOptions.fetch ?? fetch;
|
|
41
|
+
const $abort = stargateOptions.abort ?? AbortController;
|
|
42
42
|
|
|
43
43
|
type StargateEvents = {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
path: string
|
|
48
|
-
options: Mixin<ExecuteOptions, { headers: Record<string, string
|
|
49
|
-
error: Partial<Generated[
|
|
50
|
-
handleError: <K extends keyof Partial<Generated[
|
|
51
|
-
}
|
|
52
|
-
}
|
|
44
|
+
"milkio:executeBefore": { path: string; options: Mixin<ExecuteOptions, { headers: Record<string, string>; baseUrl: string }> };
|
|
45
|
+
"milkio:fetchBefore": { path: string; options: Mixin<ExecuteOptions, { headers: Record<string, string>; baseUrl: string }>; body: string };
|
|
46
|
+
"milkio:executeError": {
|
|
47
|
+
path: string;
|
|
48
|
+
options: Mixin<ExecuteOptions, { headers: Record<string, string>; baseUrl: string }>;
|
|
49
|
+
error: Partial<Generated["rejectCode"]>;
|
|
50
|
+
handleError: <K extends keyof Partial<Generated["rejectCode"]>>(error: any, key: K, handler: (error: Partial<Generated["rejectCode"][K]>) => boolean | Promise<boolean>) => Promise<void>;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
53
|
|
|
54
54
|
const handleError: any = async (error: any, key: string, handler: (error: any) => boolean | Promise<boolean>) => {
|
|
55
55
|
if (key in error) {
|
|
56
|
-
const handled = await handler(error[key])
|
|
57
|
-
if (handled) delete error[key]
|
|
56
|
+
const handled = await handler(error[key]);
|
|
57
|
+
if (handled) delete error[key];
|
|
58
58
|
}
|
|
59
|
-
}
|
|
59
|
+
};
|
|
60
60
|
|
|
61
61
|
const __initEventManager = () => {
|
|
62
|
-
const handlers = new Map<(event: any) => void, string>()
|
|
63
|
-
const indexed = new Map<string, Set<(event: any) => void>>()
|
|
62
|
+
const handlers = new Map<(event: any) => void, string>();
|
|
63
|
+
const indexed = new Map<string, Set<(event: any) => void>>();
|
|
64
64
|
|
|
65
65
|
const eventManager = {
|
|
66
66
|
on: <Key extends keyof StargateEvents, Handler extends (event: StargateEvents[Key]) => void>(key: Key, handler: Handler) => {
|
|
67
|
-
handlers.set(handler, key as string)
|
|
67
|
+
handlers.set(handler, key as string);
|
|
68
68
|
if (indexed.has(key as string) === false) {
|
|
69
|
-
indexed.set(key as string, new Set())
|
|
69
|
+
indexed.set(key as string, new Set());
|
|
70
70
|
}
|
|
71
|
-
const set = indexed.get(key as string)
|
|
72
|
-
set.add(handler)
|
|
73
|
-
handlers.set(handler, key as string)
|
|
71
|
+
const set = indexed.get(key as string)!;
|
|
72
|
+
set.add(handler);
|
|
73
|
+
handlers.set(handler, key as string);
|
|
74
74
|
|
|
75
75
|
return () => {
|
|
76
|
-
handlers.delete(handler)
|
|
77
|
-
set.delete(handler)
|
|
78
|
-
}
|
|
76
|
+
handlers.delete(handler);
|
|
77
|
+
set.delete(handler);
|
|
78
|
+
};
|
|
79
79
|
},
|
|
80
80
|
off: <Key extends keyof StargateEvents, Handler extends (event: StargateEvents[Key]) => void>(key: Key, handler: Handler) => {
|
|
81
|
-
const set = indexed.get(key as string)
|
|
82
|
-
if (!set) return
|
|
83
|
-
handlers.delete(handler)
|
|
84
|
-
set.delete(handler)
|
|
81
|
+
const set = indexed.get(key as string);
|
|
82
|
+
if (!set) return;
|
|
83
|
+
handlers.delete(handler);
|
|
84
|
+
set.delete(handler);
|
|
85
85
|
},
|
|
86
86
|
emit: async <Key extends keyof StargateEvents, Value extends StargateEvents[Key]>(key: Key, value: Value): Promise<void> => {
|
|
87
|
-
const h = indexed.get(key as string)
|
|
87
|
+
const h = indexed.get(key as string);
|
|
88
88
|
if (h) {
|
|
89
89
|
for (const handler of h) {
|
|
90
|
-
await handler(value)
|
|
90
|
+
await handler(value);
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
},
|
|
94
|
-
}
|
|
94
|
+
};
|
|
95
95
|
|
|
96
|
-
return eventManager
|
|
97
|
-
}
|
|
96
|
+
return eventManager;
|
|
97
|
+
};
|
|
98
98
|
|
|
99
|
-
const eventManager = __initEventManager()
|
|
99
|
+
const eventManager = __initEventManager();
|
|
100
100
|
|
|
101
101
|
const bootstrap = async () => {
|
|
102
|
-
let baseUrl = stargateOptions.baseUrl
|
|
103
|
-
if (typeof baseUrl ===
|
|
104
|
-
if (baseUrl.endsWith(
|
|
102
|
+
let baseUrl = stargateOptions.baseUrl;
|
|
103
|
+
if (typeof baseUrl === "function") baseUrl = await baseUrl();
|
|
104
|
+
if (baseUrl.endsWith("/")) baseUrl = baseUrl.slice(0, -1);
|
|
105
105
|
|
|
106
|
-
return baseUrl
|
|
107
|
-
}
|
|
106
|
+
return baseUrl;
|
|
107
|
+
};
|
|
108
108
|
|
|
109
|
-
const baseUrl: Promise<string> = bootstrap()
|
|
109
|
+
const baseUrl: Promise<string> = bootstrap();
|
|
110
110
|
|
|
111
111
|
const stargate = {
|
|
112
112
|
...eventManager,
|
|
@@ -114,412 +114,601 @@ export async function createStargate<Generated extends { routeSchema: any, rejec
|
|
|
114
114
|
generated: void 0 as unknown as Generated,
|
|
115
115
|
},
|
|
116
116
|
options: stargateOptions,
|
|
117
|
-
async execute<Path extends keyof Generated[
|
|
117
|
+
async execute<Path extends keyof Generated["routeSchema"]>(
|
|
118
118
|
path: Path,
|
|
119
119
|
options?: Mixin<
|
|
120
120
|
ExecuteOptions,
|
|
121
121
|
{
|
|
122
|
-
params?: Generated[
|
|
122
|
+
params?: Generated["routeSchema"][Path]["types"]["params"];
|
|
123
123
|
}
|
|
124
124
|
>,
|
|
125
125
|
): Promise<
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
[Partial<Generated[
|
|
129
|
-
|
|
130
|
-
[Partial<Generated[
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if (options
|
|
134
|
-
|
|
135
|
-
|
|
126
|
+
Generated["routeSchema"][Path]["types"]["🐣"] extends boolean
|
|
127
|
+
? // action
|
|
128
|
+
[Partial<Generated["rejectCode"]>, null, ExecuteResultsOption] | [null, Generated["routeSchema"][Path]["types"]["result"], ExecuteResultsOption]
|
|
129
|
+
: // stream
|
|
130
|
+
[Partial<Generated["rejectCode"]>, null, ExecuteResultsOption] | [null, AsyncGenerator<[Partial<Generated["rejectCode"]>, null] | [null, GeneratorGeneric<Generated["routeSchema"][Path]["types"]["result"]>], ExecuteResultsOption>]
|
|
131
|
+
> {
|
|
132
|
+
// biome-ignore lint/style/noParameterAssign: <explanation>
|
|
133
|
+
if (!options) options = {};
|
|
134
|
+
if (options.headers === undefined) options.headers = {};
|
|
135
|
+
|
|
136
|
+
let url: string;
|
|
136
137
|
if (options.baseUrl) {
|
|
137
|
-
let baseUrl = options.baseUrl
|
|
138
|
-
if (typeof baseUrl ===
|
|
139
|
-
if (baseUrl.endsWith(
|
|
140
|
-
url = baseUrl + (path as string)
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
url = (await baseUrl) + (path as string)
|
|
138
|
+
let baseUrl = options.baseUrl;
|
|
139
|
+
if (typeof baseUrl === "function") baseUrl = await baseUrl();
|
|
140
|
+
if (baseUrl.endsWith("/")) baseUrl = baseUrl.slice(0, -1);
|
|
141
|
+
url = baseUrl + (path as string);
|
|
142
|
+
} else {
|
|
143
|
+
url = (await baseUrl) + (path as string);
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
if (options.type !==
|
|
146
|
+
if (options.type !== "stream") {
|
|
147
147
|
// action
|
|
148
|
-
if (options.headers.Accept === undefined) options.headers.Accept =
|
|
149
|
-
if (options.headers[
|
|
150
|
-
let result: { value: Record<any, any> }
|
|
148
|
+
if (options.headers.Accept === undefined) options.headers.Accept = "application/json";
|
|
149
|
+
if (options.headers["Content-Type"] === undefined) options.headers["Content-Type"] = "application/json";
|
|
150
|
+
let result: { value: Record<any, any> };
|
|
151
151
|
|
|
152
152
|
try {
|
|
153
|
-
await eventManager.emit(
|
|
153
|
+
await eventManager.emit("milkio:executeBefore", { path: path as string, options: options as any });
|
|
154
154
|
|
|
155
|
-
const body = TSON.stringify(options.params) ??
|
|
156
|
-
await eventManager.emit(
|
|
155
|
+
const body = TSON.stringify(options.params) ?? "";
|
|
156
|
+
await eventManager.emit("milkio:fetchBefore", { path: path as string, options: options as any, body });
|
|
157
157
|
|
|
158
|
+
// biome-ignore lint/suspicious/noAsyncPromiseExecutor: <explanation>
|
|
158
159
|
const response = await new Promise<string>(async (resolve, reject) => {
|
|
159
|
-
const timeout = options?.timeout ?? options?.timeout ?? 6000
|
|
160
|
+
const timeout = options?.timeout ?? options?.timeout ?? 6000;
|
|
160
161
|
const timer = setTimeout(() => {
|
|
161
|
-
reject([{ REQUEST_TIMEOUT: { timeout, message: `Execute timeout after ${timeout}ms.` } }, null])
|
|
162
|
-
}, timeout)
|
|
162
|
+
reject([{ REQUEST_TIMEOUT: { timeout, message: `Execute timeout after ${timeout}ms.` } }, null]);
|
|
163
|
+
}, timeout);
|
|
163
164
|
|
|
164
165
|
try {
|
|
165
|
-
const value = await (await $fetch(url, { method:
|
|
166
|
-
clearTimeout(timer)
|
|
167
|
-
resolve(value)
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
reject(error)
|
|
166
|
+
const value = await (await $fetch(url, { method: "POST", body, headers: options.headers })).text();
|
|
167
|
+
clearTimeout(timer);
|
|
168
|
+
resolve(value);
|
|
169
|
+
} catch (error) {
|
|
170
|
+
reject(error);
|
|
171
171
|
}
|
|
172
|
-
})
|
|
173
|
-
result = { value: TSON.parse(response) }
|
|
174
|
-
}
|
|
175
|
-
catch (error: any) {
|
|
172
|
+
});
|
|
173
|
+
result = { value: TSON.parse(response) };
|
|
174
|
+
} catch (error: any) {
|
|
176
175
|
if (error?.[0]?.REQUEST_TIMEOUT) {
|
|
177
|
-
await eventManager.emit(
|
|
178
|
-
return error
|
|
176
|
+
await eventManager.emit("milkio:executeError", { handleError, path: path as string, options: options as any, error });
|
|
177
|
+
return error;
|
|
179
178
|
}
|
|
180
|
-
const errorPined = { REQUEST_FAIL: error }
|
|
181
|
-
await eventManager.emit(
|
|
182
|
-
return [errorPined, null, { executeId:
|
|
179
|
+
const errorPined = { REQUEST_FAIL: error };
|
|
180
|
+
await eventManager.emit("milkio:executeError", { handleError, path: path as string, options: options as any, error: errorPined });
|
|
181
|
+
return [errorPined, null, { executeId: "unknown" }];
|
|
183
182
|
}
|
|
184
183
|
if (result.value.success !== true) {
|
|
185
|
-
const error: any = {}
|
|
186
|
-
error[result.value.code] = result.value.reject ?? null
|
|
187
|
-
await eventManager.emit(
|
|
188
|
-
return [error, null, { executeId:
|
|
184
|
+
const error: any = {};
|
|
185
|
+
error[result.value.code] = result.value.reject ?? null;
|
|
186
|
+
await eventManager.emit("milkio:executeError", { handleError, path: path as string, options: options as any, error });
|
|
187
|
+
return [error, null, { executeId: "unknown" }];
|
|
189
188
|
}
|
|
190
189
|
|
|
191
|
-
return [null, result.value.data, { executeId: result.value.executeId }] as any
|
|
192
|
-
}
|
|
193
|
-
else {
|
|
190
|
+
return [null, result.value.data, { executeId: result.value.executeId }] as any;
|
|
191
|
+
} else {
|
|
194
192
|
// stream
|
|
195
|
-
if (options.headers.Accept === undefined) options.headers.Accept =
|
|
196
|
-
if (options.headers[
|
|
193
|
+
if (options.headers.Accept === undefined) options.headers.Accept = "text/event-stream";
|
|
194
|
+
if (options.headers["Content-Type"] === undefined) options.headers["Content-Type"] = "application/json";
|
|
197
195
|
|
|
198
196
|
const stacks: Map<
|
|
199
197
|
number,
|
|
200
198
|
{
|
|
201
|
-
done: boolean
|
|
202
|
-
promise: Promise<IteratorResult<any
|
|
203
|
-
resolve: (value: IteratorResult<any>) => void
|
|
204
|
-
reject: (reason: any) => void
|
|
199
|
+
done: boolean;
|
|
200
|
+
promise: Promise<IteratorResult<any>>;
|
|
201
|
+
resolve: (value: IteratorResult<any>) => void;
|
|
202
|
+
reject: (reason: any) => void;
|
|
205
203
|
}
|
|
206
|
-
> = new Map()
|
|
207
|
-
let stacksIndex
|
|
208
|
-
let iteratorIndex
|
|
209
|
-
let streamResult: any
|
|
210
|
-
const streamResultFetched = withResolvers<undefined>()
|
|
204
|
+
> = new Map();
|
|
205
|
+
let stacksIndex = 0;
|
|
206
|
+
let iteratorIndex = 0;
|
|
207
|
+
let streamResult: any;
|
|
208
|
+
const streamResultFetched = withResolvers<undefined>();
|
|
211
209
|
|
|
212
|
-
const timeout = stargateOptions?.timeout ?? options?.timeout ?? 6000
|
|
210
|
+
const timeout = stargateOptions?.timeout ?? options?.timeout ?? 6000;
|
|
213
211
|
const timer = setTimeout(() => {
|
|
214
|
-
streamResultFetched.reject([{ REQUEST_TIMEOUT: { timeout, message: `Execute timeout after ${timeout}ms.` } }, null, { executeId:
|
|
215
|
-
}, timeout)
|
|
212
|
+
streamResultFetched.reject([{ REQUEST_TIMEOUT: { timeout, message: `Execute timeout after ${timeout}ms.` } }, null, { executeId: "unknown" }]);
|
|
213
|
+
}, timeout);
|
|
216
214
|
|
|
217
215
|
const onmessage = (event: EventSourceMessage) => {
|
|
218
|
-
if (event.data.startsWith(
|
|
216
|
+
if (event.data.startsWith("@")) {
|
|
219
217
|
try {
|
|
220
|
-
streamResult = TSON.parse(event.data.slice(1))
|
|
221
|
-
streamResultFetched.resolve(undefined)
|
|
222
|
-
clearTimeout(timer)
|
|
218
|
+
streamResult = TSON.parse(event.data.slice(1));
|
|
219
|
+
streamResultFetched.resolve(undefined);
|
|
220
|
+
clearTimeout(timer);
|
|
221
|
+
} catch (error) {
|
|
222
|
+
streamResultFetched.reject([{ REQUEST_FAIL: error }, null, { executeId: "unknown" }]);
|
|
223
|
+
clearTimeout(timer);
|
|
223
224
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
clearTimeout(timer)
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
else {
|
|
230
|
-
const index = ++stacksIndex
|
|
225
|
+
} else {
|
|
226
|
+
const index = ++stacksIndex;
|
|
231
227
|
if (stacks.has(index)) {
|
|
232
|
-
const stack = stacks.get(index)
|
|
233
|
-
stack!.done = true
|
|
234
|
-
stack!.resolve({ done: false, value: TSON.parse(event.data) })
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
stacks.set(index, { ...stack, done: false })
|
|
228
|
+
const stack = stacks.get(index);
|
|
229
|
+
stack!.done = true;
|
|
230
|
+
stack!.resolve({ done: false, value: TSON.parse(event.data) });
|
|
231
|
+
} else {
|
|
232
|
+
const stack = withResolvers<IteratorResult<any>>();
|
|
233
|
+
stack.resolve({ done: false, value: TSON.parse(event.data) });
|
|
234
|
+
stacks.set(index, { ...stack, done: false });
|
|
240
235
|
}
|
|
241
236
|
}
|
|
242
|
-
}
|
|
237
|
+
};
|
|
243
238
|
|
|
244
|
-
let curRequestController: AbortController
|
|
239
|
+
let curRequestController: AbortController;
|
|
245
240
|
|
|
246
241
|
async function create() {
|
|
247
|
-
curRequestController = new $abort()
|
|
248
|
-
curRequestController.signal.addEventListener(
|
|
249
|
-
iterator.return()
|
|
250
|
-
})
|
|
242
|
+
curRequestController = new $abort();
|
|
243
|
+
curRequestController.signal.addEventListener("abort", () => {
|
|
244
|
+
iterator.return();
|
|
245
|
+
});
|
|
251
246
|
try {
|
|
252
|
-
await eventManager.emit(
|
|
247
|
+
await eventManager.emit("milkio:executeBefore", { path: path as string, options: options as any });
|
|
253
248
|
|
|
254
|
-
const body = TSON.stringify(options!.params) ??
|
|
255
|
-
await eventManager.emit(
|
|
249
|
+
const body = TSON.stringify(options!.params) ?? "";
|
|
250
|
+
await eventManager.emit("milkio:fetchBefore", { path: path as string, options: options as any, body });
|
|
256
251
|
|
|
257
252
|
const response = await $fetch(url, {
|
|
258
|
-
method:
|
|
253
|
+
method: "POST",
|
|
259
254
|
headers: options!.headers,
|
|
260
255
|
body,
|
|
261
256
|
signal: curRequestController.signal,
|
|
262
|
-
})
|
|
257
|
+
});
|
|
263
258
|
|
|
264
|
-
const contentType = response.headers.get(
|
|
265
|
-
if (!contentType?.startsWith(
|
|
266
|
-
throw new Error(`Expected content-type to be ${
|
|
259
|
+
const contentType = response.headers.get("Content-Type");
|
|
260
|
+
if (!contentType?.startsWith("text/event-stream")) {
|
|
261
|
+
throw new Error(`Expected content-type to be ${"text/event-stream"}, Actual: ${contentType}`);
|
|
267
262
|
}
|
|
268
263
|
|
|
269
|
-
await getBytes(response.body!, getLines(getMessages(onmessage)))
|
|
264
|
+
await getBytes(response.body!, getLines(getMessages(onmessage)));
|
|
270
265
|
|
|
271
|
-
await iterator.return()
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
await
|
|
277
|
-
|
|
278
|
-
streamResultFetched.reject([error, null, { executeId: 'unknown' }])
|
|
266
|
+
await iterator.return();
|
|
267
|
+
} catch (err) {
|
|
268
|
+
if (!curRequestController.signal.aborted) curRequestController.abort();
|
|
269
|
+
const error = { REQUEST_FAIL: err };
|
|
270
|
+
await eventManager.emit("milkio:executeError", { handleError, path: path as string, options: options as any, error });
|
|
271
|
+
await iterator.throw(err);
|
|
272
|
+
streamResultFetched.reject([error, null, { executeId: "unknown" }]);
|
|
279
273
|
}
|
|
280
274
|
}
|
|
281
275
|
|
|
282
|
-
void create()
|
|
276
|
+
void create();
|
|
283
277
|
|
|
284
278
|
const iterator = {
|
|
285
279
|
...({
|
|
286
280
|
next(): Promise<IteratorResult<unknown>> {
|
|
287
|
-
const index = ++iteratorIndex
|
|
288
|
-
if (stacks.has(index - 2)) stacks.delete(index - 2)
|
|
281
|
+
const index = ++iteratorIndex;
|
|
282
|
+
if (stacks.has(index - 2)) stacks.delete(index - 2);
|
|
289
283
|
if (!stacks.has(index) && !curRequestController.signal.aborted) {
|
|
290
|
-
const stack = withResolvers<IteratorResult<any>>()
|
|
291
|
-
stacks.set(index, { ...stack, done: false })
|
|
292
|
-
return stack.promise
|
|
284
|
+
const stack = withResolvers<IteratorResult<any>>();
|
|
285
|
+
stacks.set(index, { ...stack, done: false });
|
|
286
|
+
return stack.promise;
|
|
293
287
|
}
|
|
294
288
|
if (!stacks.has(index) && curRequestController.signal.aborted) {
|
|
295
|
-
const stack = withResolvers<IteratorResult<any>>()
|
|
296
|
-
stack.resolve({ done: true, value: undefined })
|
|
297
|
-
return stack.promise
|
|
289
|
+
const stack = withResolvers<IteratorResult<any>>();
|
|
290
|
+
stack.resolve({ done: true, value: undefined });
|
|
291
|
+
return stack.promise;
|
|
298
292
|
}
|
|
299
|
-
return stacks.get(index)!.promise
|
|
293
|
+
return stacks.get(index)!.promise;
|
|
300
294
|
},
|
|
301
295
|
async return(): Promise<IteratorResult<void>> {
|
|
302
|
-
if (!curRequestController.signal.aborted) curRequestController.abort()
|
|
303
|
-
for (const [_, iterator] of stacks) iterator.resolve({ done: true, value: undefined })
|
|
304
|
-
return { done: true, value: undefined }
|
|
296
|
+
if (!curRequestController.signal.aborted) curRequestController.abort();
|
|
297
|
+
for (const [_, iterator] of stacks) iterator.resolve({ done: true, value: undefined });
|
|
298
|
+
return { done: true, value: undefined };
|
|
305
299
|
},
|
|
306
300
|
async throw(err: any): Promise<IteratorResult<void>> {
|
|
307
301
|
streamResult = {
|
|
308
302
|
success: false,
|
|
309
|
-
executeId: streamResult?.executeId ??
|
|
303
|
+
executeId: streamResult?.executeId ?? "",
|
|
310
304
|
fail: {
|
|
311
|
-
code:
|
|
312
|
-
message:
|
|
305
|
+
code: "NETWORK_ERROR",
|
|
306
|
+
message: "Network Error",
|
|
313
307
|
fromClient: true,
|
|
314
308
|
data: err,
|
|
315
309
|
},
|
|
316
|
-
}
|
|
310
|
+
};
|
|
317
311
|
for (const [_index, stack] of stacks) {
|
|
318
|
-
if (stack.done) continue
|
|
319
|
-
stack.done = true
|
|
320
|
-
stack.resolve({ done: true, value: undefined })
|
|
312
|
+
if (stack.done) continue;
|
|
313
|
+
stack.done = true;
|
|
314
|
+
stack.resolve({ done: true, value: undefined });
|
|
321
315
|
}
|
|
322
|
-
if (!curRequestController.signal.aborted) curRequestController.abort()
|
|
323
|
-
for (const [_, iterator] of stacks) iterator.resolve({ done: true, value: undefined })
|
|
324
|
-
return { done: true, value: undefined }
|
|
316
|
+
if (!curRequestController.signal.aborted) curRequestController.abort();
|
|
317
|
+
for (const [_, iterator] of stacks) iterator.resolve({ done: true, value: undefined });
|
|
318
|
+
return { done: true, value: undefined };
|
|
325
319
|
},
|
|
326
320
|
} satisfies AsyncIterator<unknown>),
|
|
327
321
|
[Symbol.asyncIterator]() {
|
|
328
|
-
return this
|
|
322
|
+
return this;
|
|
329
323
|
},
|
|
330
|
-
}
|
|
324
|
+
};
|
|
331
325
|
|
|
332
326
|
try {
|
|
333
|
-
await streamResultFetched.promise
|
|
334
|
-
return [null, iterator, { executeId: streamResult.executeId }] as any
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
return error as any
|
|
327
|
+
await streamResultFetched.promise;
|
|
328
|
+
return [null, iterator, { executeId: streamResult.executeId }] as any;
|
|
329
|
+
} catch (error) {
|
|
330
|
+
return error as any;
|
|
338
331
|
}
|
|
339
332
|
}
|
|
340
333
|
},
|
|
341
334
|
cookbook: {
|
|
342
335
|
subscribe: async (baseUrl: string) => {
|
|
343
336
|
const headers = {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
}
|
|
347
|
-
const params = {}
|
|
337
|
+
"Content-Type": "application/json",
|
|
338
|
+
Accept: "text/event-stream",
|
|
339
|
+
};
|
|
340
|
+
const params = {};
|
|
348
341
|
|
|
349
|
-
const body = TSON.stringify(params) ??
|
|
342
|
+
const body = TSON.stringify(params) ?? "";
|
|
350
343
|
const stacks: Map<
|
|
351
344
|
number,
|
|
352
345
|
{
|
|
353
|
-
done: boolean
|
|
354
|
-
promise: Promise<IteratorResult<any
|
|
355
|
-
resolve: (value: IteratorResult<any>) => void
|
|
356
|
-
reject: (reason: any) => void
|
|
346
|
+
done: boolean;
|
|
347
|
+
promise: Promise<IteratorResult<any>>;
|
|
348
|
+
resolve: (value: IteratorResult<any>) => void;
|
|
349
|
+
reject: (reason: any) => void;
|
|
357
350
|
}
|
|
358
|
-
> = new Map()
|
|
359
|
-
let stacksIndex
|
|
360
|
-
let iteratorIndex
|
|
351
|
+
> = new Map();
|
|
352
|
+
let stacksIndex = 0;
|
|
353
|
+
let iteratorIndex = 0;
|
|
361
354
|
|
|
362
355
|
const onmessage = (event: EventSourceMessage) => {
|
|
363
|
-
const index = ++stacksIndex
|
|
356
|
+
const index = ++stacksIndex;
|
|
364
357
|
if (stacks.has(index)) {
|
|
365
|
-
const stack = stacks.get(index)
|
|
366
|
-
stack!.resolve({ done: false, value: TSON.parse(event.data) })
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
stacks.set(index, { ...stack, done: false })
|
|
358
|
+
const stack = stacks.get(index);
|
|
359
|
+
stack!.resolve({ done: false, value: TSON.parse(event.data) });
|
|
360
|
+
} else {
|
|
361
|
+
const stack = withResolvers<IteratorResult<any>>();
|
|
362
|
+
stack.resolve({ done: false, value: TSON.parse(event.data) });
|
|
363
|
+
stacks.set(index, { ...stack, done: false });
|
|
372
364
|
}
|
|
373
|
-
}
|
|
365
|
+
};
|
|
374
366
|
|
|
375
|
-
let curRequestController: AbortController
|
|
367
|
+
let curRequestController: AbortController;
|
|
376
368
|
|
|
377
369
|
async function create() {
|
|
378
|
-
curRequestController = new $abort()
|
|
379
|
-
curRequestController.signal.addEventListener(
|
|
380
|
-
iterator.return()
|
|
381
|
-
})
|
|
370
|
+
curRequestController = new $abort();
|
|
371
|
+
curRequestController.signal.addEventListener("abort", () => {
|
|
372
|
+
iterator.return();
|
|
373
|
+
});
|
|
382
374
|
try {
|
|
383
375
|
const response = await $fetch(`${baseUrl}/$subscribe`, {
|
|
384
|
-
method:
|
|
376
|
+
method: "POST",
|
|
385
377
|
headers,
|
|
386
378
|
body,
|
|
387
379
|
signal: curRequestController.signal,
|
|
388
|
-
})
|
|
380
|
+
});
|
|
389
381
|
|
|
390
|
-
const contentType = response.headers.get(
|
|
391
|
-
if (!contentType?.startsWith(
|
|
392
|
-
throw new Error(`Expected content-type to be ${
|
|
382
|
+
const contentType = response.headers.get("Content-Type");
|
|
383
|
+
if (!contentType?.startsWith("text/event-stream")) {
|
|
384
|
+
throw new Error(`Expected content-type to be ${"text/event-stream"}, Actual: ${contentType}`);
|
|
393
385
|
}
|
|
394
386
|
|
|
395
|
-
await getBytes(response.body!, getLines(getMessages(onmessage)))
|
|
387
|
+
await getBytes(response.body!, getLines(getMessages(onmessage)));
|
|
396
388
|
|
|
397
|
-
await iterator.return()
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
await iterator.throw(err)
|
|
389
|
+
await iterator.return();
|
|
390
|
+
} catch (err) {
|
|
391
|
+
if (!curRequestController.signal.aborted) curRequestController.abort();
|
|
392
|
+
await iterator.throw(err);
|
|
402
393
|
}
|
|
403
394
|
}
|
|
404
395
|
|
|
405
|
-
void create()
|
|
396
|
+
void create();
|
|
406
397
|
|
|
407
398
|
const iterator = {
|
|
408
399
|
...({
|
|
409
400
|
next(): Promise<IteratorResult<unknown>> {
|
|
410
|
-
const index = ++iteratorIndex
|
|
411
|
-
if (stacks.has(index - 2)) stacks.delete(index - 2)
|
|
401
|
+
const index = ++iteratorIndex;
|
|
402
|
+
if (stacks.has(index - 2)) stacks.delete(index - 2);
|
|
412
403
|
if (!stacks.has(index) && !curRequestController.signal.aborted) {
|
|
413
|
-
const stack = withResolvers<IteratorResult<any>>()
|
|
414
|
-
stacks.set(index, { ...stack, done: false })
|
|
415
|
-
return stack.promise
|
|
404
|
+
const stack = withResolvers<IteratorResult<any>>();
|
|
405
|
+
stacks.set(index, { ...stack, done: false });
|
|
406
|
+
return stack.promise;
|
|
416
407
|
}
|
|
417
408
|
if (!stacks.has(index) && curRequestController.signal.aborted) {
|
|
418
|
-
const stack = withResolvers<IteratorResult<any>>()
|
|
419
|
-
stack.resolve({ done: true, value: undefined })
|
|
420
|
-
return stack.promise
|
|
409
|
+
const stack = withResolvers<IteratorResult<any>>();
|
|
410
|
+
stack.resolve({ done: true, value: undefined });
|
|
411
|
+
return stack.promise;
|
|
421
412
|
}
|
|
422
|
-
return stacks.get(index)!.promise
|
|
413
|
+
return stacks.get(index)!.promise;
|
|
423
414
|
},
|
|
424
415
|
async return(): Promise<IteratorResult<void>> {
|
|
425
|
-
if (!curRequestController.signal.aborted) curRequestController.abort()
|
|
426
|
-
for (const [_, iterator] of stacks) iterator.resolve({ done: true, value: undefined })
|
|
427
|
-
return { done: true, value: undefined }
|
|
416
|
+
if (!curRequestController.signal.aborted) curRequestController.abort();
|
|
417
|
+
for (const [_, iterator] of stacks) iterator.resolve({ done: true, value: undefined });
|
|
418
|
+
return { done: true, value: undefined };
|
|
428
419
|
},
|
|
429
420
|
async throw(err: any): Promise<IteratorResult<void>> {
|
|
430
|
-
if (!curRequestController.signal.aborted) curRequestController.abort()
|
|
431
|
-
for (const [_, iterator] of stacks) iterator.resolve({ done: true, value: undefined })
|
|
432
|
-
return { done: true, value: undefined }
|
|
421
|
+
if (!curRequestController.signal.aborted) curRequestController.abort();
|
|
422
|
+
for (const [_, iterator] of stacks) iterator.resolve({ done: true, value: undefined });
|
|
423
|
+
return { done: true, value: undefined };
|
|
433
424
|
},
|
|
434
425
|
} satisfies AsyncIterator<unknown>),
|
|
435
426
|
[Symbol.asyncIterator]() {
|
|
436
|
-
return this
|
|
427
|
+
return this;
|
|
437
428
|
},
|
|
438
|
-
}
|
|
429
|
+
};
|
|
439
430
|
|
|
440
431
|
try {
|
|
441
|
-
return iterator
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
return error as any
|
|
432
|
+
return iterator;
|
|
433
|
+
} catch (error) {
|
|
434
|
+
return error as any;
|
|
445
435
|
}
|
|
446
436
|
},
|
|
447
437
|
},
|
|
448
438
|
async ping(options?: { timeout?: number }): Promise<Ping> {
|
|
439
|
+
// biome-ignore lint/suspicious/noAsyncPromiseExecutor: <explanation>
|
|
449
440
|
return await new Promise<Ping>(async (resolve) => {
|
|
450
|
-
const url = `${await baseUrl}/generate_204
|
|
451
|
-
const timeout = stargateOptions?.timeout ?? options?.timeout ?? 6000
|
|
452
|
-
const startsTime = Date.now()
|
|
441
|
+
const url = `${await baseUrl}/generate_204`;
|
|
442
|
+
const timeout = stargateOptions?.timeout ?? options?.timeout ?? 6000;
|
|
443
|
+
const startsTime = Date.now();
|
|
453
444
|
const timer = setTimeout(() => {
|
|
454
|
-
const endsTime = Date.now()
|
|
455
|
-
resolve([{ connect: false, delay: endsTime - startsTime, error: { REQUEST_TIMEOUT: { timeout, message: `Execute timeout after ${timeout}ms.` } } }, null])
|
|
456
|
-
}, timeout)
|
|
445
|
+
const endsTime = Date.now();
|
|
446
|
+
resolve([{ connect: false, delay: endsTime - startsTime, error: { REQUEST_TIMEOUT: { timeout, message: `Execute timeout after ${timeout}ms.` } } }, null]);
|
|
447
|
+
}, timeout);
|
|
457
448
|
|
|
458
449
|
try {
|
|
459
|
-
const response = await await $fetch(url, { method:
|
|
460
|
-
const endsTime = Date.now()
|
|
461
|
-
clearTimeout(timer)
|
|
450
|
+
const response = await await $fetch(url, { method: "HEAD" });
|
|
451
|
+
const endsTime = Date.now();
|
|
452
|
+
clearTimeout(timer);
|
|
462
453
|
if (response.status !== 204) {
|
|
463
|
-
resolve([{ connect: false, delay: endsTime - startsTime, error: { REQUEST_FAIL: { response, status: response.status, message:
|
|
454
|
+
resolve([{ connect: false, delay: endsTime - startsTime, error: { REQUEST_FAIL: { response, status: response.status, message: "Status code not 204" } } }, null]);
|
|
464
455
|
}
|
|
465
456
|
|
|
466
|
-
resolve([null, { connect: true, delay: endsTime - startsTime, serverTimestamp: Number(response.headers.get(
|
|
457
|
+
resolve([null, { connect: true, delay: endsTime - startsTime, serverTimestamp: Number(response.headers.get("Content-Type")!.substring(17)) }]);
|
|
458
|
+
} catch (error: any) {
|
|
459
|
+
const endsTime = Date.now();
|
|
460
|
+
return [{ connect: false, delay: endsTime - startsTime, error }, null];
|
|
467
461
|
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
462
|
+
});
|
|
463
|
+
},
|
|
464
|
+
async requestRaw<
|
|
465
|
+
T,
|
|
466
|
+
Options extends {
|
|
467
|
+
method: string;
|
|
468
|
+
type?: "action" | "stream";
|
|
469
|
+
params?: Record<any, any> | string;
|
|
470
|
+
headers?: Record<string, string>;
|
|
471
|
+
timeout?: number;
|
|
472
|
+
},
|
|
473
|
+
>(
|
|
474
|
+
path: string,
|
|
475
|
+
options: Options,
|
|
476
|
+
): Promise<
|
|
477
|
+
Options["type"] extends "stream"
|
|
478
|
+
? // stream
|
|
479
|
+
[Partial<Generated["rejectCode"]>, null, ExecuteResultsOption] | [null, AsyncGenerator<[Partial<Generated["rejectCode"]>, null] | [null, T], ExecuteResultsOption>]
|
|
480
|
+
: // action
|
|
481
|
+
[Partial<Generated["rejectCode"]>, null, ExecuteResultsOption] | [null, T, ExecuteResultsOption]
|
|
482
|
+
> {
|
|
483
|
+
if (options.headers === undefined) options.headers = {};
|
|
484
|
+
|
|
485
|
+
const url = path;
|
|
486
|
+
|
|
487
|
+
if (options.type !== "stream") {
|
|
488
|
+
// action
|
|
489
|
+
if (options.headers.Accept === undefined) options.headers.Accept = "application/json";
|
|
490
|
+
if (options.headers["Content-Type"] === undefined) options.headers["Content-Type"] = "application/json";
|
|
491
|
+
let result: { value: Record<any, any> };
|
|
492
|
+
|
|
493
|
+
try {
|
|
494
|
+
const body = TSON.stringify(options.params) ?? "";
|
|
495
|
+
// biome-ignore lint/suspicious/noAsyncPromiseExecutor: <explanation>
|
|
496
|
+
const response = await new Promise<string>(async (resolve, reject) => {
|
|
497
|
+
const timeout = options?.timeout ?? options?.timeout ?? 6000;
|
|
498
|
+
const timer = setTimeout(() => {
|
|
499
|
+
reject([{ REQUEST_TIMEOUT: { timeout, message: `Execute timeout after ${timeout}ms.` } }, null]);
|
|
500
|
+
}, timeout);
|
|
501
|
+
|
|
502
|
+
try {
|
|
503
|
+
const value = await (await $fetch(url, { method: "POST", body, headers: options.headers })).text();
|
|
504
|
+
clearTimeout(timer);
|
|
505
|
+
resolve(value);
|
|
506
|
+
} catch (error) {
|
|
507
|
+
reject(error);
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
result = { value: TSON.parse(response) };
|
|
511
|
+
} catch (error: any) {
|
|
512
|
+
if (error?.[0]?.REQUEST_TIMEOUT) {
|
|
513
|
+
return error;
|
|
514
|
+
}
|
|
515
|
+
const errorPined = { REQUEST_FAIL: error };
|
|
516
|
+
return [errorPined, null, { executeId: "unknown" }];
|
|
517
|
+
}
|
|
518
|
+
if (result.value.success !== true) {
|
|
519
|
+
const error: any = {};
|
|
520
|
+
error[result.value.code] = result.value.reject ?? null;
|
|
521
|
+
return [error, null, { executeId: "unknown" }];
|
|
471
522
|
}
|
|
472
|
-
|
|
523
|
+
|
|
524
|
+
return [null, result.value.data, { executeId: result.value.executeId }] as any;
|
|
525
|
+
} else {
|
|
526
|
+
// stream
|
|
527
|
+
if (options.headers.Accept === undefined) options.headers.Accept = "text/event-stream";
|
|
528
|
+
if (options.headers["Content-Type"] === undefined) options.headers["Content-Type"] = "application/json";
|
|
529
|
+
|
|
530
|
+
const stacks: Map<
|
|
531
|
+
number,
|
|
532
|
+
{
|
|
533
|
+
done: boolean;
|
|
534
|
+
promise: Promise<IteratorResult<any>>;
|
|
535
|
+
resolve: (value: IteratorResult<any>) => void;
|
|
536
|
+
reject: (reason: any) => void;
|
|
537
|
+
}
|
|
538
|
+
> = new Map();
|
|
539
|
+
let stacksIndex = 0;
|
|
540
|
+
let iteratorIndex = 0;
|
|
541
|
+
let streamResult: any;
|
|
542
|
+
const streamResultFetched = withResolvers<undefined>();
|
|
543
|
+
|
|
544
|
+
const timeout = stargateOptions?.timeout ?? options?.timeout ?? 6000;
|
|
545
|
+
const timer = setTimeout(() => {
|
|
546
|
+
streamResultFetched.reject([{ REQUEST_TIMEOUT: { timeout, message: `Execute timeout after ${timeout}ms.` } }, null, { executeId: "unknown" }]);
|
|
547
|
+
}, timeout);
|
|
548
|
+
|
|
549
|
+
const onmessage = (event: EventSourceMessage) => {
|
|
550
|
+
if (event.data.startsWith("@")) {
|
|
551
|
+
try {
|
|
552
|
+
streamResult = TSON.parse(event.data.slice(1));
|
|
553
|
+
streamResultFetched.resolve(undefined);
|
|
554
|
+
clearTimeout(timer);
|
|
555
|
+
} catch (error) {
|
|
556
|
+
streamResultFetched.reject([{ REQUEST_FAIL: error }, null, { executeId: "unknown" }]);
|
|
557
|
+
clearTimeout(timer);
|
|
558
|
+
}
|
|
559
|
+
} else {
|
|
560
|
+
const index = ++stacksIndex;
|
|
561
|
+
if (stacks.has(index)) {
|
|
562
|
+
const stack = stacks.get(index);
|
|
563
|
+
stack!.done = true;
|
|
564
|
+
stack!.resolve({ done: false, value: TSON.parse(event.data) });
|
|
565
|
+
} else {
|
|
566
|
+
const stack = withResolvers<IteratorResult<any>>();
|
|
567
|
+
stack.resolve({ done: false, value: TSON.parse(event.data) });
|
|
568
|
+
stacks.set(index, { ...stack, done: false });
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
|
|
573
|
+
let curRequestController: AbortController;
|
|
574
|
+
|
|
575
|
+
async function create() {
|
|
576
|
+
curRequestController = new $abort();
|
|
577
|
+
curRequestController.signal.addEventListener("abort", () => {
|
|
578
|
+
iterator.return();
|
|
579
|
+
});
|
|
580
|
+
try {
|
|
581
|
+
const body = TSON.stringify(options!.params) ?? "";
|
|
582
|
+
const response = await $fetch(url, {
|
|
583
|
+
method: "POST",
|
|
584
|
+
headers: options!.headers,
|
|
585
|
+
body,
|
|
586
|
+
signal: curRequestController.signal,
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
const contentType = response.headers.get("Content-Type");
|
|
590
|
+
if (!contentType?.startsWith("text/event-stream")) {
|
|
591
|
+
throw new Error(`Expected content-type to be ${"text/event-stream"}, Actual: ${contentType}`);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
await getBytes(response.body!, getLines(getMessages(onmessage)));
|
|
595
|
+
|
|
596
|
+
await iterator.return();
|
|
597
|
+
} catch (err) {
|
|
598
|
+
if (!curRequestController.signal.aborted) curRequestController.abort();
|
|
599
|
+
const error = { REQUEST_FAIL: err };
|
|
600
|
+
await iterator.throw(err);
|
|
601
|
+
streamResultFetched.reject([error, null, { executeId: "unknown" }]);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
void create();
|
|
606
|
+
|
|
607
|
+
const iterator = {
|
|
608
|
+
...({
|
|
609
|
+
next(): Promise<IteratorResult<unknown>> {
|
|
610
|
+
const index = ++iteratorIndex;
|
|
611
|
+
if (stacks.has(index - 2)) stacks.delete(index - 2);
|
|
612
|
+
if (!stacks.has(index) && !curRequestController.signal.aborted) {
|
|
613
|
+
const stack = withResolvers<IteratorResult<any>>();
|
|
614
|
+
stacks.set(index, { ...stack, done: false });
|
|
615
|
+
return stack.promise;
|
|
616
|
+
}
|
|
617
|
+
if (!stacks.has(index) && curRequestController.signal.aborted) {
|
|
618
|
+
const stack = withResolvers<IteratorResult<any>>();
|
|
619
|
+
stack.resolve({ done: true, value: undefined });
|
|
620
|
+
return stack.promise;
|
|
621
|
+
}
|
|
622
|
+
return stacks.get(index)!.promise;
|
|
623
|
+
},
|
|
624
|
+
async return(): Promise<IteratorResult<void>> {
|
|
625
|
+
if (!curRequestController.signal.aborted) curRequestController.abort();
|
|
626
|
+
for (const [_, iterator] of stacks) iterator.resolve({ done: true, value: undefined });
|
|
627
|
+
return { done: true, value: undefined };
|
|
628
|
+
},
|
|
629
|
+
async throw(err: any): Promise<IteratorResult<void>> {
|
|
630
|
+
streamResult = {
|
|
631
|
+
success: false,
|
|
632
|
+
executeId: streamResult?.executeId ?? "",
|
|
633
|
+
fail: {
|
|
634
|
+
code: "NETWORK_ERROR",
|
|
635
|
+
message: "Network Error",
|
|
636
|
+
fromClient: true,
|
|
637
|
+
data: err,
|
|
638
|
+
},
|
|
639
|
+
};
|
|
640
|
+
for (const [_index, stack] of stacks) {
|
|
641
|
+
if (stack.done) continue;
|
|
642
|
+
stack.done = true;
|
|
643
|
+
stack.resolve({ done: true, value: undefined });
|
|
644
|
+
}
|
|
645
|
+
if (!curRequestController.signal.aborted) curRequestController.abort();
|
|
646
|
+
for (const [_, iterator] of stacks) iterator.resolve({ done: true, value: undefined });
|
|
647
|
+
return { done: true, value: undefined };
|
|
648
|
+
},
|
|
649
|
+
} satisfies AsyncIterator<unknown>),
|
|
650
|
+
[Symbol.asyncIterator]() {
|
|
651
|
+
return this;
|
|
652
|
+
},
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
try {
|
|
656
|
+
await streamResultFetched.promise;
|
|
657
|
+
return [null, iterator, { executeId: streamResult.executeId }] as any;
|
|
658
|
+
} catch (error) {
|
|
659
|
+
return error as any;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
473
662
|
},
|
|
474
|
-
}
|
|
663
|
+
};
|
|
475
664
|
|
|
476
|
-
return stargate
|
|
665
|
+
return stargate;
|
|
477
666
|
}
|
|
478
667
|
|
|
479
668
|
export interface ExecuteStreamOptions {
|
|
480
|
-
headers?: Record<string, string
|
|
481
|
-
timeout?: number
|
|
669
|
+
headers?: Record<string, string>;
|
|
670
|
+
timeout?: number;
|
|
482
671
|
}
|
|
483
672
|
|
|
484
673
|
export interface ApiSchemaExtend {
|
|
485
674
|
apiValidator: {
|
|
486
|
-
generatedAt: number
|
|
487
|
-
validate: Record<any, any
|
|
488
|
-
}
|
|
489
|
-
apiMethodsSchema: Record<any, any
|
|
490
|
-
apiMethodsTypeSchema: Record<any, any
|
|
491
|
-
apiTestsSchema: Record<any, any
|
|
675
|
+
generatedAt: number;
|
|
676
|
+
validate: Record<any, any>;
|
|
677
|
+
};
|
|
678
|
+
apiMethodsSchema: Record<any, any>;
|
|
679
|
+
apiMethodsTypeSchema: Record<any, any>;
|
|
680
|
+
apiTestsSchema: Record<any, any>;
|
|
492
681
|
}
|
|
493
682
|
|
|
494
|
-
export type FailCodeExtend = Record<any, (...args: Array<any>) => any
|
|
683
|
+
export type FailCodeExtend = Record<any, (...args: Array<any>) => any>;
|
|
495
684
|
|
|
496
|
-
export type BootstrapMiddleware = (data: { storage: ClientStorage }) => Promise<void> | void
|
|
497
|
-
export type BeforeExecuteMiddleware = (data: { path: string
|
|
498
|
-
export type AfterExecuteMiddleware = (data: { path: string
|
|
685
|
+
export type BootstrapMiddleware = (data: { storage: ClientStorage }) => Promise<void> | void;
|
|
686
|
+
export type BeforeExecuteMiddleware = (data: { path: string; params: any; headers: Record<string, string>; storage: ClientStorage }) => Promise<void> | void;
|
|
687
|
+
export type AfterExecuteMiddleware = (data: { path: string; result: { value: any }; storage: ClientStorage }) => Promise<void> | void;
|
|
499
688
|
|
|
500
689
|
export interface MiddlewareOptions {
|
|
501
|
-
bootstrap?: BootstrapMiddleware
|
|
502
|
-
beforeExecute?: BeforeExecuteMiddleware
|
|
503
|
-
afterExecute?: AfterExecuteMiddleware
|
|
690
|
+
bootstrap?: BootstrapMiddleware;
|
|
691
|
+
beforeExecute?: BeforeExecuteMiddleware;
|
|
692
|
+
afterExecute?: AfterExecuteMiddleware;
|
|
504
693
|
}
|
|
505
694
|
|
|
506
695
|
export interface ClientStorage {
|
|
507
|
-
getItem: (key: string) => Promise<string | null
|
|
508
|
-
setItem: (key: string, value: string) => Promise<void
|
|
509
|
-
removeItem: (key: string) => Promise<void
|
|
696
|
+
getItem: (key: string) => Promise<string | null>;
|
|
697
|
+
setItem: (key: string, value: string) => Promise<void>;
|
|
698
|
+
removeItem: (key: string) => Promise<void>;
|
|
510
699
|
}
|
|
511
700
|
|
|
512
701
|
export interface ExecuteResultSuccess<Result> {
|
|
513
|
-
executeId: string
|
|
514
|
-
success: true
|
|
515
|
-
data: Result
|
|
702
|
+
executeId: string;
|
|
703
|
+
success: true;
|
|
704
|
+
data: Result;
|
|
516
705
|
}
|
|
517
706
|
|
|
518
|
-
export type GeneratorGeneric<T> = T extends AsyncGenerator<infer I> ? I : never
|
|
707
|
+
export type GeneratorGeneric<T> = T extends AsyncGenerator<infer I> ? I : never;
|
|
519
708
|
|
|
520
|
-
export type FlattenKeys<T
|
|
709
|
+
export type FlattenKeys<T, Prefix extends string = ""> = {
|
|
521
710
|
[K in keyof T]: T[K] extends object ? FlattenKeys<T[K], `${Prefix}${Exclude<K, symbol>}.`> : `$input.${Prefix}${Exclude<K, symbol>}`;
|
|
522
|
-
}[keyof T]
|
|
711
|
+
}[keyof T];
|
|
523
712
|
|
|
524
713
|
// *** This part of the code is based on `@microsoft/fetch-event-source` rewrite, thanks to the work of Microsoft *** //
|
|
525
714
|
// *** https://github.com/Azure/fetch-event-source/blob/main/src/parse.ts *** //
|
|
@@ -529,7 +718,7 @@ export type FlattenKeys<T extends any, Prefix extends string = ''> = {
|
|
|
529
718
|
* https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format
|
|
530
719
|
*/
|
|
531
720
|
export interface EventSourceMessage {
|
|
532
|
-
data: string
|
|
721
|
+
data: string;
|
|
533
722
|
}
|
|
534
723
|
|
|
535
724
|
/**
|
|
@@ -539,20 +728,14 @@ export interface EventSourceMessage {
|
|
|
539
728
|
* @returns {Promise<void>} A promise that will be resolved when the stream closes.
|
|
540
729
|
*/
|
|
541
730
|
export async function getBytes(stream: ReadableStream<Uint8Array>, onChunk: (arr: Uint8Array) => void) {
|
|
542
|
-
const reader = stream.getReader()
|
|
543
|
-
let result: ReadableStreamReadResult<Uint8Array
|
|
731
|
+
const reader = stream.getReader();
|
|
732
|
+
let result: ReadableStreamReadResult<Uint8Array>;
|
|
733
|
+
// biome-ignore lint/suspicious/noAssignInExpressions: <explanation>
|
|
544
734
|
while (!(result = await reader.read()).done) {
|
|
545
|
-
onChunk(result.value)
|
|
735
|
+
onChunk(result.value);
|
|
546
736
|
}
|
|
547
737
|
}
|
|
548
738
|
|
|
549
|
-
const enum ControlChars {
|
|
550
|
-
NewLine = 10,
|
|
551
|
-
CarriageReturn = 13,
|
|
552
|
-
Space = 32,
|
|
553
|
-
Colon = 58,
|
|
554
|
-
}
|
|
555
|
-
|
|
556
739
|
/**
|
|
557
740
|
* Parses arbitary byte chunks into EventSource line buffers.
|
|
558
741
|
* Each line should be of the format "field: value" and ends with \r, \n, or \r\n.
|
|
@@ -560,75 +743,73 @@ const enum ControlChars {
|
|
|
560
743
|
* @returns A function that should be called for each incoming byte chunk.
|
|
561
744
|
*/
|
|
562
745
|
export function getLines(onLine: (line: Uint8Array, fieldLength: number) => void) {
|
|
563
|
-
let buffer: Uint8Array | undefined
|
|
564
|
-
let position: number // current read position
|
|
565
|
-
let fieldLength: number // length of the `field` portion of the line
|
|
566
|
-
let discardTrailingNewline = false
|
|
746
|
+
let buffer: Uint8Array | undefined;
|
|
747
|
+
let position: number; // current read position
|
|
748
|
+
let fieldLength: number; // length of the `field` portion of the line
|
|
749
|
+
let discardTrailingNewline = false;
|
|
567
750
|
|
|
568
751
|
// return a function that can process each incoming byte chunk:
|
|
569
752
|
return function onChunk(arr: Uint8Array) {
|
|
570
753
|
if (buffer === undefined) {
|
|
571
|
-
buffer = arr
|
|
572
|
-
position = 0
|
|
573
|
-
fieldLength = -1
|
|
574
|
-
}
|
|
575
|
-
else {
|
|
754
|
+
buffer = arr;
|
|
755
|
+
position = 0;
|
|
756
|
+
fieldLength = -1;
|
|
757
|
+
} else {
|
|
576
758
|
// we're still parsing the old line. Append the new bytes into buffer:
|
|
577
|
-
buffer = concat(buffer, arr)
|
|
759
|
+
buffer = concat(buffer, arr);
|
|
578
760
|
}
|
|
579
761
|
|
|
580
|
-
const bufLength = buffer.length
|
|
581
|
-
let lineStart = 0 // index where the current line starts
|
|
762
|
+
const bufLength = buffer.length;
|
|
763
|
+
let lineStart = 0; // index where the current line starts
|
|
582
764
|
while (position < bufLength) {
|
|
583
765
|
if (discardTrailingNewline) {
|
|
584
|
-
if (buffer[position] ===
|
|
585
|
-
lineStart = ++position // skip to next char
|
|
766
|
+
if (buffer[position] === 10) {
|
|
767
|
+
lineStart = ++position; // skip to next char
|
|
586
768
|
}
|
|
587
769
|
|
|
588
|
-
discardTrailingNewline = false
|
|
770
|
+
discardTrailingNewline = false;
|
|
589
771
|
}
|
|
590
772
|
|
|
591
773
|
// start looking forward till the end of line:
|
|
592
|
-
let lineEnd = -1 // index of the \r or \n char
|
|
774
|
+
let lineEnd = -1; // index of the \r or \n char
|
|
593
775
|
for (; position < bufLength && lineEnd === -1; ++position) {
|
|
594
776
|
switch (buffer[position]) {
|
|
595
|
-
case
|
|
777
|
+
case 58:
|
|
596
778
|
if (fieldLength === -1) {
|
|
597
779
|
// first colon in line
|
|
598
|
-
fieldLength = position - lineStart
|
|
780
|
+
fieldLength = position - lineStart;
|
|
599
781
|
}
|
|
600
|
-
break
|
|
601
|
-
//
|
|
602
|
-
case
|
|
603
|
-
discardTrailingNewline = true
|
|
604
|
-
case
|
|
605
|
-
lineEnd = position
|
|
606
|
-
break
|
|
782
|
+
break;
|
|
783
|
+
// biome-ignore lint/suspicious/noFallthroughSwitchClause: <explanation>
|
|
784
|
+
case 13:
|
|
785
|
+
discardTrailingNewline = true;
|
|
786
|
+
case 10:
|
|
787
|
+
lineEnd = position;
|
|
788
|
+
break;
|
|
607
789
|
}
|
|
608
790
|
}
|
|
609
791
|
|
|
610
792
|
if (lineEnd === -1) {
|
|
611
793
|
// We reached the end of the buffer but the line hasn't ended.
|
|
612
794
|
// Wait for the next arr and then continue parsing:
|
|
613
|
-
break
|
|
795
|
+
break;
|
|
614
796
|
}
|
|
615
797
|
|
|
616
798
|
// we've reached the line end, send it out:
|
|
617
|
-
onLine(buffer.subarray(lineStart, lineEnd), fieldLength)
|
|
618
|
-
lineStart = position // we're now on the next line
|
|
619
|
-
fieldLength = -1
|
|
799
|
+
onLine(buffer.subarray(lineStart, lineEnd), fieldLength);
|
|
800
|
+
lineStart = position; // we're now on the next line
|
|
801
|
+
fieldLength = -1;
|
|
620
802
|
}
|
|
621
803
|
|
|
622
804
|
if (lineStart === bufLength) {
|
|
623
|
-
buffer = undefined // we've finished reading it
|
|
624
|
-
}
|
|
625
|
-
else if (lineStart !== 0) {
|
|
805
|
+
buffer = undefined; // we've finished reading it
|
|
806
|
+
} else if (lineStart !== 0) {
|
|
626
807
|
// Create a new view into buffer beginning at lineStart so we don't
|
|
627
808
|
// need to copy over the previous lines when we get the new arr:
|
|
628
|
-
buffer = buffer.subarray(lineStart)
|
|
629
|
-
position -= lineStart
|
|
809
|
+
buffer = buffer.subarray(lineStart);
|
|
810
|
+
position -= lineStart;
|
|
630
811
|
}
|
|
631
|
-
}
|
|
812
|
+
};
|
|
632
813
|
}
|
|
633
814
|
|
|
634
815
|
/**
|
|
@@ -639,54 +820,53 @@ export function getLines(onLine: (line: Uint8Array, fieldLength: number) => void
|
|
|
639
820
|
* @returns A function that should be called for each incoming line buffer.
|
|
640
821
|
*/
|
|
641
822
|
export function getMessages(onMessage?: (msg: EventSourceMessage) => void) {
|
|
642
|
-
let message = newMessage()
|
|
643
|
-
const decoder = new TextDecoder()
|
|
823
|
+
let message = newMessage();
|
|
824
|
+
const decoder = new TextDecoder();
|
|
644
825
|
|
|
645
826
|
// return a function that can process each incoming line buffer:
|
|
646
827
|
return function onLine(line: Uint8Array, fieldLength: number) {
|
|
647
828
|
if (line.length === 0) {
|
|
648
829
|
// empty line denotes end of message. Trigger the callback and start a new message:
|
|
649
|
-
onMessage?.(message)
|
|
650
|
-
message = newMessage()
|
|
651
|
-
}
|
|
652
|
-
else if (fieldLength > 0) {
|
|
830
|
+
onMessage?.(message);
|
|
831
|
+
message = newMessage();
|
|
832
|
+
} else if (fieldLength > 0) {
|
|
653
833
|
// exclude comments and lines with no values
|
|
654
834
|
// line is of format "<field>:<value>" or "<field>: <value>"
|
|
655
835
|
// https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation
|
|
656
|
-
const field = decoder.decode(line.subarray(0, fieldLength))
|
|
657
|
-
const valueOffset = fieldLength + (line[fieldLength + 1] ===
|
|
658
|
-
const value = decoder.decode(line.subarray(valueOffset))
|
|
836
|
+
const field = decoder.decode(line.subarray(0, fieldLength));
|
|
837
|
+
const valueOffset = fieldLength + (line[fieldLength + 1] === 32 ? 2 : 1);
|
|
838
|
+
const value = decoder.decode(line.subarray(valueOffset));
|
|
659
839
|
|
|
660
840
|
switch (field) {
|
|
661
|
-
case
|
|
841
|
+
case "data":
|
|
662
842
|
// if this message already has data, append the new value to the old.
|
|
663
843
|
// otherwise, just set to the new value:
|
|
664
|
-
message.data = message.data ? `${message.data}\n${value}` : value // otherwise,
|
|
665
|
-
break
|
|
844
|
+
message.data = message.data ? `${message.data}\n${value}` : value; // otherwise,
|
|
845
|
+
break;
|
|
666
846
|
}
|
|
667
847
|
}
|
|
668
|
-
}
|
|
848
|
+
};
|
|
669
849
|
}
|
|
670
850
|
|
|
671
851
|
function concat(a: Uint8Array, b: Uint8Array) {
|
|
672
|
-
const res = new Uint8Array(a.length + b.length)
|
|
673
|
-
res.set(a)
|
|
674
|
-
res.set(b, a.length)
|
|
675
|
-
return res
|
|
852
|
+
const res = new Uint8Array(a.length + b.length);
|
|
853
|
+
res.set(a);
|
|
854
|
+
res.set(b, a.length);
|
|
855
|
+
return res;
|
|
676
856
|
}
|
|
677
857
|
|
|
678
858
|
function newMessage(): EventSourceMessage {
|
|
679
859
|
return {
|
|
680
|
-
data:
|
|
681
|
-
}
|
|
860
|
+
data: "",
|
|
861
|
+
};
|
|
682
862
|
}
|
|
683
863
|
|
|
684
864
|
export function withResolvers<T = any>(): PromiseWithResolvers<T> {
|
|
685
|
-
let resolve: PromiseWithResolvers<T>[
|
|
686
|
-
let reject: PromiseWithResolvers<T>[
|
|
865
|
+
let resolve: PromiseWithResolvers<T>["resolve"];
|
|
866
|
+
let reject: PromiseWithResolvers<T>["reject"];
|
|
687
867
|
const promise = new Promise<T>((res, rej) => {
|
|
688
|
-
resolve = res
|
|
689
|
-
reject = rej
|
|
690
|
-
})
|
|
691
|
-
return { promise, resolve: resolve!, reject: reject! }
|
|
868
|
+
resolve = res;
|
|
869
|
+
reject = rej;
|
|
870
|
+
});
|
|
871
|
+
return { promise, resolve: resolve!, reject: reject! };
|
|
692
872
|
}
|