@avijs/core 0.0.0-stage → 0.1.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.
Files changed (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +12 -2
  3. package/dist/app.d.ts +51 -0
  4. package/dist/app.d.ts.map +1 -0
  5. package/dist/app.js +215 -0
  6. package/dist/app.js.map +1 -0
  7. package/dist/consumers.d.ts +10 -0
  8. package/dist/consumers.d.ts.map +1 -0
  9. package/dist/consumers.js +31 -0
  10. package/dist/consumers.js.map +1 -0
  11. package/dist/controller.d.ts +36 -0
  12. package/dist/controller.d.ts.map +1 -0
  13. package/dist/controller.js +61 -0
  14. package/dist/controller.js.map +1 -0
  15. package/dist/decorators.d.ts +52 -0
  16. package/dist/decorators.d.ts.map +1 -0
  17. package/dist/decorators.js +113 -0
  18. package/dist/decorators.js.map +1 -0
  19. package/dist/describe.d.ts +37 -0
  20. package/dist/describe.d.ts.map +1 -0
  21. package/dist/describe.js +83 -0
  22. package/dist/describe.js.map +1 -0
  23. package/dist/index.d.ts +20 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +11 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/module.d.ts +36 -0
  28. package/dist/module.d.ts.map +1 -0
  29. package/dist/module.js +46 -0
  30. package/dist/module.js.map +1 -0
  31. package/dist/pipeline.d.ts +53 -0
  32. package/dist/pipeline.d.ts.map +1 -0
  33. package/dist/pipeline.js +29 -0
  34. package/dist/pipeline.js.map +1 -0
  35. package/dist/registry.d.ts +32 -0
  36. package/dist/registry.d.ts.map +1 -0
  37. package/dist/registry.js +86 -0
  38. package/dist/registry.js.map +1 -0
  39. package/dist/request.d.ts +5 -0
  40. package/dist/request.d.ts.map +1 -0
  41. package/dist/request.js +5 -0
  42. package/dist/request.js.map +1 -0
  43. package/package.json +53 -3
  44. package/src/app.ts +321 -0
  45. package/src/consumers.ts +33 -0
  46. package/src/controller.ts +102 -0
  47. package/src/decorators.ts +161 -0
  48. package/src/describe.ts +131 -0
  49. package/src/index.ts +85 -0
  50. package/src/module.ts +92 -0
  51. package/src/pipeline.ts +91 -0
  52. package/src/registry.ts +140 -0
  53. package/src/request.ts +7 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Christopher Robin Chase
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,13 @@
1
- # Temporary Holding Version
1
+ # @avijs/core
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The Avi.js public API: createAvi, AviFactory, modules, controllers, decorators, middleware, guards and validation.
4
+
5
+ ```sh
6
+ npm install @avijs/core
7
+ ```
8
+
9
+ Part of [Avi.js](https://github.com/chrischase011/avi-js), a TypeScript framework for Node.js with
10
+ modules, controllers and dependency injection, without `reflect-metadata`. See the
11
+ [tutorial](https://github.com/chrischase011/avi-js/blob/main/docs/tutorial/README.md) to get started.
12
+
13
+ Requires Node.js 22.12 or newer. MIT licensed.
package/dist/app.d.ts ADDED
@@ -0,0 +1,51 @@
1
+ import { type Provider, type Token, type TokenType } from '@avijs/di';
2
+ import { type HttpMethod, type ServerOptions } from '@avijs/http';
3
+ import { type RouteSchema } from '@avijs/validation';
4
+ import type { AddressInfo } from 'node:net';
5
+ import { type AppDescription } from './describe.js';
6
+ import { type ModuleOptions, type ModuleRef } from './module.js';
7
+ import { type GuardRef, type MiddlewareRef, type RouteDocs } from './pipeline.js';
8
+ export interface AviOptions extends ServerOptions {
9
+ /** Runs first, for every route. */
10
+ middleware?: readonly MiddlewareRef[];
11
+ /** Checked first, for every route. */
12
+ guards?: readonly GuardRef[];
13
+ /**
14
+ * A manifest from `describeApp()` or `avi-compile manifest`. `init()` fails if the app no longer
15
+ * matches it, so a stale build artifact cannot ship unnoticed.
16
+ */
17
+ manifest?: AppDescription;
18
+ /**
19
+ * Providers that replace the provider for the same token anywhere in the app, for tests.
20
+ * An override that matches no provider is an error.
21
+ */
22
+ overrides?: readonly Provider[];
23
+ }
24
+ export interface AviApp {
25
+ /** Adds a root module: `defineModule`, an `@Module` class or inline options. Checks run in `init()`. */
26
+ register(module: ModuleRef | ModuleOptions): this;
27
+ /** Validates the module graph and routes, then creates all singletons. `listen()` calls it. */
28
+ init(): Promise<void>;
29
+ /** Starts the server. Use port `0` for a random free port. */
30
+ listen(port: number, host?: string): Promise<AddressInfo>;
31
+ /** Resolves a provider visible to the root modules. For tests and scripts, not app code. */
32
+ get<K extends Token>(token: K): TokenType<K>;
33
+ /** Every registered route, including runtime-created ones. Available after `init()`. */
34
+ routes(): readonly RouteInfo[];
35
+ /** Stops accepting connections, waits for in-flight requests, then disposes providers. */
36
+ close(): Promise<void>;
37
+ }
38
+ export interface RouteInfo {
39
+ readonly method: HttpMethod;
40
+ readonly path: string;
41
+ readonly module: string;
42
+ readonly controller: string;
43
+ readonly schema: RouteSchema | undefined;
44
+ readonly openapi: RouteDocs | undefined;
45
+ }
46
+ export declare function createAvi(options?: AviOptions): AviApp;
47
+ export declare const AviFactory: {
48
+ /** Registers `module` and runs `init()`. The returned app is ready to `listen()`. */
49
+ create(module: ModuleRef | ModuleOptions, options?: AviOptions): Promise<AviApp>;
50
+ };
51
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,QAAQ,EAAE,KAAK,KAAK,EAAE,KAAK,SAAS,EAAE,MAAM,WAAW,CAAC;AACjF,OAAO,EAML,KAAK,UAAU,EAIf,KAAK,aAAa,EACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,OAAO,EAAoC,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AACtF,OAAO,EAIL,KAAK,aAAa,EAClB,KAAK,SAAS,EACf,MAAM,aAAa,CAAC;AACrB,OAAO,EAIL,KAAK,QAAQ,EAEb,KAAK,aAAa,EAClB,KAAK,SAAS,EACf,MAAM,eAAe,CAAC;AAGvB,MAAM,WAAW,UAAW,SAAQ,aAAa;IAC/C,mCAAmC;IACnC,UAAU,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IACtC,sCAAsC;IACtC,MAAM,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAC;IAC7B;;;OAGG;IACH,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B;;;OAGG;IACH,SAAS,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,MAAM;IACrB,wGAAwG;IACxG,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,IAAI,CAAC;IAClD,+FAA+F;IAC/F,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,8DAA8D;IAC9D,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAC1D,4FAA4F;IAC5F,GAAG,CAAC,CAAC,SAAS,KAAK,EAAE,KAAK,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;IAC7C,wFAAwF;IACxF,MAAM,IAAI,SAAS,SAAS,EAAE,CAAC;IAC/B,0FAA0F;IAC1F,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,CAAC;CACzC;AAED,wBAAgB,SAAS,CAAC,OAAO,GAAE,UAAe,GAAG,MAAM,CAE1D;AAED,eAAO,MAAM,UAAU;IACrB,qFAAqF;mBAChE,SAAS,GAAG,aAAa,YAAY,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;CAKvF,CAAC"}
package/dist/app.js ADDED
@@ -0,0 +1,215 @@
1
+ import { Container } from '@avijs/di';
2
+ import { HttpServer, Router, compose, } from '@avijs/http';
3
+ import { withValidation } from '@avijs/validation';
4
+ import { joinPath } from './controller.js';
5
+ import { checkConsumers, injectOf } from './consumers.js';
6
+ import { describeModules, firstDifference } from './describe.js';
7
+ import { defineModule, toModule, } from './module.js';
8
+ import { checkRefs, isClass, } from './pipeline.js';
9
+ import { REQUEST, REQUEST_TOKENS } from './request.js';
10
+ export function createAvi(options = {}) {
11
+ return new Application(options);
12
+ }
13
+ export const AviFactory = {
14
+ /** Registers `module` and runs `init()`. The returned app is ready to `listen()`. */
15
+ async create(module, options) {
16
+ const app = createAvi(options).register(module);
17
+ await app.init();
18
+ return app;
19
+ },
20
+ };
21
+ class Application {
22
+ #router = new Router();
23
+ #server;
24
+ #roots = [];
25
+ #middleware;
26
+ #guards;
27
+ #manifest;
28
+ #overrides;
29
+ #logger;
30
+ #container;
31
+ #routes;
32
+ #init;
33
+ constructor(options) {
34
+ this.#server = new HttpServer(this.#router, options);
35
+ this.#middleware = checkRefs(options.middleware, 'use', 'App middleware');
36
+ this.#guards = checkRefs(options.guards, 'canActivate', 'App guards');
37
+ this.#manifest = options.manifest;
38
+ this.#overrides = options.overrides ?? [];
39
+ this.#logger = options.logger ?? {
40
+ error(message, error) {
41
+ console.error(message, error);
42
+ },
43
+ };
44
+ }
45
+ register(module) {
46
+ if (this.#init)
47
+ throw new Error('Cannot register modules after init() or listen()');
48
+ if (typeof module === 'function' && !toModule(module)) {
49
+ throw new TypeError(`${module.name || 'This class'} is not a module: add @Module()`);
50
+ }
51
+ this.#roots.push(toModule(module) ?? defineModule(module));
52
+ return this;
53
+ }
54
+ init() {
55
+ return (this.#init ??= this.#start());
56
+ }
57
+ async listen(port, host) {
58
+ await this.init();
59
+ return this.#server.listen(port, host);
60
+ }
61
+ get(token) {
62
+ if (!this.#container)
63
+ throw new Error('Call await app.init() before app.get()');
64
+ return this.#container.get(token);
65
+ }
66
+ routes() {
67
+ if (!this.#routes)
68
+ throw new Error('Call await app.init() before app.routes()');
69
+ return this.#routes;
70
+ }
71
+ async close() {
72
+ await this.#server.close();
73
+ await this.#container?.dispose();
74
+ }
75
+ async #start() {
76
+ const container = new Container(this.#roots, {
77
+ overrides: this.#overrides,
78
+ requestTokens: REQUEST_TOKENS,
79
+ });
80
+ const modules = container.modules;
81
+ checkConsumers(container);
82
+ await container.init();
83
+ try {
84
+ const appScope = new Pipeline((token) => container.get(token));
85
+ const appMiddleware = appScope.middleware(this.#middleware);
86
+ const appGuards = appScope.guards(this.#guards);
87
+ const routes = [];
88
+ const infos = [];
89
+ for (const module of modules) {
90
+ const scope = new Pipeline((token) => container.resolve(module, token));
91
+ const moduleMiddleware = [...appMiddleware, ...scope.middleware(module.middleware)];
92
+ const moduleGuards = [...appGuards, ...scope.guards(module.guards)];
93
+ for (const controller of module.controllers) {
94
+ const perRequest = container.needsRequest(module, controller.inject);
95
+ const deps = controller.inject.map((token) => perRequest && container.needsRequest(module, [token])
96
+ ? placeholder(token, controller.name)
97
+ : container.resolve(module, token));
98
+ const base = joinPath(module.prefix, controller.prefix);
99
+ const middleware = [...moduleMiddleware, ...scope.middleware(controller.middleware)];
100
+ const guards = [...moduleGuards, ...scope.guards(controller.guards)];
101
+ for (const [index, route] of controller.createRoutes(deps).entries()) {
102
+ const handler = perRequest
103
+ ? this.#perRequest(container, module, controller, index)
104
+ : route.handler;
105
+ const path = joinPath(base, route.path);
106
+ infos.push({
107
+ method: route.method,
108
+ path,
109
+ module: module.name,
110
+ controller: controller.name,
111
+ schema: route.schema,
112
+ openapi: route.openapi,
113
+ });
114
+ routes.push({
115
+ method: route.method,
116
+ path,
117
+ handler: compose([...middleware, ...scope.middleware(route.middleware ?? [])], [...guards, ...scope.guards(route.guards ?? [])], route.schema ? withValidation(route.schema, handler) : handler),
118
+ });
119
+ }
120
+ }
121
+ }
122
+ this.#router.add(routes);
123
+ this.#routes = Object.freeze(infos);
124
+ if (this.#manifest) {
125
+ const difference = firstDifference(this.#manifest, describeModules(container));
126
+ if (difference) {
127
+ throw new Error(`The app does not match its manifest (first difference at ${difference}). Regenerate the manifest.`);
128
+ }
129
+ }
130
+ this.#container = container;
131
+ }
132
+ catch (error) {
133
+ await container.dispose().catch(() => undefined);
134
+ throw error;
135
+ }
136
+ }
137
+ /** Builds the controller for each request, so its request-scoped dependencies are fresh. */
138
+ #perRequest(container, module, controller, index) {
139
+ return async (ctx) => {
140
+ const scope = container.requestScope(new Map([[REQUEST, ctx]]));
141
+ try {
142
+ const deps = controller.inject.map((token) => scope.resolve(module, token));
143
+ const route = controller.createRoutes(deps)[index];
144
+ if (!route) {
145
+ throw new Error(`${controller.name} returned fewer routes for this request than at startup`);
146
+ }
147
+ return await route.handler(ctx);
148
+ }
149
+ finally {
150
+ await scope.dispose().catch((error) => {
151
+ this.#logger.error(`Failed to dispose request-scoped providers: ${ctx.method} ${ctx.path}`, error);
152
+ });
153
+ }
154
+ };
155
+ }
156
+ }
157
+ function tokenLabel(token) {
158
+ return typeof token === 'function' ? token.name : token.description;
159
+ }
160
+ /** Stands in for request-scoped dependencies while a controller's routes are read at startup. */
161
+ function placeholder(token, owner) {
162
+ const fail = () => {
163
+ throw new Error(`${owner} used request-scoped ${tokenLabel(token)} while being created at startup. Use it inside route handlers only.`);
164
+ };
165
+ return new Proxy(function placeholder() {
166
+ return undefined;
167
+ }, {
168
+ get: fail,
169
+ set: fail,
170
+ has: fail,
171
+ apply: fail,
172
+ construct: fail,
173
+ ownKeys: fail,
174
+ defineProperty: fail,
175
+ deleteProperty: fail,
176
+ });
177
+ }
178
+ /** Turns middleware and guard refs into functions, building each class once per scope. */
179
+ class Pipeline {
180
+ resolve;
181
+ #instances = new Map();
182
+ constructor(resolve) {
183
+ this.resolve = resolve;
184
+ }
185
+ middleware(refs) {
186
+ return refs.map((ref) => {
187
+ if (!isClass(ref))
188
+ return ref;
189
+ const instance = this.#instance(ref);
190
+ return (ctx, next) => instance.use(ctx, next);
191
+ });
192
+ }
193
+ guards(refs) {
194
+ return refs.map((ref) => {
195
+ if (!isClass(ref))
196
+ return ref;
197
+ const instance = this.#instance(ref);
198
+ return (ctx) => instance.canActivate(ctx);
199
+ });
200
+ }
201
+ #instance(cls) {
202
+ let instance = this.#instances.get(cls);
203
+ if (!instance) {
204
+ const inject = injectOf(cls);
205
+ if (cls.length > inject.length) {
206
+ throw new TypeError(`${cls.name} takes ${String(cls.length)} constructor arguments but static inject lists ${String(inject.length)}`);
207
+ }
208
+ const Ctor = cls;
209
+ instance = new Ctor(...inject.map(this.resolve));
210
+ this.#instances.set(cls, instance);
211
+ }
212
+ return instance;
213
+ }
214
+ }
215
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { Container, Token } from '@avijs/di';
2
+ /**
3
+ * Checks that controllers, middleware and guard classes can see what they inject, and that
4
+ * middleware and guards do not need a request scope.
5
+ */
6
+ export declare function checkConsumers(container: Container): void;
7
+ export declare function injectOf(cls: {
8
+ readonly inject?: readonly Token[];
9
+ }): readonly Token[];
10
+ //# sourceMappingURL=consumers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consumers.d.ts","sourceRoot":"","sources":["../src/consumers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAIlD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAoBzD;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,KAAK,EAAE,CAAA;CAAE,GAAG,SAAS,KAAK,EAAE,CAEtF"}
@@ -0,0 +1,31 @@
1
+ import { isClass } from './pipeline.js';
2
+ /**
3
+ * Checks that controllers, middleware and guard classes can see what they inject, and that
4
+ * middleware and guards do not need a request scope.
5
+ */
6
+ export function checkConsumers(container) {
7
+ for (const module of container.modules) {
8
+ const checkRef = (ref, label) => {
9
+ if (!isClass(ref))
10
+ return;
11
+ const cls = ref;
12
+ const inject = injectOf(cls);
13
+ container.check(module, inject, `${label} ${cls.name}`);
14
+ if (container.needsRequest(module, inject)) {
15
+ throw new Error(`${label} ${cls.name} depends on a request-scoped provider. Middleware and guards are created once; read per-request data from ctx or ctx.state instead.`);
16
+ }
17
+ };
18
+ for (const ref of [...module.middleware, ...module.guards])
19
+ checkRef(ref, module.name);
20
+ for (const controller of module.controllers) {
21
+ const label = `${module.name} controller ${controller.prefix}`;
22
+ container.check(module, controller.inject, label);
23
+ for (const ref of [...controller.middleware, ...controller.guards])
24
+ checkRef(ref, label);
25
+ }
26
+ }
27
+ }
28
+ export function injectOf(cls) {
29
+ return cls.inject ?? [];
30
+ }
31
+ //# sourceMappingURL=consumers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consumers.js","sourceRoot":"","sources":["../src/consumers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAsD,MAAM,eAAe,CAAC;AAE5F;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,SAAoB;IACjD,KAAK,MAAM,MAAM,IAAI,SAAS,CAAC,OAAsB,EAAE,CAAC;QACtD,MAAM,QAAQ,GAAG,CAAC,GAA6B,EAAE,KAAa,EAAE,EAAE;YAChE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC;gBAAE,OAAO;YAC1B,MAAM,GAAG,GAAG,GAAiB,CAAC;YAC9B,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;YAC7B,SAAS,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;YACxD,IAAI,SAAS,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;gBAC3C,MAAM,IAAI,KAAK,CACb,GAAG,KAAK,IAAI,GAAG,CAAC,IAAI,qIAAqI,CAC1J,CAAC;YACJ,CAAC;QACH,CAAC,CAAC;QACF,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG,MAAM,CAAC,UAAU,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC;YAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QACvF,KAAK,MAAM,UAAU,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;YAC5C,MAAM,KAAK,GAAG,GAAG,MAAM,CAAC,IAAI,eAAe,UAAU,CAAC,MAAM,EAAE,CAAC;YAC/D,SAAS,CAAC,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YAClD,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG,UAAU,CAAC,UAAU,EAAE,GAAG,UAAU,CAAC,MAAM,CAAC;gBAAE,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC3F,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,GAA2C;IAClE,OAAO,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,36 @@
1
+ import type { ResolveTokens, Token } from '@avijs/di';
2
+ import type { HttpMethod } from '@avijs/http';
3
+ import { type GuardRef, type MiddlewareRef, type Route } from './pipeline.js';
4
+ export interface ControllerOptions<K extends readonly Token[] = []> {
5
+ /** Used in manifests and error messages. Default: 'controller <prefix>'. */
6
+ name?: string;
7
+ /** Path prefix for every route. Default: `/`. */
8
+ prefix?: string;
9
+ /** Dependencies passed to `routes`, in order. */
10
+ inject?: K;
11
+ /** Runs for every route in this controller, after app and module middleware. */
12
+ middleware?: readonly MiddlewareRef[];
13
+ /** Checked for every route in this controller, after app and module guards. */
14
+ guards?: readonly GuardRef[];
15
+ routes: readonly Route[] | ((...deps: ResolveTokens<K>) => readonly Route[]);
16
+ }
17
+ /** The normalized shape both controller styles produce. */
18
+ export interface StaticRoute {
19
+ readonly method: HttpMethod;
20
+ readonly path: string;
21
+ }
22
+ export interface ControllerDefinition {
23
+ readonly name: string;
24
+ readonly prefix: string;
25
+ readonly inject: readonly Token[];
26
+ readonly middleware: readonly MiddlewareRef[];
27
+ readonly guards: readonly GuardRef[];
28
+ /** Routes known without creating the controller; 'dynamic' for a routes factory. */
29
+ readonly staticRoutes: readonly StaticRoute[] | 'dynamic';
30
+ createRoutes(deps: readonly unknown[]): readonly Route[];
31
+ }
32
+ export declare function defineController<const K extends readonly Token[] = []>(options: ControllerOptions<K>): ControllerDefinition;
33
+ export declare function checkRoutes(value: unknown): readonly Route[];
34
+ /** Joins a prefix and a route path, collapsing repeated slashes and dropping a trailing one. */
35
+ export declare function joinPath(prefix: string, path: string): string;
36
+ //# sourceMappingURL=controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,EAAa,KAAK,QAAQ,EAAE,KAAK,aAAa,EAAE,KAAK,KAAK,EAAE,MAAM,eAAe,CAAC;AAEzF,MAAM,WAAW,iBAAiB,CAAC,CAAC,SAAS,SAAS,KAAK,EAAE,GAAG,EAAE;IAChE,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,MAAM,CAAC,EAAE,CAAC,CAAC;IACX,gFAAgF;IAChF,UAAU,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IACtC,+EAA+E;IAC/E,MAAM,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAC;IAC7B,MAAM,EAAE,SAAS,KAAK,EAAE,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,KAAK,SAAS,KAAK,EAAE,CAAC,CAAC;CAC9E;AAED,2DAA2D;AAC3D,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;IAClC,QAAQ,CAAC,UAAU,EAAE,SAAS,aAAa,EAAE,CAAC;IAC9C,QAAQ,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,CAAC;IACrC,oFAAoF;IACpF,QAAQ,CAAC,YAAY,EAAE,SAAS,WAAW,EAAE,GAAG,SAAS,CAAC;IAC1D,YAAY,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,SAAS,KAAK,EAAE,CAAC;CAC1D;AAED,wBAAgB,gBAAgB,CAAC,KAAK,CAAC,CAAC,SAAS,SAAS,KAAK,EAAE,GAAG,EAAE,EACpE,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAC5B,oBAAoB,CAsCtB;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,KAAK,EAAE,CAiB5D;AAED,gGAAgG;AAChG,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAG7D"}
@@ -0,0 +1,61 @@
1
+ import { checkRouteSchema } from '@avijs/validation';
2
+ import { checkRefs } from './pipeline.js';
3
+ export function defineController(options) {
4
+ const prefix = options.prefix ?? '/';
5
+ if (!prefix.startsWith('/')) {
6
+ throw new TypeError(`Controller prefix must start with "/": ${prefix}`);
7
+ }
8
+ const inject = Object.freeze([...(options.inject ?? [])]);
9
+ const middleware = checkRefs(options.middleware, 'use', `Controller ${prefix} middleware`);
10
+ const guards = checkRefs(options.guards, 'canActivate', `Controller ${prefix} guards`);
11
+ const name = options.name ?? `controller ${prefix}`;
12
+ const { routes } = options;
13
+ if (typeof routes !== 'function') {
14
+ const fixed = checkRoutes(routes);
15
+ return Object.freeze({
16
+ name,
17
+ prefix,
18
+ inject,
19
+ middleware,
20
+ guards,
21
+ staticRoutes: Object.freeze(fixed.map(({ method, path }) => ({ method, path }))),
22
+ createRoutes: () => fixed,
23
+ });
24
+ }
25
+ if (routes.length > inject.length) {
26
+ throw new TypeError(`Controller ${prefix}: routes takes ${String(routes.length)} arguments but inject lists ${String(inject.length)}`);
27
+ }
28
+ const factory = routes;
29
+ return Object.freeze({
30
+ name,
31
+ prefix,
32
+ inject,
33
+ middleware,
34
+ guards,
35
+ staticRoutes: 'dynamic',
36
+ createRoutes: (deps) => checkRoutes(factory(...deps)),
37
+ });
38
+ }
39
+ export function checkRoutes(value) {
40
+ if (!Array.isArray(value))
41
+ throw new TypeError('Controller routes must be an array');
42
+ const routes = value;
43
+ return Object.freeze(routes.map((route) => {
44
+ if (route.path !== '' && !route.path.startsWith('/')) {
45
+ throw new TypeError(`Route path must be empty or start with "/": ${route.path}`);
46
+ }
47
+ const where = `Route ${route.method} ${route.path}`;
48
+ return Object.freeze({
49
+ ...route,
50
+ middleware: checkRefs(route.middleware, 'use', `${where} middleware`),
51
+ guards: checkRefs(route.guards, 'canActivate', `${where} guards`),
52
+ schema: checkRouteSchema(route.schema, where),
53
+ });
54
+ }));
55
+ }
56
+ /** Joins a prefix and a route path, collapsing repeated slashes and dropping a trailing one. */
57
+ export function joinPath(prefix, path) {
58
+ const joined = `${prefix}/${path}`.replace(/\/{2,}/g, '/');
59
+ return joined.length > 1 && joined.endsWith('/') ? joined.slice(0, -1) : joined;
60
+ }
61
+ //# sourceMappingURL=controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller.js","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ import type { Class, ResolveTokens, Scope, Token } from '@avijs/di';
2
+ import type { Context, NoInput } from '@avijs/http';
3
+ import { type InputOf, type PathParams, type RouteSchema } from '@avijs/validation';
4
+ import { type ModuleOptions } from './module.js';
5
+ import { type GuardRef, type MiddlewareRef, type RouteDocs } from './pipeline.js';
6
+ type InjectOf<C> = C extends {
7
+ inject: infer K extends readonly Token[];
8
+ } ? K : [];
9
+ interface InjectMismatch {
10
+ readonly 'static inject must list the constructor parameter types in order, declared `as const`': never;
11
+ }
12
+ /** Resolves to `unknown` when `static inject` fits the constructor, else to an error type. */
13
+ export type CheckInject<C extends Class> = ResolveTokens<InjectOf<C>> extends ConstructorParameters<C> ? unknown : InjectMismatch;
14
+ export declare function Controller(prefix?: string): <C extends Class & CheckInject<C>>(value: C, context: ClassDecoratorContext<C>) => void;
15
+ /** Adds middleware to every route of a controller class, or to one route method. */
16
+ export declare function UseMiddleware(...middleware: MiddlewareRef[]): (_value: unknown, context: ClassDecoratorContext | ClassMethodDecoratorContext) => void;
17
+ /** Adds guards to every route of a controller class, or to one route method. */
18
+ export declare function UseGuards(...guards: GuardRef[]): (_value: unknown, context: ClassDecoratorContext | ClassMethodDecoratorContext) => void;
19
+ export declare function Injectable(options?: {
20
+ scope?: Scope;
21
+ }): <C extends Class & CheckInject<C>>(value: C, context: ClassDecoratorContext<C>) => void;
22
+ export declare function Module(options?: ModuleOptions): <C extends Class>(value: C, context: ClassDecoratorContext<C>) => void;
23
+ export declare const Get: <const P extends string = "", S extends RouteSchema = NoInput>(path?: P, options?: {
24
+ schema?: S;
25
+ openapi?: RouteDocs;
26
+ }) => <This, F extends (this: This, ctx: Context<InputOf<S>, PathParams<P>>) => unknown>(_value: F, context: ClassMethodDecoratorContext<This, F>) => void;
27
+ export declare const Post: <const P extends string = "", S extends RouteSchema = NoInput>(path?: P, options?: {
28
+ schema?: S;
29
+ openapi?: RouteDocs;
30
+ }) => <This, F extends (this: This, ctx: Context<InputOf<S>, PathParams<P>>) => unknown>(_value: F, context: ClassMethodDecoratorContext<This, F>) => void;
31
+ export declare const Put: <const P extends string = "", S extends RouteSchema = NoInput>(path?: P, options?: {
32
+ schema?: S;
33
+ openapi?: RouteDocs;
34
+ }) => <This, F extends (this: This, ctx: Context<InputOf<S>, PathParams<P>>) => unknown>(_value: F, context: ClassMethodDecoratorContext<This, F>) => void;
35
+ export declare const Patch: <const P extends string = "", S extends RouteSchema = NoInput>(path?: P, options?: {
36
+ schema?: S;
37
+ openapi?: RouteDocs;
38
+ }) => <This, F extends (this: This, ctx: Context<InputOf<S>, PathParams<P>>) => unknown>(_value: F, context: ClassMethodDecoratorContext<This, F>) => void;
39
+ export declare const Delete: <const P extends string = "", S extends RouteSchema = NoInput>(path?: P, options?: {
40
+ schema?: S;
41
+ openapi?: RouteDocs;
42
+ }) => <This, F extends (this: This, ctx: Context<InputOf<S>, PathParams<P>>) => unknown>(_value: F, context: ClassMethodDecoratorContext<This, F>) => void;
43
+ export declare const Head: <const P extends string = "", S extends RouteSchema = NoInput>(path?: P, options?: {
44
+ schema?: S;
45
+ openapi?: RouteDocs;
46
+ }) => <This, F extends (this: This, ctx: Context<InputOf<S>, PathParams<P>>) => unknown>(_value: F, context: ClassMethodDecoratorContext<This, F>) => void;
47
+ export declare const Options: <const P extends string = "", S extends RouteSchema = NoInput>(path?: P, options?: {
48
+ schema?: S;
49
+ openapi?: RouteDocs;
50
+ }) => <This, F extends (this: This, ctx: Context<InputOf<S>, PathParams<P>>) => unknown>(_value: F, context: ClassMethodDecoratorContext<This, F>) => void;
51
+ export {};
52
+ //# sourceMappingURL=decorators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"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"}