@lpdjs/firestore-repo-service 2.2.9-beta.8 → 2.2.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 +92 -34
- package/dist/servers/hono/cli.cjs.map +1 -1
- package/dist/servers/hono/cli.js +92 -34
- package/dist/servers/hono/cli.js.map +1 -1
- package/dist/servers/hono/index.cjs +5 -5
- package/dist/servers/hono/index.cjs.map +1 -1
- package/dist/servers/hono/index.d.cts +110 -37
- package/dist/servers/hono/index.d.ts +110 -37
- package/dist/servers/hono/index.js +5 -5
- package/dist/servers/hono/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MiddlewareHandler, Context, Hono } from 'hono';
|
|
1
|
+
import { Env, MiddlewareHandler, Context, Hono } from 'hono';
|
|
2
2
|
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
3
3
|
import { z, ZodError } from 'zod';
|
|
4
4
|
|
|
@@ -16,20 +16,24 @@ type HttpMethod = "get" | "post" | "put" | "patch" | "delete";
|
|
|
16
16
|
/** Where the validated payload comes from. */
|
|
17
17
|
type PayloadSource = "json" | "query" | "form" | "param";
|
|
18
18
|
/** Handler signature — receives a single typed context object. */
|
|
19
|
-
type RouteHandler<TIn, TOut> = (ctx: {
|
|
19
|
+
type RouteHandler<TIn, TOut, TEnv extends Env = Env> = (ctx: {
|
|
20
20
|
/** Validated (and typed) request payload. `void` when no `input` schema is defined. */
|
|
21
21
|
input: TIn;
|
|
22
22
|
/** Raw Hono `Context` for headers, set status, redirect, etc. */
|
|
23
|
-
c: Context
|
|
23
|
+
c: Context<TEnv>;
|
|
24
24
|
}) => Promise<TOut | Response> | TOut | Response;
|
|
25
25
|
/**
|
|
26
26
|
* One route declaration. Default-exported by every `routes.ts` file inside the
|
|
27
27
|
* domain tree. Use {@link defineRoute} for full type inference.
|
|
28
28
|
*/
|
|
29
|
-
interface RouteDef<TIn extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefined, TOut extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefined> {
|
|
29
|
+
interface RouteDef<TIn extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefined, TOut extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefined, TEnv extends Env = Env> {
|
|
30
30
|
/**
|
|
31
31
|
* Logical API tag — routes sharing the same `api` are mounted on the same
|
|
32
32
|
* `HonoServer` (typically one Cloud Function per `api`).
|
|
33
|
+
*
|
|
34
|
+
* To expose the **same logic** under several APIs with different
|
|
35
|
+
* inputs/outputs, export multiple `defineRoute({...})` from the same file
|
|
36
|
+
* (default + named exports are both picked up by the codegen).
|
|
33
37
|
*/
|
|
34
38
|
api: string;
|
|
35
39
|
/** HTTP method. */
|
|
@@ -56,7 +60,7 @@ interface RouteDef<TIn extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefin
|
|
|
56
60
|
/** Status code for the success response. Default: 200. */
|
|
57
61
|
status?: number;
|
|
58
62
|
/** Hono middlewares applied to this route only (after global middlewares). */
|
|
59
|
-
middlewares?: MiddlewareHandler[];
|
|
63
|
+
middlewares?: MiddlewareHandler<TEnv>[];
|
|
60
64
|
summary?: string;
|
|
61
65
|
description?: string;
|
|
62
66
|
tags?: string[];
|
|
@@ -65,7 +69,7 @@ interface RouteDef<TIn extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefin
|
|
|
65
69
|
/** Security requirements (operationId-level override). */
|
|
66
70
|
security?: Array<Record<string, string[]>>;
|
|
67
71
|
/** The request handler. */
|
|
68
|
-
handler: RouteHandler<TIn extends z.ZodTypeAny ? z.infer<TIn> : void, TOut extends z.ZodTypeAny ? z.infer<TOut> : unknown>;
|
|
72
|
+
handler: RouteHandler<TIn extends z.ZodTypeAny ? z.infer<TIn> : void, TOut extends z.ZodTypeAny ? z.infer<TOut> : unknown, TEnv>;
|
|
69
73
|
}
|
|
70
74
|
/** Erased `RouteDef` used by registry/codegen — handler signature is opaque. */
|
|
71
75
|
type AnyRouteDef = RouteDef<any, any>;
|
|
@@ -119,7 +123,7 @@ declare class ValidationError extends Error {
|
|
|
119
123
|
* }
|
|
120
124
|
* ```
|
|
121
125
|
*/
|
|
122
|
-
type RouteInterceptor = (ctx: {
|
|
126
|
+
type RouteInterceptor<TEnv extends Env = Env> = (ctx: {
|
|
123
127
|
/**
|
|
124
128
|
* Calls validation + handler and returns the raw value.
|
|
125
129
|
* Throws {@link ValidationError} on Zod failure or any error thrown by the handler.
|
|
@@ -128,7 +132,7 @@ type RouteInterceptor = (ctx: {
|
|
|
128
132
|
/** Route metadata (read-only). */
|
|
129
133
|
route: AnyRouteDef;
|
|
130
134
|
/** Hono request context. */
|
|
131
|
-
c: Context
|
|
135
|
+
c: Context<TEnv>;
|
|
132
136
|
}) => Promise<Response | unknown> | Response | unknown;
|
|
133
137
|
/** OpenAPI document info (subset of the spec used by the helper). */
|
|
134
138
|
interface OpenAPIInfo {
|
|
@@ -155,7 +159,7 @@ interface OpenAPIConfig {
|
|
|
155
159
|
security?: Array<Record<string, string[]>>;
|
|
156
160
|
}
|
|
157
161
|
/** Options consumed by the {@link HonoServer} constructor. */
|
|
158
|
-
interface HonoServerOptions {
|
|
162
|
+
interface HonoServerOptions<TEnv extends Env = Env> {
|
|
159
163
|
/**
|
|
160
164
|
* API tag — only routes whose `api` matches this value are mounted.
|
|
161
165
|
* If omitted, every route in the registry is mounted.
|
|
@@ -166,12 +170,12 @@ interface HonoServerOptions {
|
|
|
166
170
|
/** URL prefix mounted before every route path. Default: `""`. */
|
|
167
171
|
basePath?: string;
|
|
168
172
|
/** Hono middlewares applied to every route (after the built-ins). */
|
|
169
|
-
middlewares?: MiddlewareHandler[];
|
|
173
|
+
middlewares?: MiddlewareHandler<TEnv>[];
|
|
170
174
|
/**
|
|
171
175
|
* Alias for `middlewares` — global middlewares applied to every route.
|
|
172
176
|
* If both are provided, `globalMiddlewares` is appended after `middlewares`.
|
|
173
177
|
*/
|
|
174
|
-
globalMiddlewares?: MiddlewareHandler[];
|
|
178
|
+
globalMiddlewares?: MiddlewareHandler<TEnv>[];
|
|
175
179
|
/**
|
|
176
180
|
* If `true`, the server validates the value returned by every handler
|
|
177
181
|
* against the route's `output` schema and rejects mismatches with a 500
|
|
@@ -183,15 +187,15 @@ interface HonoServerOptions {
|
|
|
183
187
|
/** OpenAPI configuration. Omit to disable. */
|
|
184
188
|
openapi?: OpenAPIConfig;
|
|
185
189
|
/** Custom 404 handler. */
|
|
186
|
-
notFound?: (c: Context) => Response | Promise<Response>;
|
|
190
|
+
notFound?: (c: Context<TEnv>) => Response | Promise<Response>;
|
|
187
191
|
/** Custom error handler. */
|
|
188
|
-
onError?: (err: unknown, c: Context) => Response | Promise<Response>;
|
|
192
|
+
onError?: (err: unknown, c: Context<TEnv>) => Response | Promise<Response>;
|
|
189
193
|
/**
|
|
190
194
|
* Cross-cutting interceptor wrapping every handler call.
|
|
191
195
|
* Ideal for response envelopes, business-error mapping, tracing.
|
|
192
196
|
* See {@link RouteInterceptor}.
|
|
193
197
|
*/
|
|
194
|
-
interceptor?: RouteInterceptor
|
|
198
|
+
interceptor?: RouteInterceptor<TEnv>;
|
|
195
199
|
}
|
|
196
200
|
|
|
197
201
|
/**
|
|
@@ -212,15 +216,15 @@ interface HonoServerOptions {
|
|
|
212
216
|
* stays decoupled from a specific firebase-functions version. We import the
|
|
213
217
|
* real type only when users pass `onRequest` to `toFunction(...)`.
|
|
214
218
|
*/
|
|
215
|
-
type OnRequestFn = (...args: any[]) => any;
|
|
216
|
-
declare class HonoServer {
|
|
219
|
+
type OnRequestFn$1 = (...args: any[]) => any;
|
|
220
|
+
declare class HonoServer<TEnv extends Env = Env> {
|
|
217
221
|
private readonly app;
|
|
218
222
|
private readonly options;
|
|
219
223
|
private readonly mountedRoutes;
|
|
220
224
|
private cachedSpec;
|
|
221
|
-
constructor(options: HonoServerOptions);
|
|
225
|
+
constructor(options: HonoServerOptions<TEnv>);
|
|
222
226
|
/** Underlying Hono instance — useful for advanced composition / tests. */
|
|
223
|
-
get hono(): Hono
|
|
227
|
+
get hono(): Hono<TEnv>;
|
|
224
228
|
/** Raw `(req, res)` handler suitable for `onRequest()` / `http.createServer`. */
|
|
225
229
|
get nodeHandler(): (req: IncomingMessage, res: ServerResponse) => void;
|
|
226
230
|
/**
|
|
@@ -231,7 +235,7 @@ declare class HonoServer {
|
|
|
231
235
|
* @param httpsOptions Options forwarded as the first argument to
|
|
232
236
|
* `onRequest()` (region, memory, invoker, etc.).
|
|
233
237
|
*/
|
|
234
|
-
toFunction(onRequest: OnRequestFn, httpsOptions?: Record<string, unknown>): any;
|
|
238
|
+
toFunction(onRequest: OnRequestFn$1, httpsOptions?: Record<string, unknown>): any;
|
|
235
239
|
/** Generate (and cache) the OpenAPI 3.1 spec for the mounted routes. */
|
|
236
240
|
buildOpenApiSpec(): Record<string, unknown>;
|
|
237
241
|
private mountRoutes;
|
|
@@ -239,32 +243,101 @@ declare class HonoServer {
|
|
|
239
243
|
}
|
|
240
244
|
|
|
241
245
|
/**
|
|
242
|
-
*
|
|
243
|
-
*
|
|
246
|
+
* Typed multi-API registry.
|
|
247
|
+
*
|
|
248
|
+
* Lets you declare every API tag (= every Cloud Function) in **one place**,
|
|
249
|
+
* with full TypeScript safety: the `api` field of {@link defineRoute} is
|
|
250
|
+
* narrowed to the registered tags, and {@link toFunctions} returns one
|
|
251
|
+
* `onRequest` Cloud Function per tag, named after its key.
|
|
244
252
|
*
|
|
245
253
|
* @example
|
|
246
254
|
* ```ts
|
|
247
|
-
*
|
|
248
|
-
* import {
|
|
249
|
-
* import {
|
|
255
|
+
* // apis.ts
|
|
256
|
+
* import { createApiRegistry } from "@lpdjs/firestore-repo-service/servers/hono";
|
|
257
|
+
* import { enrichUser } from "./middlewares/enrich-user.js";
|
|
250
258
|
*
|
|
251
|
-
* export
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
* const useCase = new ActivitiesCreateOrUpdateCustomUseCase(new RepositoryActivities());
|
|
261
|
-
* return useCase.execute(payload);
|
|
259
|
+
* export const apis = createApiRegistry({
|
|
260
|
+
* v1: {
|
|
261
|
+
* basePath: "/v1",
|
|
262
|
+
* middlewares: [enrichUser],
|
|
263
|
+
* openapi: { info: { title: "Public API", version: "1.0.0" } },
|
|
264
|
+
* },
|
|
265
|
+
* webhooks: {
|
|
266
|
+
* basePath: "/hooks",
|
|
267
|
+
* openapi: { info: { title: "Webhooks", version: "1.0.0" } },
|
|
262
268
|
* },
|
|
263
269
|
* });
|
|
270
|
+
*
|
|
271
|
+
* // Use in routes — `api` is now typed "v1" | "webhooks".
|
|
272
|
+
* export const defineRoute = apis.defineRoute;
|
|
273
|
+
*
|
|
274
|
+
* // index.ts (Cloud Functions entrypoint)
|
|
275
|
+
* import { onRequest } from "firebase-functions/v2/https";
|
|
276
|
+
* import { apis } from "./apis.js";
|
|
277
|
+
* import { routes } from "./domains/__generated__/routes.js";
|
|
278
|
+
*
|
|
279
|
+
* export const { v1, webhooks } = apis.toFunctions(routes, onRequest, {
|
|
280
|
+
* defaults: { region: "us-central1", invoker: "public" },
|
|
281
|
+
* per: { v1: { memory: "512MiB" } },
|
|
282
|
+
* });
|
|
283
|
+
* // → URLs: https://<region>-<project>.cloudfunctions.net/v1/posts
|
|
284
|
+
* // https://<region>-<project>.cloudfunctions.net/webhooks/...
|
|
264
285
|
* ```
|
|
265
286
|
*/
|
|
266
287
|
|
|
267
|
-
|
|
288
|
+
type OnRequestFn = (...args: any[]) => any;
|
|
289
|
+
/**
|
|
290
|
+
* Per-API configuration. Same shape as {@link HonoServerOptions} minus the
|
|
291
|
+
* `routes` (resolved by the registry) and `api` (the registry key).
|
|
292
|
+
*/
|
|
293
|
+
type ApiConfig<TEnv extends Env = Env> = Omit<HonoServerOptions<TEnv>, "routes" | "api">;
|
|
294
|
+
/** Map of API tag → its config. */
|
|
295
|
+
type ApiConfigMap = Record<string, ApiConfig>;
|
|
296
|
+
interface ApiRegistry<TMap extends ApiConfigMap> {
|
|
297
|
+
/** The registered configs (read-only). */
|
|
298
|
+
readonly configs: TMap;
|
|
299
|
+
/**
|
|
300
|
+
* Typed `defineRoute` — the `api` field is constrained to `keyof TMap`.
|
|
301
|
+
*
|
|
302
|
+
* To expose the same logical endpoint under several APIs with different
|
|
303
|
+
* `input` / `output` schemas, call `defineRoute` once per route and wrap
|
|
304
|
+
* them in an array — per-call inference is preserved:
|
|
305
|
+
*
|
|
306
|
+
* ```ts
|
|
307
|
+
* export default [
|
|
308
|
+
* defineRoute({ api: "v1", input: V1Input, handler: ({ input }) => ... }),
|
|
309
|
+
* defineRoute({ api: "v2", input: V2Input, handler: ({ input }) => ... }),
|
|
310
|
+
* ];
|
|
311
|
+
* ```
|
|
312
|
+
*/
|
|
313
|
+
defineRoute<TIn extends z.ZodTypeAny | undefined = undefined, TOut extends z.ZodTypeAny | undefined = undefined>(def: Omit<RouteDef<TIn, TOut>, "api"> & {
|
|
314
|
+
api: keyof TMap & string;
|
|
315
|
+
}): RouteDef<TIn, TOut> & {
|
|
316
|
+
api: keyof TMap & string;
|
|
317
|
+
};
|
|
318
|
+
/**
|
|
319
|
+
* Build one Cloud Function per registered API and return them as a map
|
|
320
|
+
* keyed by API tag — spread it directly into your `index.ts` exports.
|
|
321
|
+
*
|
|
322
|
+
* @param routes Pre-resolved route registry (typically the codegen output).
|
|
323
|
+
* @param onRequest The `onRequest` factory imported from
|
|
324
|
+
* `firebase-functions/v2/https`.
|
|
325
|
+
* @param opts Optional defaults and per-API overrides for `httpsOptions`.
|
|
326
|
+
*/
|
|
327
|
+
toFunctions(routes: AnyRouteDef[], onRequest: OnRequestFn, opts?: {
|
|
328
|
+
defaults?: Record<string, unknown>;
|
|
329
|
+
per?: Partial<Record<keyof TMap & string, Record<string, unknown>>>;
|
|
330
|
+
}): {
|
|
331
|
+
[K in keyof TMap & string]: ReturnType<OnRequestFn>;
|
|
332
|
+
};
|
|
333
|
+
/** Build the underlying {@link HonoServer} for a given API (escape hatch). */
|
|
334
|
+
serverFor<K extends keyof TMap & string>(api: K, routes: AnyRouteDef[]): HonoServer;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Factory — declare every API tag once and get back a typed `defineRoute`
|
|
338
|
+
* + `toFunctions`. See the file-level example.
|
|
339
|
+
*/
|
|
340
|
+
declare function createApiRegistry<const TMap extends ApiConfigMap>(configs: TMap): ApiRegistry<TMap>;
|
|
268
341
|
|
|
269
342
|
/**
|
|
270
343
|
* OpenAPI 3.1 spec generator from {@link RouteDef} entries.
|
|
@@ -379,4 +452,4 @@ declare function generateRoutesManifest(routes: ScannedRoute[], opts: GeneratorO
|
|
|
379
452
|
/** Convenience helper used by the CLI — combines scan + generate in one call. */
|
|
380
453
|
declare function generateFromRoot(rootAbs: string, outFileRel: string, derive: PathDeriveOptions, importExtension: string, scan: (root: string) => ScannedRoute[]): GenerationResult;
|
|
381
454
|
|
|
382
|
-
export { type AnyRouteDef, 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 RouteDef, type RouteHandler, type RouteInterceptor, type RouteModuleDefault, type ScannedRoute, type ScannerOptions, ValidationError, buildOpenApiDocument,
|
|
455
|
+
export { type AnyRouteDef, type ApiConfig, type ApiConfigMap, type ApiRegistry, 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 RouteDef, type RouteHandler, type RouteInterceptor, type RouteModuleDefault, type ScannedRoute, type ScannerOptions, ValidationError, buildOpenApiDocument, createApiRegistry, derivePath, generateFromRoot, generateRoutesManifest, renderDocsHtml, scanRoutes, toImportSpecifier };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {Hono}from'hono';import {getRequestListener}from'@hono/node-server';import {OpenAPIRegistry,OpenApiGeneratorV31}from'@asteasolutions/zod-to-openapi';import {readdirSync,statSync,mkdirSync,writeFileSync}from'fs';import {join,relative,sep,dirname}from'path';var m=class extends Error{constructor(n,o){super("Request validation failed");this.zodError=n;this.source=o;this.statusCode=400;this.name="ValidationError";}};var
|
|
1
|
+
import {Hono}from'hono';import {getRequestListener}from'@hono/node-server';import {extendZodWithOpenApi,OpenAPIRegistry,OpenApiGeneratorV31}from'@asteasolutions/zod-to-openapi';import {z as z$1}from'zod';import {readdirSync,statSync,mkdirSync,writeFileSync}from'fs';import {join,relative,sep,dirname}from'path';var m=class extends Error{constructor(n,o){super("Request validation failed");this.zodError=n;this.source=o;this.statusCode=400;this.name="ValidationError";}};extendZodWithOpenApi(z$1);var S="Successful response";function j(t){return t==="get"?"query":"json"}function v(t,e,n){let o=new OpenAPIRegistry;if(n.securitySchemes)for(let[s,a]of Object.entries(n.securitySchemes))o.registerComponent("securitySchemes",s,a);for(let s of t){let a=s.method,u=s.source??j(a),p=z(e,s.path??"/"),c=s.status??200,d=$(a,u,s.input),l=w(u,s.input,"query"),f=w(u,s.input,"param"),g=Z(a,p);o.registerPath({method:a,path:q(p),operationId:g,summary:s.summary,description:s.description,tags:s.tags,deprecated:s.deprecated,security:s.security,request:{...l?{query:l}:{},...f?{params:f}:{},...d?{body:d}:{}},responses:s.output?{[c]:{description:S,content:{"application/json":{schema:s.output}}}}:{[c]:{description:S}}});}return new OpenApiGeneratorV31(o.definitions).generateDocument({openapi:"3.1.0",info:n.info,servers:n.servers,security:n.security})}function $(t,e,n){return !n||t==="get"?null:e==="json"?{content:{"application/json":{schema:n}}}:e==="form"?{content:{"application/x-www-form-urlencoded":{schema:n}}}:null}function w(t,e,n){if(e&&(n==="query"&&t==="query"||n==="param"&&t==="param"))return e}function q(t){return t.replace(/:([A-Za-z0-9_]+)/g,"{$1}")}function z(t,e){let n=t.endsWith("/")?t.slice(0,-1):t,o=e.startsWith("/")?e:`/${e}`,r=`${n}${o}`;return r===""?"/":r}function Z(t,e){let n=e.replace(/[{}]/g,"").replace(/\/+/g,"_").replace(/[^A-Za-z0-9_]/g,"").replace(/^_+|_+$/g,"");return `${t}_${n||"root"}`}function E(t,e){let n=t.replace(/"/g,""");return `<!doctype html>
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8" />
|
|
@@ -9,7 +9,7 @@ import {Hono}from'hono';import {getRequestListener}from'@hono/node-server';impor
|
|
|
9
9
|
<script id="api-reference" data-url="${n}"></script>
|
|
10
10
|
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
|
|
11
11
|
</body>
|
|
12
|
-
</html>`}var
|
|
12
|
+
</html>`}var y=class{constructor(e){this.cachedSpec=null;this.options=e,this.app=new Hono,this.mountedRoutes=L(e.routes,e.api);let n=[...e.middlewares??[],...e.globalMiddlewares??[]];for(let o of n)this.app.use("*",o);this.mountRoutes(),this.mountOpenApi(),e.notFound&&this.app.notFound(e.notFound),e.onError&&this.app.onError(e.onError);}get hono(){return this.app}get nodeHandler(){return getRequestListener(this.app.fetch,{overrideGlobalObjects:false})}toFunction(e,n){let o=this.nodeHandler;return n?e(n,o):e(o)}buildOpenApiSpec(){if(this.cachedSpec)return this.cachedSpec;if(!this.options.openapi)throw new Error("[HonoServer] openapi config not set");return this.cachedSpec=v(this.mountedRoutes,this.options.basePath??"",this.options.openapi),this.cachedSpec}mountRoutes(){let e=this.options.basePath??"",n=this.options.validateOutput??false,o=this.options.verbose??false;for(let r of this.mountedRoutes){if(!r.path)throw new Error(`[HonoServer] route "${r.method.toUpperCase()} (no path)" \u2014 missing \`path\`. Run the codegen so the path is derived from the file location, or set it explicitly.`);let i=A(e,r.path),s=r.middlewares??[],a=r.source??(r.method==="get"?"query":"json"),u=B(r,a,n,this.options.interceptor),p=r.method.toUpperCase();this.app.on(p,[i],...s,u),o&&console.log(`[HonoServer] ${r.method.toUpperCase().padEnd(6)} ${i}`);}}mountOpenApi(){let e=this.options.openapi;if(!e)return;let n=e.path??"/openapi.json",o=e.docsPath===void 0?"/docs":e.docsPath,r=A(this.options.basePath??"",n),i=o===false?null:A(this.options.basePath??"",o);if(this.app.get(r,s=>s.json(this.buildOpenApiSpec())),i){let s=U(i,r);this.app.get(i,a=>a.html(E(s,e.info.title)));}}};function L(t,e){return e?t.filter(n=>Array.isArray(n.api)?n.api.includes(e):n.api===e):t.slice()}function A(t,e){let n=t.endsWith("/")?t.slice(0,-1):t,o=e.startsWith("/")?e:`/${e}`,r=`${n}${o}`;return r===""?"/":r}function U(t,e){let n=t.split("/").filter(Boolean),o=e.split("/").filter(Boolean);n.pop();let r=0;for(;r<n.length&&r<o.length&&n[r]===o[r];)r++;let i=n.length-r;return [...Array(i).fill(".."),...o.slice(r)].join("/")||"./"}function B(t,e,n,o){let r=t.input,i=t.output,s=t.status??200;return async a=>{let u=async()=>{let c;if(r){let l;try{l=await W(a,e,t.method);}catch(g){throw new h(g instanceof Error?g.message:String(g))}let f=r.safeParse(l);if(!f.success)throw new m(f.error,e);c=f.data;}let d=await t.handler({input:c,c:a});if(n&&i&&!(d instanceof Response)){let l=i.safeParse(d);if(!l.success)throw new R(l.error);return l.data}return d},p;if(o)p=await o({next:u,route:t,c:a});else try{p=await u();}catch(c){let d=V(a,c);if(d)return d;throw c}return p instanceof Response?p:a.json(p,s)}}var h=class extends Error{constructor(n){super(n);this.statusCode=400;this.name="BadRequestError";}},R=class extends Error{constructor(n){super("Output validation failed");this.zodError=n;this.statusCode=500;this.name="OutputValidationError";}};function V(t,e){return e instanceof m?t.json({success:false,error:"Validation failed",issues:x(e.zodError)},400):e instanceof h?t.json({success:false,error:"Bad Request",message:e.message},400):e instanceof R?t.json({success:false,error:"Output validation failed",issues:x(e.zodError)},500):null}async function W(t,e,n){switch(e){case "json":{if(n==="get")return t.req.query();let o=await t.req.text();if(!o)return {};try{return JSON.parse(o)}catch(r){throw new Error(`Invalid JSON body: ${r instanceof Error?r.message:String(r)}`)}}case "query":return t.req.query();case "form":return await t.req.parseBody();case "param":return t.req.param();default:return {}}}function x(t){return t.issues.map(e=>({path:e.path.join("."),code:e.code,message:e.message}))}function J(t){return {configs:t,defineRoute(e){return e},serverFor(e,n){let o=t[e];if(!o)throw new Error(`[ApiRegistry] unknown api "${e}". Registered: ${Object.keys(t).join(", ")}`);return new y({...o,api:e,routes:n})},toFunctions(e,n,o){let r={};for(let i of Object.keys(t)){let s={...o?.defaults??{},...o?.per?.[i]??{}},a=new y({...t[i],api:i,routes:e});r[i]=Object.keys(s).length?a.toFunction(n,s):a.toFunction(n);}return r}}}var D={skipSegments:["useCases","useCase","use-cases","use-case"],casing:"preserve"};function P(t,e=D){let n=new Set(e.skipSegments.map(r=>r.toLowerCase()));return "/"+t.split("/").filter(Boolean).filter(r=>!n.has(r.toLowerCase())).map(r=>e.casing==="kebab"?K(r):r).join("/")}function K(t){return t.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/[\s_]+/g,"-").toLowerCase()}function T(t,e,n){let o=O(t),r=O(e),i=0;for(;i<o.length&&i<r.length&&o[i]===r[i];)i++;let s=o.length-i,a=r.slice(i),p=(a[a.length-1]??"").replace(/\.[mc]?[tj]sx?$/i,""),c=n===""?p:`${p}${n}`;return a[a.length-1]=c,(s===0?"./":"../".repeat(s))+a.join("/")}function O(t){return t.replace(/\\/g,"/").replace(/\/+$/,"").split("/").filter((n,o)=>!(o===0&&n===""))}var b={routesFile:"routes.ts",excludeSegments:["node_modules","__generated__","tests","__tests__",".turbo","dist","build",".next"]};function ne(t,e=b){let n=[];return k(t,t,e,n),n.sort((o,r)=>o.relPath.localeCompare(r.relPath)),n}function k(t,e,n,o){let r;try{r=readdirSync(e);}catch{return}for(let i of r){if(n.excludeSegments.includes(i))continue;let s=join(e,i),a;try{a=statSync(s);}catch{continue}if(a.isDirectory())k(t,s,n,o);else if(a.isFile()&&i===n.routesFile){let u=relative(t,s).split(sep).join("/"),p=u.replace(/\/?[^/]+$/,"");o.push({absPath:s,relPath:u,relDir:p});}}}var M="/**\n * AUTO-GENERATED by `@lpdjs/firestore-repo-service` Hono codegen.\n * Do not edit by hand \u2014 re-run `hono:gen` after adding / removing route files.\n */\n";function I(t,e){let n=dirname(e.outFile);mkdirSync(n,{recursive:true});let o=e.banner??M,r=(e.now??new Date).toISOString(),i=e.importExtension,s=[],a=[],u=[];t.forEach((c,d)=>{let l=T(n,c.absPath,i),f=P(c.relDir,e.derive);s.push(`import mod${d} from ${JSON.stringify(l)};`),a.push(` { __derivedPath: ${JSON.stringify(f)}, mod: mod${d} },`),u.push({source:c.relPath,url:f});});let p=`${o}// Generated at ${r} \u2014 ${t.length} route file${t.length===1?"":"s"}.
|
|
13
13
|
|
|
14
14
|
import type { AnyRouteDef, RouteModuleDefault } from "@lpdjs/firestore-repo-service/servers/hono";
|
|
15
15
|
|
|
@@ -18,13 +18,13 @@ import type { AnyRouteDef, RouteModuleDefault } from "@lpdjs/firestore-repo-serv
|
|
|
18
18
|
|
|
19
19
|
`:`
|
|
20
20
|
`)+`const __defs: { __derivedPath: string; mod: RouteModuleDefault }[] = [
|
|
21
|
-
`+
|
|
22
|
-
`)+(
|
|
21
|
+
`+a.join(`
|
|
22
|
+
`)+(a.length?`
|
|
23
23
|
`:"")+`];
|
|
24
24
|
|
|
25
25
|
export const routes: AnyRouteDef[] = __defs.flatMap(({ __derivedPath, mod }) => {
|
|
26
26
|
const list = Array.isArray(mod) ? mod : [mod];
|
|
27
27
|
return list.map((route) => ({ ...route, path: route.path ?? __derivedPath }));
|
|
28
28
|
});
|
|
29
|
-
`;return writeFileSync(e.outFile,
|
|
29
|
+
`;return writeFileSync(e.outFile,p,"utf8"),{outFile:e.outFile,routeCount:t.length,derivedPaths:u}}function ae(t,e,n,o,r){let i=r(t),s=join(t,e);return I(i,{outFile:s,derive:n,importExtension:o})}export{D as DEFAULT_DERIVE,M as DEFAULT_GENERATOR_BANNER,b as DEFAULT_SCANNER,y as HonoServer,m as ValidationError,v as buildOpenApiDocument,J as createApiRegistry,P as derivePath,ae as generateFromRoot,I as generateRoutesManifest,E as renderDocsHtml,ne as scanRoutes,T as toImportSpecifier};//# sourceMappingURL=index.js.map
|
|
30
30
|
//# sourceMappingURL=index.js.map
|