@lucaapp/service-utils 5.29.1 → 5.30.0
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/dist/lib/api/api.d.ts
CHANGED
|
@@ -7,6 +7,12 @@ import { ZodObjectSchemaOrUndefined } from './types/utils';
|
|
|
7
7
|
type OpenApiVersion = '3.0.0' | '3.0.1' | '3.0.2' | '3.0.3' | '3.1.0';
|
|
8
8
|
type ApiConstructorOptions = {
|
|
9
9
|
prefixPath?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Keep every endpoint registered on this Api out of the generated OpenAPI
|
|
12
|
+
* spec. Inherited by children and never turned off again, so a hidden branch
|
|
13
|
+
* stays hidden. Routing is unaffected; this only controls documentation.
|
|
14
|
+
*/
|
|
15
|
+
hidden?: boolean;
|
|
10
16
|
openApiVersion?: OpenApiVersion;
|
|
11
17
|
apiVersion?: string;
|
|
12
18
|
apiTitle?: string;
|
|
@@ -19,6 +25,7 @@ type ApiConstructorOptions = {
|
|
|
19
25
|
type ApiChildOptions = {
|
|
20
26
|
prefixPath?: string;
|
|
21
27
|
tags?: string[];
|
|
28
|
+
hidden?: boolean;
|
|
22
29
|
};
|
|
23
30
|
export declare class Api {
|
|
24
31
|
router: Router;
|
|
@@ -31,6 +38,7 @@ export declare class Api {
|
|
|
31
38
|
private rootPrefixPath;
|
|
32
39
|
debug: boolean;
|
|
33
40
|
tags: string[];
|
|
41
|
+
private hidden;
|
|
34
42
|
constructor(options?: ApiConstructorOptions);
|
|
35
43
|
child(options: ApiChildOptions): Api;
|
|
36
44
|
get<TResponseSchemas extends ReadonlyArray<EndpointResponseSchema>, TRequestBodySchema extends ZodObjectSchemaOrUndefined = undefined, TRequestParamsSchema extends ZodObjectSchemaOrUndefined = undefined, TRequestQuerySchema extends ZodObjectSchemaOrUndefined = undefined, TRequestHeadersSchema extends ZodObjectSchemaOrUndefined = undefined, TMiddlewares extends ReadonlyArray<Middleware<any, any, any, any, any, any>> | undefined = undefined>(path: string, summary: string, options: EndpointOptions<TResponseSchemas, TRequestBodySchema, TRequestParamsSchema, TRequestQuerySchema, TRequestHeadersSchema, TMiddlewares>, handler: EndpointHandler<TResponseSchemas, TRequestBodySchema, TRequestParamsSchema, TRequestQuerySchema, TRequestHeadersSchema, TMiddlewares>): void;
|
package/dist/lib/api/api.js
CHANGED
|
@@ -30,6 +30,7 @@ class Api {
|
|
|
30
30
|
this.apiDescription = options.apiDescription || '';
|
|
31
31
|
this.debug = options.debug || false;
|
|
32
32
|
this.tags = options.tags || [];
|
|
33
|
+
this.hidden = options.hidden || false;
|
|
33
34
|
this.router.use(body_parser_1.default.json({ verify: rawBodySaver, limit: '2mb' }));
|
|
34
35
|
}
|
|
35
36
|
child(options) {
|
|
@@ -39,29 +40,31 @@ class Api {
|
|
|
39
40
|
tags: [...this.tags, ...(options.tags || [])],
|
|
40
41
|
registry: this.registry,
|
|
41
42
|
router: this.router,
|
|
43
|
+
// Once hidden, always hidden: a child cannot re-expose its parent's tree.
|
|
44
|
+
hidden: this.hidden || options.hidden || false,
|
|
42
45
|
});
|
|
43
46
|
child.rootPrefixPath = this.rootPrefixPath;
|
|
44
47
|
return child;
|
|
45
48
|
}
|
|
46
49
|
get(path, summary, options, handler) {
|
|
47
50
|
(0, endpoint_1.mountEndpoint)(this.router, 'get', path, options, handler, this.debug);
|
|
48
|
-
(0, openapi_1.registerEndpoint)(this.registry, 'get', this.prefixPath, this.rootPrefixPath, path, summary, options, this.tags, this.debug);
|
|
51
|
+
(0, openapi_1.registerEndpoint)(this.registry, 'get', this.prefixPath, this.rootPrefixPath, path, summary, options, this.tags, this.debug, this.hidden);
|
|
49
52
|
}
|
|
50
53
|
post(path, summary, options, handler) {
|
|
51
54
|
(0, endpoint_1.mountEndpoint)(this.router, 'post', path, options, handler, this.debug);
|
|
52
|
-
(0, openapi_1.registerEndpoint)(this.registry, 'post', this.prefixPath, this.rootPrefixPath, path, summary, options, this.tags, this.debug);
|
|
55
|
+
(0, openapi_1.registerEndpoint)(this.registry, 'post', this.prefixPath, this.rootPrefixPath, path, summary, options, this.tags, this.debug, this.hidden);
|
|
53
56
|
}
|
|
54
57
|
patch(path, summary, options, handler) {
|
|
55
58
|
(0, endpoint_1.mountEndpoint)(this.router, 'patch', path, options, handler, this.debug);
|
|
56
|
-
(0, openapi_1.registerEndpoint)(this.registry, 'patch', this.prefixPath, this.rootPrefixPath, path, summary, options, this.tags, this.debug);
|
|
59
|
+
(0, openapi_1.registerEndpoint)(this.registry, 'patch', this.prefixPath, this.rootPrefixPath, path, summary, options, this.tags, this.debug, this.hidden);
|
|
57
60
|
}
|
|
58
61
|
put(path, summary, options, handler) {
|
|
59
62
|
(0, endpoint_1.mountEndpoint)(this.router, 'put', path, options, handler, this.debug);
|
|
60
|
-
(0, openapi_1.registerEndpoint)(this.registry, 'put', this.prefixPath, this.rootPrefixPath, path, summary, options, this.tags, this.debug);
|
|
63
|
+
(0, openapi_1.registerEndpoint)(this.registry, 'put', this.prefixPath, this.rootPrefixPath, path, summary, options, this.tags, this.debug, this.hidden);
|
|
61
64
|
}
|
|
62
65
|
delete(path, summary, options, handler) {
|
|
63
66
|
(0, endpoint_1.mountEndpoint)(this.router, 'delete', path, options, handler, this.debug);
|
|
64
|
-
(0, openapi_1.registerEndpoint)(this.registry, 'delete', this.prefixPath, this.rootPrefixPath, path, summary, options, this.tags, this.debug);
|
|
67
|
+
(0, openapi_1.registerEndpoint)(this.registry, 'delete', this.prefixPath, this.rootPrefixPath, path, summary, options, this.tags, this.debug, this.hidden);
|
|
65
68
|
}
|
|
66
69
|
generateOpenAPISpec() {
|
|
67
70
|
let generator;
|
|
@@ -25,5 +25,5 @@ export declare const hoistCombinatorRequiredFields: (spec: {
|
|
|
25
25
|
schemas?: Record<string, unknown>;
|
|
26
26
|
};
|
|
27
27
|
}) => void;
|
|
28
|
-
export declare const registerEndpoint: (registry: OpenAPIRegistry, method: HttpMethod, prefixPath: string, rootPrefixPath: string, path: string, summary: string, options: EndpointOptions<any, any, any, any, any, any>, tags: string[], debug: boolean) => void;
|
|
28
|
+
export declare const registerEndpoint: (registry: OpenAPIRegistry, method: HttpMethod, prefixPath: string, rootPrefixPath: string, path: string, summary: string, options: EndpointOptions<any, any, any, any, any, any>, tags: string[], debug: boolean, hiddenApi?: boolean) => void;
|
|
29
29
|
export {};
|
package/dist/lib/api/openapi.js
CHANGED
|
@@ -225,40 +225,29 @@ const hoistCombinatorRequiredFields = (spec) => {
|
|
|
225
225
|
visit(schemas);
|
|
226
226
|
};
|
|
227
227
|
exports.hoistCombinatorRequiredFields = hoistCombinatorRequiredFields;
|
|
228
|
-
/**
|
|
229
|
-
* Turn each middleware's declared schemes into OpenAPI security requirements.
|
|
230
|
-
*
|
|
231
|
-
* OpenAPI expresses the two relations differently: separate objects in the
|
|
232
|
-
* `security` array are alternatives (OR), while several schemes inside one
|
|
233
|
-
* object are all required (AND). A middleware's own `security: [a, b]` means
|
|
234
|
-
* "a or b" -- it accepts either credential -- but two middlewares on the same
|
|
235
|
-
* route both run, so their requirements are combined with AND.
|
|
236
|
-
*
|
|
237
|
-
* That makes the result the cartesian product across middlewares:
|
|
238
|
-
*
|
|
239
|
-
* [[a, b], [c]] -> [{a, c}, {b, c}]
|
|
240
|
-
*
|
|
241
|
-
* A single middleware therefore still renders as plain alternatives, which is
|
|
242
|
-
* what it did before, and stacked middlewares no longer render as if presenting
|
|
243
|
-
* any one of their credentials were enough.
|
|
244
|
-
*/
|
|
245
|
-
const buildSecurityRequirements = (securityByMiddleware) => securityByMiddleware.reduce((requirements, alternatives) => requirements.flatMap(requirement =>
|
|
246
|
-
// Duplicates collapse on their own: the same scheme required twice is
|
|
247
|
-
// still one entry in the object.
|
|
248
|
-
alternatives.map(scheme => ({ ...requirement, [scheme]: [] }))), [{}]);
|
|
249
228
|
const registerEndpoint = (registry, method, prefixPath, rootPrefixPath, path, summary,
|
|
250
229
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
251
|
-
options, tags, debug) => {
|
|
230
|
+
options, tags, debug, hiddenApi = false) => {
|
|
231
|
+
// Hidden endpoints are never registered, so they leave no path, no
|
|
232
|
+
// operationId and no security behind. Checked before any schema work: there
|
|
233
|
+
// is nothing to build for an operation that will not be published.
|
|
234
|
+
if (hiddenApi || options.hidden) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
252
237
|
let bodySchema = options.schemas?.body || undefined;
|
|
253
238
|
let paramsSchema = options.schemas?.params || undefined;
|
|
254
239
|
let querySchema = options.schemas?.query || undefined;
|
|
255
240
|
let headersSchema = options.schemas?.headers || undefined;
|
|
256
241
|
let responseSchemas = options.responses;
|
|
257
|
-
//
|
|
258
|
-
//
|
|
259
|
-
//
|
|
260
|
-
// are all
|
|
261
|
-
|
|
242
|
+
// Union across middlewares, one scheme per requirement object, which OpenAPI
|
|
243
|
+
// reads as "any one of these". Deliberately not one object holding all of
|
|
244
|
+
// them, which would read as "all of these at once": jwt_operator,
|
|
245
|
+
// jwt_support, jwt_operator_device and jwt_consumer are all apiKey in the
|
|
246
|
+
// same X-Auth header, so requiring two of them is unsatisfiable. Nearly every
|
|
247
|
+
// location-guarded route stacks requireOperatorOROperatorDevice with
|
|
248
|
+
// authorizeLocation(Group), and 5.29.1 documented those 281 routes as needing
|
|
249
|
+
// an operator token and an operator-device token simultaneously.
|
|
250
|
+
const securitySchemes = new Set();
|
|
262
251
|
if (options.errors) {
|
|
263
252
|
for (const errorType of Object.keys(options.errors)) {
|
|
264
253
|
const statusCode = options.errors[errorType];
|
|
@@ -319,9 +308,8 @@ options, tags, debug) => {
|
|
|
319
308
|
});
|
|
320
309
|
}
|
|
321
310
|
}
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
securityByMiddleware.push([...middlewareSecurity]);
|
|
311
|
+
for (const scheme of middleware.options.security || []) {
|
|
312
|
+
securitySchemes.add(scheme);
|
|
325
313
|
}
|
|
326
314
|
}
|
|
327
315
|
// Add ZodError response
|
|
@@ -381,8 +369,8 @@ options, tags, debug) => {
|
|
|
381
369
|
responses: responseMap,
|
|
382
370
|
operationId: options.operationId ||
|
|
383
371
|
deriveOperationId(registry, method, prefixPath.slice(rootPrefixPath.length) + path),
|
|
384
|
-
...(
|
|
385
|
-
security:
|
|
372
|
+
...(securitySchemes.size > 0 && {
|
|
373
|
+
security: [...securitySchemes].map(scheme => ({ [scheme]: [] })),
|
|
386
374
|
}),
|
|
387
375
|
});
|
|
388
376
|
};
|
|
@@ -21,4 +21,11 @@ export type EndpointOptions<TResponseSchemas extends ReadonlyArray<EndpointRespo
|
|
|
21
21
|
responses: TResponseSchemas;
|
|
22
22
|
errors?: Record<string, number>;
|
|
23
23
|
operationId?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Keep this endpoint out of the generated OpenAPI spec. For routes that only
|
|
26
|
+
* a known caller may use -- provider webhooks, service-to-service endpoints,
|
|
27
|
+
* job triggers -- where publishing the shape invites traffic that has no
|
|
28
|
+
* business arriving. Routing is unaffected.
|
|
29
|
+
*/
|
|
30
|
+
hidden?: boolean;
|
|
24
31
|
};
|