@milkio/stargate 1.0.0-alpha.9 → 1.0.0-alpha.91

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