vovk 3.0.0-draft.8 → 3.0.0-draft.80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -95
- package/{HttpException.d.ts → dist/HttpException.d.ts} +2 -2
- package/{HttpException.js → dist/HttpException.js} +3 -3
- package/{StreamResponse.d.ts → dist/StreamJSONResponse.d.ts} +3 -3
- package/{StreamResponse.js → dist/StreamJSONResponse.js} +5 -5
- package/{Segment.d.ts → dist/VovkApp.d.ts} +11 -10
- package/{Segment.js → dist/VovkApp.js} +28 -24
- package/dist/client/createRPC.d.ts +4 -0
- package/{client/clientizeController.js → dist/client/createRPC.js} +22 -40
- package/dist/client/defaultFetcher.d.ts +4 -0
- package/{client → dist/client}/defaultFetcher.js +19 -8
- package/{client → dist/client}/defaultHandler.d.ts +1 -1
- package/dist/client/defaultHandler.js +22 -0
- package/dist/client/defaultStreamHandler.d.ts +4 -0
- package/{client → dist/client}/defaultStreamHandler.js +5 -5
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.js +8 -0
- package/dist/client/types.d.ts +100 -0
- package/dist/createDecorator.d.ts +4 -0
- package/{createDecorator.js → dist/createDecorator.js} +16 -15
- package/{createSegment.d.ts → dist/createVovkApp.d.ts} +10 -10
- package/{createSegment.js → dist/createVovkApp.js} +30 -30
- package/dist/index.d.ts +60 -0
- package/dist/index.js +24 -0
- package/dist/openapi/fromSchema.d.ts +3 -0
- package/dist/openapi/fromSchema.js +26 -0
- package/dist/openapi/index.d.ts +1 -0
- package/dist/openapi/index.js +5 -0
- package/dist/openapi/openapi.d.ts +11 -0
- package/dist/openapi/openapi.js +70 -0
- package/dist/types.d.ts +148 -0
- package/dist/types.js +65 -0
- package/dist/utils/generateStaticAPI.d.ts +4 -0
- package/{generateStaticAPI.js → dist/utils/generateStaticAPI.js} +3 -3
- package/{utils → dist/utils}/getSchema.d.ts +1 -2
- package/dist/utils/getSchema.js +29 -0
- package/dist/utils/parseQuery.d.ts +25 -0
- package/dist/utils/parseQuery.js +156 -0
- package/dist/utils/reqForm.d.ts +2 -0
- package/dist/utils/reqForm.js +13 -0
- package/{utils → dist/utils}/reqMeta.d.ts +1 -2
- package/{utils → dist/utils}/reqQuery.d.ts +1 -2
- package/dist/utils/reqQuery.js +10 -0
- package/dist/utils/serializeQuery.d.ts +13 -0
- package/dist/utils/serializeQuery.js +65 -0
- package/dist/utils/setHandlerValidation.d.ts +4 -0
- package/dist/utils/setHandlerValidation.js +15 -0
- package/package.json +8 -2
- package/.npmignore +0 -2
- package/client/clientizeController.d.ts +0 -4
- package/client/defaultFetcher.d.ts +0 -4
- package/client/defaultHandler.js +0 -21
- package/client/defaultStreamHandler.d.ts +0 -4
- package/client/index.d.ts +0 -4
- package/client/index.js +0 -5
- package/client/types.d.ts +0 -100
- package/createDecorator.d.ts +0 -4
- package/generateStaticAPI.d.ts +0 -4
- package/index.d.ts +0 -60
- package/index.js +0 -20
- package/types.d.ts +0 -186
- package/types.js +0 -65
- package/utils/getSchema.js +0 -38
- package/utils/reqQuery.js +0 -25
- package/utils/setClientValidatorsForHandler.d.ts +0 -5
- package/utils/setClientValidatorsForHandler.js +0 -28
- package/worker/index.d.ts +0 -3
- package/worker/index.js +0 -7
- package/worker/promisifyWorker.d.ts +0 -2
- package/worker/promisifyWorker.js +0 -143
- package/worker/types.d.ts +0 -31
- package/worker/types.js +0 -2
- package/worker/worker.d.ts +0 -1
- package/worker/worker.js +0 -44
- /package/{client → dist/client}/types.js +0 -0
- /package/{utils → dist/utils}/reqMeta.js +0 -0
- /package/{utils → dist/utils}/shim.d.ts +0 -0
- /package/{utils → dist/utils}/shim.js +0 -0
package/README.md
CHANGED
|
@@ -4,108 +4,21 @@
|
|
|
4
4
|
<source width="300" media="(prefers-color-scheme: light)" srcset="https://vovk.dev/vovk-logo.svg">
|
|
5
5
|
<img width="300" alt="vovk" src="https://vovk.dev/vovk-logo.svg">
|
|
6
6
|
</picture><br>
|
|
7
|
-
<strong>
|
|
8
|
-
|
|
7
|
+
<strong>REST + RPC = ♥️</strong>
|
|
9
8
|
</p>
|
|
10
9
|
|
|
11
10
|
<p align="center">
|
|
12
|
-
|
|
13
|
-
<br><br>
|
|
14
|
-
ℹ️ Improved syntax for Zod and DTO validation is coming soon. Stay tuned!
|
|
15
|
-
</p>
|
|
16
|
-
|
|
17
|
-
<p align="center">
|
|
18
|
-
<a href="https://vovk.dev/">Documentation</a>
|
|
19
|
-
<a href="https://discord.gg/qdT8WEHUuP">Discord</a>
|
|
20
|
-
<a href="https://github.com/finom/vovk-examples">Code Examples</a>
|
|
21
|
-
<a href="https://github.com/finom/vovk-zod">vovk-zod</a>
|
|
22
|
-
<a href="https://github.com/finom/vovk-hello-world">vovk-hello-world</a>
|
|
23
|
-
<a href="https://github.com/finom/vovk-react-native-example">vovk-react-native-example</a>
|
|
24
|
-
</p>
|
|
25
|
-
<p align="center">
|
|
26
|
-
<a href="https://www.npmjs.com/package/vovk"><img src="https://badge.fury.io/js/vovk.svg" alt="npm version" /></a>
|
|
27
|
-
<a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg" alt="TypeScript" /></a>
|
|
28
|
-
<a href="https://github.com/finom/vovk/actions/workflows/main.yml"><img src="https://github.com/finom/vovk/actions/workflows/main.yml/badge.svg" alt="Build status" /></a>
|
|
11
|
+
Back-end meta-framework for <a href="https://nextjs.org/docs/app">Next.js</a>
|
|
29
12
|
</p>
|
|
30
13
|
|
|
14
|
+
---
|
|
31
15
|
|
|
32
|
-
|
|
16
|
+
## vovk [](https://www.npmjs.com/package/vovk)
|
|
33
17
|
|
|
34
|
-
|
|
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.
|
|
35
19
|
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
import { get, prefix, type VovkRequest } from 'vovk';
|
|
39
|
-
import PostService from './PostService';
|
|
40
|
-
|
|
41
|
-
@prefix('posts')
|
|
42
|
-
export default class PostController {
|
|
43
|
-
/**
|
|
44
|
-
* Create a comment on a post
|
|
45
|
-
* POST /api/posts/:postId/comments
|
|
46
|
-
*/
|
|
47
|
-
@post(':postId/comments')
|
|
48
|
-
static async createComment(
|
|
49
|
-
// decorate NextRequest type with body and query types
|
|
50
|
-
req: VovkRequest<
|
|
51
|
-
{ content: string; userId: string },
|
|
52
|
-
{ notificationType: 'push' | 'email' }
|
|
53
|
-
>,
|
|
54
|
-
{ postId }: { postId: string } // params
|
|
55
|
-
) {
|
|
56
|
-
// use standard Next.js API to get body and query
|
|
57
|
-
const { content, userId } = await req.json();
|
|
58
|
-
const notificationType = req.nextUrl.searchParams.get('notificationType');
|
|
59
|
-
|
|
60
|
-
// perform the request to the database in a custom service
|
|
61
|
-
return PostService.createComment({
|
|
62
|
-
postId, content, userId, notificationType,
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
}
|
|
20
|
+
```sh
|
|
21
|
+
npm install vovk
|
|
66
22
|
```
|
|
67
23
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
```tsx
|
|
71
|
-
'use client';
|
|
72
|
-
import { useState } from 'react';
|
|
73
|
-
import { PostController } from 'vovk-client';
|
|
74
|
-
import type { VovkReturnType } from 'vovk';
|
|
75
|
-
|
|
76
|
-
export default function Example() {
|
|
77
|
-
const [response, setResponse] = useState<VovkReturnType<typeof PostController.createComment>>();
|
|
78
|
-
|
|
79
|
-
return (
|
|
80
|
-
<>
|
|
81
|
-
<button
|
|
82
|
-
onClick={async () => setResponse(
|
|
83
|
-
await PostController.createComment({
|
|
84
|
-
body: {
|
|
85
|
-
content: 'Hello, World!',
|
|
86
|
-
userId: '1',
|
|
87
|
-
},
|
|
88
|
-
params: { postId: '69' },
|
|
89
|
-
query: { notificationType: 'push' }
|
|
90
|
-
})
|
|
91
|
-
)}
|
|
92
|
-
>
|
|
93
|
-
Post a comment
|
|
94
|
-
</button>
|
|
95
|
-
<div>{JSON.stringify(response)}</div>
|
|
96
|
-
</>
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
Alternatively, the resource can be fetched wit the regular `fetch` function:
|
|
102
|
-
|
|
103
|
-
```ts
|
|
104
|
-
fetch('/api/posts/69?notificationType=push', {
|
|
105
|
-
method: 'POST',
|
|
106
|
-
body: JSON.stringify({
|
|
107
|
-
content: 'Hello, World!',
|
|
108
|
-
userId: '1',
|
|
109
|
-
}),
|
|
110
|
-
})
|
|
111
|
-
```
|
|
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,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare class
|
|
1
|
+
import type { HttpStatus } from './types';
|
|
2
|
+
export declare class HttpException extends Error {
|
|
3
3
|
statusCode: HttpStatus;
|
|
4
4
|
message: string;
|
|
5
5
|
cause?: unknown;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
class
|
|
3
|
+
exports.HttpException = void 0;
|
|
4
|
+
class HttpException extends Error {
|
|
5
5
|
statusCode;
|
|
6
6
|
message;
|
|
7
7
|
cause;
|
|
@@ -12,4 +12,4 @@ class _HttpException extends Error {
|
|
|
12
12
|
this.cause = cause;
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
|
-
exports.
|
|
15
|
+
exports.HttpException = HttpException;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { KnownAny, StreamAbortMessage } from './types';
|
|
2
2
|
import './utils/shim';
|
|
3
|
-
export declare class
|
|
3
|
+
export declare class StreamJSONResponse<T> extends Response {
|
|
4
4
|
static defaultHeaders: {
|
|
5
|
-
'
|
|
5
|
+
'content-type': string;
|
|
6
6
|
};
|
|
7
7
|
isClosed: boolean;
|
|
8
8
|
controller?: ReadableStreamDefaultController;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.StreamJSONResponse = void 0;
|
|
4
4
|
require("./utils/shim");
|
|
5
|
-
class
|
|
5
|
+
class StreamJSONResponse extends Response {
|
|
6
6
|
static defaultHeaders = {
|
|
7
|
-
'
|
|
7
|
+
'content-type': 'text/plain; format=jsonlines',
|
|
8
8
|
};
|
|
9
9
|
isClosed = false;
|
|
10
10
|
controller;
|
|
@@ -23,7 +23,7 @@ class _StreamResponse extends Response {
|
|
|
23
23
|
});
|
|
24
24
|
super(readableStream, {
|
|
25
25
|
...init,
|
|
26
|
-
headers: init?.headers ??
|
|
26
|
+
headers: init?.headers ?? StreamJSONResponse.defaultHeaders,
|
|
27
27
|
});
|
|
28
28
|
this.readableStream = readableStream;
|
|
29
29
|
this.encoder = encoder;
|
|
@@ -50,4 +50,4 @@ class _StreamResponse extends Response {
|
|
|
50
50
|
this.close();
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
-
exports.
|
|
53
|
+
exports.StreamJSONResponse = StreamJSONResponse;
|
|
@@ -1,27 +1,28 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import type { NextRequest } from 'next/server';
|
|
2
|
+
import { HttpMethod, HttpStatus, type RouteHandler, type VovkController, type DecoratorOptions } from './types';
|
|
3
|
+
export declare class VovkApp {
|
|
3
4
|
#private;
|
|
4
5
|
private static getHeadersFromOptions;
|
|
5
|
-
|
|
6
|
-
GET: (req:
|
|
6
|
+
routes: Record<HttpMethod, Map<VovkController, Record<string, RouteHandler>>>;
|
|
7
|
+
GET: (req: NextRequest, data: {
|
|
7
8
|
params: Promise<Record<string, string[]>>;
|
|
8
9
|
}) => Promise<Response>;
|
|
9
|
-
POST: (req:
|
|
10
|
+
POST: (req: NextRequest, data: {
|
|
10
11
|
params: Promise<Record<string, string[]>>;
|
|
11
12
|
}) => Promise<Response>;
|
|
12
|
-
PUT: (req:
|
|
13
|
+
PUT: (req: NextRequest, data: {
|
|
13
14
|
params: Promise<Record<string, string[]>>;
|
|
14
15
|
}) => Promise<Response>;
|
|
15
|
-
PATCH: (req:
|
|
16
|
+
PATCH: (req: NextRequest, data: {
|
|
16
17
|
params: Promise<Record<string, string[]>>;
|
|
17
18
|
}) => Promise<Response>;
|
|
18
|
-
DELETE: (req:
|
|
19
|
+
DELETE: (req: NextRequest, data: {
|
|
19
20
|
params: Promise<Record<string, string[]>>;
|
|
20
21
|
}) => Promise<Response>;
|
|
21
|
-
HEAD: (req:
|
|
22
|
+
HEAD: (req: NextRequest, data: {
|
|
22
23
|
params: Promise<Record<string, string[]>>;
|
|
23
24
|
}) => Promise<Response>;
|
|
24
|
-
OPTIONS: (req:
|
|
25
|
+
OPTIONS: (req: NextRequest, data: {
|
|
25
26
|
params: Promise<Record<string, string[]>>;
|
|
26
27
|
}) => Promise<Response>;
|
|
27
28
|
respond: (status: HttpStatus, body: unknown, options?: DecoratorOptions) => Response;
|
|
@@ -4,13 +4,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
var _a;
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.
|
|
7
|
+
exports.VovkApp = void 0;
|
|
8
8
|
const types_1 = require("./types");
|
|
9
9
|
const HttpException_1 = require("./HttpException");
|
|
10
|
-
const
|
|
10
|
+
const StreamJSONResponse_1 = require("./StreamJSONResponse");
|
|
11
11
|
const reqQuery_1 = __importDefault(require("./utils/reqQuery"));
|
|
12
12
|
const reqMeta_1 = __importDefault(require("./utils/reqMeta"));
|
|
13
|
-
|
|
13
|
+
const reqForm_1 = __importDefault(require("./utils/reqForm"));
|
|
14
|
+
class VovkApp {
|
|
14
15
|
static getHeadersFromOptions(options) {
|
|
15
16
|
if (!options)
|
|
16
17
|
return {};
|
|
@@ -25,7 +26,7 @@ class _Segment {
|
|
|
25
26
|
};
|
|
26
27
|
return headers;
|
|
27
28
|
}
|
|
28
|
-
|
|
29
|
+
routes = {
|
|
29
30
|
GET: new Map(),
|
|
30
31
|
POST: new Map(),
|
|
31
32
|
PUT: new Map(),
|
|
@@ -34,13 +35,13 @@ class _Segment {
|
|
|
34
35
|
HEAD: new Map(),
|
|
35
36
|
OPTIONS: new Map(),
|
|
36
37
|
};
|
|
37
|
-
GET = async (req, data) => this.#callMethod(types_1.
|
|
38
|
-
POST = async (req, data) => this.#callMethod(types_1.
|
|
39
|
-
PUT = async (req, data) => this.#callMethod(types_1.
|
|
40
|
-
PATCH = async (req, data) => this.#callMethod(types_1.
|
|
41
|
-
DELETE = async (req, data) => this.#callMethod(types_1.
|
|
42
|
-
HEAD = async (req, data) => this.#callMethod(types_1.
|
|
43
|
-
OPTIONS = async (req, data) => this.#callMethod(types_1.
|
|
38
|
+
GET = async (req, data) => this.#callMethod(types_1.HttpMethod.GET, req, await data.params);
|
|
39
|
+
POST = async (req, data) => this.#callMethod(types_1.HttpMethod.POST, req, await data.params);
|
|
40
|
+
PUT = async (req, data) => this.#callMethod(types_1.HttpMethod.PUT, req, await data.params);
|
|
41
|
+
PATCH = async (req, data) => this.#callMethod(types_1.HttpMethod.PATCH, req, await data.params);
|
|
42
|
+
DELETE = async (req, data) => this.#callMethod(types_1.HttpMethod.DELETE, req, await data.params);
|
|
43
|
+
HEAD = async (req, data) => this.#callMethod(types_1.HttpMethod.HEAD, req, await data.params);
|
|
44
|
+
OPTIONS = async (req, data) => this.#callMethod(types_1.HttpMethod.OPTIONS, req, await data.params);
|
|
44
45
|
respond = (status, body, options) => {
|
|
45
46
|
return new Response(JSON.stringify(body), {
|
|
46
47
|
status,
|
|
@@ -50,22 +51,24 @@ class _Segment {
|
|
|
50
51
|
},
|
|
51
52
|
});
|
|
52
53
|
};
|
|
53
|
-
#respondWithError = (statusCode, message, options) => {
|
|
54
|
+
#respondWithError = (statusCode, message, options, cause) => {
|
|
54
55
|
return this.respond(statusCode, {
|
|
56
|
+
cause,
|
|
55
57
|
statusCode,
|
|
56
58
|
message,
|
|
57
59
|
isError: true,
|
|
58
60
|
}, options);
|
|
59
61
|
};
|
|
60
|
-
#callMethod = async (httpMethod,
|
|
61
|
-
const
|
|
62
|
+
#callMethod = async (httpMethod, nextReq, params) => {
|
|
63
|
+
const req = nextReq;
|
|
64
|
+
const controllers = this.routes[httpMethod];
|
|
62
65
|
const methodParams = {};
|
|
63
66
|
const path = params[Object.keys(params)[0]];
|
|
64
67
|
const handlers = {};
|
|
65
68
|
controllers.forEach((staticMethods, controller) => {
|
|
66
69
|
const prefix = controller._prefix ?? '';
|
|
67
70
|
if (!controller._activated) {
|
|
68
|
-
throw new HttpException_1.
|
|
71
|
+
throw new HttpException_1.HttpException(types_1.HttpStatus.INTERNAL_SERVER_ERROR, `Controller "${controller.name}" found but not activated`);
|
|
69
72
|
}
|
|
70
73
|
Object.entries(staticMethods).forEach(([path, staticMethod]) => {
|
|
71
74
|
const fullPath = [prefix, path].filter(Boolean).join('/');
|
|
@@ -96,7 +99,7 @@ class _Segment {
|
|
|
96
99
|
if (routeSegment.startsWith(':')) {
|
|
97
100
|
const parameter = routeSegment.slice(1);
|
|
98
101
|
if (parameter in methodParams) {
|
|
99
|
-
throw new HttpException_1.
|
|
102
|
+
throw new HttpException_1.HttpException(types_1.HttpStatus.INTERNAL_SERVER_ERROR, `Duplicate parameter "${parameter}"`);
|
|
100
103
|
}
|
|
101
104
|
// If it's a parameterized segment, capture the parameter value.
|
|
102
105
|
methodParams[parameter] = pathSegment;
|
|
@@ -110,7 +113,7 @@ class _Segment {
|
|
|
110
113
|
});
|
|
111
114
|
}
|
|
112
115
|
if (methodKeys.length > 1) {
|
|
113
|
-
throw new HttpException_1.
|
|
116
|
+
throw new HttpException_1.HttpException(types_1.HttpStatus.INTERNAL_SERVER_ERROR, `Conflicting routes found: ${methodKeys.join(', ')}`);
|
|
114
117
|
}
|
|
115
118
|
const [methodKey] = methodKeys;
|
|
116
119
|
if (methodKey) {
|
|
@@ -120,13 +123,14 @@ class _Segment {
|
|
|
120
123
|
};
|
|
121
124
|
const handler = getHandler();
|
|
122
125
|
if (!handler) {
|
|
123
|
-
return this.#respondWithError(types_1.
|
|
126
|
+
return this.#respondWithError(types_1.HttpStatus.NOT_FOUND, `Route ${path.join('/')} is not found`);
|
|
124
127
|
}
|
|
125
128
|
const { staticMethod, controller } = handler;
|
|
126
129
|
req.vovk = {
|
|
127
130
|
body: () => req.json(),
|
|
128
131
|
query: () => (0, reqQuery_1.default)(req),
|
|
129
132
|
meta: (meta) => (0, reqMeta_1.default)(req, meta),
|
|
133
|
+
form: () => (0, reqForm_1.default)(req),
|
|
130
134
|
};
|
|
131
135
|
try {
|
|
132
136
|
const result = await staticMethod.call(controller, req, methodParams);
|
|
@@ -137,9 +141,9 @@ class _Segment {
|
|
|
137
141
|
(Reflect.has(result, Symbol.asyncIterator) &&
|
|
138
142
|
typeof result[Symbol.asyncIterator] === 'function'));
|
|
139
143
|
if (isIterator && !(result instanceof Array)) {
|
|
140
|
-
const streamResponse = new
|
|
144
|
+
const streamResponse = new StreamJSONResponse_1.StreamJSONResponse({
|
|
141
145
|
headers: {
|
|
142
|
-
...
|
|
146
|
+
...StreamJSONResponse_1.StreamJSONResponse.defaultHeaders,
|
|
143
147
|
..._a.getHeadersFromOptions(staticMethod._options),
|
|
144
148
|
},
|
|
145
149
|
});
|
|
@@ -171,12 +175,12 @@ class _Segment {
|
|
|
171
175
|
console.error(onErrorError);
|
|
172
176
|
}
|
|
173
177
|
if (err.message !== 'NEXT_REDIRECT' && err.message !== 'NEXT_NOT_FOUND') {
|
|
174
|
-
const statusCode = err.statusCode ?? types_1.
|
|
175
|
-
return this.#respondWithError(statusCode, err.message, staticMethod._options);
|
|
178
|
+
const statusCode = err.statusCode ?? types_1.HttpStatus.INTERNAL_SERVER_ERROR;
|
|
179
|
+
return this.#respondWithError(statusCode, err.message, staticMethod._options, err.cause);
|
|
176
180
|
}
|
|
177
181
|
throw e; // if NEXT_REDIRECT or NEXT_NOT_FOUND, rethrow it
|
|
178
182
|
}
|
|
179
183
|
};
|
|
180
184
|
}
|
|
181
|
-
exports.
|
|
182
|
-
_a =
|
|
185
|
+
exports.VovkApp = VovkApp;
|
|
186
|
+
_a = VovkApp;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type VovkControllerSchema, type KnownAny } from '../types';
|
|
2
|
+
import { type VovkClientOptions, type VovkClient, type VovkDefaultFetcherOptions } from './types';
|
|
3
|
+
declare const createRPC: <T, OPTS extends Record<string, KnownAny> = VovkDefaultFetcherOptions>(controllerSchema: VovkControllerSchema, segmentName?: string, options?: VovkClientOptions<OPTS>) => VovkClient<T, OPTS>;
|
|
4
|
+
export default createRPC;
|
|
@@ -3,68 +3,48 @@ 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._clientizeController = exports.ARRAY_QUERY_KEY = void 0;
|
|
7
6
|
const defaultFetcher_1 = __importDefault(require("./defaultFetcher"));
|
|
8
7
|
const defaultHandler_1 = require("./defaultHandler");
|
|
9
8
|
const defaultStreamHandler_1 = require("./defaultStreamHandler");
|
|
10
|
-
|
|
9
|
+
const serializeQuery_1 = __importDefault(require("../utils/serializeQuery"));
|
|
11
10
|
const trimPath = (path) => path.trim().replace(/^\/|\/$/g, '');
|
|
12
11
|
const getHandlerPath = (endpoint, params, query) => {
|
|
13
12
|
let result = endpoint;
|
|
14
13
|
for (const [key, value] of Object.entries(params ?? {})) {
|
|
15
14
|
result = result.replace(`:${key}`, value);
|
|
16
15
|
}
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
const arrayKeys = [];
|
|
20
|
-
for (const [key, value] of Object.entries(query ?? {})) {
|
|
21
|
-
if (typeof value === 'undefined')
|
|
22
|
-
continue;
|
|
23
|
-
if (value instanceof Array) {
|
|
24
|
-
arrayKeys.push(key);
|
|
25
|
-
for (const val of value) {
|
|
26
|
-
searchParams.append(key, val);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
searchParams.set(key, value);
|
|
31
|
-
}
|
|
32
|
-
hasQuery = true;
|
|
33
|
-
}
|
|
34
|
-
if (arrayKeys.length) {
|
|
35
|
-
searchParams.set(exports.ARRAY_QUERY_KEY, arrayKeys.join(','));
|
|
36
|
-
}
|
|
37
|
-
return `${result}${hasQuery ? '?' : ''}${searchParams.toString()}`;
|
|
16
|
+
const queryStr = query ? (0, serializeQuery_1.default)(query) : null;
|
|
17
|
+
return `${result}${queryStr ? '?' : ''}${queryStr}`;
|
|
38
18
|
};
|
|
39
|
-
const
|
|
40
|
-
const
|
|
19
|
+
const createRPC = (controllerSchema, segmentName, options) => {
|
|
20
|
+
const schema = controllerSchema;
|
|
41
21
|
const client = {};
|
|
42
|
-
if (!controller)
|
|
43
|
-
throw new Error(`Unable to clientize. Controller schema is not provided`);
|
|
44
|
-
const schema = controller._handlers;
|
|
45
22
|
if (!schema)
|
|
46
|
-
throw new Error(`Unable to clientize.
|
|
47
|
-
|
|
23
|
+
throw new Error(`Unable to clientize. Controller schema is not provided`);
|
|
24
|
+
if (!schema.handlers)
|
|
25
|
+
throw new Error(`Unable to clientize. No schema for controller ${String(schema?.controllerName)} provided`);
|
|
26
|
+
const controllerPrefix = trimPath(schema.prefix ?? '');
|
|
48
27
|
const { fetcher: settingsFetcher = defaultFetcher_1.default } = options ?? {};
|
|
49
|
-
for (const [staticMethodName,
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
28
|
+
for (const [staticMethodName, handlerSchema] of Object.entries(schema.handlers)) {
|
|
29
|
+
const { path, httpMethod, validation } = handlerSchema;
|
|
30
|
+
const getEndpoint = ({ apiRoot, params, query, }) => {
|
|
31
|
+
const mainPrefix = (apiRoot.startsWith('http://') || apiRoot.startsWith('https://') || apiRoot.startsWith('/') ? '' : '/') +
|
|
32
|
+
(apiRoot.endsWith('/') ? apiRoot : `${apiRoot}/`) +
|
|
53
33
|
(segmentName ? `${segmentName}/` : '');
|
|
54
34
|
return mainPrefix + getHandlerPath([controllerPrefix, path].filter(Boolean).join('/'), params, query);
|
|
55
35
|
};
|
|
56
36
|
const handler = (input = {}) => {
|
|
57
37
|
const fetcher = input.fetcher ?? settingsFetcher;
|
|
58
38
|
const validate = async ({ body, query, endpoint }) => {
|
|
59
|
-
await (input.validateOnClient ?? options?.validateOnClient)?.({ body, query, endpoint },
|
|
39
|
+
await (input.validateOnClient ?? options?.validateOnClient)?.({ body, query, endpoint }, validation ?? {});
|
|
60
40
|
};
|
|
61
41
|
const internalOptions = {
|
|
62
42
|
name: staticMethodName,
|
|
63
|
-
httpMethod,
|
|
43
|
+
httpMethod: httpMethod,
|
|
64
44
|
getEndpoint,
|
|
65
45
|
validate,
|
|
66
|
-
defaultHandler: defaultHandler_1.
|
|
67
|
-
defaultStreamHandler: defaultStreamHandler_1.
|
|
46
|
+
defaultHandler: defaultHandler_1.defaultHandler,
|
|
47
|
+
defaultStreamHandler: defaultStreamHandler_1.defaultStreamHandler,
|
|
68
48
|
};
|
|
69
49
|
const internalInput = {
|
|
70
50
|
...options?.defaultOptions,
|
|
@@ -85,9 +65,11 @@ const _clientizeController = (givenController, segmentName, options) => {
|
|
|
85
65
|
return Promise.resolve(fetcherPromise);
|
|
86
66
|
return input.transform ? fetcherPromise.then(input.transform) : fetcherPromise;
|
|
87
67
|
};
|
|
88
|
-
|
|
68
|
+
handler.schema = handlerSchema;
|
|
69
|
+
handler.controllerSchema = schema;
|
|
70
|
+
// @ts-expect-error TODO
|
|
89
71
|
client[staticMethodName] = handler;
|
|
90
72
|
}
|
|
91
73
|
return client;
|
|
92
74
|
};
|
|
93
|
-
exports.
|
|
75
|
+
exports.default = createRPC;
|
|
@@ -3,21 +3,26 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.DEFAULT_ERROR_MESSAGE = void 0;
|
|
4
4
|
const types_1 = require("../types");
|
|
5
5
|
const HttpException_1 = require("../HttpException");
|
|
6
|
-
exports.DEFAULT_ERROR_MESSAGE = 'Unknown error at defaultFetcher';
|
|
6
|
+
exports.DEFAULT_ERROR_MESSAGE = 'Unknown error at the defaultFetcher';
|
|
7
7
|
// defaultFetcher uses HttpException class to throw errors of fake HTTP status 0 if client-side error occurs
|
|
8
8
|
// For normal HTTP errors, it uses message and status code from the response of VovkErrorResponse type
|
|
9
|
-
const defaultFetcher = async ({ httpMethod, getEndpoint, validate, defaultHandler, defaultStreamHandler }, { params, query, body,
|
|
10
|
-
const endpoint = getEndpoint({
|
|
9
|
+
const defaultFetcher = async ({ httpMethod, getEndpoint, validate, defaultHandler, defaultStreamHandler }, { params, query, body, apiRoot = '/api', ...options }) => {
|
|
10
|
+
const endpoint = getEndpoint({ apiRoot, params, query });
|
|
11
11
|
if (!options.disableClientValidation) {
|
|
12
12
|
try {
|
|
13
13
|
await validate({ body, query, endpoint });
|
|
14
14
|
}
|
|
15
15
|
catch (e) {
|
|
16
16
|
// if HttpException is thrown, rethrow it
|
|
17
|
-
if (e instanceof HttpException_1.
|
|
17
|
+
if (e instanceof HttpException_1.HttpException)
|
|
18
18
|
throw e;
|
|
19
19
|
// otherwise, throw HttpException with status 0
|
|
20
|
-
throw new HttpException_1.
|
|
20
|
+
throw new HttpException_1.HttpException(types_1.HttpStatus.NULL, e.message ?? exports.DEFAULT_ERROR_MESSAGE, {
|
|
21
|
+
body,
|
|
22
|
+
query,
|
|
23
|
+
params,
|
|
24
|
+
endpoint,
|
|
25
|
+
});
|
|
21
26
|
}
|
|
22
27
|
}
|
|
23
28
|
const init = {
|
|
@@ -36,12 +41,18 @@ const defaultFetcher = async ({ httpMethod, getEndpoint, validate, defaultHandle
|
|
|
36
41
|
}
|
|
37
42
|
catch (e) {
|
|
38
43
|
// handle network errors
|
|
39
|
-
throw new HttpException_1.
|
|
44
|
+
throw new HttpException_1.HttpException(types_1.HttpStatus.NULL, e?.message ?? exports.DEFAULT_ERROR_MESSAGE, {
|
|
45
|
+
body,
|
|
46
|
+
query,
|
|
47
|
+
params,
|
|
48
|
+
endpoint,
|
|
49
|
+
});
|
|
40
50
|
}
|
|
41
|
-
|
|
51
|
+
const contentType = response.headers.get('content-type');
|
|
52
|
+
if (contentType?.startsWith('application/json')) {
|
|
42
53
|
return defaultHandler(response);
|
|
43
54
|
}
|
|
44
|
-
if (
|
|
55
|
+
if (contentType === 'text/plain; format=jsonlines') {
|
|
45
56
|
return defaultStreamHandler(response);
|
|
46
57
|
}
|
|
47
58
|
return response;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare const DEFAULT_ERROR_MESSAGE = "Unknown error at defaultHandler";
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const defaultHandler: (response: Response) => Promise<unknown>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultHandler = exports.DEFAULT_ERROR_MESSAGE = void 0;
|
|
4
|
+
const HttpException_1 = require("../HttpException");
|
|
5
|
+
exports.DEFAULT_ERROR_MESSAGE = 'Unknown error at defaultHandler';
|
|
6
|
+
const defaultHandler = async (response) => {
|
|
7
|
+
let result;
|
|
8
|
+
try {
|
|
9
|
+
result = await response.json();
|
|
10
|
+
}
|
|
11
|
+
catch (e) {
|
|
12
|
+
// handle parsing errors
|
|
13
|
+
throw new HttpException_1.HttpException(response.status, e?.message ?? exports.DEFAULT_ERROR_MESSAGE);
|
|
14
|
+
}
|
|
15
|
+
if (!response.ok) {
|
|
16
|
+
// handle server errors
|
|
17
|
+
const errorResponse = result;
|
|
18
|
+
throw new HttpException_1.HttpException(response.status, errorResponse?.message ?? exports.DEFAULT_ERROR_MESSAGE, errorResponse?.cause);
|
|
19
|
+
}
|
|
20
|
+
return result;
|
|
21
|
+
};
|
|
22
|
+
exports.defaultHandler = defaultHandler;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { VovkStreamAsyncIterable } from './types';
|
|
2
|
+
import '../utils/shim';
|
|
3
|
+
export declare const DEFAULT_ERROR_MESSAGE = "Unknown error at defaultStreamHandler";
|
|
4
|
+
export declare const defaultStreamHandler: (response: Response) => Promise<VovkStreamAsyncIterable<unknown>>;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.defaultStreamHandler = exports.DEFAULT_ERROR_MESSAGE = void 0;
|
|
4
4
|
const types_1 = require("../types");
|
|
5
5
|
const HttpException_1 = require("../HttpException");
|
|
6
6
|
require("../utils/shim");
|
|
7
7
|
exports.DEFAULT_ERROR_MESSAGE = 'Unknown error at defaultStreamHandler';
|
|
8
|
-
const
|
|
8
|
+
const defaultStreamHandler = async (response) => {
|
|
9
9
|
if (!response.ok) {
|
|
10
10
|
let result;
|
|
11
11
|
try {
|
|
@@ -15,10 +15,10 @@ const _defaultStreamHandler = async (response) => {
|
|
|
15
15
|
// ignore parsing errors
|
|
16
16
|
}
|
|
17
17
|
// handle server errors
|
|
18
|
-
throw new HttpException_1.
|
|
18
|
+
throw new HttpException_1.HttpException(response.status, result.message ?? exports.DEFAULT_ERROR_MESSAGE);
|
|
19
19
|
}
|
|
20
20
|
if (!response.body)
|
|
21
|
-
throw new HttpException_1.
|
|
21
|
+
throw new HttpException_1.HttpException(types_1.HttpStatus.NULL, 'Stream body is falsy. Check your controller code.');
|
|
22
22
|
const reader = response.body.getReader();
|
|
23
23
|
// if streaming is too rapid, we need to make sure that the loop is stopped
|
|
24
24
|
let canceled = false;
|
|
@@ -79,4 +79,4 @@ const _defaultStreamHandler = async (response) => {
|
|
|
79
79
|
},
|
|
80
80
|
};
|
|
81
81
|
};
|
|
82
|
-
exports.
|
|
82
|
+
exports.defaultStreamHandler = defaultStreamHandler;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createRPC = void 0;
|
|
7
|
+
var createRPC_1 = require("./createRPC");
|
|
8
|
+
Object.defineProperty(exports, "createRPC", { enumerable: true, get: function () { return __importDefault(createRPC_1).default; } });
|