vovk 3.0.0-draft.52 → 3.0.0-draft.54

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.
@@ -1,6 +1,6 @@
1
1
 
2
2
  
3
- > vovk@3.0.0-draft.52 build
3
+ > vovk@3.0.0-draft.54 build
4
4
  > tsc
5
5
 
6
6
  \
@@ -1,6 +1,6 @@
1
1
 
2
2
  
3
- > vovk@3.0.0-draft.49 tsc
3
+ > vovk@3.0.0-draft.53 tsc
4
4
  > tsc --noEmit
5
5
 
6
6
  \
package/README.md CHANGED
@@ -1 +1,24 @@
1
- Description is coming soon.
1
+ <p align="center">
2
+ <picture>
3
+ <source width="300" media="(prefers-color-scheme: dark)" srcset="https://vovk.dev/vovk-logo-white.svg">
4
+ <source width="300" media="(prefers-color-scheme: light)" srcset="https://vovk.dev/vovk-logo.svg">
5
+ <img width="300" alt="vovk" src="https://vovk.dev/vovk-logo.svg">
6
+ </picture><br>
7
+ <strong>REST + RPC = ♥️</strong>
8
+ </p>
9
+
10
+ <p align="center">
11
+ Back-end meta-framework for <a href="https://nextjs.org/docs/app">Next.js</a>
12
+ </p>
13
+
14
+ ---
15
+
16
+ ## vovk [![npm version](https://badge.fury.io/js/vovk.svg)](https://www.npmjs.com/package/vovk)
17
+
18
+ The main library with [zero dependencies](https://bundlephobia.com/result?p=vovk) that's going to be used in production. It provides a wrapper for Next.js API routes, internal RPC API, utilities and types.
19
+
20
+ ```sh
21
+ npm install vovk
22
+ ```
23
+
24
+ For more information, please visit the [getting started guide](https://vovk.dev/getting-started) or check out the [Vovk.ts examples](https://vovk-examples.vercel.app/).
@@ -1,4 +1,3 @@
1
1
  import { type VovkControllerSchema, type KnownAny } from '../types';
2
2
  import { type VovkClientOptions, type VovkClient, type VovkDefaultFetcherOptions } from './types';
3
- export declare const ARRAY_QUERY_KEY = "_vovkarr";
4
3
  export declare const createRPC: <T, OPTS extends Record<string, KnownAny> = VovkDefaultFetcherOptions>(controllerSchema: VovkControllerSchema, segmentName?: string, options?: VovkClientOptions<OPTS>) => VovkClient<T, OPTS>;
@@ -3,39 +3,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.createRPC = exports.ARRAY_QUERY_KEY = void 0;
6
+ exports.createRPC = void 0;
7
7
  const defaultFetcher_1 = __importDefault(require("./defaultFetcher"));
8
8
  const defaultHandler_1 = require("./defaultHandler");
9
9
  const defaultStreamHandler_1 = require("./defaultStreamHandler");
10
- // TODO Ugly workaround. Need your ideas how to distinguish between array and non-array query params
11
- exports.ARRAY_QUERY_KEY = '_vovkarr';
10
+ const serializeQuery_1 = __importDefault(require("../utils/serializeQuery"));
12
11
  const trimPath = (path) => path.trim().replace(/^\/|\/$/g, '');
13
12
  const getHandlerPath = (endpoint, params, query) => {
14
13
  let result = endpoint;
15
14
  for (const [key, value] of Object.entries(params ?? {})) {
16
15
  result = result.replace(`:${key}`, value);
17
16
  }
18
- const searchParams = new URLSearchParams();
19
- let hasQuery = false;
20
- const arrayKeys = [];
21
- for (const [key, value] of Object.entries(query ?? {})) {
22
- if (typeof value === 'undefined')
23
- continue;
24
- if (value instanceof Array) {
25
- arrayKeys.push(key);
26
- for (const val of value) {
27
- searchParams.append(key, val);
28
- }
29
- }
30
- else {
31
- searchParams.set(key, value);
32
- }
33
- hasQuery = true;
34
- }
35
- if (arrayKeys.length) {
36
- searchParams.set(exports.ARRAY_QUERY_KEY, arrayKeys.join(','));
37
- }
38
- return `${result}${hasQuery ? '?' : ''}${searchParams.toString()}`;
17
+ const queryStr = query ? (0, serializeQuery_1.default)(query) : null;
18
+ return `${result}${queryStr ? '?' : ''}${queryStr}`;
39
19
  };
40
20
  const createRPC = (controllerSchema, segmentName, options) => {
41
21
  const schema = controllerSchema;
@@ -46,7 +26,8 @@ const createRPC = (controllerSchema, segmentName, options) => {
46
26
  throw new Error(`Unable to clientize. No schema for controller ${String(schema?.controllerName)} provided`);
47
27
  const controllerPrefix = trimPath(schema.prefix ?? '');
48
28
  const { fetcher: settingsFetcher = defaultFetcher_1.default } = options ?? {};
49
- for (const [staticMethodName, { path, httpMethod, validation }] of Object.entries(schema.handlers)) {
29
+ for (const [staticMethodName, handlerSchema] of Object.entries(schema.handlers)) {
30
+ const { path, httpMethod, validation } = handlerSchema;
50
31
  const getEndpoint = ({ apiRoot, params, query, }) => {
51
32
  const mainPrefix = (apiRoot.startsWith('http://') || apiRoot.startsWith('https://') || apiRoot.startsWith('/') ? '' : '/') +
52
33
  (apiRoot.endsWith('/') ? apiRoot : `${apiRoot}/`) +
@@ -85,6 +66,7 @@ const createRPC = (controllerSchema, segmentName, options) => {
85
66
  return Promise.resolve(fetcherPromise);
86
67
  return input.transform ? fetcherPromise.then(input.transform) : fetcherPromise;
87
68
  };
69
+ handler.schema = handlerSchema;
88
70
  // @ts-expect-error TODO
89
71
  client[staticMethodName] = handler;
90
72
  }
@@ -1,4 +1,2 @@
1
- import { createRPC } from './createRPC';
2
- import type { VovkClientFetcher, VovkClientOptions, VovkDefaultFetcherOptions, VovkValidateOnClient } from './types';
3
- export { createRPC };
4
- export type { VovkClientFetcher, VovkClientOptions, VovkDefaultFetcherOptions, VovkValidateOnClient };
1
+ export { createRPC } from './createRPC';
2
+ export type { VovkClientFetcher, VovkClientOptions, VovkDefaultFetcherOptions, VovkValidateOnClient } from './types';
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createRPC = void 0;
4
- const createRPC_1 = require("./createRPC");
4
+ var createRPC_1 = require("./createRPC");
5
5
  Object.defineProperty(exports, "createRPC", { enumerable: true, get: function () { return createRPC_1.createRPC; } });
@@ -1,4 +1,4 @@
1
- import type { KnownAny, HttpMethod, ControllerStaticMethod, VovkControllerBody, VovkControllerQuery, VovkControllerParams } from '../types';
1
+ import type { KnownAny, HttpMethod, ControllerStaticMethod, VovkControllerBody, VovkControllerQuery, VovkControllerParams, VovkHandlerSchema } from '../types';
2
2
  import type { StreamJSONResponse } from '../StreamJSONResponse';
3
3
  import type { NextResponse } from 'next/server';
4
4
  export type StaticMethodInput<T extends ControllerStaticMethod> = (VovkControllerBody<T> extends undefined | void ? {
@@ -26,7 +26,7 @@ export type StreamAsyncIterator<T> = {
26
26
  };
27
27
  type StaticMethodReturn<T extends ControllerStaticMethod> = ReturnType<T> extends NextResponse<infer U> | Promise<NextResponse<infer U>> ? U : ReturnType<T> extends Response | Promise<Response> ? Awaited<ReturnType<T>> : ReturnType<T>;
28
28
  type StaticMethodReturnPromise<T extends ControllerStaticMethod> = ToPromise<StaticMethodReturn<T>>;
29
- type ClientMethod<T extends (...args: KnownAny[]) => void | object | StreamJSONResponse<STREAM> | Promise<StreamJSONResponse<STREAM>>, OPTS extends Record<string, KnownAny>, STREAM extends KnownAny = unknown> = <R>(options: (StaticMethodInput<T> extends {
29
+ type ClientMethod<T extends (...args: KnownAny[]) => void | object | StreamJSONResponse<STREAM> | Promise<StreamJSONResponse<STREAM>>, OPTS extends Record<string, KnownAny>, STREAM extends KnownAny = unknown> = (<R>(options: (StaticMethodInput<T> extends {
30
30
  body?: undefined | null;
31
31
  query?: undefined;
32
32
  params?: undefined;
@@ -34,7 +34,9 @@ type ClientMethod<T extends (...args: KnownAny[]) => void | object | StreamJSONR
34
34
  params: StaticMethodInput<T>['params'];
35
35
  } : unknown : StaticMethodInput<T>) & (Partial<OPTS & {
36
36
  transform: (staticMethodReturn: Awaited<StaticMethodReturn<T>>) => R;
37
- }> | void)) => ReturnType<T> extends Promise<StreamJSONResponse<infer U>> | StreamJSONResponse<infer U> | Iterator<infer U> | AsyncIterator<infer U> ? Promise<StreamAsyncIterator<U>> : R extends object ? Promise<R> : StaticMethodReturnPromise<T>;
37
+ }> | void)) => ReturnType<T> extends Promise<StreamJSONResponse<infer U>> | StreamJSONResponse<infer U> | Iterator<infer U> | AsyncIterator<infer U> ? Promise<StreamAsyncIterator<U>> : R extends object ? Promise<R> : StaticMethodReturnPromise<T>) & {
38
+ schema: VovkHandlerSchema;
39
+ };
38
40
  type OmitNever<T> = {
39
41
  [K in keyof T as T[K] extends never ? never : K]: T[K];
40
42
  };
@@ -1,4 +1,4 @@
1
- import type { HandlerSchema, KnownAny, VovkController, VovkRequest } from './types';
1
+ import type { VovkHandlerSchema, KnownAny, VovkController, VovkRequest } from './types';
2
2
  type Next = () => Promise<unknown>;
3
- export declare function createDecorator<ARGS extends unknown[], REQUEST = VovkRequest>(handler: null | ((this: VovkController, req: REQUEST, next: Next, ...args: ARGS) => unknown), initHandler?: (this: VovkController, ...args: ARGS) => Omit<HandlerSchema, 'path' | 'httpMethod'> | ((handlerSchema: HandlerSchema | null) => Omit<HandlerSchema, 'path' | 'httpMethod'>) | null | undefined): (...args: ARGS) => (target: KnownAny, propertyKey: string) => void;
3
+ export declare function createDecorator<ARGS extends unknown[], REQUEST = VovkRequest>(handler: null | ((this: VovkController, req: REQUEST, next: Next, ...args: ARGS) => unknown), initHandler?: (this: VovkController, ...args: ARGS) => Omit<VovkHandlerSchema, 'path' | 'httpMethod'> | ((handlerSchema: VovkHandlerSchema | null) => Omit<VovkHandlerSchema, 'path' | 'httpMethod'>) | null | undefined): (...args: ARGS) => (target: KnownAny, propertyKey: string) => void;
4
4
  export {};
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createVovkApp } from './createVovkApp';
2
- import { HttpStatus as HttpStatus, HttpMethod as HttpMethod, type VovkErrorResponse, type VovkRequest, type VovkBody, type VovkQuery, type VovkParams, type VovkReturnType, type VovkYieldType, type VovkControllerBody, type VovkControllerQuery, type VovkControllerParams, type VovkControllerYieldType, type VovkSchema, type VovkWorkerSchema, type VovkControllerSchema } from './types';
2
+ import { HttpStatus as HttpStatus, HttpMethod as HttpMethod, type VovkErrorResponse, type VovkRequest, type VovkBody, type VovkQuery, type VovkParams, type VovkReturnType, type VovkYieldType, type VovkControllerBody, type VovkControllerQuery, type VovkControllerParams, type VovkControllerYieldType, type VovkSchema, type VovkWorkerSchema, type VovkControllerSchema, type VovkHandlerSchema } from './types';
3
3
  import { type VovkClientOptions, type VovkClientFetcher, type VovkDefaultFetcherOptions, type VovkValidateOnClient, createRPC } from './client';
4
4
  import { HttpException } from './HttpException';
5
5
  import { createDecorator } from './createDecorator';
@@ -7,7 +7,7 @@ import { StreamJSONResponse } from './StreamJSONResponse';
7
7
  import { worker, createWPC } from './worker';
8
8
  import { generateStaticAPI } from './utils/generateStaticAPI';
9
9
  import { setClientValidatorsForHandler } from './utils/setClientValidatorsForHandler';
10
- export { type VovkClientFetcher, type VovkDefaultFetcherOptions, type VovkValidateOnClient, type VovkSchema, type VovkErrorResponse, type VovkRequest, type VovkControllerBody, type VovkControllerQuery, type VovkControllerParams, type VovkControllerYieldType, type VovkBody, type VovkQuery, type VovkParams, type VovkYieldType, type VovkReturnType, type VovkClientOptions, type VovkWorkerSchema, type VovkControllerSchema, StreamJSONResponse, HttpException, HttpStatus, HttpMethod, createVovkApp, createDecorator, worker, createWPC, createRPC, generateStaticAPI, setClientValidatorsForHandler, };
10
+ export { type VovkClientFetcher, type VovkDefaultFetcherOptions, type VovkValidateOnClient, type VovkSchema, type VovkErrorResponse, type VovkRequest, type VovkControllerBody, type VovkControllerQuery, type VovkControllerParams, type VovkControllerYieldType, type VovkBody, type VovkQuery, type VovkParams, type VovkYieldType, type VovkReturnType, type VovkClientOptions, type VovkWorkerSchema, type VovkControllerSchema, type VovkHandlerSchema, StreamJSONResponse, HttpException, HttpStatus, HttpMethod, createVovkApp, createDecorator, worker, createWPC, createRPC, generateStaticAPI, setClientValidatorsForHandler, };
11
11
  export declare const get: {
12
12
  (givenPath?: string | undefined, options?: import("./types").DecoratorOptions | undefined): ReturnType<(givenPath?: string, options?: import("./types").DecoratorOptions) => (givenTarget: import("./types").KnownAny, propertyKey: string) => void>;
13
13
  auto: (options?: import("./types").DecoratorOptions) => (givenTarget: import("./types").KnownAny, propertyKey: string) => void;
package/dist/types.d.ts CHANGED
@@ -3,7 +3,7 @@ import type { StreamJSONResponse } from './StreamJSONResponse';
3
3
  import { StreamAsyncIterator } from './client/types';
4
4
  export type KnownAny = any;
5
5
  export type StaticClass = Function;
6
- export type HandlerSchema = {
6
+ export type VovkHandlerSchema = {
7
7
  path: string;
8
8
  httpMethod: HttpMethod;
9
9
  validation?: {
@@ -16,7 +16,7 @@ export type VovkControllerSchema = {
16
16
  controllerName: string;
17
17
  originalControllerName: string;
18
18
  prefix?: string;
19
- handlers: Record<string, HandlerSchema>;
19
+ handlers: Record<string, VovkHandlerSchema>;
20
20
  };
21
21
  export type VovkWorkerSchema = {
22
22
  workerName: string;
@@ -77,7 +77,7 @@ export interface VovkRequest<BODY = undefined, QUERY extends object | undefined
77
77
  form: <T = KnownAny>() => Promise<T>;
78
78
  };
79
79
  }
80
- export type ControllerStaticMethod<REQ extends VovkRequest<KnownAny, KnownAny> = VovkRequest<undefined, Record<string, string | string[]>>, PARAMS extends {
80
+ export type ControllerStaticMethod<REQ extends VovkRequest<KnownAny, KnownAny> = VovkRequest<undefined, Record<string, KnownAny>>, PARAMS extends {
81
81
  [key: string]: string;
82
82
  } = KnownAny> = ((req: REQ, params: PARAMS) => unknown) & {
83
83
  _controller?: VovkController;
@@ -0,0 +1,25 @@
1
+ import type { KnownAny } from '../types';
2
+ /**
3
+ * Deserialize a bracket-based query string into an object.
4
+ *
5
+ * Supports:
6
+ * - Key/value pairs with nested brackets (e.g. "a[b][0]=value")
7
+ * - Arrays with empty bracket (e.g. "arr[]=1&arr[]=2")
8
+ * - Mixed arrays of objects, etc.
9
+ *
10
+ * @example
11
+ * parseQuery("x=xx&y[0]=yy&y[1]=uu&z[f]=x&z[u][0]=uu&z[u][1]=xx&z[d][x]=ee")
12
+ * => {
13
+ * x: "xx",
14
+ * y: ["yy", "uu"],
15
+ * z: {
16
+ * f: "x",
17
+ * u: ["uu", "xx"],
18
+ * d: { x: "ee" }
19
+ * }
20
+ * }
21
+ *
22
+ * @param queryString - The raw query string (e.g. location.search.slice(1))
23
+ * @returns - A nested object representing the query params
24
+ */
25
+ export default function parseQuery(queryString: string): Record<string, KnownAny>;
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = parseQuery;
4
+ /**
5
+ * Parse a bracket-based key (e.g. "z[d][0][x]" or "arr[]")
6
+ * into an array of path segments (strings or special push-markers).
7
+ *
8
+ * Example: "z[d][0][x]" => ["z", "d", "0", "x"]
9
+ * Example: "arr[]" => ["arr", "" ] // "" indicates "push" onto array
10
+ */
11
+ function parseKey(key) {
12
+ // The first segment is everything up to the first '[' (or the entire key if no '[')
13
+ const segments = [];
14
+ const topKeyMatch = key.match(/^([^[\]]+)/);
15
+ if (topKeyMatch) {
16
+ segments.push(topKeyMatch[1]);
17
+ }
18
+ else {
19
+ // If it starts with brackets, treat it as empty? (edge case)
20
+ segments.push('');
21
+ }
22
+ // Now capture all bracket parts: [something], [0], []
23
+ const bracketRegex = /\[([^[\]]*)\]/g;
24
+ let match;
25
+ while ((match = bracketRegex.exec(key)) !== null) {
26
+ // match[1] is the content inside the brackets
27
+ segments.push(match[1]);
28
+ }
29
+ return segments;
30
+ }
31
+ /**
32
+ * Recursively set a value in a nested object/array, given a path of segments.
33
+ * - If segment is numeric => treat as array index
34
+ * - If segment is empty "" => push to array
35
+ * - Else => object property
36
+ */
37
+ function setValue(obj, path, value) {
38
+ let current = obj;
39
+ for (let i = 0; i < path.length; i++) {
40
+ const segment = path[i];
41
+ // If we're at the last segment, set the value
42
+ if (i === path.length - 1) {
43
+ if (segment === '') {
44
+ // Empty bracket => push
45
+ if (!Array.isArray(current)) {
46
+ current = [];
47
+ }
48
+ current.push(value);
49
+ }
50
+ else if (!isNaN(Number(segment))) {
51
+ // Numeric segment => array index
52
+ const idx = Number(segment);
53
+ if (!Array.isArray(current)) {
54
+ current = [];
55
+ }
56
+ current[idx] = value;
57
+ }
58
+ else {
59
+ // Object property
60
+ current[segment] = value;
61
+ }
62
+ }
63
+ else {
64
+ // Not the last segment: descend into existing structure or create it
65
+ const nextSegment = path[i + 1];
66
+ if (segment === '') {
67
+ // Empty bracket => push
68
+ if (!Array.isArray(current)) {
69
+ // Convert the current node into an array, if not one
70
+ current = [];
71
+ }
72
+ // If we are not at the last path, we need a placeholder object or array
73
+ // for the next segment. We'll push something and move current to that.
74
+ if (current.length === 0) {
75
+ // nothing in array yet
76
+ current.push(typeof nextSegment === 'string' && !isNaN(Number(nextSegment)) ? [] : {});
77
+ }
78
+ else if (typeof nextSegment === 'string' && !isNaN(Number(nextSegment))) {
79
+ // next is numeric => we want an array
80
+ if (!Array.isArray(current[current.length - 1])) {
81
+ current[current.length - 1] = [];
82
+ }
83
+ }
84
+ else {
85
+ // next is not numeric => we want an object
86
+ if (typeof current[current.length - 1] !== 'object') {
87
+ current[current.length - 1] = {};
88
+ }
89
+ }
90
+ current = current[current.length - 1];
91
+ }
92
+ else if (!isNaN(Number(segment))) {
93
+ // segment is numeric => array index
94
+ const idx = Number(segment);
95
+ if (!Array.isArray(current)) {
96
+ current = [];
97
+ }
98
+ if (current[idx] === undefined) {
99
+ // Create placeholder for next segment
100
+ current[idx] = typeof nextSegment === 'string' && !isNaN(Number(nextSegment)) ? [] : {};
101
+ }
102
+ current = current[idx];
103
+ }
104
+ else {
105
+ // segment is an object key
106
+ if (current[segment] === undefined) {
107
+ // Create placeholder
108
+ current[segment] = typeof nextSegment === 'string' && !isNaN(Number(nextSegment)) ? [] : {};
109
+ }
110
+ current = current[segment];
111
+ }
112
+ }
113
+ }
114
+ }
115
+ /**
116
+ * Deserialize a bracket-based query string into an object.
117
+ *
118
+ * Supports:
119
+ * - Key/value pairs with nested brackets (e.g. "a[b][0]=value")
120
+ * - Arrays with empty bracket (e.g. "arr[]=1&arr[]=2")
121
+ * - Mixed arrays of objects, etc.
122
+ *
123
+ * @example
124
+ * parseQuery("x=xx&y[0]=yy&y[1]=uu&z[f]=x&z[u][0]=uu&z[u][1]=xx&z[d][x]=ee")
125
+ * => {
126
+ * x: "xx",
127
+ * y: ["yy", "uu"],
128
+ * z: {
129
+ * f: "x",
130
+ * u: ["uu", "xx"],
131
+ * d: { x: "ee" }
132
+ * }
133
+ * }
134
+ *
135
+ * @param queryString - The raw query string (e.g. location.search.slice(1))
136
+ * @returns - A nested object representing the query params
137
+ */
138
+ function parseQuery(queryString) {
139
+ const result = {};
140
+ if (!queryString)
141
+ return result;
142
+ // Split into key=value pairs
143
+ const pairs = queryString
144
+ .replace(/^\?/, '') // Remove leading "?" if present
145
+ .split('&');
146
+ for (const pair of pairs) {
147
+ const [rawKey, rawVal = ''] = pair.split('=');
148
+ const decodedKey = decodeURIComponent(rawKey || '');
149
+ const decodedVal = decodeURIComponent(rawVal);
150
+ // Parse bracket notation
151
+ const pathSegments = parseKey(decodedKey);
152
+ // Insert into the result object
153
+ setValue(result, pathSegments, decodedVal);
154
+ }
155
+ return result;
156
+ }
@@ -1,25 +1,10 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.default = reqQuery;
4
- const createRPC_1 = require("../client/createRPC");
7
+ const parseQuery_1 = __importDefault(require("./parseQuery"));
5
8
  function reqQuery(req) {
6
- const queryArr = req.nextUrl.searchParams.get(createRPC_1.ARRAY_QUERY_KEY)?.split(',') ?? null;
7
- const entries = [...req.nextUrl.searchParams.entries()];
8
- const query = entries.reduce((acc, [key, value]) => {
9
- if (key === createRPC_1.ARRAY_QUERY_KEY)
10
- return acc;
11
- if (queryArr?.includes(key)) {
12
- if (!(key in acc)) {
13
- acc[key] = [value];
14
- }
15
- else {
16
- acc[key].push(value);
17
- }
18
- }
19
- else {
20
- acc[key] = value;
21
- }
22
- return acc;
23
- }, {});
24
- return query;
9
+ return (0, parseQuery_1.default)(req.nextUrl.search);
25
10
  }
@@ -0,0 +1,13 @@
1
+ import type { KnownAny } from '../types';
2
+ /**
3
+ * Serialize a nested object (including arrays, arrays of objects, etc.)
4
+ * into a bracket-based query string.
5
+ *
6
+ * @example
7
+ * serializeQuery({ x: 'xx', y: [1, 2], z: { f: 'x' } })
8
+ * => "x=xx&y[0]=1&y[1]=2&z[f]=x"
9
+ *
10
+ * @param obj - The input object to be serialized
11
+ * @returns - A bracket-based query string (without leading "?")
12
+ */
13
+ export default function serializeQuery(obj: Record<string, KnownAny>): string;
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = serializeQuery;
4
+ /**
5
+ * Recursively build query parameters from an object.
6
+ *
7
+ * @param key - The query key so far (e.g. 'user', 'user[0]', 'user[0][name]')
8
+ * @param value - The current value to serialize
9
+ * @returns - An array of `key=value` strings
10
+ */
11
+ function buildParams(key, value) {
12
+ if (value === null || value === undefined) {
13
+ return []; // skip null/undefined values entirely
14
+ }
15
+ // If value is an object or array, we need to recurse
16
+ if (typeof value === 'object') {
17
+ // Array case
18
+ if (Array.isArray(value)) {
19
+ /**
20
+ * We use index-based bracket notation here:
21
+ * e.g. for value = ['aa', 'bb'] and key = 'foo'
22
+ * => "foo[0]=aa&foo[1]=bb"
23
+ *
24
+ * If you prefer "foo[]=aa&foo[]=bb" style, replace:
25
+ * `${key}[${i}]`
26
+ * with:
27
+ * `${key}[]`
28
+ */
29
+ return value.flatMap((v, i) => {
30
+ const newKey = `${key}[${i}]`;
31
+ return buildParams(newKey, v);
32
+ });
33
+ }
34
+ // Plain object case
35
+ return Object.keys(value).flatMap((k) => {
36
+ const newKey = `${key}[${k}]`;
37
+ return buildParams(newKey, value[k]);
38
+ });
39
+ }
40
+ return [`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`];
41
+ }
42
+ /**
43
+ * Serialize a nested object (including arrays, arrays of objects, etc.)
44
+ * into a bracket-based query string.
45
+ *
46
+ * @example
47
+ * serializeQuery({ x: 'xx', y: [1, 2], z: { f: 'x' } })
48
+ * => "x=xx&y[0]=1&y[1]=2&z[f]=x"
49
+ *
50
+ * @param obj - The input object to be serialized
51
+ * @returns - A bracket-based query string (without leading "?")
52
+ */
53
+ function serializeQuery(obj) {
54
+ if (!obj || typeof obj !== 'object')
55
+ return '';
56
+ // Collect query segments
57
+ const segments = [];
58
+ for (const key in obj) {
59
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
60
+ const value = obj[key];
61
+ segments.push(...buildParams(key, value));
62
+ }
63
+ }
64
+ return segments.join('&');
65
+ }
@@ -1,2 +1,2 @@
1
1
  import type { WorkerPromiseInstance } from './types';
2
- export declare function createWPC<T extends object>(currentWorker: Worker | null, workerSchema: object): WorkerPromiseInstance<T>;
2
+ export declare function createWPC<T extends object>(workerSchema: object, currentWorker?: Worker): WorkerPromiseInstance<T>;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createWPC = createWPC;
4
- function createWPC(currentWorker, workerSchema) {
4
+ function createWPC(workerSchema, currentWorker) {
5
5
  if (!workerSchema)
6
6
  throw new Error('Worker schema is not provided');
7
7
  const schema = workerSchema;
@@ -23,7 +23,7 @@ function createWPC(currentWorker, workerSchema) {
23
23
  instance.worker = worker;
24
24
  return instance;
25
25
  };
26
- instance.fork = (worker) => createWPC(worker, schema);
26
+ instance.fork = (worker) => createWPC(schema, worker);
27
27
  for (const methodName of Object.keys(schema.handlers)) {
28
28
  const { isGenerator } = schema.handlers[methodName];
29
29
  if (isGenerator) {
@@ -1,3 +1,2 @@
1
- import { worker } from './worker';
2
- import { createWPC } from './createWPC';
3
- export { worker, createWPC };
1
+ export { worker } from './worker';
2
+ export { createWPC } from './createWPC';
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createWPC = exports.worker = void 0;
4
- const worker_1 = require("./worker");
4
+ var worker_1 = require("./worker");
5
5
  Object.defineProperty(exports, "worker", { enumerable: true, get: function () { return worker_1.worker; } });
6
- const createWPC_1 = require("./createWPC");
6
+ var createWPC_1 = require("./createWPC");
7
7
  Object.defineProperty(exports, "createWPC", { enumerable: true, get: function () { return createWPC_1.createWPC; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vovk",
3
- "version": "3.0.0-draft.52",
3
+ "version": "3.0.0-draft.54",
4
4
  "main": "dist/index.js",
5
5
  "description": "RESTful RPC for Next.js - Transforms Next.js into a powerful REST API platform with RPC capabilities.",
6
6
  "repository": {
@@ -10,9 +10,7 @@ import { type VovkClientOptions, type VovkClient, type VovkDefaultFetcherOptions
10
10
  import defaultFetcher from './defaultFetcher';
11
11
  import { defaultHandler } from './defaultHandler';
12
12
  import { defaultStreamHandler } from './defaultStreamHandler';
13
-
14
- // TODO Ugly workaround. Need your ideas how to distinguish between array and non-array query params
15
- export const ARRAY_QUERY_KEY = '_vovkarr';
13
+ import serializeQuery from '../utils/serializeQuery';
16
14
 
17
15
  const trimPath = (path: string) => path.trim().replace(/^\/|\/$/g, '');
18
16
 
@@ -26,27 +24,9 @@ const getHandlerPath = <T extends ControllerStaticMethod>(
26
24
  result = result.replace(`:${key}`, value as string);
27
25
  }
28
26
 
29
- const searchParams = new URLSearchParams();
30
- let hasQuery = false;
31
- const arrayKeys: string[] = [];
32
- for (const [key, value] of Object.entries(query ?? {})) {
33
- if (typeof value === 'undefined') continue;
34
- if (value instanceof Array) {
35
- arrayKeys.push(key);
36
- for (const val of value) {
37
- searchParams.append(key, val);
38
- }
39
- } else {
40
- searchParams.set(key, value);
41
- }
42
- hasQuery = true;
43
- }
27
+ const queryStr = query ? serializeQuery(query) : null;
44
28
 
45
- if (arrayKeys.length) {
46
- searchParams.set(ARRAY_QUERY_KEY, arrayKeys.join(','));
47
- }
48
-
49
- return `${result}${hasQuery ? '?' : ''}${searchParams.toString()}`;
29
+ return `${result}${queryStr ? '?' : ''}${queryStr}`;
50
30
  };
51
31
 
52
32
  export const createRPC = <T, OPTS extends Record<string, KnownAny> = VovkDefaultFetcherOptions>(
@@ -62,7 +42,8 @@ export const createRPC = <T, OPTS extends Record<string, KnownAny> = VovkDefault
62
42
  const controllerPrefix = trimPath(schema.prefix ?? '');
63
43
  const { fetcher: settingsFetcher = defaultFetcher } = options ?? {};
64
44
 
65
- for (const [staticMethodName, { path, httpMethod, validation }] of Object.entries(schema.handlers)) {
45
+ for (const [staticMethodName, handlerSchema] of Object.entries(schema.handlers)) {
46
+ const { path, httpMethod, validation } = handlerSchema;
66
47
  const getEndpoint = ({
67
48
  apiRoot,
68
49
  params,
@@ -125,6 +106,8 @@ export const createRPC = <T, OPTS extends Record<string, KnownAny> = VovkDefault
125
106
  return input.transform ? fetcherPromise.then(input.transform) : fetcherPromise;
126
107
  };
127
108
 
109
+ handler.schema = handlerSchema;
110
+
128
111
  // @ts-expect-error TODO
129
112
  client[staticMethodName] = handler;
130
113
  }
@@ -1,5 +1,2 @@
1
- import { createRPC } from './createRPC';
2
- import type { VovkClientFetcher, VovkClientOptions, VovkDefaultFetcherOptions, VovkValidateOnClient } from './types';
3
-
4
- export { createRPC };
5
- export type { VovkClientFetcher, VovkClientOptions, VovkDefaultFetcherOptions, VovkValidateOnClient };
1
+ export { createRPC } from './createRPC';
2
+ export type { VovkClientFetcher, VovkClientOptions, VovkDefaultFetcherOptions, VovkValidateOnClient } from './types';
@@ -5,6 +5,7 @@ import type {
5
5
  VovkControllerBody,
6
6
  VovkControllerQuery,
7
7
  VovkControllerParams,
8
+ VovkHandlerSchema,
8
9
  } from '../types';
9
10
  import type { StreamJSONResponse } from '../StreamJSONResponse';
10
11
  import type { NextResponse } from 'next/server';
@@ -40,7 +41,7 @@ type ClientMethod<
40
41
  T extends (...args: KnownAny[]) => void | object | StreamJSONResponse<STREAM> | Promise<StreamJSONResponse<STREAM>>,
41
42
  OPTS extends Record<string, KnownAny>,
42
43
  STREAM extends KnownAny = unknown,
43
- > = <R>(
44
+ > = (<R>(
44
45
  options: (StaticMethodInput<T> extends { body?: undefined | null; query?: undefined; params?: undefined }
45
46
  ? unknown
46
47
  : Parameters<T>[0] extends void
@@ -61,7 +62,9 @@ type ClientMethod<
61
62
  ? Promise<StreamAsyncIterator<U>>
62
63
  : R extends object
63
64
  ? Promise<R>
64
- : StaticMethodReturnPromise<T>;
65
+ : StaticMethodReturnPromise<T>) & {
66
+ schema: VovkHandlerSchema;
67
+ };
65
68
 
66
69
  type OmitNever<T> = {
67
70
  [K in keyof T as T[K] extends never ? never : K]: T[K];
@@ -1,4 +1,4 @@
1
- import type { HandlerSchema, KnownAny, VovkController, VovkRequest } from './types';
1
+ import type { VovkHandlerSchema, KnownAny, VovkController, VovkRequest } from './types';
2
2
 
3
3
  type Next = () => Promise<unknown>;
4
4
 
@@ -8,8 +8,8 @@ export function createDecorator<ARGS extends unknown[], REQUEST = VovkRequest>(
8
8
  this: VovkController,
9
9
  ...args: ARGS
10
10
  ) =>
11
- | Omit<HandlerSchema, 'path' | 'httpMethod'>
12
- | ((handlerSchema: HandlerSchema | null) => Omit<HandlerSchema, 'path' | 'httpMethod'>)
11
+ | Omit<VovkHandlerSchema, 'path' | 'httpMethod'>
12
+ | ((handlerSchema: VovkHandlerSchema | null) => Omit<VovkHandlerSchema, 'path' | 'httpMethod'>)
13
13
  | null
14
14
  | undefined
15
15
  ) {
@@ -25,7 +25,7 @@ export function createDecorator<ARGS extends unknown[], REQUEST = VovkRequest>(
25
25
  }
26
26
  const sourceMethod = originalMethod._sourceMethod ?? originalMethod;
27
27
 
28
- const handlerSchema: HandlerSchema | null = controller._handlers?.[propertyKey] ?? null;
28
+ const handlerSchema: VovkHandlerSchema | null = controller._handlers?.[propertyKey] ?? null;
29
29
  const initResultReturn = initHandler?.call(controller, ...args);
30
30
  const initResult = typeof initResultReturn === 'function' ? initResultReturn(handlerSchema) : initResultReturn;
31
31
 
package/src/index.ts CHANGED
@@ -16,6 +16,7 @@ import {
16
16
  type VovkSchema,
17
17
  type VovkWorkerSchema,
18
18
  type VovkControllerSchema,
19
+ type VovkHandlerSchema,
19
20
  } from './types';
20
21
  import {
21
22
  type VovkClientOptions,
@@ -50,6 +51,7 @@ export {
50
51
  type VovkClientOptions,
51
52
  type VovkWorkerSchema,
52
53
  type VovkControllerSchema,
54
+ type VovkHandlerSchema,
53
55
  StreamJSONResponse,
54
56
  HttpException,
55
57
  HttpStatus,
package/src/types.ts CHANGED
@@ -6,7 +6,7 @@ export type KnownAny = any; // eslint-disable-line @typescript-eslint/no-explici
6
6
 
7
7
  export type StaticClass = Function; // eslint-disable-line @typescript-eslint/no-unsafe-function-type
8
8
 
9
- export type HandlerSchema = {
9
+ export type VovkHandlerSchema = {
10
10
  path: string;
11
11
  httpMethod: HttpMethod;
12
12
  validation?: { query?: KnownAny; body?: KnownAny };
@@ -17,7 +17,7 @@ export type VovkControllerSchema = {
17
17
  controllerName: string;
18
18
  originalControllerName: string;
19
19
  prefix?: string;
20
- handlers: Record<string, HandlerSchema>;
20
+ handlers: Record<string, VovkHandlerSchema>;
21
21
  };
22
22
 
23
23
  export type VovkWorkerSchema = {
@@ -107,7 +107,7 @@ export interface VovkRequest<BODY = undefined, QUERY extends object | undefined
107
107
  }
108
108
 
109
109
  export type ControllerStaticMethod<
110
- REQ extends VovkRequest<KnownAny, KnownAny> = VovkRequest<undefined, Record<string, string | string[]>>,
110
+ REQ extends VovkRequest<KnownAny, KnownAny> = VovkRequest<undefined, Record<string, KnownAny>>,
111
111
  PARAMS extends { [key: string]: string } = KnownAny,
112
112
  > = ((req: REQ, params: PARAMS) => unknown) & {
113
113
  _controller?: VovkController;
@@ -0,0 +1,160 @@
1
+ import type { KnownAny } from '../types';
2
+
3
+ /**
4
+ * Parse a bracket-based key (e.g. "z[d][0][x]" or "arr[]")
5
+ * into an array of path segments (strings or special push-markers).
6
+ *
7
+ * Example: "z[d][0][x]" => ["z", "d", "0", "x"]
8
+ * Example: "arr[]" => ["arr", "" ] // "" indicates "push" onto array
9
+ */
10
+ function parseKey(key: string): string[] {
11
+ // The first segment is everything up to the first '[' (or the entire key if no '[')
12
+ const segments: string[] = [];
13
+ const topKeyMatch = key.match(/^([^[\]]+)/);
14
+ if (topKeyMatch) {
15
+ segments.push(topKeyMatch[1]);
16
+ } else {
17
+ // If it starts with brackets, treat it as empty? (edge case)
18
+ segments.push('');
19
+ }
20
+
21
+ // Now capture all bracket parts: [something], [0], []
22
+ const bracketRegex = /\[([^[\]]*)\]/g;
23
+ let match: RegExpExecArray | null;
24
+ while ((match = bracketRegex.exec(key)) !== null) {
25
+ // match[1] is the content inside the brackets
26
+ segments.push(match[1]);
27
+ }
28
+
29
+ return segments;
30
+ }
31
+
32
+ /**
33
+ * Recursively set a value in a nested object/array, given a path of segments.
34
+ * - If segment is numeric => treat as array index
35
+ * - If segment is empty "" => push to array
36
+ * - Else => object property
37
+ */
38
+ function setValue(obj: Record<string, KnownAny>, path: string[], value: KnownAny): void {
39
+ let current: KnownAny = obj;
40
+
41
+ for (let i = 0; i < path.length; i++) {
42
+ const segment = path[i];
43
+
44
+ // If we're at the last segment, set the value
45
+ if (i === path.length - 1) {
46
+ if (segment === '') {
47
+ // Empty bracket => push
48
+ if (!Array.isArray(current)) {
49
+ current = [];
50
+ }
51
+ current.push(value);
52
+ } else if (!isNaN(Number(segment))) {
53
+ // Numeric segment => array index
54
+ const idx = Number(segment);
55
+ if (!Array.isArray(current)) {
56
+ current = [];
57
+ }
58
+ current[idx] = value;
59
+ } else {
60
+ // Object property
61
+ current[segment] = value;
62
+ }
63
+ } else {
64
+ // Not the last segment: descend into existing structure or create it
65
+ const nextSegment = path[i + 1];
66
+
67
+ if (segment === '') {
68
+ // Empty bracket => push
69
+ if (!Array.isArray(current)) {
70
+ // Convert the current node into an array, if not one
71
+ current = [];
72
+ }
73
+ // If we are not at the last path, we need a placeholder object or array
74
+ // for the next segment. We'll push something and move current to that.
75
+ if (current.length === 0) {
76
+ // nothing in array yet
77
+ current.push(typeof nextSegment === 'string' && !isNaN(Number(nextSegment)) ? [] : {});
78
+ } else if (typeof nextSegment === 'string' && !isNaN(Number(nextSegment))) {
79
+ // next is numeric => we want an array
80
+ if (!Array.isArray(current[current.length - 1])) {
81
+ current[current.length - 1] = [];
82
+ }
83
+ } else {
84
+ // next is not numeric => we want an object
85
+ if (typeof current[current.length - 1] !== 'object') {
86
+ current[current.length - 1] = {};
87
+ }
88
+ }
89
+ current = current[current.length - 1];
90
+ } else if (!isNaN(Number(segment))) {
91
+ // segment is numeric => array index
92
+ const idx = Number(segment);
93
+ if (!Array.isArray(current)) {
94
+ current = [];
95
+ }
96
+ if (current[idx] === undefined) {
97
+ // Create placeholder for next segment
98
+ current[idx] = typeof nextSegment === 'string' && !isNaN(Number(nextSegment)) ? [] : {};
99
+ }
100
+ current = current[idx];
101
+ } else {
102
+ // segment is an object key
103
+ if (current[segment] === undefined) {
104
+ // Create placeholder
105
+ current[segment] = typeof nextSegment === 'string' && !isNaN(Number(nextSegment)) ? [] : {};
106
+ }
107
+ current = current[segment];
108
+ }
109
+ }
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Deserialize a bracket-based query string into an object.
115
+ *
116
+ * Supports:
117
+ * - Key/value pairs with nested brackets (e.g. "a[b][0]=value")
118
+ * - Arrays with empty bracket (e.g. "arr[]=1&arr[]=2")
119
+ * - Mixed arrays of objects, etc.
120
+ *
121
+ * @example
122
+ * parseQuery("x=xx&y[0]=yy&y[1]=uu&z[f]=x&z[u][0]=uu&z[u][1]=xx&z[d][x]=ee")
123
+ * => {
124
+ * x: "xx",
125
+ * y: ["yy", "uu"],
126
+ * z: {
127
+ * f: "x",
128
+ * u: ["uu", "xx"],
129
+ * d: { x: "ee" }
130
+ * }
131
+ * }
132
+ *
133
+ * @param queryString - The raw query string (e.g. location.search.slice(1))
134
+ * @returns - A nested object representing the query params
135
+ */
136
+ export default function parseQuery(queryString: string): Record<string, KnownAny> {
137
+ const result: Record<string, KnownAny> = {};
138
+
139
+ if (!queryString) return result;
140
+
141
+ // Split into key=value pairs
142
+ const pairs = queryString
143
+ .replace(/^\?/, '') // Remove leading "?" if present
144
+ .split('&');
145
+
146
+ for (const pair of pairs) {
147
+ const [rawKey, rawVal = ''] = pair.split('=');
148
+
149
+ const decodedKey = decodeURIComponent(rawKey || '');
150
+ const decodedVal = decodeURIComponent(rawVal);
151
+
152
+ // Parse bracket notation
153
+ const pathSegments = parseKey(decodedKey);
154
+
155
+ // Insert into the result object
156
+ setValue(result, pathSegments, decodedVal);
157
+ }
158
+
159
+ return result;
160
+ }
@@ -1,26 +1,6 @@
1
- import { ARRAY_QUERY_KEY } from '../client/createRPC';
2
1
  import type { KnownAny, VovkRequest } from '../types';
2
+ import parseQuery from './parseQuery';
3
3
 
4
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;
5
+ return parseQuery(req.nextUrl.search) as NonNullable<T>;
26
6
  }
@@ -0,0 +1,69 @@
1
+ import type { KnownAny } from '../types';
2
+
3
+ /**
4
+ * Recursively build query parameters from an object.
5
+ *
6
+ * @param key - The query key so far (e.g. 'user', 'user[0]', 'user[0][name]')
7
+ * @param value - The current value to serialize
8
+ * @returns - An array of `key=value` strings
9
+ */
10
+ function buildParams(key: string, value: KnownAny): string[] {
11
+ if (value === null || value === undefined) {
12
+ return []; // skip null/undefined values entirely
13
+ }
14
+
15
+ // If value is an object or array, we need to recurse
16
+ if (typeof value === 'object') {
17
+ // Array case
18
+ if (Array.isArray(value)) {
19
+ /**
20
+ * We use index-based bracket notation here:
21
+ * e.g. for value = ['aa', 'bb'] and key = 'foo'
22
+ * => "foo[0]=aa&foo[1]=bb"
23
+ *
24
+ * If you prefer "foo[]=aa&foo[]=bb" style, replace:
25
+ * `${key}[${i}]`
26
+ * with:
27
+ * `${key}[]`
28
+ */
29
+ return value.flatMap((v, i) => {
30
+ const newKey = `${key}[${i}]`;
31
+ return buildParams(newKey, v);
32
+ });
33
+ }
34
+
35
+ // Plain object case
36
+ return Object.keys(value).flatMap((k) => {
37
+ const newKey = `${key}[${k}]`;
38
+ return buildParams(newKey, value[k]);
39
+ });
40
+ }
41
+
42
+ return [`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`];
43
+ }
44
+
45
+ /**
46
+ * Serialize a nested object (including arrays, arrays of objects, etc.)
47
+ * into a bracket-based query string.
48
+ *
49
+ * @example
50
+ * serializeQuery({ x: 'xx', y: [1, 2], z: { f: 'x' } })
51
+ * => "x=xx&y[0]=1&y[1]=2&z[f]=x"
52
+ *
53
+ * @param obj - The input object to be serialized
54
+ * @returns - A bracket-based query string (without leading "?")
55
+ */
56
+ export default function serializeQuery(obj: Record<string, KnownAny>): string {
57
+ if (!obj || typeof obj !== 'object') return '';
58
+
59
+ // Collect query segments
60
+ const segments: string[] = [];
61
+ for (const key in obj) {
62
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
63
+ const value = obj[key];
64
+ segments.push(...buildParams(key, value));
65
+ }
66
+ }
67
+
68
+ return segments.join('&');
69
+ }
@@ -1,10 +1,7 @@
1
1
  import type { VovkWorkerSchema } from '../types';
2
2
  import type { WorkerInput, WorkerOutput, WorkerPromiseInstance } from './types';
3
3
 
4
- export function createWPC<T extends object>(
5
- currentWorker: Worker | null,
6
- workerSchema: object
7
- ): WorkerPromiseInstance<T> {
4
+ export function createWPC<T extends object>(workerSchema: object, currentWorker?: Worker): WorkerPromiseInstance<T> {
8
5
  if (!workerSchema) throw new Error('Worker schema is not provided');
9
6
  const schema = workerSchema as T & VovkWorkerSchema;
10
7
  const instance = {
@@ -26,7 +23,7 @@ export function createWPC<T extends object>(
26
23
  return instance;
27
24
  };
28
25
 
29
- instance.fork = (worker: Worker) => createWPC<T>(worker, schema);
26
+ instance.fork = (worker: Worker) => createWPC<T>(schema, worker);
30
27
 
31
28
  for (const methodName of Object.keys(schema.handlers) as (keyof T & string)[]) {
32
29
  const { isGenerator } = schema.handlers[methodName];
@@ -1,4 +1,2 @@
1
- import { worker } from './worker';
2
- import { createWPC } from './createWPC';
3
-
4
- export { worker, createWPC };
1
+ export { worker } from './worker';
2
+ export { createWPC } from './createWPC';