vovk 3.0.0-draft.5 → 3.0.0-draft.50

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.
Files changed (97) hide show
  1. package/.DS_Store +0 -0
  2. package/.npmignore +2 -1
  3. package/.turbo/turbo-build.log +6 -0
  4. package/.turbo/turbo-ncu.log +9 -0
  5. package/.turbo/turbo-tsc.log +6 -0
  6. package/README.md +1 -111
  7. package/{HttpException.d.ts → dist/HttpException.d.ts} +2 -2
  8. package/{HttpException.js → dist/HttpException.js} +3 -3
  9. package/{StreamResponse.d.ts → dist/StreamJSONResponse.d.ts} +4 -3
  10. package/{StreamResponse.js → dist/StreamJSONResponse.js} +6 -5
  11. package/{Segment.d.ts → dist/VovkApp.d.ts} +3 -3
  12. package/{Segment.js → dist/VovkApp.js} +26 -23
  13. package/dist/client/createRPC.d.ts +4 -0
  14. package/{client/clientizeController.js → dist/client/createRPC.js} +17 -17
  15. package/dist/client/defaultFetcher.d.ts +4 -0
  16. package/{client → dist/client}/defaultFetcher.js +7 -7
  17. package/{client → dist/client}/defaultHandler.d.ts +1 -1
  18. package/dist/client/defaultHandler.js +22 -0
  19. package/dist/client/defaultStreamHandler.d.ts +4 -0
  20. package/{client → dist/client}/defaultStreamHandler.js +5 -5
  21. package/dist/client/index.d.ts +4 -0
  22. package/dist/client/index.js +5 -0
  23. package/dist/client/types.d.ts +100 -0
  24. package/dist/createDecorator.d.ts +4 -0
  25. package/{createDecorator.js → dist/createDecorator.js} +4 -4
  26. package/{createSegment.d.ts → dist/createVovkApp.d.ts} +2 -2
  27. package/{createSegment.js → dist/createVovkApp.js} +25 -25
  28. package/dist/index.d.ts +61 -0
  29. package/dist/index.js +25 -0
  30. package/dist/types.d.ts +157 -0
  31. package/dist/types.js +65 -0
  32. package/dist/utils/generateStaticAPI.d.ts +4 -0
  33. package/{generateStaticAPI.js → dist/utils/generateStaticAPI.js} +3 -3
  34. package/{utils → dist/utils}/getSchema.d.ts +1 -1
  35. package/{utils → dist/utils}/getSchema.js +9 -9
  36. package/dist/utils/reqForm.d.ts +2 -0
  37. package/dist/utils/reqForm.js +13 -0
  38. package/{utils → dist/utils}/reqMeta.d.ts +1 -2
  39. package/{utils → dist/utils}/reqQuery.d.ts +1 -2
  40. package/{utils → dist/utils}/reqQuery.js +3 -3
  41. package/dist/utils/setClientValidatorsForHandler.d.ts +5 -0
  42. package/{utils → dist/utils}/setClientValidatorsForHandler.js +4 -6
  43. package/dist/worker/createWPC.d.ts +2 -0
  44. package/{worker/promisifyWorker.js → dist/worker/createWPC.js} +7 -9
  45. package/dist/worker/index.d.ts +3 -0
  46. package/dist/worker/index.js +7 -0
  47. package/{worker → dist/worker}/types.d.ts +7 -7
  48. package/dist/worker/worker.d.ts +1 -0
  49. package/{worker → dist/worker}/worker.js +2 -3
  50. package/package.json +5 -2
  51. package/src/HttpException.ts +16 -0
  52. package/src/StreamJSONResponse.ts +62 -0
  53. package/src/VovkApp.ts +242 -0
  54. package/src/client/createRPC.ts +133 -0
  55. package/src/client/defaultFetcher.ts +57 -0
  56. package/src/client/defaultHandler.ts +23 -0
  57. package/src/client/defaultStreamHandler.ts +88 -0
  58. package/src/client/index.ts +5 -0
  59. package/src/client/types.ts +115 -0
  60. package/src/createDecorator.ts +60 -0
  61. package/src/createVovkApp.ts +167 -0
  62. package/src/index.ts +66 -0
  63. package/src/types.ts +215 -0
  64. package/src/utils/generateStaticAPI.ts +18 -0
  65. package/src/utils/getSchema.ts +48 -0
  66. package/src/utils/reqForm.ts +16 -0
  67. package/src/utils/reqMeta.ts +16 -0
  68. package/src/utils/reqQuery.ts +26 -0
  69. package/src/utils/setClientValidatorsForHandler.ts +45 -0
  70. package/src/utils/shim.ts +17 -0
  71. package/src/worker/createWPC.ts +156 -0
  72. package/src/worker/index.ts +4 -0
  73. package/src/worker/types.ts +45 -0
  74. package/src/worker/worker.ts +53 -0
  75. package/client/clientizeController.d.ts +0 -4
  76. package/client/defaultFetcher.d.ts +0 -4
  77. package/client/defaultHandler.js +0 -21
  78. package/client/defaultStreamHandler.d.ts +0 -4
  79. package/client/index.d.ts +0 -4
  80. package/client/index.js +0 -5
  81. package/client/types.d.ts +0 -102
  82. package/createDecorator.d.ts +0 -4
  83. package/generateStaticAPI.d.ts +0 -4
  84. package/index.d.ts +0 -60
  85. package/index.js +0 -20
  86. package/types.d.ts +0 -191
  87. package/types.js +0 -65
  88. package/utils/setClientValidatorsForHandler.d.ts +0 -5
  89. package/worker/index.d.ts +0 -3
  90. package/worker/index.js +0 -7
  91. package/worker/promisifyWorker.d.ts +0 -2
  92. package/worker/worker.d.ts +0 -1
  93. /package/{client → dist/client}/types.js +0 -0
  94. /package/{utils → dist/utils}/reqMeta.js +0 -0
  95. /package/{utils → dist/utils}/shim.d.ts +0 -0
  96. /package/{utils → dist/utils}/shim.js +0 -0
  97. /package/{worker → dist/worker}/types.js +0 -0
package/src/types.ts ADDED
@@ -0,0 +1,215 @@
1
+ import type { NextRequest } from 'next/server';
2
+ import type { StreamJSONResponse } from './StreamJSONResponse';
3
+ import { StreamAsyncIterator } from './client/types';
4
+
5
+ export type KnownAny = any; // eslint-disable-line @typescript-eslint/no-explicit-any
6
+
7
+ export type StaticClass = Function; // eslint-disable-line @typescript-eslint/no-unsafe-function-type
8
+
9
+ export type HandlerSchema = {
10
+ path: string;
11
+ httpMethod: HttpMethod;
12
+ validation?: { query?: KnownAny; body?: KnownAny };
13
+ custom?: Record<string, KnownAny>;
14
+ };
15
+
16
+ export type VovkControllerSchema = {
17
+ controllerName: string;
18
+ originalControllerName: string;
19
+ prefix?: string;
20
+ handlers: Record<string, HandlerSchema>;
21
+ };
22
+
23
+ export type VovkWorkerSchema = {
24
+ workerName: string;
25
+ originalWorkerName: string;
26
+ handlers: Record<
27
+ string,
28
+ {
29
+ isGenerator?: true;
30
+ }
31
+ >;
32
+ };
33
+
34
+ export type VovkSchema = {
35
+ emitSchema: boolean;
36
+ segmentName: string;
37
+ workers: Record<string, VovkWorkerSchema>;
38
+ controllers: Record<string, VovkControllerSchema>;
39
+ };
40
+
41
+ export type VovkErrorResponse = {
42
+ cause?: unknown;
43
+ statusCode: HttpStatus;
44
+ message: string;
45
+ isError: true;
46
+ };
47
+
48
+ export type VovkControllerInternal = {
49
+ _controllerName?: VovkControllerSchema['controllerName'];
50
+ _prefix?: VovkControllerSchema['prefix'];
51
+ _handlers: VovkControllerSchema['handlers'];
52
+ _activated?: true;
53
+ _onError?: (err: Error, req: VovkRequest) => void | Promise<void>;
54
+ };
55
+
56
+ export type VovkController = StaticClass &
57
+ VovkControllerInternal & {
58
+ [key: string]: unknown;
59
+ };
60
+
61
+ export type VovkWorker = StaticClass & {
62
+ _handlers: VovkWorkerSchema['handlers'];
63
+ [key: string]: unknown;
64
+ };
65
+
66
+ export type DecoratorOptions = {
67
+ cors?: boolean;
68
+ headers?: Record<string, string>;
69
+ };
70
+
71
+ export type RouteHandler = ((
72
+ req: VovkRequest,
73
+ params: Record<string, string>
74
+ ) => Response | Promise<Response> | Iterable<unknown> | AsyncIterable<unknown>) & {
75
+ _options?: DecoratorOptions;
76
+ };
77
+
78
+ export interface VovkRequest<BODY = undefined, QUERY extends object | undefined = undefined>
79
+ extends Omit<NextRequest, 'json' | 'nextUrl'> {
80
+ json: () => Promise<BODY>;
81
+ nextUrl: Omit<NextRequest['nextUrl'], 'searchParams'> & {
82
+ searchParams: Omit<
83
+ NextRequest['nextUrl']['searchParams'],
84
+ 'get' | 'getAll' | 'entries' | 'forEach' | 'keys' | 'values'
85
+ > & {
86
+ get: <KEY extends keyof QUERY>(key: KEY) => QUERY[KEY] extends readonly (infer ITEM)[] ? ITEM : QUERY[KEY];
87
+ getAll: <KEY extends keyof QUERY>(key: KEY) => QUERY[KEY] extends KnownAny[] ? QUERY[KEY] : QUERY[KEY][];
88
+ entries: () => IterableIterator<[keyof QUERY, QUERY[keyof QUERY]]>;
89
+ forEach: (
90
+ callbackfn: (
91
+ value: QUERY[keyof QUERY],
92
+ key: keyof QUERY,
93
+ searchParams: NextRequest['nextUrl']['searchParams'] // original searchParams
94
+ ) => void
95
+ ) => void;
96
+ keys: () => IterableIterator<keyof QUERY>;
97
+ values: () => IterableIterator<QUERY[keyof QUERY]>;
98
+ // TODO (?) append, delete, set
99
+ };
100
+ };
101
+ vovk: {
102
+ body: () => Promise<BODY>;
103
+ query: () => QUERY;
104
+ meta: <T = Record<KnownAny, KnownAny>>(meta?: T | null) => T;
105
+ form: <T = KnownAny>() => Promise<T>;
106
+ };
107
+ }
108
+
109
+ export type ControllerStaticMethod<
110
+ REQ extends VovkRequest<KnownAny, KnownAny> = VovkRequest<undefined, Record<string, string | string[]>>,
111
+ PARAMS extends { [key: string]: string } = KnownAny,
112
+ > = ((req: REQ, params: PARAMS) => unknown) & {
113
+ _controller?: VovkController;
114
+ };
115
+
116
+ export type VovkControllerBody<T extends (...args: KnownAny) => KnownAny> = Awaited<
117
+ ReturnType<Parameters<T>[0]['vovk']['body']>
118
+ >;
119
+
120
+ export type VovkControllerQuery<T extends (...args: KnownAny) => KnownAny> = ReturnType<
121
+ Parameters<T>[0]['vovk']['query']
122
+ >;
123
+
124
+ export type VovkControllerParams<T extends (...args: KnownAny) => KnownAny> = Parameters<T>[1];
125
+
126
+ export type VovkControllerYieldType<T extends (req: VovkRequest<KnownAny, KnownAny>) => KnownAny> = T extends (
127
+ ...args: KnownAny[]
128
+ ) => AsyncGenerator<infer Y, KnownAny, KnownAny>
129
+ ? Y
130
+ : T extends (...args: KnownAny[]) => Generator<infer Y, KnownAny, KnownAny>
131
+ ? Y
132
+ : T extends (...args: KnownAny[]) => Promise<StreamJSONResponse<infer Y>> | StreamJSONResponse<infer Y>
133
+ ? Y
134
+ : never;
135
+
136
+ export type VovkBody<T extends (...args: KnownAny[]) => unknown> = Parameters<T>[0]['body'];
137
+
138
+ export type VovkQuery<T extends (...args: KnownAny[]) => unknown> = Parameters<T>[0]['query'];
139
+
140
+ export type VovkParams<T extends (...args: KnownAny[]) => unknown> = Parameters<T>[0]['params'];
141
+
142
+ export type VovkYieldType<T extends (...args: KnownAny[]) => unknown> = T extends (
143
+ ...args: KnownAny[]
144
+ ) => Promise<StreamAsyncIterator<infer Y>>
145
+ ? Y
146
+ : never;
147
+
148
+ export type VovkReturnType<T extends (...args: KnownAny) => unknown> = Awaited<ReturnType<T>>;
149
+
150
+ export type StreamAbortMessage = {
151
+ isError: true;
152
+ reason: KnownAny;
153
+ };
154
+
155
+ export enum HttpMethod {
156
+ GET = 'GET',
157
+ POST = 'POST',
158
+ PUT = 'PUT',
159
+ PATCH = 'PATCH',
160
+ DELETE = 'DELETE',
161
+ HEAD = 'HEAD',
162
+ OPTIONS = 'OPTIONS',
163
+ }
164
+
165
+ export enum HttpStatus {
166
+ NULL = 0,
167
+ CONTINUE = 100,
168
+ SWITCHING_PROTOCOLS = 101,
169
+ PROCESSING = 102,
170
+ EARLYHINTS = 103,
171
+ OK = 200,
172
+ CREATED = 201,
173
+ ACCEPTED = 202,
174
+ NON_AUTHORITATIVE_INFORMATION = 203,
175
+ NO_CONTENT = 204,
176
+ RESET_CONTENT = 205,
177
+ PARTIAL_CONTENT = 206,
178
+ AMBIGUOUS = 300,
179
+ MOVED_PERMANENTLY = 301,
180
+ FOUND = 302,
181
+ SEE_OTHER = 303,
182
+ NOT_MODIFIED = 304,
183
+ TEMPORARY_REDIRECT = 307,
184
+ PERMANENT_REDIRECT = 308,
185
+ BAD_REQUEST = 400,
186
+ UNAUTHORIZED = 401,
187
+ PAYMENT_REQUIRED = 402,
188
+ FORBIDDEN = 403,
189
+ NOT_FOUND = 404,
190
+ METHOD_NOT_ALLOWED = 405,
191
+ NOT_ACCEPTABLE = 406,
192
+ PROXY_AUTHENTICATION_REQUIRED = 407,
193
+ REQUEST_TIMEOUT = 408,
194
+ CONFLICT = 409,
195
+ GONE = 410,
196
+ LENGTH_REQUIRED = 411,
197
+ PRECONDITION_FAILED = 412,
198
+ PAYLOAD_TOO_LARGE = 413,
199
+ URI_TOO_LONG = 414,
200
+ UNSUPPORTED_MEDIA_TYPE = 415,
201
+ REQUESTED_RANGE_NOT_SATISFIABLE = 416,
202
+ EXPECTATION_FAILED = 417,
203
+ I_AM_A_TEAPOT = 418,
204
+ MISDIRECTED = 421,
205
+ UNPROCESSABLE_ENTITY = 422,
206
+ FAILED_DEPENDENCY = 424,
207
+ PRECONDITION_REQUIRED = 428,
208
+ TOO_MANY_REQUESTS = 429,
209
+ INTERNAL_SERVER_ERROR = 500,
210
+ NOT_IMPLEMENTED = 501,
211
+ BAD_GATEWAY = 502,
212
+ SERVICE_UNAVAILABLE = 503,
213
+ GATEWAY_TIMEOUT = 504,
214
+ HTTP_VERSION_NOT_SUPPORTED = 505,
215
+ }
@@ -0,0 +1,18 @@
1
+ import type { VovkController, StaticClass } from '../types';
2
+
3
+ export function generateStaticAPI(c: Record<string, StaticClass>, slug = 'vovk') {
4
+ const controllers = c as Record<string, VovkController>;
5
+ return [
6
+ { [slug]: ['_schema_'] },
7
+ ...Object.values(controllers)
8
+ .map((controller) => {
9
+ const handlers = controller._handlers;
10
+ const splitPrefix = controller._prefix?.split('/') ?? [];
11
+
12
+ return Object.values(handlers).map((handler) => {
13
+ return { [slug]: [...splitPrefix, ...handler.path.split('/')].filter(Boolean) };
14
+ });
15
+ })
16
+ .flat(),
17
+ ];
18
+ }
@@ -0,0 +1,48 @@
1
+ import type { VovkSchema, VovkController, VovkWorker, StaticClass } from '../types';
2
+
3
+ export default function getSchema(options: {
4
+ emitSchema?: boolean;
5
+ segmentName?: string;
6
+ controllers: Record<string, StaticClass>;
7
+ workers?: Record<string, StaticClass>;
8
+ exposeValidation?: boolean;
9
+ }) {
10
+ const exposeValidation = options?.exposeValidation ?? true;
11
+ const emitSchema = options.emitSchema ?? true;
12
+ const schema: VovkSchema = {
13
+ emitSchema,
14
+ segmentName: options.segmentName ?? '',
15
+ controllers: {},
16
+ workers: {},
17
+ };
18
+
19
+ if (!emitSchema) return schema;
20
+
21
+ for (const [controllerName, controller] of Object.entries(options.controllers) as [string, VovkController][]) {
22
+ schema.controllers[controllerName] = {
23
+ controllerName: controllerName,
24
+ originalControllerName: controller.name,
25
+ prefix: controller._prefix ?? '',
26
+ handlers: {
27
+ ...(exposeValidation
28
+ ? controller._handlers
29
+ : Object.fromEntries(
30
+ Object.entries(controller._handlers ?? {}).map(([key, value]) => [
31
+ key,
32
+ { ...value, validation: undefined },
33
+ ])
34
+ )),
35
+ },
36
+ };
37
+ }
38
+
39
+ for (const [workerName, worker] of Object.entries(options.workers ?? {}) as [string, VovkWorker][]) {
40
+ schema.workers[workerName] = {
41
+ workerName,
42
+ originalWorkerName: worker.name,
43
+ handlers: { ...worker._handlers },
44
+ };
45
+ }
46
+
47
+ return schema;
48
+ }
@@ -0,0 +1,16 @@
1
+ import type { KnownAny, VovkRequest } from '../types';
2
+
3
+ const formMap = new WeakMap();
4
+
5
+ export default async function reqForm<T = KnownAny>(req: VovkRequest<KnownAny, KnownAny>): Promise<T> {
6
+ if (formMap.has(req)) {
7
+ return formMap.get(req) as T;
8
+ }
9
+
10
+ const body = await req.formData();
11
+ const formData = Object.fromEntries(body.entries()) as T;
12
+
13
+ formMap.set(req, formData);
14
+
15
+ return formData;
16
+ }
@@ -0,0 +1,16 @@
1
+ import type { KnownAny, VovkRequest } from '../types';
2
+
3
+ const metaMap = new WeakMap();
4
+
5
+ export default function reqMeta<T = Record<KnownAny, KnownAny>>(
6
+ req: VovkRequest<KnownAny, KnownAny>,
7
+ meta?: T | null
8
+ ): T {
9
+ if (meta) {
10
+ metaMap.set(req, { ...metaMap.get(req), ...meta });
11
+ } else if (meta === null) {
12
+ metaMap.delete(req);
13
+ }
14
+
15
+ return (metaMap.get(req) ?? {}) as T;
16
+ }
@@ -0,0 +1,26 @@
1
+ import { ARRAY_QUERY_KEY } from '../client/createRPC';
2
+ import type { KnownAny, VovkRequest } from '../types';
3
+
4
+ export default function reqQuery<T extends object | undefined>(req: VovkRequest<KnownAny, T>): T {
5
+ type Query = NonNullable<T>;
6
+ const queryArr = (req.nextUrl.searchParams.get(ARRAY_QUERY_KEY as keyof T) as string | undefined)?.split(',') ?? null;
7
+ const entries = [...req.nextUrl.searchParams.entries()] as [string, string][];
8
+ const query = entries.reduce(
9
+ (acc, [key, value]) => {
10
+ if (key === ARRAY_QUERY_KEY) return acc;
11
+ if (queryArr?.includes(key)) {
12
+ if (!(key in acc)) {
13
+ acc[key] = [value];
14
+ } else {
15
+ (acc[key] as string[]).push(value);
16
+ }
17
+ } else {
18
+ acc[key] = value;
19
+ }
20
+ return acc;
21
+ },
22
+ {} as Record<string, string | string[]>
23
+ );
24
+
25
+ return query as Query;
26
+ }
@@ -0,0 +1,45 @@
1
+ import type { KnownAny, VovkController } from '../types';
2
+
3
+ export function setClientValidatorsForHandler(
4
+ h: (...args: KnownAny[]) => KnownAny,
5
+ validation: {
6
+ body: unknown;
7
+ query: unknown;
8
+ }
9
+ ) {
10
+ return new Promise<void>((resolve) => {
11
+ // the setTimeout is necessary to ensure that the _controller is already defined
12
+ setTimeout(() => {
13
+ const controller = (h as unknown as { _controller: VovkController })._controller;
14
+
15
+ if (!controller) {
16
+ throw new Error(
17
+ 'Error setting client validators. Controller not found. Did you forget to use an HTTP decorator?'
18
+ );
19
+ }
20
+
21
+ const handlerName = Object.getOwnPropertyNames(controller).find(
22
+ (key) =>
23
+ (
24
+ controller[key] as {
25
+ _sourceMethod?: unknown;
26
+ }
27
+ )._sourceMethod === h
28
+ );
29
+
30
+ if (!handlerName) {
31
+ throw new Error('Error setting client validators. Handler not found.');
32
+ }
33
+
34
+ controller._handlers = {
35
+ ...controller._handlers,
36
+ [handlerName]: {
37
+ ...controller._handlers[handlerName],
38
+ validation,
39
+ },
40
+ };
41
+
42
+ resolve();
43
+ }, 0);
44
+ });
45
+ }
@@ -0,0 +1,17 @@
1
+ if (typeof Symbol.dispose !== 'symbol') {
2
+ Object.defineProperty(Symbol, 'dispose', {
3
+ configurable: false,
4
+ enumerable: false,
5
+ writable: false,
6
+ value: Symbol.for('dispose'),
7
+ });
8
+ }
9
+
10
+ if (typeof Symbol.asyncDispose !== 'symbol') {
11
+ Object.defineProperty(Symbol, 'asyncDispose', {
12
+ configurable: false,
13
+ enumerable: false,
14
+ writable: false,
15
+ value: Symbol.for('asyncDispose'),
16
+ });
17
+ }
@@ -0,0 +1,156 @@
1
+ import type { VovkWorkerSchema } from '../types';
2
+ import type { WorkerInput, WorkerOutput, WorkerPromiseInstance } from './types';
3
+
4
+ export function createWPC<T extends object>(
5
+ currentWorker: Worker | null,
6
+ workerSchema: object
7
+ ): WorkerPromiseInstance<T> {
8
+ if (!workerSchema) throw new Error('Worker schema is not provided');
9
+ const schema = workerSchema as T & VovkWorkerSchema;
10
+ const instance = {
11
+ worker: currentWorker,
12
+ } as WorkerPromiseInstance<T>;
13
+ let callsKey = 0;
14
+
15
+ instance.terminate = () => {
16
+ if (instance._isTerminated) return;
17
+ instance._isTerminated = true;
18
+ instance.worker?.terminate();
19
+ instance.worker = null;
20
+ };
21
+
22
+ instance.employ = (worker: Worker) => {
23
+ if (instance._isTerminated) return instance;
24
+ instance._isTerminated = true;
25
+ instance.worker = worker;
26
+ return instance;
27
+ };
28
+
29
+ instance.fork = (worker: Worker) => createWPC<T>(worker, schema);
30
+
31
+ for (const methodName of Object.keys(schema.handlers) as (keyof T & string)[]) {
32
+ const { isGenerator } = schema.handlers[methodName];
33
+
34
+ if (isGenerator) {
35
+ const method = (...args: unknown[]) => {
36
+ const key = callsKey;
37
+ callsKey += 1;
38
+ return {
39
+ async *[Symbol.asyncIterator]() {
40
+ if (!instance.worker) {
41
+ throw new Error('Worker is not provided or terminated');
42
+ }
43
+ const w = instance.worker;
44
+ const messageQueue: WorkerOutput[] = [];
45
+ let messageResolver: ((message: WorkerOutput) => void) | null = null;
46
+
47
+ const onMessage = (e: MessageEvent<WorkerOutput>) => {
48
+ const { methodName: m, key: k } = e.data;
49
+ if (k !== key || m !== methodName) {
50
+ return;
51
+ }
52
+ if (messageResolver) {
53
+ messageResolver(e.data);
54
+ messageResolver = null;
55
+ } else {
56
+ messageQueue.push(e.data);
57
+ }
58
+ };
59
+
60
+ const onError = (e: ErrorEvent) => {
61
+ if (messageResolver) {
62
+ messageResolver({ error: e.error } as WorkerOutput);
63
+ messageResolver = null;
64
+ } else {
65
+ messageQueue.push({ error: e.error } as WorkerOutput);
66
+ }
67
+
68
+ w.removeEventListener('message', onMessage);
69
+ w.removeEventListener('error', onError);
70
+ throw e.error;
71
+ };
72
+
73
+ w.addEventListener('message', onMessage);
74
+ w.addEventListener('error', onError);
75
+
76
+ w.postMessage({ key, args, methodName } satisfies WorkerInput);
77
+
78
+ try {
79
+ while (true) {
80
+ let message: WorkerOutput | null = null;
81
+ if (messageQueue.length > 0) {
82
+ message = messageQueue.shift()!;
83
+ } else {
84
+ message = await new Promise<WorkerOutput>((resolve) => {
85
+ messageResolver = resolve;
86
+ });
87
+ }
88
+
89
+ const { result, error, done } = message;
90
+
91
+ if (error) {
92
+ throw error;
93
+ }
94
+
95
+ if (done) {
96
+ break;
97
+ }
98
+
99
+ yield result;
100
+ }
101
+
102
+ w.removeEventListener('message', onMessage);
103
+ w.removeEventListener('error', onError);
104
+ } catch (e) {
105
+ w.removeEventListener('message', onMessage);
106
+ w.removeEventListener('error', onError);
107
+ throw e;
108
+ }
109
+ },
110
+ };
111
+ };
112
+
113
+ // @ts-expect-error TODO
114
+ instance[methodName] = method;
115
+ } else {
116
+ const method = (...args: unknown[]) => {
117
+ if (!instance.worker) {
118
+ throw new Error('Worker is not provided or terminated');
119
+ }
120
+ const w = instance.worker;
121
+ return new Promise((resolve, reject) => {
122
+ const key = callsKey;
123
+ callsKey += 1;
124
+
125
+ const onError = (e: ErrorEvent) => {
126
+ w.removeEventListener('message', onMessage);
127
+ w.removeEventListener('error', onError);
128
+ reject(e);
129
+ };
130
+
131
+ const onMessage = (e: MessageEvent<WorkerOutput>) => {
132
+ const { result, error, key: k, methodName: m } = e.data;
133
+ if (k !== key || m !== methodName) {
134
+ return;
135
+ }
136
+ w.removeEventListener('message', onMessage);
137
+ w.removeEventListener('error', onError);
138
+ if (error) {
139
+ reject(error);
140
+ } else {
141
+ resolve(result);
142
+ }
143
+ };
144
+
145
+ w.addEventListener('message', onMessage);
146
+ w.addEventListener('error', onError);
147
+ w.postMessage({ key, args, methodName } satisfies WorkerInput);
148
+ });
149
+ };
150
+
151
+ // @ts-expect-error TODO
152
+ instance[methodName] = method;
153
+ }
154
+ }
155
+ return instance;
156
+ }
@@ -0,0 +1,4 @@
1
+ import { worker } from './worker';
2
+ import { createWPC } from './createWPC';
3
+
4
+ export { worker, createWPC };
@@ -0,0 +1,45 @@
1
+ import type { KnownAny } from '../types';
2
+
3
+ type ToPromise<T> = T extends PromiseLike<unknown> ? T : Promise<T>;
4
+ type ToAsyncGenerator<T> =
5
+ T extends AsyncGenerator<unknown, unknown, unknown>
6
+ ? T
7
+ : T extends Generator<infer U, unknown, unknown>
8
+ ? AsyncGenerator<U, unknown, unknown>
9
+ : AsyncGenerator<T, unknown, unknown>;
10
+ type ToProperReturnType<T> = T extends Generator<unknown, unknown, unknown> | AsyncGenerator<unknown, unknown, unknown>
11
+ ? ToAsyncGenerator<T>
12
+ : ToPromise<T>;
13
+
14
+ type OmitNever<T> = {
15
+ [K in keyof T as T[K] extends never ? never : K]: T[K];
16
+ };
17
+
18
+ export type WorkerPromiseInstanceWithNever<T> = {
19
+ [K in keyof T]: T[K] extends (...args: KnownAny[]) => KnownAny
20
+ ? (...args: Parameters<T[K]>) => ToProperReturnType<ReturnType<T[K]>>
21
+ : never;
22
+ };
23
+
24
+ export type WorkerPromiseInstance<T> = OmitNever<WorkerPromiseInstanceWithNever<T>> & {
25
+ terminate: () => void;
26
+ employ: (w: Worker) => WorkerPromiseInstance<T>;
27
+ fork: (w: Worker) => WorkerPromiseInstance<T>;
28
+ worker: Worker | null;
29
+ _isTerminated?: true;
30
+ [Symbol.dispose]: () => void;
31
+ };
32
+
33
+ export interface WorkerInput {
34
+ methodName: string;
35
+ args: unknown[];
36
+ key: number;
37
+ }
38
+
39
+ export interface WorkerOutput {
40
+ methodName: string;
41
+ result?: unknown;
42
+ error?: unknown;
43
+ done?: true;
44
+ key: number;
45
+ }
@@ -0,0 +1,53 @@
1
+ import type { VovkWorker } from '../types';
2
+ import type { WorkerInput, WorkerOutput } from './types';
3
+
4
+ export function worker() {
5
+ return (t: object) => {
6
+ const target = t as Record<
7
+ string,
8
+ (
9
+ ...args: unknown[]
10
+ ) => Iterable<unknown> | AsyncIterable<unknown> | Promise<Iterable<unknown> | AsyncIterable<unknown>>
11
+ > &
12
+ VovkWorker;
13
+ target._handlers = {};
14
+
15
+ for (const key of Object.getOwnPropertyNames(target)) {
16
+ const member = target[key];
17
+ if (typeof member === 'function') {
18
+ const prototype = Object.getPrototypeOf(member) as unknown;
19
+ const isGenerator =
20
+ prototype === Object.getPrototypeOf(function* () {}) ||
21
+ prototype === Object.getPrototypeOf(async function* () {});
22
+ target._handlers[key] = {};
23
+
24
+ if (isGenerator) {
25
+ target._handlers[key].isGenerator = true;
26
+ }
27
+ }
28
+ }
29
+
30
+ if (typeof self === 'undefined') return; // no-op in non-worker environment
31
+
32
+ const w = self as unknown as Worker;
33
+
34
+ w.onmessage = async (evt: MessageEvent<WorkerInput>) => {
35
+ const { methodName, args, key } = evt.data;
36
+ try {
37
+ const result = await target[methodName](...args);
38
+
39
+ if (result && typeof result === 'object' && 'next' in result && typeof result.next === 'function') {
40
+ const iterable = result as Iterable<unknown> | AsyncIterable<unknown>;
41
+ for await (const result of iterable) {
42
+ w.postMessage({ result, key, methodName } satisfies WorkerOutput);
43
+ }
44
+ w.postMessage({ done: true, key, methodName } satisfies WorkerOutput);
45
+ } else {
46
+ w.postMessage({ result, key, methodName } satisfies WorkerOutput);
47
+ }
48
+ } catch (e) {
49
+ w.postMessage({ error: e, key, methodName } satisfies WorkerOutput);
50
+ }
51
+ };
52
+ };
53
+ }
@@ -1,4 +0,0 @@
1
- import { type _VovkControllerSchema as VovkControllerSchema, type _KnownAny as KnownAny } from '../types';
2
- import { type _VovkClientOptions as VovkClientOptions, type _VovkClient as VovkClient, type _VovkDefaultFetcherOptions as VovkDefaultFetcherOptions } from './types';
3
- export declare const ARRAY_QUERY_KEY = "_vovkarr";
4
- export declare const _clientizeController: <T, OPTS extends Record<string, KnownAny> = VovkDefaultFetcherOptions>(givenController: VovkControllerSchema, segmentName?: string, options?: VovkClientOptions<OPTS>) => VovkClient<T, OPTS>;
@@ -1,4 +0,0 @@
1
- import type { _VovkDefaultFetcherOptions as VovkDefaultFetcherOptions, _VovkClientFetcher as VovkClientFetcher } from './types';
2
- export declare const DEFAULT_ERROR_MESSAGE = "Unknown error at defaultFetcher";
3
- declare const defaultFetcher: VovkClientFetcher<VovkDefaultFetcherOptions>;
4
- export default defaultFetcher;