@lpdjs/firestore-repo-service 2.6.7 → 2.6.9
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/servers/hono/cli.cjs +62 -52
- package/dist/servers/hono/cli.cjs.map +1 -1
- package/dist/servers/hono/cli.js +62 -52
- package/dist/servers/hono/cli.js.map +1 -1
- package/dist/servers/hono/index.cjs +7 -7
- package/dist/servers/hono/index.cjs.map +1 -1
- package/dist/servers/hono/index.d.cts +470 -11
- package/dist/servers/hono/index.d.ts +470 -11
- package/dist/servers/hono/index.js +7 -7
- package/dist/servers/hono/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -206,6 +206,52 @@ type AnyServicesContainer = ServicesContainer<Record<string, any>>;
|
|
|
206
206
|
type HttpMethod = "get" | "post" | "put" | "patch" | "delete";
|
|
207
207
|
/** Where the validated payload comes from. */
|
|
208
208
|
type PayloadSource = "json" | "query" | "form" | "param";
|
|
209
|
+
/**
|
|
210
|
+
* Structured logger injected into every handler / interceptor / error-handler
|
|
211
|
+
* context. Implement it, or extend the package's `BaseLogger` and override its
|
|
212
|
+
* `write` hook to route to your sink (Firebase logger, pino, …).
|
|
213
|
+
*
|
|
214
|
+
* `error()` returns a correlation id so the same value can be both logged and
|
|
215
|
+
* returned to the client.
|
|
216
|
+
*/
|
|
217
|
+
interface Logger {
|
|
218
|
+
info(message: string, meta?: unknown): void;
|
|
219
|
+
warn(message: string, meta?: unknown): void;
|
|
220
|
+
/** @returns a correlation id (e.g. to include in the HTTP error response). */
|
|
221
|
+
error(error: unknown, meta?: unknown): string;
|
|
222
|
+
debug(message: string, meta?: unknown): void;
|
|
223
|
+
}
|
|
224
|
+
/** Context passed to {@link ErrorHandler.handle}. */
|
|
225
|
+
interface ErrorHandlerContext<TEnv extends Env = Env, TServices extends AnyServicesContainer = AnyServicesContainer> {
|
|
226
|
+
/** The thrown value (an `AppError`, a Zod {@link ValidationError}, …). */
|
|
227
|
+
error: unknown;
|
|
228
|
+
/** Hono request context — set status, read headers, build the response. */
|
|
229
|
+
c: Context<TEnv>;
|
|
230
|
+
/** Route metadata (read-only). */
|
|
231
|
+
route: AnyRouteDef;
|
|
232
|
+
/** Global DI services container. */
|
|
233
|
+
services: TServices;
|
|
234
|
+
/** Injected {@link Logger} when one was passed to the API, else `undefined`. */
|
|
235
|
+
logger?: Logger;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Cross-cutting error strategy — a class (or object) you pass to the API and
|
|
239
|
+
* that the server injects everywhere **and** applies automatically.
|
|
240
|
+
*
|
|
241
|
+
* Implement {@link ErrorHandler.handle} to map any thrown value to an HTTP
|
|
242
|
+
* `Response` (e.g. your `AppError` → status + localized body, plus structured
|
|
243
|
+
* logging with a correlation id). Return `null` to decline the error and let
|
|
244
|
+
* the built-in fallback (`ValidationError` envelope) / `onError` take over.
|
|
245
|
+
*
|
|
246
|
+
* Prefer extending the package's {@link BaseErrorHandler} (it already maps the
|
|
247
|
+
* built-in errors) and overriding its `mapError` / `logError` hooks. Pass it
|
|
248
|
+
* **per API** via `ApiConfig.errorHandler`, or once via the registry
|
|
249
|
+
* (`{ services, errorHandler }`); it is then available in every handler /
|
|
250
|
+
* interceptor context as `errorHandler` and auto-applied on any uncaught error.
|
|
251
|
+
*/
|
|
252
|
+
interface ErrorHandler<TEnv extends Env = Env, TServices extends AnyServicesContainer = AnyServicesContainer> {
|
|
253
|
+
handle(ctx: ErrorHandlerContext<TEnv, TServices>): Response | null | Promise<Response | null>;
|
|
254
|
+
}
|
|
209
255
|
/** Handler signature — receives a single typed context object. */
|
|
210
256
|
type RouteHandler<TIn, TOut, TEnv extends Env = Env, TServices extends AnyServicesContainer = AnyServicesContainer> = (ctx: {
|
|
211
257
|
/** Validated (and typed) request payload. `void` when no `input` schema is defined. */
|
|
@@ -218,6 +264,13 @@ type RouteHandler<TIn, TOut, TEnv extends Env = Env, TServices extends AnyServic
|
|
|
218
264
|
* `services` option.
|
|
219
265
|
*/
|
|
220
266
|
services: TServices;
|
|
267
|
+
/**
|
|
268
|
+
* Injected {@link ErrorHandler} when one was passed to the API, else
|
|
269
|
+
* `undefined`. Usually you just `throw` and let it apply automatically.
|
|
270
|
+
*/
|
|
271
|
+
errorHandler?: ErrorHandler<TEnv, TServices>;
|
|
272
|
+
/** Injected {@link Logger} when one was passed to the API, else `undefined`. */
|
|
273
|
+
logger?: Logger;
|
|
221
274
|
}) => Promise<TOut | Response> | TOut | Response;
|
|
222
275
|
/**
|
|
223
276
|
* One route declaration. Default-exported by every `routes.ts` file inside the
|
|
@@ -264,7 +317,7 @@ interface RouteDef<TIn extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefin
|
|
|
264
317
|
/** Mark the operation as deprecated in the generated spec. */
|
|
265
318
|
deprecated?: boolean;
|
|
266
319
|
/** Security requirements (operationId-level override). */
|
|
267
|
-
security?:
|
|
320
|
+
security?: SecurityRequirement[];
|
|
268
321
|
/** The request handler. */
|
|
269
322
|
handler: RouteHandler<TIn extends z.ZodTypeAny ? z.infer<TIn> : void, TOut extends z.ZodTypeAny ? z.infer<TOut> : unknown, TEnv>;
|
|
270
323
|
}
|
|
@@ -374,13 +427,137 @@ type RouteInterceptor<TEnv extends Env = Env, TServices extends AnyServicesConta
|
|
|
374
427
|
c: Context<TEnv>;
|
|
375
428
|
/** Global DI services container. See {@link RouteHandler}. */
|
|
376
429
|
services: TServices;
|
|
430
|
+
/**
|
|
431
|
+
* Injected {@link ErrorHandler} when one was passed to the API. Call
|
|
432
|
+
* `errorHandler?.handle({ error, c, route, services })` in your `catch` to
|
|
433
|
+
* reuse the shared mapping, or simply rethrow to let it apply automatically.
|
|
434
|
+
*/
|
|
435
|
+
errorHandler?: ErrorHandler<TEnv, TServices>;
|
|
436
|
+
/** Injected {@link Logger} when one was passed to the API, else `undefined`. */
|
|
437
|
+
logger?: Logger;
|
|
377
438
|
}) => Promise<Response | unknown> | Response | unknown;
|
|
439
|
+
/**
|
|
440
|
+
* Success-envelope schema declared alongside an interceptor so the generated
|
|
441
|
+
* OpenAPI spec documents what the **wrapper actually returns** (not the raw
|
|
442
|
+
* handler output).
|
|
443
|
+
*
|
|
444
|
+
* - a **static** Zod schema → same envelope for every route (e.g.
|
|
445
|
+
* `z.object({ data: z.any(), intercepted: z.boolean() })`);
|
|
446
|
+
* - a **factory** `(routeOutput) => schema` → wraps each route's own `output`,
|
|
447
|
+
* so `data` is typed per endpoint in the docs.
|
|
448
|
+
*/
|
|
449
|
+
type InterceptorOutput = z.ZodTypeAny | ((routeOutput: z.ZodTypeAny | undefined) => z.ZodTypeAny);
|
|
450
|
+
/** A single declared error response for the OpenAPI spec. */
|
|
451
|
+
type InterceptorErrorResponse = z.ZodTypeAny | {
|
|
452
|
+
description?: string;
|
|
453
|
+
schema?: z.ZodTypeAny;
|
|
454
|
+
};
|
|
455
|
+
/**
|
|
456
|
+
* Structured interceptor — pairs the cross-cutting {@link RouteInterceptor}
|
|
457
|
+
* `handler` with the OpenAPI metadata describing what it returns, so the docs
|
|
458
|
+
* reflect the real wrapped responses (success envelope + error shapes).
|
|
459
|
+
*
|
|
460
|
+
* @example
|
|
461
|
+
* ```ts
|
|
462
|
+
* interceptor: {
|
|
463
|
+
* // factory: `data` reflects each route's own output schema
|
|
464
|
+
* output: (routeOutput) =>
|
|
465
|
+
* z.object({ data: routeOutput ?? z.unknown(), intercepted: z.boolean() }),
|
|
466
|
+
* errors: {
|
|
467
|
+
* 400: ValidationErrorSchema,
|
|
468
|
+
* 500: { description: "Internal", schema: ErrorSchema },
|
|
469
|
+
* },
|
|
470
|
+
* handler: async ({ c, next }) => {
|
|
471
|
+
* const data = await next();
|
|
472
|
+
* return c.json({ data, intercepted: true });
|
|
473
|
+
* },
|
|
474
|
+
* }
|
|
475
|
+
* ```
|
|
476
|
+
*/
|
|
477
|
+
interface InterceptorConfig<TEnv extends Env = Env, TServices extends AnyServicesContainer = AnyServicesContainer> {
|
|
478
|
+
/** Success-envelope schema (static) or per-route factory. */
|
|
479
|
+
output?: InterceptorOutput;
|
|
480
|
+
/**
|
|
481
|
+
* Error responses applied to **every** operation, keyed by HTTP status. Pass
|
|
482
|
+
* a bare Zod schema or `{ description, schema }`.
|
|
483
|
+
*/
|
|
484
|
+
errors?: Record<number, InterceptorErrorResponse>;
|
|
485
|
+
/** The interceptor function. See {@link RouteInterceptor}. */
|
|
486
|
+
handler: RouteInterceptor<TEnv, TServices>;
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Interceptor option accepted by the server / registry — either a bare
|
|
490
|
+
* {@link RouteInterceptor} function (legacy) or a structured
|
|
491
|
+
* {@link InterceptorConfig} carrying OpenAPI metadata.
|
|
492
|
+
*/
|
|
493
|
+
type InterceptorOption<TEnv extends Env = Env, TServices extends AnyServicesContainer = AnyServicesContainer> = RouteInterceptor<TEnv, TServices> | InterceptorConfig<TEnv, TServices>;
|
|
378
494
|
/** OpenAPI document info (subset of the spec used by the helper). */
|
|
379
495
|
interface OpenAPIInfo {
|
|
380
496
|
title: string;
|
|
381
497
|
version: string;
|
|
382
498
|
description?: string;
|
|
383
499
|
}
|
|
500
|
+
/** Fields shared by every security scheme. */
|
|
501
|
+
interface SecuritySchemeBase {
|
|
502
|
+
/** Human-readable description (CommonMark). */
|
|
503
|
+
description?: string;
|
|
504
|
+
}
|
|
505
|
+
/** API key carried in a header, query param or cookie. */
|
|
506
|
+
interface ApiKeySecurityScheme extends SecuritySchemeBase {
|
|
507
|
+
type: "apiKey";
|
|
508
|
+
/** Name of the header, query parameter or cookie. */
|
|
509
|
+
name: string;
|
|
510
|
+
/** Location of the API key. */
|
|
511
|
+
in: "query" | "header" | "cookie";
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* HTTP authentication (RFC 7235), e.g. `bearer` (Firebase ID tokens) or
|
|
515
|
+
* `basic`.
|
|
516
|
+
*/
|
|
517
|
+
interface HttpSecurityScheme extends SecuritySchemeBase {
|
|
518
|
+
type: "http";
|
|
519
|
+
/** Auth scheme name — `"bearer"`, `"basic"`, `"digest"`, … */
|
|
520
|
+
scheme: "bearer" | "basic" | "digest" | (string & {});
|
|
521
|
+
/** Hint for the bearer token format, e.g. `"JWT"` / `"Firebase JWT"`. */
|
|
522
|
+
bearerFormat?: string;
|
|
523
|
+
}
|
|
524
|
+
/** Mutual TLS authentication. */
|
|
525
|
+
interface MutualTlsSecurityScheme extends SecuritySchemeBase {
|
|
526
|
+
type: "mutualTLS";
|
|
527
|
+
}
|
|
528
|
+
/** A single OAuth2 flow. */
|
|
529
|
+
interface OAuthFlowObject {
|
|
530
|
+
authorizationUrl?: string;
|
|
531
|
+
tokenUrl?: string;
|
|
532
|
+
refreshUrl?: string;
|
|
533
|
+
/** Available scopes → description. */
|
|
534
|
+
scopes: Record<string, string>;
|
|
535
|
+
}
|
|
536
|
+
/** The OAuth2 flows supported by an {@link OAuth2SecurityScheme}. */
|
|
537
|
+
interface OAuthFlowsObject {
|
|
538
|
+
implicit?: OAuthFlowObject;
|
|
539
|
+
password?: OAuthFlowObject;
|
|
540
|
+
clientCredentials?: OAuthFlowObject;
|
|
541
|
+
authorizationCode?: OAuthFlowObject;
|
|
542
|
+
}
|
|
543
|
+
/** OAuth2 authentication. */
|
|
544
|
+
interface OAuth2SecurityScheme extends SecuritySchemeBase {
|
|
545
|
+
type: "oauth2";
|
|
546
|
+
flows: OAuthFlowsObject;
|
|
547
|
+
}
|
|
548
|
+
/** OpenID Connect Discovery. */
|
|
549
|
+
interface OpenIdConnectSecurityScheme extends SecuritySchemeBase {
|
|
550
|
+
type: "openIdConnect";
|
|
551
|
+
openIdConnectUrl: string;
|
|
552
|
+
}
|
|
553
|
+
/** OpenAPI 3.1 Security Scheme Object (discriminated on `type`). */
|
|
554
|
+
type SecurityScheme = ApiKeySecurityScheme | HttpSecurityScheme | MutualTlsSecurityScheme | OAuth2SecurityScheme | OpenIdConnectSecurityScheme;
|
|
555
|
+
/**
|
|
556
|
+
* A single Security Requirement Object: maps a scheme name (a key of
|
|
557
|
+
* {@link OpenAPIConfig.securitySchemes}) to the list of required scopes
|
|
558
|
+
* (empty `[]` for `http` / `apiKey`).
|
|
559
|
+
*/
|
|
560
|
+
type SecurityRequirement = Record<string, string[]>;
|
|
384
561
|
/** OpenAPI configuration on the server. */
|
|
385
562
|
interface OpenAPIConfig {
|
|
386
563
|
/** Path served by the JSON spec (e.g. `/openapi.json`). Default: `/openapi.json`. */
|
|
@@ -394,10 +571,37 @@ interface OpenAPIConfig {
|
|
|
394
571
|
url: string;
|
|
395
572
|
description?: string;
|
|
396
573
|
}[];
|
|
397
|
-
/**
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
574
|
+
/**
|
|
575
|
+
* Reusable security schemes, keyed by name. The keys are referenced from
|
|
576
|
+
* {@link OpenAPIConfig.security} / per-route `security`.
|
|
577
|
+
*
|
|
578
|
+
* @example
|
|
579
|
+
* ```ts
|
|
580
|
+
* securitySchemes: {
|
|
581
|
+
* bearerAuth: { type: "http", scheme: "bearer", bearerFormat: "Firebase JWT" },
|
|
582
|
+
* }
|
|
583
|
+
* ```
|
|
584
|
+
*/
|
|
585
|
+
securitySchemes?: Record<string, SecurityScheme>;
|
|
586
|
+
/**
|
|
587
|
+
* Default security requirement applied to every operation. Each entry maps a
|
|
588
|
+
* scheme name (a key of {@link OpenAPIConfig.securitySchemes}) to its scopes.
|
|
589
|
+
*
|
|
590
|
+
* @example `security: [{ bearerAuth: [] }]`
|
|
591
|
+
*/
|
|
592
|
+
security?: SecurityRequirement[];
|
|
593
|
+
/**
|
|
594
|
+
* Hono middleware(s) guarding **only** the docs UI and JSON spec endpoints
|
|
595
|
+
* (not the API routes). Use a raw middleware for a custom flow, or the
|
|
596
|
+
* built-in {@link firebaseBearerAuth} / {@link basicAuth} helpers.
|
|
597
|
+
*
|
|
598
|
+
* @example
|
|
599
|
+
* ```ts
|
|
600
|
+
* import { firebaseBearerAuth } from "@lpdjs/firestore-repo-service/servers/hono";
|
|
601
|
+
* openapi: { info, docsAuth: firebaseBearerAuth({ getAuth }) }
|
|
602
|
+
* ```
|
|
603
|
+
*/
|
|
604
|
+
docsAuth?: MiddlewareHandler | MiddlewareHandler[];
|
|
401
605
|
}
|
|
402
606
|
/** Options consumed by the {@link HonoServer} constructor. */
|
|
403
607
|
interface HonoServerOptions<TEnv extends Env = Env> {
|
|
@@ -434,9 +638,27 @@ interface HonoServerOptions<TEnv extends Env = Env> {
|
|
|
434
638
|
/**
|
|
435
639
|
* Cross-cutting interceptor wrapping every handler call.
|
|
436
640
|
* Ideal for response envelopes, business-error mapping, tracing.
|
|
437
|
-
*
|
|
641
|
+
*
|
|
642
|
+
* Pass a bare {@link RouteInterceptor} function, or an
|
|
643
|
+
* {@link InterceptorConfig} (`{ output?, errors?, handler }`) so the
|
|
644
|
+
* generated OpenAPI spec documents the wrapped responses.
|
|
645
|
+
*/
|
|
646
|
+
interceptor?: InterceptorOption<TEnv>;
|
|
647
|
+
/**
|
|
648
|
+
* Cross-cutting error strategy applied to every uncaught error and injected
|
|
649
|
+
* into every handler / interceptor context. See {@link ErrorHandler}.
|
|
650
|
+
*
|
|
651
|
+
* Typically shared across APIs — pass it once to `createApiRegistry` via
|
|
652
|
+
* `{ services, errorHandler }`; this per-API field overrides it.
|
|
653
|
+
*/
|
|
654
|
+
errorHandler?: ErrorHandler<TEnv>;
|
|
655
|
+
/**
|
|
656
|
+
* Structured {@link Logger} injected into every handler / interceptor /
|
|
657
|
+
* error-handler context. Extend the package's `BaseLogger` to route to your
|
|
658
|
+
* sink. Typically shared via `createApiRegistry({ services, logger })`; this
|
|
659
|
+
* per-API field overrides it.
|
|
438
660
|
*/
|
|
439
|
-
|
|
661
|
+
logger?: Logger;
|
|
440
662
|
/**
|
|
441
663
|
* Global DI services container (see {@link createServices}).
|
|
442
664
|
*
|
|
@@ -645,6 +867,25 @@ type OnRequestFn = (...args: any[]) => any;
|
|
|
645
867
|
type ApiConfig<TEnv extends Env = Env> = Omit<HonoServerOptions<TEnv>, "routes" | "api" | "services">;
|
|
646
868
|
/** Map of API tag → its config. */
|
|
647
869
|
type ApiConfigMap = Record<string, ApiConfig>;
|
|
870
|
+
/**
|
|
871
|
+
* Per-key excess-property guard.
|
|
872
|
+
*
|
|
873
|
+
* `createApiRegistry` infers its config map generically (`<const TMap …>`),
|
|
874
|
+
* which normally **defeats** TypeScript's excess-property checking — a typo
|
|
875
|
+
* like `openApi` or `middleware` (instead of `openapi` / `middlewares`) would
|
|
876
|
+
* pass silently and the option would be ignored at runtime.
|
|
877
|
+
*
|
|
878
|
+
* This intersects the concrete {@link ApiConfig} shape (so every known key
|
|
879
|
+
* keeps its real type **and JSDoc**, including nested objects such as
|
|
880
|
+
* `openapi`) with a `never` mapping for any key absent from `ApiConfig` — which
|
|
881
|
+
* makes typos a compile error, matching the strictness already enforced on the
|
|
882
|
+
* CRUD server's `openapi`.
|
|
883
|
+
*
|
|
884
|
+
* @internal
|
|
885
|
+
*/
|
|
886
|
+
type StrictApiConfig<T, TEnv extends Env = Env> = ApiConfig<TEnv> & {
|
|
887
|
+
[K in keyof T as K extends keyof ApiConfig<TEnv> ? never : K]: never;
|
|
888
|
+
};
|
|
648
889
|
/**
|
|
649
890
|
* Options accepted by {@link createApiRegistry} alongside the per-API
|
|
650
891
|
* configs.
|
|
@@ -657,6 +898,18 @@ interface ApiRegistryOptions<TServices extends AnyServicesContainer = AnyService
|
|
|
657
898
|
* `services` to every handler / interceptor.
|
|
658
899
|
*/
|
|
659
900
|
services?: TServices;
|
|
901
|
+
/**
|
|
902
|
+
* Cross-cutting {@link ErrorHandler} shared across every API — injected into
|
|
903
|
+
* every handler / interceptor context and applied automatically on any
|
|
904
|
+
* uncaught error. A per-API `errorHandler` (on the config) overrides it.
|
|
905
|
+
*/
|
|
906
|
+
errorHandler?: ErrorHandler;
|
|
907
|
+
/**
|
|
908
|
+
* Structured {@link Logger} shared across every API — injected into every
|
|
909
|
+
* handler / interceptor / error-handler context. A per-API `logger` (on the
|
|
910
|
+
* config) overrides it.
|
|
911
|
+
*/
|
|
912
|
+
logger?: Logger;
|
|
660
913
|
}
|
|
661
914
|
interface ApiRegistry<TMap extends ApiConfigMap, TServices extends AnyServicesContainer = AnyServicesContainer> {
|
|
662
915
|
/** The registered configs (read-only). */
|
|
@@ -710,10 +963,16 @@ interface ApiRegistry<TMap extends ApiConfigMap, TServices extends AnyServicesCo
|
|
|
710
963
|
* Factory — declare every API tag once and get back a typed `defineRoute`
|
|
711
964
|
* + `toFunctions`. See the file-level example.
|
|
712
965
|
*
|
|
713
|
-
*
|
|
966
|
+
* Each per-API config is strictly checked against {@link ApiConfig}: unknown
|
|
967
|
+
* keys (typos like `openApi` / `middleware`) are rejected at compile time,
|
|
968
|
+
* including nested objects such as `openapi` — mirroring the CRUD server.
|
|
969
|
+
*
|
|
970
|
+
* @param configs API-tag → per-API config (see {@link ApiConfig}).
|
|
714
971
|
* @param options Cross-API options (shared services container, etc).
|
|
715
972
|
*/
|
|
716
|
-
declare function createApiRegistry<const TMap extends ApiConfigMap, TServices extends AnyServicesContainer = AnyServicesContainer>(configs:
|
|
973
|
+
declare function createApiRegistry<const TMap extends ApiConfigMap, TServices extends AnyServicesContainer = AnyServicesContainer>(configs: {
|
|
974
|
+
[K in keyof TMap]: StrictApiConfig<TMap[K]>;
|
|
975
|
+
}, options?: ApiRegistryOptions<TServices>): ApiRegistry<TMap, TServices>;
|
|
717
976
|
|
|
718
977
|
/**
|
|
719
978
|
* OpenAPI 3.1 spec generator from {@link RouteDef} entries.
|
|
@@ -723,13 +982,213 @@ declare function createApiRegistry<const TMap extends ApiConfigMap, TServices ex
|
|
|
723
982
|
*/
|
|
724
983
|
|
|
725
984
|
/** Build the OpenAPI document from the mounted route registry. */
|
|
726
|
-
declare function buildOpenApiDocument(routes: AnyRouteDef[], basePath: string, config: OpenAPIConfig): Record<string, unknown>;
|
|
985
|
+
declare function buildOpenApiDocument(routes: AnyRouteDef[], basePath: string, config: OpenAPIConfig, interceptor?: InterceptorConfig): Record<string, unknown>;
|
|
727
986
|
/**
|
|
728
987
|
* Render a self-contained Scalar API Reference HTML page that points to the
|
|
729
988
|
* generated spec. Loaded from CDN — no build step required.
|
|
730
989
|
*/
|
|
731
990
|
declare function renderDocsHtml(specUrl: string, title: string): string;
|
|
732
991
|
|
|
992
|
+
/**
|
|
993
|
+
* `BaseErrorHandler` — the package's ready-to-use {@link ErrorHandler}.
|
|
994
|
+
*
|
|
995
|
+
* Use it as-is for an API that only needs the built-in error mapping
|
|
996
|
+
* (`ValidationError` / `BadRequestError` / `OutputValidationError`), or extend
|
|
997
|
+
* it and override the two hooks to plug your own domain errors + logger:
|
|
998
|
+
*
|
|
999
|
+
* - {@link BaseErrorHandler.mapError} — map your `AppError` → `Response`
|
|
1000
|
+
* (return `null` to defer to the built-in mapping);
|
|
1001
|
+
* - {@link BaseErrorHandler.logError} — log via your `AppLogger`.
|
|
1002
|
+
*
|
|
1003
|
+
* Pass an instance **per API** (`ApiConfig.errorHandler`) so different APIs can
|
|
1004
|
+
* use different strategies (e.g. one with user-facing localized errors, one
|
|
1005
|
+
* with just the defaults).
|
|
1006
|
+
*
|
|
1007
|
+
* @example
|
|
1008
|
+
* ```ts
|
|
1009
|
+
* class AppErrorHandler extends BaseErrorHandler {
|
|
1010
|
+
* protected mapError({ error, c }) {
|
|
1011
|
+
* if (error instanceof AppError) {
|
|
1012
|
+
* return c.json({ error: error.message, errorId: error.errorId }, error.statusCode);
|
|
1013
|
+
* }
|
|
1014
|
+
* return null; // → built-in mapping
|
|
1015
|
+
* }
|
|
1016
|
+
* protected logError({ error }) {
|
|
1017
|
+
* AppLogger.err(error);
|
|
1018
|
+
* }
|
|
1019
|
+
* }
|
|
1020
|
+
*
|
|
1021
|
+
* // apis.ts
|
|
1022
|
+
* v1: { ..., errorHandler: new AppErrorHandler() }, // user-facing API
|
|
1023
|
+
* v2: { ..., errorHandler: new BaseErrorHandler() }, // defaults only
|
|
1024
|
+
* ```
|
|
1025
|
+
*/
|
|
1026
|
+
|
|
1027
|
+
declare class BaseErrorHandler<TEnv extends Env = Env, TServices extends AnyServicesContainer = AnyServicesContainer> implements ErrorHandler<TEnv, TServices> {
|
|
1028
|
+
/**
|
|
1029
|
+
* Orchestration — not meant to be overridden. Tries the user mapping first,
|
|
1030
|
+
* logs it when matched, then falls back to the built-in mapping.
|
|
1031
|
+
*/
|
|
1032
|
+
handle(ctx: ErrorHandlerContext<TEnv, TServices>): Promise<Response | null>;
|
|
1033
|
+
/**
|
|
1034
|
+
* Map a domain error (your `AppError`) to a `Response`. Return `null` to let
|
|
1035
|
+
* {@link BaseErrorHandler.handleBuiltin} handle it. Default: `null`.
|
|
1036
|
+
*/
|
|
1037
|
+
protected mapError(_ctx: ErrorHandlerContext<TEnv, TServices>): Response | null | Promise<Response | null>;
|
|
1038
|
+
/**
|
|
1039
|
+
* Log a mapped error (e.g. via your `AppLogger`). Called only when
|
|
1040
|
+
* {@link BaseErrorHandler.mapError} produced a response. Default: no-op.
|
|
1041
|
+
*/
|
|
1042
|
+
protected logError(_ctx: ErrorHandlerContext<TEnv, TServices>, _response: Response): void;
|
|
1043
|
+
/**
|
|
1044
|
+
* Built-in mapping of the package's own errors (`ValidationError`,
|
|
1045
|
+
* `BadRequestError`, `OutputValidationError`). Returns `null` for unknown
|
|
1046
|
+
* errors so they bubble to `onError` / Hono.
|
|
1047
|
+
*/
|
|
1048
|
+
protected handleBuiltin(ctx: ErrorHandlerContext<TEnv, TServices>): Response | null;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
/**
|
|
1052
|
+
* `BaseLogger` — the package's ready-to-use {@link Logger}.
|
|
1053
|
+
*
|
|
1054
|
+
* Use it as-is (writes structured JSON to `console`), or extend it and override
|
|
1055
|
+
* the single {@link BaseLogger.write} hook to route to your sink (Firebase
|
|
1056
|
+
* `logger`, pino, Datadog, …). Each level funnels through `write`, so one
|
|
1057
|
+
* override covers them all.
|
|
1058
|
+
*
|
|
1059
|
+
* Pass an instance **per API** (`ApiConfig.logger`) or once via the registry
|
|
1060
|
+
* (`createApiRegistry({ services, logger })`); it is then injected into every
|
|
1061
|
+
* handler / interceptor / error-handler context as `logger`.
|
|
1062
|
+
*
|
|
1063
|
+
* @example
|
|
1064
|
+
* ```ts
|
|
1065
|
+
* import { logger as fnLogger } from "firebase-functions/v2";
|
|
1066
|
+
* class AppLogger extends BaseLogger {
|
|
1067
|
+
* protected write(severity, payload) {
|
|
1068
|
+
* fnLogger.write({ severity, ...payload });
|
|
1069
|
+
* }
|
|
1070
|
+
* }
|
|
1071
|
+
* ```
|
|
1072
|
+
*/
|
|
1073
|
+
|
|
1074
|
+
type LogSeverity = "DEBUG" | "INFO" | "WARNING" | "ERROR";
|
|
1075
|
+
declare class BaseLogger implements Logger {
|
|
1076
|
+
info(message: string, meta?: unknown): void;
|
|
1077
|
+
warn(message: string, meta?: unknown): void;
|
|
1078
|
+
debug(message: string, meta?: unknown): void;
|
|
1079
|
+
/**
|
|
1080
|
+
* Log an error and return a correlation id. If the error already carries an
|
|
1081
|
+
* `errorId` it is reused, otherwise a fresh one is generated.
|
|
1082
|
+
*/
|
|
1083
|
+
error(error: unknown, meta?: unknown): string;
|
|
1084
|
+
/** Build a structured payload from a message + optional metadata. */
|
|
1085
|
+
protected payload(message: string, meta?: unknown): Record<string, unknown>;
|
|
1086
|
+
/**
|
|
1087
|
+
* Sink hook — override to route logs elsewhere. Default: structured
|
|
1088
|
+
* `console` write keyed by severity.
|
|
1089
|
+
*/
|
|
1090
|
+
protected write(severity: LogSeverity, payload: Record<string, unknown>): void;
|
|
1091
|
+
/** Reuse an error's `errorId` when present, else generate one. */
|
|
1092
|
+
protected static errorId(error: unknown): string;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
/**
|
|
1096
|
+
* Built-in error types thrown by the server pipeline and their default
|
|
1097
|
+
* HTTP mapping — shared by the request handler and {@link BaseErrorHandler}.
|
|
1098
|
+
*/
|
|
1099
|
+
|
|
1100
|
+
/** Thrown when the request body cannot be read / parsed → HTTP 400. */
|
|
1101
|
+
declare class BadRequestError extends Error {
|
|
1102
|
+
readonly statusCode: 400;
|
|
1103
|
+
constructor(message: string);
|
|
1104
|
+
}
|
|
1105
|
+
/** Thrown when a handler's return value fails the `output` schema → HTTP 500. */
|
|
1106
|
+
declare class OutputValidationError extends Error {
|
|
1107
|
+
readonly zodError: ZodError;
|
|
1108
|
+
readonly statusCode: 500;
|
|
1109
|
+
constructor(zodError: ZodError);
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* Default JSON mapping for the package's own errors (`ValidationError`,
|
|
1113
|
+
* `BadRequestError`, `OutputValidationError`). Returns `null` for anything
|
|
1114
|
+
* else so the caller can decide (rethrow / custom handler).
|
|
1115
|
+
*/
|
|
1116
|
+
declare function defaultErrorResponse(c: any, err: unknown): Response | null;
|
|
1117
|
+
|
|
1118
|
+
/**
|
|
1119
|
+
* Hono-native auth guards for the OpenAPI docs / spec endpoints.
|
|
1120
|
+
*
|
|
1121
|
+
* These return plain Hono `MiddlewareHandler`s, so they slot into
|
|
1122
|
+
* `OpenAPIConfig.docsAuth` (which protects only `/docs` + `/openapi.json`,
|
|
1123
|
+
* never the API routes). For a fully custom flow, pass your own middleware
|
|
1124
|
+
* instead of these helpers.
|
|
1125
|
+
*/
|
|
1126
|
+
|
|
1127
|
+
/** Decoded token shape — kept minimal to avoid a hard firebase-admin import. */
|
|
1128
|
+
interface DecodedBearerToken {
|
|
1129
|
+
uid: string;
|
|
1130
|
+
email?: string;
|
|
1131
|
+
[claim: string]: any;
|
|
1132
|
+
}
|
|
1133
|
+
/** Minimal Firebase Admin Auth surface used by {@link firebaseBearerAuth}. */
|
|
1134
|
+
interface FirebaseBearerAuthLike {
|
|
1135
|
+
verifyIdToken(idToken: string, checkRevoked?: boolean): Promise<DecodedBearerToken>;
|
|
1136
|
+
}
|
|
1137
|
+
/** Options for {@link firebaseBearerAuth}. */
|
|
1138
|
+
interface FirebaseBearerAuthOptions {
|
|
1139
|
+
/**
|
|
1140
|
+
* Returns the Firebase Admin Auth instance, e.g. `() => getAuth()`. Called
|
|
1141
|
+
* lazily on each request so `initializeApp()` runs first.
|
|
1142
|
+
*/
|
|
1143
|
+
getAuth: () => FirebaseBearerAuthLike;
|
|
1144
|
+
/**
|
|
1145
|
+
* Authorization policy. Return `false` (or throw) to reject the request,
|
|
1146
|
+
* any truthy value to allow. Defaults to allowing any verified token.
|
|
1147
|
+
*/
|
|
1148
|
+
allow?: (token: DecodedBearerToken) => boolean | Promise<boolean>;
|
|
1149
|
+
/** Revoke check passed to `verifyIdToken`. Default: `false`. */
|
|
1150
|
+
checkRevoked?: boolean;
|
|
1151
|
+
/**
|
|
1152
|
+
* Context key under which the decoded token is stored (`c.set(key, token)`)
|
|
1153
|
+
* for downstream handlers. Default: `"docsUser"`.
|
|
1154
|
+
*/
|
|
1155
|
+
contextKey?: string;
|
|
1156
|
+
}
|
|
1157
|
+
/**
|
|
1158
|
+
* Guard the docs / spec endpoints with a Firebase ID token (Bearer scheme).
|
|
1159
|
+
*
|
|
1160
|
+
* @example
|
|
1161
|
+
* ```ts
|
|
1162
|
+
* import { getAuth } from "firebase-admin/auth";
|
|
1163
|
+
* import { firebaseBearerAuth } from "@lpdjs/firestore-repo-service/servers/hono";
|
|
1164
|
+
*
|
|
1165
|
+
* openapi: {
|
|
1166
|
+
* info,
|
|
1167
|
+
* docsAuth: firebaseBearerAuth({
|
|
1168
|
+
* getAuth: () => getAuth(),
|
|
1169
|
+
* allow: (t) => t.admin === true,
|
|
1170
|
+
* }),
|
|
1171
|
+
* }
|
|
1172
|
+
* ```
|
|
1173
|
+
*/
|
|
1174
|
+
declare function firebaseBearerAuth(options: FirebaseBearerAuthOptions): MiddlewareHandler;
|
|
1175
|
+
/** Options for {@link basicAuth}. */
|
|
1176
|
+
interface BasicAuthOptions {
|
|
1177
|
+
username: string;
|
|
1178
|
+
password: string;
|
|
1179
|
+
/** Realm advertised in the `WWW-Authenticate` header. Default: `"Docs"`. */
|
|
1180
|
+
realm?: string;
|
|
1181
|
+
}
|
|
1182
|
+
/**
|
|
1183
|
+
* Guard the docs / spec endpoints with HTTP Basic Auth.
|
|
1184
|
+
*
|
|
1185
|
+
* @example
|
|
1186
|
+
* ```ts
|
|
1187
|
+
* openapi: { info, docsAuth: basicAuth({ username: "admin", password: "secret" }) }
|
|
1188
|
+
* ```
|
|
1189
|
+
*/
|
|
1190
|
+
declare function basicAuth(options: BasicAuthOptions): MiddlewareHandler;
|
|
1191
|
+
|
|
733
1192
|
/**
|
|
734
1193
|
* URL path inference from filesystem layout.
|
|
735
1194
|
*
|
|
@@ -828,4 +1287,4 @@ declare function generateRoutesManifest(routes: ScannedRoute[], opts: GeneratorO
|
|
|
828
1287
|
/** Convenience helper used by the CLI — combines scan + generate in one call. */
|
|
829
1288
|
declare function generateFromRoot(rootAbs: string, outFileRel: string, derive: PathDeriveOptions, importExtension: string, scan: (root: string) => ScannedRoute[]): GenerationResult;
|
|
830
1289
|
|
|
831
|
-
export { type AnyRouteDef, type AnyServicesContainer, type ApiConfig, type ApiConfigMap, type ApiRegistry, type ApiRegistryOptions, DEFAULT_DERIVE, DEFAULT_GENERATOR_BANNER, DEFAULT_SCANNER, type GenerationResult, type GeneratorOptions, HonoServer, type HonoServerOptions, type HttpMethod, type OpenAPIConfig, type OpenAPIInfo, type PathDeriveOptions, type PayloadSource, type RequestContext, type RouteDef, type RouteHandler, type RouteInput, type RouteInterceptor, type RouteModuleDefault, type RouteOutput, type RoutesUnion, type ScannedRoute, type ScannerOptions, type ServiceProvider, type ServiceProviderMap, type ServicesContainer, type ServicesOf, UseCase, type UseCaseClass, type UseCaseRouteMeta, ValidationError, buildOpenApiDocument, createApiRegistry, createRequestContextMiddleware, createServices, defineRoutes, derivePath, generateFromRoot, generateRoutesManifest, renderDocsHtml, scanRoutes, toImportSpecifier, useCaseRoute, withRequestContext };
|
|
1290
|
+
export { type AnyRouteDef, type AnyServicesContainer, type ApiConfig, type ApiConfigMap, type ApiKeySecurityScheme, type ApiRegistry, type ApiRegistryOptions, BadRequestError, BaseErrorHandler, BaseLogger, type BasicAuthOptions, DEFAULT_DERIVE, DEFAULT_GENERATOR_BANNER, DEFAULT_SCANNER, type DecodedBearerToken, type ErrorHandler, type ErrorHandlerContext, type FirebaseBearerAuthLike, type FirebaseBearerAuthOptions, type GenerationResult, type GeneratorOptions, HonoServer, type HonoServerOptions, type HttpMethod, type HttpSecurityScheme, type InterceptorConfig, type InterceptorErrorResponse, type InterceptorOption, type InterceptorOutput, type LogSeverity, type Logger, type MutualTlsSecurityScheme, type OAuth2SecurityScheme, type OAuthFlowObject, type OAuthFlowsObject, type OpenAPIConfig, type OpenAPIInfo, type OpenIdConnectSecurityScheme, OutputValidationError, type PathDeriveOptions, type PayloadSource, type RequestContext, type RouteDef, type RouteHandler, type RouteInput, type RouteInterceptor, type RouteModuleDefault, type RouteOutput, type RoutesUnion, type ScannedRoute, type ScannerOptions, type SecurityRequirement, type SecurityScheme, type SecuritySchemeBase, type ServiceProvider, type ServiceProviderMap, type ServicesContainer, type ServicesOf, UseCase, type UseCaseClass, type UseCaseRouteMeta, ValidationError, basicAuth, buildOpenApiDocument, createApiRegistry, createRequestContextMiddleware, createServices, defaultErrorResponse, defineRoutes, derivePath, firebaseBearerAuth, generateFromRoot, generateRoutesManifest, renderDocsHtml, scanRoutes, toImportSpecifier, useCaseRoute, withRequestContext };
|