@milkio/stargate 1.0.0-alpha.8 → 1.0.0-alpha.80

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