@aklinker1/zeta 2.1.2 → 2.1.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.
@@ -0,0 +1,2 @@
1
+ import { $ as SchemaAdapter, A as LifeCycleHookName, B as OnGlobalAfterResponseHook, C as GetResponseInputFromDef, D as GetRouteHandler, E as GetResponseStatusMap, F as MergeAppData, G as OnMapResponseContext, H as OnGlobalErrorHook, I as MergeRoutes, J as OnTransformHook, K as OnMapResponseHook, L as OnAfterHandleHook, M as MaybePromise, N as Merge, O as GetRouteHandlerReturnType, P as MergeApp, Q as RouterData, R as OnBeforeHandleContext, S as GetResponseInput, T as GetResponseOutputFromDef, U as OnGlobalRequestContext, V as OnGlobalErrorContext, W as OnGlobalRequestHook, X as RouteDef, Y as PrefixObjectKeys, Z as RouteHandler, _ as GetAppRoutes, a as AppData, at as Transport, b as GetRequestParamsOutput, c as BaseCtx, d as BaseRoutes, et as ServerSideFetch, f as BuildHandlerContext, g as GetAppDataCtx, h as GetAppData, i as App, it as StatusResult, j as LifeCycleHooks, k as LifeCycleHook, l as BasePath, m as DefaultAppData, n as AfterResponseContext, nt as Simplify, o as ApplyAppDataPrefix, ot as UseApp, p as CompiledRouteHandler, q as OnTransformContext, r as AnyDef, rt as StatusFn, s as ApplyAppPrefix, st as UseAppData, t as AfterHandleContext, tt as Setter, u as BasePrefix, v as GetRequestParamsInput, w as GetResponseOutput, x as GetRequestParamsOutputFromDef, y as GetRequestParamsInputFromDef, z as OnBeforeHandleHook } from "./types-D9oRVe1E.mjs";
2
+ export { AfterHandleContext, AfterResponseContext, AnyDef, App, AppData, ApplyAppDataPrefix, ApplyAppPrefix, BaseCtx, BasePath, BasePrefix, BaseRoutes, BuildHandlerContext, CompiledRouteHandler, DefaultAppData, GetAppData, GetAppDataCtx, GetAppRoutes, GetRequestParamsInput, GetRequestParamsInputFromDef, GetRequestParamsOutput, GetRequestParamsOutputFromDef, GetResponseInput, GetResponseInputFromDef, GetResponseOutput, GetResponseOutputFromDef, GetResponseStatusMap, GetRouteHandler, GetRouteHandlerReturnType, LifeCycleHook, LifeCycleHookName, LifeCycleHooks, MaybePromise, Merge, MergeApp, MergeAppData, MergeRoutes, OnAfterHandleHook, OnBeforeHandleContext, OnBeforeHandleHook, OnGlobalAfterResponseHook, OnGlobalErrorContext, OnGlobalErrorHook, OnGlobalRequestContext, OnGlobalRequestHook, OnMapResponseContext, OnMapResponseHook, OnTransformContext, OnTransformHook, PrefixObjectKeys, RouteDef, RouteHandler, RouterData, SchemaAdapter, ServerSideFetch, Setter, Simplify, StatusFn, StatusResult, Transport, UseApp, UseAppData };
package/dist/types.mjs ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,27 +1,48 @@
1
1
  {
2
2
  "name": "@aklinker1/zeta",
3
3
  "description": "Composable, testable, OpenAPI-first backend framework with validation built-in",
4
- "version": "2.1.2",
4
+ "version": "2.1.3",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "packageManager": "bun@1.3.5",
8
- "module": "src/index.ts",
9
- "types": "src/index.ts",
8
+ "module": "dist/index.mjs",
9
+ "types": "dist/index.d.mts",
10
10
  "exports": {
11
- ".": "./src/index.ts",
12
- "./types": "./src/types.ts",
13
- "./client": "./src/client.ts",
14
- "./testing": "./src/testing.ts",
15
- "./schema": "./src/schema.ts",
16
- "./adapters/zod-schema-adapter": "./src/adapters/zod-schema-adapter.ts",
17
- "./transports/bun-transport": "./src/transports/bun-transport.ts",
18
- "./transports/deno-transport": "./src/transports/deno-transport.ts"
11
+ ".": {
12
+ "types": "./dist/index.d.mts",
13
+ "default": "./dist/index.mjs"
14
+ },
15
+ "./types": {
16
+ "types": "./dist/types.d.mts",
17
+ "default": "./dist/types.mjs"
18
+ },
19
+ "./client": {
20
+ "types": "./dist/client.d.mts",
21
+ "default": "./dist/client.mjs"
22
+ },
23
+ "./testing": {
24
+ "types": "./dist/testing.d.mts",
25
+ "default": "./dist/testing.mjs"
26
+ },
27
+ "./schema": {
28
+ "types": "./dist/schema.d.mts",
29
+ "default": "./dist/schema.mjs"
30
+ },
31
+ "./adapters/zod-schema-adapter": {
32
+ "types": "./dist/adapters/zod-schema-adapter.d.mts",
33
+ "default": "./dist/adapters/zod-schema-adapter.mjs"
34
+ },
35
+ "./transports/bun-transport": {
36
+ "types": "./dist/transports/bun-transport.d.mts",
37
+ "default": "./dist/transports/bun-transport.mjs"
38
+ },
39
+ "./transports/deno-transport": {
40
+ "types": "./dist/transports/deno-transport.d.mts",
41
+ "default": "./dist/transports/deno-transport.mjs"
42
+ }
19
43
  },
20
44
  "files": [
21
- "src/*.ts",
22
- "src/adapters/*.ts",
23
- "src/internal/*.ts",
24
- "src/transports/*.ts"
45
+ "dist/*"
25
46
  ],
26
47
  "publishConfig": {
27
48
  "access": "public"
@@ -37,7 +58,7 @@
37
58
  ],
38
59
  "scripts": {
39
60
  "dev": "bun test --watch",
40
- "build": "echo 'Noop - nothing to build.'",
61
+ "build": "tsdown src/{index,types,client,testing,schema,adapters/zod-schema-adapter,transports/bun-transport,transports/deno-transport}.ts",
41
62
  "bench": "bun run src/__tests__/bench.ts",
42
63
  "example": "bun --watch run example.ts",
43
64
  "example:prod": "NODE_ENV=production bun run example",
@@ -72,6 +93,7 @@
72
93
  "prettier": "^3.5.3",
73
94
  "publint": "^0.3.12",
74
95
  "tinybench": "^4.0.1",
96
+ "tsdown": "^0.21.2",
75
97
  "vitepress": "^2.0.0-alpha.12",
76
98
  "vitepress-plugin-mermaid": "^2.0.17",
77
99
  "zod": "^4.1.11"
@@ -1,29 +0,0 @@
1
- /**
2
- * Contains a schema adapter for app's using [`zod`](https://npmjs.com/package/zod).
3
- * @module
4
- */
5
- import { z } from "zod";
6
- import type { SchemaAdapter } from "../types";
7
-
8
- /**
9
- * Usage:
10
- *
11
- * ```ts
12
- * import { zodSchemaAdapter } from "@aklinker1/zeta/adapters/zod-schema-adapter";
13
- *
14
- * const app = createApp({
15
- * schemaAdapter: zodSchemaAdapter,
16
- * });
17
- * ```
18
- */
19
- export const zodSchemaAdapter: SchemaAdapter = {
20
- toJsonSchema: (schema) => {
21
- if (!("_zod" in schema)) throw Error("input schema is not a Zod schema");
22
- const res = z.toJSONSchema(schema, { target: "openapi-3.0" });
23
- delete res.$schema;
24
- return res;
25
- },
26
- getMeta: (schema: any) => {
27
- return z.globalRegistry.get(schema);
28
- },
29
- };
package/src/app.ts DELETED
@@ -1,479 +0,0 @@
1
- import type { OpenAPI, OpenAPIV3_1 } from "openapi-types";
2
- import { addRoute, createRouter } from "rou3";
3
- import { compileRouter } from "rou3/compiler";
4
- import { compileFetchFunction } from "./internal/compile-fetch-function";
5
- import { compileRouteHandler } from "./internal/compile-route-handler";
6
- import { detectTransport } from "./internal/utils";
7
- import { buildOpenApiDocs, buildScalarHtml } from "./open-api";
8
- import type {
9
- App,
10
- BasePath,
11
- BasePrefix,
12
- DefaultAppData,
13
- LifeCycleHook,
14
- LifeCycleHookName,
15
- RouteDef,
16
- RouterData,
17
- SchemaAdapter,
18
- ServerSideFetch,
19
- Transport,
20
- } from "./types";
21
-
22
- let appIdInc = 0;
23
- const nextAppId = () => `app-${appIdInc++}`;
24
-
25
- let _hookIdInc = 0;
26
- const nextHookId = (appId: string) => `${appId}/hook-${_hookIdInc++}`;
27
-
28
- /**
29
- * Create a server-side, Zeta application.
30
- *
31
- * Zeta provides simple support for serving applications using `Bun.serve` and
32
- * `Deno.serve` by calling `app.listen(3000)`.
33
- *
34
- * If you need more customization, you can use the `build` method to create a
35
- * `fetch` function and serve it however you like.
36
- *
37
- * @example
38
- * ```ts
39
- * import { createApp } from "@aklinker1/zeta";
40
- *
41
- * const app = createApp({ prefix: "/api" })
42
- * .get("/health", () => "OK")
43
- * .get("/users", () => ["user1", "user2"]);
44
- *
45
- * app.listen(3000);
46
- *
47
- * // Or serve the app yourself
48
- * const fetch = app.build();
49
- * Bun.serve({ fetch, ... });
50
- * Deno.serve({ fetch, ... });
51
- * ```
52
- *
53
- * @param options Configure application behavior.
54
- */
55
- export function createApp<TPrefix extends BasePrefix = "">(
56
- options?: CreateAppOptions<TPrefix>,
57
- ): App<{
58
- ctx: {};
59
- exported: false;
60
- prefix: TPrefix;
61
- routes: {};
62
- }> {
63
- const appId = nextAppId();
64
-
65
- const { origin = "http://localhost", prefix = "" } = options ?? {};
66
- const hooks: App["~zeta"]["hooks"] = {};
67
- const routes: App["~zeta"]["routes"] = {};
68
-
69
- const addRoutesEntry = (method: string, route: string, data: RouterData) => {
70
- routes[method] ??= {};
71
- if (routes[method][route]) {
72
- console.warn(`Route ${route} already exists`);
73
- }
74
- routes[method][route] = data;
75
- };
76
-
77
- const cloneHooks = (): App["~zeta"]["hooks"] => {
78
- const cloned: App["~zeta"]["hooks"] = {};
79
- for (const key of Object.keys(hooks) as LifeCycleHookName[]) {
80
- if (hooks[key]) cloned[key] = [...hooks[key]] as any;
81
- }
82
- return cloned;
83
- };
84
-
85
- const app: App<DefaultAppData> = {
86
- // @ts-expect-error
87
- [Symbol.toStringTag]: "ZetaApp",
88
-
89
- "~zeta": {
90
- id: appId,
91
- prefix,
92
- routes,
93
- hooks,
94
- },
95
-
96
- build: () => {
97
- const jsonRoute = options?.openApiRoute ?? "/openapi.json";
98
- const scalarRoute = options?.scalarRoute ?? "/scalar";
99
- const docs = buildOpenApiDocs(options, app);
100
- if (docs.type === "error") {
101
- console.error("Failed to build OpenAPI docs:", docs.error);
102
- }
103
-
104
- app.get(jsonRoute, () => {
105
- if (docs.type === "error") {
106
- console.error("Failed to build OpenAPI docs:", docs.error);
107
- throw docs.error;
108
- }
109
- return docs.spec;
110
- });
111
- if (docs.type === "success") {
112
- const scalarHtml = buildScalarHtml(jsonRoute, options);
113
- app.get(
114
- scalarRoute,
115
- () =>
116
- new Response(scalarHtml, {
117
- headers: { "content-type": "text/html;charset=utf-8" },
118
- }),
119
- );
120
- }
121
-
122
- const router = createRouter<RouterData>();
123
- for (const [method, methodValue] of Object.entries(routes)) {
124
- for (const [path, data] of Object.entries(methodValue)) {
125
- addRoute(
126
- router,
127
- method === Method.Any ? undefined : method,
128
- path,
129
- data,
130
- );
131
- }
132
- }
133
-
134
- const getRoute = compileRouter(router);
135
- return compileFetchFunction({ getRoute, hooks, origin });
136
- },
137
-
138
- getOpenApiSpec: () => {
139
- const res = buildOpenApiDocs(options, app);
140
- if (res.type === "error") throw res.error;
141
-
142
- return res.spec;
143
- },
144
-
145
- export: () => {
146
- app["~zeta"].exported = true;
147
- return app as any;
148
- },
149
-
150
- listen: (port, cb) => {
151
- const transport = options?.transport ?? detectTransport();
152
- transport.listen(port, app.build(), cb);
153
- return app;
154
- },
155
-
156
- decorate: (...args: any[]) => {
157
- const obj: Record<string, any> =
158
- args.length === 2 ? { [args[0]]: args[1] } : args[0];
159
-
160
- hooks.onTransform ??= [];
161
- hooks.onTransform.push({
162
- id: nextHookId(appId),
163
- applyTo: "local",
164
- callback: () => obj,
165
- });
166
-
167
- return app;
168
- },
169
-
170
- onGlobalRequest(callback: any) {
171
- hooks.onGlobalRequest ??= [];
172
- hooks.onGlobalRequest.push({
173
- id: nextHookId(appId),
174
- applyTo: "global",
175
- callback,
176
- });
177
- return app;
178
- },
179
- onTransform(callback: any) {
180
- hooks.onTransform ??= [];
181
- hooks.onTransform.push({
182
- id: nextHookId(appId),
183
- applyTo: "local",
184
- callback,
185
- });
186
- return app;
187
- },
188
- onBeforeHandle(callback: any) {
189
- hooks.onBeforeHandle ??= [];
190
- hooks.onBeforeHandle.push({
191
- id: nextHookId(appId),
192
- applyTo: "local",
193
- callback,
194
- });
195
- return app;
196
- },
197
- onAfterHandle(callback: any) {
198
- hooks.onAfterHandle ??= [];
199
- hooks.onAfterHandle.push({
200
- id: nextHookId(appId),
201
- applyTo: "local",
202
- callback,
203
- });
204
- return app;
205
- },
206
- onMapResponse(callback: any) {
207
- hooks.onMapResponse ??= [];
208
- hooks.onMapResponse.push({
209
- id: nextHookId(appId),
210
- applyTo: "local",
211
- callback,
212
- });
213
- return app;
214
- },
215
- onGlobalError(callback: any) {
216
- hooks.onGlobalError ??= [];
217
- hooks.onGlobalError.push({
218
- id: nextHookId(appId),
219
- applyTo: "global",
220
- callback,
221
- });
222
- return app;
223
- },
224
- onGlobalAfterResponse(callback: any) {
225
- hooks.onGlobalAfterResponse ??= [];
226
- hooks.onGlobalAfterResponse.push({
227
- id: nextHookId(appId),
228
- applyTo: "global",
229
- callback,
230
- });
231
- return app;
232
- },
233
-
234
- get: (...args: any[]) =>
235
- app.method.apply(app, [Method.Get, ...args] as any) as any,
236
- post: (...args: any[]) =>
237
- app.method.apply(app, [Method.Post, ...args] as any) as any,
238
- put: (...args: any[]) =>
239
- app.method.apply(app, [Method.Put, ...args] as any) as any,
240
- delete: (...args: any[]) =>
241
- app.method.apply(app, [Method.Delete, ...args] as any) as any,
242
- any: (...args: any[]) =>
243
- app.method.apply(app, [Method.Any, ...args] as any) as any,
244
-
245
- method(method: string, path: BasePath, ...args: any[]) {
246
- const routeDef: RouteDef | undefined =
247
- args.length === 2 ? args[0] : undefined;
248
- const handler = args[1] ?? args[0];
249
- const route = `${prefix}${path}`;
250
- const hooks = cloneHooks();
251
-
252
- // Merge app-level tags and security into route definition
253
- const def: RouteDef | undefined = mergeAppDefaults(routeDef, options);
254
-
255
- const compiledHandler = compileRouteHandler({
256
- schemaAdapter: options?.schemaAdapter,
257
- def,
258
- hooks,
259
- method,
260
- route,
261
- handler,
262
- });
263
- addRoutesEntry(method, route, {
264
- def,
265
- route,
266
- hooks,
267
- compiledHandler,
268
- handler,
269
- });
270
- return app;
271
- },
272
-
273
- mount(...args: any[]) {
274
- let path = "";
275
- let routeDef: RouteDef = {};
276
- let fetch: ServerSideFetch;
277
-
278
- if (args.length === 1) {
279
- fetch = args[0];
280
- } else if (args.length === 2) {
281
- path = args[0];
282
- fetch = args[1];
283
- } else {
284
- path = args[0];
285
- routeDef = args[1];
286
- fetch = args[2];
287
- }
288
-
289
- const route = `${prefix}${path}/**`;
290
- const hooks = cloneHooks();
291
-
292
- // Merge app-level tags and security into route definition
293
- const def = mergeAppDefaults(routeDef, options);
294
-
295
- const compiledHandler = compileRouteHandler({
296
- schemaAdapter: options?.schemaAdapter,
297
- hooks,
298
- method: "ANY",
299
- route,
300
- fetch,
301
- def,
302
- });
303
-
304
- addRoutesEntry(Method.Any, route, {
305
- def,
306
- fetch,
307
- route,
308
- hooks,
309
- compiledHandler,
310
- });
311
-
312
- return app as any;
313
- },
314
-
315
- use: (childApp) => {
316
- // Bring in routes
317
- for (const [method, methodValue] of Object.entries(
318
- childApp["~zeta"].routes,
319
- )) {
320
- for (const [subRoute, routeValue] of Object.entries(methodValue)) {
321
- const route = `${prefix}${subRoute}`;
322
- addRoutesEntry(method, route, { ...routeValue, route });
323
- }
324
- }
325
-
326
- // Add the child's global hooks to parent
327
- for (const hookName of Object.keys(
328
- childApp["~zeta"].hooks,
329
- ) as LifeCycleHookName[]) {
330
- for (const hook of childApp["~zeta"].hooks[
331
- hookName
332
- ]! as LifeCycleHook<any>[]) {
333
- if (hook.applyTo !== "global" && !childApp["~zeta"].exported)
334
- continue;
335
-
336
- if (hooks[hookName]) {
337
- // Don't add a hook if it's already there
338
- if (!hooks[hookName].includes(hook)) hooks[hookName].push(hook);
339
- } else {
340
- hooks[hookName] = [hook];
341
- }
342
- }
343
- }
344
-
345
- return app as any;
346
- },
347
- };
348
-
349
- return app as any;
350
- }
351
-
352
- /**
353
- * Configure how the app is created.
354
- */
355
- export type CreateAppOptions<TPrefix extends BasePrefix = ""> = {
356
- /**
357
- * The origin to use when constructing URLs.
358
- * @default "http://localhost"
359
- */
360
- origin?: string;
361
-
362
- /**
363
- * Add a prefix to the beginning of all routes in the app.
364
- */
365
- prefix?: TPrefix;
366
-
367
- /**
368
- * Tell Zeta which library you're using for validation. OpenAPI docs cannot
369
- * be served without a schema adapter.
370
- *
371
- * @example
372
- * ```ts
373
- * import { zodSchemaAdapter } from "@aklinker1/zeta/adapters/zod-schema-adapter"
374
- *
375
- * const app = createApp({
376
- * openApi: {
377
- * schemaAdapter: zodSchemaAdapter,
378
- * },
379
- * });
380
- * ```
381
- */
382
- schemaAdapter?: SchemaAdapter;
383
-
384
- /**
385
- * Tell Zeta how to serve your app over a port. By default, Zeta will detect
386
- * if you're runtime is Bun or Deno, and use the appropriate transport.
387
- *
388
- * If you need to customize the transport, like adding an `idleTimeout` to
389
- * bun, you can do so by passing options into the transport's factory function.
390
- *
391
- * @example
392
- * ```ts
393
- * import { createBunTransport } from "@aklinker1/zeta/transports/bun-transport"
394
- *
395
- * const app = createApp({
396
- * transport: createBunTransport(),
397
- * });
398
- * ```
399
- */
400
- transport?: Transport;
401
-
402
- /**
403
- * Where the OpenAPI JSON docs is hosted.
404
- * @default "/openapi.json"
405
- */
406
- openApiRoute?: BasePath;
407
- /**
408
- * Where the Scalar UI is hosted.
409
- * @default "/scalar"
410
- */
411
- scalarRoute?: BasePath;
412
-
413
- /** Configure how your application's OpenAPI docs are generated. */
414
- openApi?: Partial<OpenAPIV3_1.Document> & {};
415
-
416
- /**
417
- * OpenAPI tags to apply to all routes in this app. Route-level tags will
418
- * override these app-level tags.
419
- *
420
- * @example
421
- * ```ts
422
- * const usersApp = createApp({
423
- * prefix: "/users",
424
- * tags: ["Users"],
425
- * })
426
- * .get("/", {}, () => [...]) // Will have ["Users"] tag
427
- * .get("/:id", { tags: ["Admin"] }, () => {...}) // Will have ["Admin"] tag (overrides app-level)
428
- * ```
429
- */
430
- tags?: string[];
431
-
432
- /**
433
- * OpenAPI security requirements to apply to all routes in this app.
434
- * Route-level security will override these app-level security requirements.
435
- *
436
- * @example
437
- * ```ts
438
- * const authApp = createApp({
439
- * prefix: "/auth",
440
- * security: [{ bearerAuth: [] }],
441
- * })
442
- * .get("/profile", {}, () => {...}) // Will require bearerAuth
443
- * .get("/admin", { security: [{ adminKey: [] }] }, () => {...}) // Will require adminKey (overrides app-level)
444
- * ```
445
- */
446
- security?: OpenAPI.Document["security"];
447
- /**
448
- * Configure [Scalar](https://scalar.com/) UI docs.
449
- * @see https://github.com/scalar/scalar/blob/main/documentation/configuration.md#list-of-all-attributes
450
- */
451
- scalar?: any;
452
- };
453
-
454
- /**
455
- * Apply app-level defaults (tags, security) to a route definition.
456
- * Route-level values override app-level defaults.
457
- */
458
- function mergeAppDefaults(
459
- routeDef: RouteDef | undefined,
460
- options: CreateAppOptions<any> | undefined,
461
- ): RouteDef | undefined {
462
- if (!options?.tags?.length && !options?.security?.length) {
463
- return routeDef;
464
- }
465
-
466
- return {
467
- tags: options.tags,
468
- security: options.security,
469
- ...routeDef,
470
- };
471
- }
472
-
473
- enum Method {
474
- Get = "GET",
475
- Post = "POST",
476
- Put = "PUT",
477
- Delete = "DELETE",
478
- Any = "ANY",
479
- }