@zero-server/grpc 0.9.1 → 0.9.3
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/LICENSE +21 -21
- package/index.d.ts +1 -1
- package/index.js +27 -27
- package/lib/debug.js +372 -0
- package/lib/grpc/balancer.js +378 -0
- package/lib/grpc/call.js +708 -0
- package/lib/grpc/client.js +764 -0
- package/lib/grpc/codec.js +1221 -0
- package/lib/grpc/credentials.js +398 -0
- package/lib/grpc/frame.js +262 -0
- package/lib/grpc/health.js +287 -0
- package/lib/grpc/index.js +121 -0
- package/lib/grpc/metadata.js +461 -0
- package/lib/grpc/proto.js +821 -0
- package/lib/grpc/reflection.js +590 -0
- package/lib/grpc/server.js +445 -0
- package/lib/grpc/status.js +118 -0
- package/lib/grpc/watch.js +173 -0
- package/package.json +10 -3
- package/types/app.d.ts +223 -0
- package/types/auth.d.ts +520 -0
- package/types/body.d.ts +14 -0
- package/types/cli.d.ts +2 -0
- package/types/cluster.d.ts +75 -0
- package/types/env.d.ts +80 -0
- package/types/errors.d.ts +316 -0
- package/types/fetch.d.ts +43 -0
- package/types/grpc.d.ts +432 -0
- package/types/index.d.ts +384 -0
- package/types/lifecycle.d.ts +60 -0
- package/types/middleware.d.ts +320 -0
- package/types/observe.d.ts +304 -0
- package/types/orm.d.ts +1887 -0
- package/types/request.d.ts +109 -0
- package/types/response.d.ts +157 -0
- package/types/router.d.ts +78 -0
- package/types/sse.d.ts +78 -0
- package/types/websocket.d.ts +126 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module grpc/watch
|
|
3
|
+
* @description Proto file hot-reload for development.
|
|
4
|
+
* Watches `.proto` files for changes using `fs.watch()` and
|
|
5
|
+
* re-parses/re-registers gRPC services automatically.
|
|
6
|
+
*
|
|
7
|
+
* **Dev-only** — disabled by default when `NODE_ENV=production`.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* const { createApp, watchProto } = require('@zero-server/sdk');
|
|
11
|
+
* const app = createApp();
|
|
12
|
+
*
|
|
13
|
+
* watchProto(app, './protos/greeter.proto', 'Greeter', handlers, {
|
|
14
|
+
* onReload: (schema) => console.log('Reloaded!'),
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* app.listen(50051, { http2: true });
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const fs = require('fs');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
const log = require('../debug')('zero:grpc:watch');
|
|
23
|
+
|
|
24
|
+
// Lazy-load proto parser
|
|
25
|
+
let _parseProtoFile = null;
|
|
26
|
+
function _getParser()
|
|
27
|
+
{
|
|
28
|
+
if (!_parseProtoFile) _parseProtoFile = require('./proto').parseProtoFile;
|
|
29
|
+
return _parseProtoFile;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// -- Constants ---------------------------------------------------
|
|
33
|
+
|
|
34
|
+
const DEFAULT_DEBOUNCE = 300; // ms
|
|
35
|
+
|
|
36
|
+
// -- watchProto --------------------------------------------------
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Watch a `.proto` file and hot-reload the gRPC service on changes.
|
|
40
|
+
* Parses the file, registers the service, then watches for modifications.
|
|
41
|
+
*
|
|
42
|
+
* Disabled in production unless `opts.production` is `true`.
|
|
43
|
+
*
|
|
44
|
+
* @param {object} app - The zero-server App instance.
|
|
45
|
+
* @param {string} protoPath - Path to the `.proto` file.
|
|
46
|
+
* @param {string} serviceName - Service name to register.
|
|
47
|
+
* @param {Object<string, Function>} handlers - Method handlers map.
|
|
48
|
+
* @param {object} [opts] - Options.
|
|
49
|
+
* @param {Function[]} [opts.interceptors] - Per-service interceptors.
|
|
50
|
+
* @param {number} [opts.maxMessageSize] - Max incoming message size.
|
|
51
|
+
* @param {boolean} [opts.compress=false] - Compress outgoing messages.
|
|
52
|
+
* @param {number} [opts.debounce=300] - Debounce interval in ms.
|
|
53
|
+
* @param {boolean} [opts.production=false] - Allow in production.
|
|
54
|
+
* @param {Function} [opts.onReload] - `(schema) => void` callback after reload.
|
|
55
|
+
* @param {Function} [opts.onError] - `(err) => void` callback on parse/reload error.
|
|
56
|
+
* @returns {{ stop: () => void, schema: object }} Controller with `stop()` and current schema.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* const watcher = watchProto(app, './hello.proto', 'Greeter', {
|
|
60
|
+
* SayHello(call) { return { message: 'Hello ' + call.request.name }; },
|
|
61
|
+
* });
|
|
62
|
+
* // Later: watcher.stop();
|
|
63
|
+
*/
|
|
64
|
+
function watchProto(app, protoPath, serviceName, handlers, opts = {})
|
|
65
|
+
{
|
|
66
|
+
if (!opts.production && process.env.NODE_ENV === 'production')
|
|
67
|
+
{
|
|
68
|
+
log.warn('watchProto disabled in production (set { production: true } to override)');
|
|
69
|
+
// Still do an initial load
|
|
70
|
+
const parseProtoFile = _getParser();
|
|
71
|
+
const schema = parseProtoFile(protoPath);
|
|
72
|
+
app.grpc(schema, serviceName, handlers, opts);
|
|
73
|
+
return { stop() {}, schema };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const debounceMs = opts.debounce || DEFAULT_DEBOUNCE;
|
|
77
|
+
const resolvedPath = path.resolve(protoPath);
|
|
78
|
+
|
|
79
|
+
const parseProtoFile = _getParser();
|
|
80
|
+
let currentSchema = null;
|
|
81
|
+
|
|
82
|
+
// Initial load
|
|
83
|
+
try
|
|
84
|
+
{
|
|
85
|
+
currentSchema = parseProtoFile(resolvedPath);
|
|
86
|
+
app.grpc(currentSchema, serviceName, handlers, opts);
|
|
87
|
+
log.info('proto loaded: %s → %s', resolvedPath, serviceName);
|
|
88
|
+
}
|
|
89
|
+
catch (err)
|
|
90
|
+
{
|
|
91
|
+
log.error('initial proto parse failed: %s', err.message);
|
|
92
|
+
if (typeof opts.onError === 'function') opts.onError(err);
|
|
93
|
+
else throw err;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Debounced reload
|
|
97
|
+
let debounceTimer = null;
|
|
98
|
+
|
|
99
|
+
function _reload()
|
|
100
|
+
{
|
|
101
|
+
try
|
|
102
|
+
{
|
|
103
|
+
const schema = parseProtoFile(resolvedPath);
|
|
104
|
+
|
|
105
|
+
// Verify the service still exists in the schema
|
|
106
|
+
if (!schema.services[serviceName])
|
|
107
|
+
{
|
|
108
|
+
const err = new Error(`Service "${serviceName}" not found after reload`);
|
|
109
|
+
log.error(err.message);
|
|
110
|
+
if (typeof opts.onError === 'function') opts.onError(err);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Re-register: the grpc registry replaces the existing service entry
|
|
115
|
+
if (app._grpcRegistry)
|
|
116
|
+
{
|
|
117
|
+
app._grpcRegistry.addService(schema, serviceName, handlers, opts);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
currentSchema = schema;
|
|
121
|
+
log.info('proto reloaded: %s', resolvedPath);
|
|
122
|
+
|
|
123
|
+
if (typeof opts.onReload === 'function') opts.onReload(schema);
|
|
124
|
+
}
|
|
125
|
+
catch (err)
|
|
126
|
+
{
|
|
127
|
+
log.error('proto reload failed: %s', err.message);
|
|
128
|
+
if (typeof opts.onError === 'function') opts.onError(err);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Watch the file
|
|
133
|
+
let watcher;
|
|
134
|
+
try
|
|
135
|
+
{
|
|
136
|
+
watcher = fs.watch(resolvedPath, (eventType) =>
|
|
137
|
+
{
|
|
138
|
+
if (eventType !== 'change') return;
|
|
139
|
+
|
|
140
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
141
|
+
debounceTimer = setTimeout(_reload, debounceMs);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
watcher.on('error', (err) =>
|
|
145
|
+
{
|
|
146
|
+
log.error('fs.watch error: %s', err.message);
|
|
147
|
+
if (typeof opts.onError === 'function') opts.onError(err);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
log.info('watching proto file: %s', resolvedPath);
|
|
151
|
+
}
|
|
152
|
+
catch (err)
|
|
153
|
+
{
|
|
154
|
+
log.error('failed to watch proto file: %s', err.message);
|
|
155
|
+
if (typeof opts.onError === 'function') opts.onError(err);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
stop()
|
|
160
|
+
{
|
|
161
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
162
|
+
if (watcher) watcher.close();
|
|
163
|
+
log.info('stopped watching: %s', resolvedPath);
|
|
164
|
+
},
|
|
165
|
+
get schema() { return currentSchema; },
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// -- Exports -------------------------------------------------
|
|
170
|
+
|
|
171
|
+
module.exports = {
|
|
172
|
+
watchProto,
|
|
173
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zero-server/grpc",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.3",
|
|
4
4
|
"description": "gRPC server, client, codec, framing, status, metadata, health, reflection, balancer.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"zero-server",
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
"./package.json": "./package.json"
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
|
+
"lib",
|
|
24
|
+
"types",
|
|
23
25
|
"index.js",
|
|
24
26
|
"index.d.ts",
|
|
25
27
|
"README.md",
|
|
@@ -42,7 +44,12 @@
|
|
|
42
44
|
"access": "public"
|
|
43
45
|
},
|
|
44
46
|
"sideEffects": false,
|
|
45
|
-
"
|
|
46
|
-
"@zero-server/sdk": "0.9.
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"@zero-server/sdk": ">=0.9.3"
|
|
49
|
+
},
|
|
50
|
+
"peerDependenciesMeta": {
|
|
51
|
+
"@zero-server/sdk": {
|
|
52
|
+
"optional": true
|
|
53
|
+
}
|
|
47
54
|
}
|
|
48
55
|
}
|
package/types/app.d.ts
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
|
|
3
|
+
import { Server as HttpServer } from 'http';
|
|
4
|
+
import { Server as HttpsServer, ServerOptions as TlsOptions } from 'https';
|
|
5
|
+
import { Http2Server, Http2SecureServer } from 'http2';
|
|
6
|
+
import { Request } from './request';
|
|
7
|
+
import { Response } from './response';
|
|
8
|
+
import { RouterInstance, RouteChain, RouteInfo, RouteOptions, RouteHandler } from './router';
|
|
9
|
+
import { MiddlewareFunction, ErrorHandlerFunction, NextFunction } from './middleware';
|
|
10
|
+
import { WebSocketHandler, WebSocketOptions, WebSocketPool } from './websocket';
|
|
11
|
+
import { SSEStream } from './sse';
|
|
12
|
+
import { LifecycleState } from './lifecycle';
|
|
13
|
+
import { MetricsRegistry, HealthCheckResult } from './observe';
|
|
14
|
+
import { ProtoSchema, GrpcServiceOptions, GrpcInterceptor, GrpcHandler } from './grpc';
|
|
15
|
+
|
|
16
|
+
export interface ListenOptions {
|
|
17
|
+
/** Create an HTTP/2 server. Combined with TLS options for h2 over TLS, or h2c (cleartext) otherwise. */
|
|
18
|
+
http2?: boolean;
|
|
19
|
+
/** Allow HTTP/1.1 fallback on HTTP/2 TLS servers (ALPN negotiation). Default: true. */
|
|
20
|
+
allowHTTP1?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface App {
|
|
24
|
+
/** Internal router instance. */
|
|
25
|
+
router: RouterInstance;
|
|
26
|
+
/** Middleware stack. */
|
|
27
|
+
middlewares: MiddlewareFunction[];
|
|
28
|
+
/** Application-level locals, merged into every request/response locals. */
|
|
29
|
+
locals: Record<string, any>;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Register middleware or mount a sub-router.
|
|
33
|
+
*/
|
|
34
|
+
use(fn: MiddlewareFunction): App;
|
|
35
|
+
use(path: string, fn: MiddlewareFunction): App;
|
|
36
|
+
use(path: string, router: RouterInstance): App;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Register a global error handler.
|
|
40
|
+
*/
|
|
41
|
+
onError(fn: ErrorHandlerFunction): void;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Core request handler for use with `http.createServer()`.
|
|
45
|
+
*/
|
|
46
|
+
handler(req: import('http').IncomingMessage, res: import('http').ServerResponse): void;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Start listening for connections.
|
|
50
|
+
* Pass `{ http2: true }` to create an HTTP/2 server (h2c cleartext or TLS with ALPN).
|
|
51
|
+
*/
|
|
52
|
+
listen(port?: number, cb?: () => void): HttpServer;
|
|
53
|
+
listen(port: number, opts: TlsOptions, cb?: () => void): HttpsServer;
|
|
54
|
+
listen(port: number, opts: ListenOptions & { http2: true } & TlsOptions, cb?: () => void): Http2SecureServer;
|
|
55
|
+
listen(port: number, opts: ListenOptions & { http2: true }, cb?: () => void): Http2Server;
|
|
56
|
+
listen(port: number, opts: ListenOptions, cb?: () => void): HttpServer | HttpsServer | Http2Server | Http2SecureServer;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Gracefully close the server.
|
|
60
|
+
*/
|
|
61
|
+
close(cb?: (err?: Error) => void): void;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Perform a full graceful shutdown.
|
|
65
|
+
*/
|
|
66
|
+
shutdown(opts?: { timeout?: number }): Promise<void>;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Register a lifecycle event listener.
|
|
70
|
+
*/
|
|
71
|
+
on(event: 'beforeShutdown' | 'shutdown', fn: () => void | Promise<void>): App;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Remove a lifecycle event listener.
|
|
75
|
+
*/
|
|
76
|
+
off(event: 'beforeShutdown' | 'shutdown', fn: () => void | Promise<void>): App;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Register a WebSocket pool for graceful shutdown.
|
|
80
|
+
*/
|
|
81
|
+
registerPool(pool: WebSocketPool): App;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Unregister a WebSocket pool from lifecycle management.
|
|
85
|
+
*/
|
|
86
|
+
unregisterPool(pool: WebSocketPool): App;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Track an SSE stream for graceful shutdown.
|
|
90
|
+
*/
|
|
91
|
+
trackSSE(stream: SSEStream): App;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Register an ORM Database for graceful shutdown.
|
|
95
|
+
*/
|
|
96
|
+
registerDatabase(db: { close(): Promise<void> }): App;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Unregister an ORM Database from lifecycle management.
|
|
100
|
+
*/
|
|
101
|
+
unregisterDatabase(db: { close(): Promise<void> }): App;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Configure the shutdown timeout in milliseconds.
|
|
105
|
+
*/
|
|
106
|
+
shutdownTimeout(ms: number): App;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Current lifecycle state.
|
|
110
|
+
*/
|
|
111
|
+
readonly lifecycleState: LifecycleState;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Register a liveness health check endpoint.
|
|
115
|
+
*/
|
|
116
|
+
health(path?: string, checks?: Record<string, () => HealthCheckResult | boolean | Promise<HealthCheckResult | boolean>>): App;
|
|
117
|
+
health(checks?: Record<string, () => HealthCheckResult | boolean | Promise<HealthCheckResult | boolean>>): App;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Register a readiness health check endpoint.
|
|
121
|
+
*/
|
|
122
|
+
ready(path?: string, checks?: Record<string, () => HealthCheckResult | boolean | Promise<HealthCheckResult | boolean>>): App;
|
|
123
|
+
ready(checks?: Record<string, () => HealthCheckResult | boolean | Promise<HealthCheckResult | boolean>>): App;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Register a custom health check.
|
|
127
|
+
*/
|
|
128
|
+
addHealthCheck(name: string, fn: () => HealthCheckResult | boolean | Promise<HealthCheckResult | boolean>): App;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get the application metrics registry.
|
|
132
|
+
*/
|
|
133
|
+
metrics(): MetricsRegistry;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Mount a Prometheus metrics endpoint.
|
|
137
|
+
*/
|
|
138
|
+
metricsEndpoint(path?: string, opts?: { registry?: MetricsRegistry }): App;
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Register a WebSocket upgrade handler.
|
|
142
|
+
*/
|
|
143
|
+
ws(path: string, handler: WebSocketHandler): void;
|
|
144
|
+
ws(path: string, opts: WebSocketOptions, handler: WebSocketHandler): void;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Register a gRPC service with handlers.
|
|
148
|
+
*/
|
|
149
|
+
grpc(schema: ProtoSchema, serviceName: string, handlers: Record<string, GrpcHandler>, opts?: GrpcServiceOptions): App;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Add a global gRPC interceptor.
|
|
153
|
+
*/
|
|
154
|
+
grpcInterceptor(fn: GrpcInterceptor): App;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Return a flat list of all registered routes.
|
|
158
|
+
*/
|
|
159
|
+
routes(): RouteInfo[];
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Register a route with a specific HTTP method.
|
|
163
|
+
*/
|
|
164
|
+
route(method: string, path: string, ...handlers: (RouteOptions | RouteHandler)[]): App;
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Get a setting value (1 arg) or set a setting value (2 args).
|
|
168
|
+
*/
|
|
169
|
+
set(key: string): any;
|
|
170
|
+
set(key: string, val: any): App;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Get a setting value, or register a GET route.
|
|
174
|
+
* With 1 string arg: returns the setting value.
|
|
175
|
+
* With path + handlers: registers a GET route.
|
|
176
|
+
*/
|
|
177
|
+
get(key: string): any;
|
|
178
|
+
get(path: string, ...handlers: (RouteOptions | RouteHandler)[]): App;
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Enable a boolean setting (set to `true`).
|
|
182
|
+
*/
|
|
183
|
+
enable(key: string): App;
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Disable a boolean setting (set to `false`).
|
|
187
|
+
*/
|
|
188
|
+
disable(key: string): App;
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Check if a setting is truthy.
|
|
192
|
+
*/
|
|
193
|
+
enabled(key: string): boolean;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Check if a setting is falsy.
|
|
197
|
+
*/
|
|
198
|
+
disabled(key: string): boolean;
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Register a parameter pre-processing handler.
|
|
202
|
+
*/
|
|
203
|
+
param(name: string, fn: (req: Request, res: Response, next: NextFunction, value: string) => void): App;
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Create a route group under a prefix with shared middleware.
|
|
207
|
+
*/
|
|
208
|
+
group(prefix: string, ...args: [...MiddlewareFunction[], (router: RouterInstance) => void]): App;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Create a chainable route builder for the given path.
|
|
212
|
+
*/
|
|
213
|
+
chain(path: string): RouteChain;
|
|
214
|
+
|
|
215
|
+
// HTTP method shortcuts
|
|
216
|
+
post(path: string, ...handlers: (RouteOptions | RouteHandler)[]): App;
|
|
217
|
+
put(path: string, ...handlers: (RouteOptions | RouteHandler)[]): App;
|
|
218
|
+
delete(path: string, ...handlers: (RouteOptions | RouteHandler)[]): App;
|
|
219
|
+
patch(path: string, ...handlers: (RouteOptions | RouteHandler)[]): App;
|
|
220
|
+
options(path: string, ...handlers: (RouteOptions | RouteHandler)[]): App;
|
|
221
|
+
head(path: string, ...handlers: (RouteOptions | RouteHandler)[]): App;
|
|
222
|
+
all(path: string, ...handlers: (RouteOptions | RouteHandler)[]): App;
|
|
223
|
+
}
|