honest-node 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +277 -0
- package/dist/app.d.ts +1 -0
- package/dist/app.js +36 -0
- package/dist/application/plugin-entries.d.ts +13 -0
- package/dist/application/plugin-entries.js +38 -0
- package/dist/application/startup-guide.d.ts +4 -0
- package/dist/application/startup-guide.js +53 -0
- package/dist/application-context.d.ts +13 -0
- package/dist/application-context.js +22 -0
- package/dist/application.d.ts +34 -0
- package/dist/application.js +224 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/components/layout.component.d.ts +31 -0
- package/dist/components/layout.component.js +94 -0
- package/dist/constants/index.d.ts +2 -0
- package/dist/constants/index.js +2 -0
- package/dist/constants/pipeline.constants.d.ts +6 -0
- package/dist/constants/pipeline.constants.js +6 -0
- package/dist/constants/version.constants.d.ts +5 -0
- package/dist/constants/version.constants.js +5 -0
- package/dist/decorators/controller.decorator.d.ts +9 -0
- package/dist/decorators/controller.decorator.js +16 -0
- package/dist/decorators/http-method.decorator.d.ts +43 -0
- package/dist/decorators/http-method.decorator.js +44 -0
- package/dist/decorators/index.d.ts +11 -0
- package/dist/decorators/index.js +11 -0
- package/dist/decorators/module.decorator.d.ts +8 -0
- package/dist/decorators/module.decorator.js +12 -0
- package/dist/decorators/mvc.decorator.d.ts +26 -0
- package/dist/decorators/mvc.decorator.js +36 -0
- package/dist/decorators/parameter.decorator.d.ts +41 -0
- package/dist/decorators/parameter.decorator.js +59 -0
- package/dist/decorators/service.decorator.d.ts +6 -0
- package/dist/decorators/service.decorator.js +11 -0
- package/dist/decorators/use-component.decorator.d.ts +10 -0
- package/dist/decorators/use-component.decorator.js +19 -0
- package/dist/decorators/use-filters.decorator.d.ts +8 -0
- package/dist/decorators/use-filters.decorator.js +17 -0
- package/dist/decorators/use-guards.decorator.d.ts +9 -0
- package/dist/decorators/use-guards.decorator.js +18 -0
- package/dist/decorators/use-middleware.decorator.d.ts +9 -0
- package/dist/decorators/use-middleware.decorator.js +18 -0
- package/dist/decorators/use-pipes.decorator.d.ts +9 -0
- package/dist/decorators/use-pipes.decorator.js +18 -0
- package/dist/di/container.d.ts +34 -0
- package/dist/di/container.js +114 -0
- package/dist/di/index.d.ts +1 -0
- package/dist/di/index.js +1 -0
- package/dist/errors/framework.error.d.ts +19 -0
- package/dist/errors/framework.error.js +23 -0
- package/dist/errors/index.d.ts +1 -0
- package/dist/errors/index.js +1 -0
- package/dist/handlers/error.handler.d.ts +28 -0
- package/dist/handlers/error.handler.js +17 -0
- package/dist/handlers/index.d.ts +2 -0
- package/dist/handlers/index.js +2 -0
- package/dist/handlers/not-found.handler.d.ts +14 -0
- package/dist/handlers/not-found.handler.js +17 -0
- package/dist/helpers/create-error-response.helper.d.ts +25 -0
- package/dist/helpers/create-error-response.helper.js +90 -0
- package/dist/helpers/create-http-method-decorator.helper.d.ts +16 -0
- package/dist/helpers/create-http-method-decorator.helper.js +30 -0
- package/dist/helpers/create-param-decorator.helper.d.ts +16 -0
- package/dist/helpers/create-param-decorator.helper.js +60 -0
- package/dist/helpers/index.d.ts +3 -0
- package/dist/helpers/index.js +3 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +16 -0
- package/dist/interfaces/application-context.interface.d.ts +35 -0
- package/dist/interfaces/application-context.interface.js +1 -0
- package/dist/interfaces/controller-options.interface.d.ts +17 -0
- package/dist/interfaces/controller-options.interface.js +1 -0
- package/dist/interfaces/di-container.interface.d.ts +35 -0
- package/dist/interfaces/di-container.interface.js +1 -0
- package/dist/interfaces/error-response.interface.d.ts +13 -0
- package/dist/interfaces/error-response.interface.js +1 -0
- package/dist/interfaces/filter.interface.d.ts +20 -0
- package/dist/interfaces/filter.interface.js +1 -0
- package/dist/interfaces/guard.interface.d.ts +21 -0
- package/dist/interfaces/guard.interface.js +1 -0
- package/dist/interfaces/handler-invocation.interface.d.ts +10 -0
- package/dist/interfaces/handler-invocation.interface.js +1 -0
- package/dist/interfaces/honest-options.interface.d.ts +121 -0
- package/dist/interfaces/honest-options.interface.js +1 -0
- package/dist/interfaces/http-method-options.interface.d.ts +38 -0
- package/dist/interfaces/http-method-options.interface.js +1 -0
- package/dist/interfaces/index.d.ts +21 -0
- package/dist/interfaces/index.js +21 -0
- package/dist/interfaces/logger.interface.d.ts +23 -0
- package/dist/interfaces/logger.interface.js +1 -0
- package/dist/interfaces/metadata-repository.interface.d.ts +30 -0
- package/dist/interfaces/metadata-repository.interface.js +1 -0
- package/dist/interfaces/middleware.interface.d.ts +22 -0
- package/dist/interfaces/middleware.interface.js +1 -0
- package/dist/interfaces/module-options.interface.d.ts +18 -0
- package/dist/interfaces/module-options.interface.js +1 -0
- package/dist/interfaces/parameter-metadata.interface.d.ts +27 -0
- package/dist/interfaces/parameter-metadata.interface.js +1 -0
- package/dist/interfaces/parameter-resolution.interface.d.ts +14 -0
- package/dist/interfaces/parameter-resolution.interface.js +1 -0
- package/dist/interfaces/pipe.interface.d.ts +37 -0
- package/dist/interfaces/pipe.interface.js +1 -0
- package/dist/interfaces/pipeline-context.interface.d.ts +9 -0
- package/dist/interfaces/pipeline-context.interface.js +1 -0
- package/dist/interfaces/plugin.interface.d.ts +74 -0
- package/dist/interfaces/plugin.interface.js +1 -0
- package/dist/interfaces/route-definition.interface.d.ts +51 -0
- package/dist/interfaces/route-definition.interface.js +1 -0
- package/dist/interfaces/route-info.interface.d.ts +42 -0
- package/dist/interfaces/route-info.interface.js +1 -0
- package/dist/interfaces/service-registry.interface.d.ts +7 -0
- package/dist/interfaces/service-registry.interface.js +1 -0
- package/dist/loggers/console.logger.d.ts +7 -0
- package/dist/loggers/console.logger.js +21 -0
- package/dist/loggers/index.d.ts +2 -0
- package/dist/loggers/index.js +2 -0
- package/dist/loggers/noop.logger.d.ts +7 -0
- package/dist/loggers/noop.logger.js +8 -0
- package/dist/managers/component.manager.d.ts +48 -0
- package/dist/managers/component.manager.js +210 -0
- package/dist/managers/handler.invoker.d.ts +7 -0
- package/dist/managers/handler.invoker.js +37 -0
- package/dist/managers/index.d.ts +5 -0
- package/dist/managers/index.js +5 -0
- package/dist/managers/parameter.resolver.d.ts +13 -0
- package/dist/managers/parameter.resolver.js +58 -0
- package/dist/managers/pipeline.executor.d.ts +28 -0
- package/dist/managers/pipeline.executor.js +71 -0
- package/dist/managers/route.manager.d.ts +36 -0
- package/dist/managers/route.manager.js +149 -0
- package/dist/registries/index.d.ts +4 -0
- package/dist/registries/index.js +4 -0
- package/dist/registries/metadata.registry.d.ts +163 -0
- package/dist/registries/metadata.registry.js +250 -0
- package/dist/registries/metadata.repository.d.ts +30 -0
- package/dist/registries/metadata.repository.js +151 -0
- package/dist/registries/route.registry.d.ts +16 -0
- package/dist/registries/route.registry.js +46 -0
- package/dist/registries/service.registry.d.ts +8 -0
- package/dist/registries/service.registry.js +9 -0
- package/dist/testing/create-controller-test-application.d.ts +5 -0
- package/dist/testing/create-controller-test-application.js +11 -0
- package/dist/testing/create-service-test-container.d.ts +5 -0
- package/dist/testing/create-service-test-container.js +31 -0
- package/dist/testing/create-test-application.d.ts +5 -0
- package/dist/testing/create-test-application.js +20 -0
- package/dist/testing/create-testing-module.d.ts +6 -0
- package/dist/testing/create-testing-module.js +13 -0
- package/dist/testing/fixtures/application-test-fixtures.d.ts +17 -0
- package/dist/testing/fixtures/application-test-fixtures.js +230 -0
- package/dist/testing/index.d.ts +5 -0
- package/dist/testing/index.js +5 -0
- package/dist/testing/testing.interface.d.ts +96 -0
- package/dist/testing/testing.interface.js +1 -0
- package/dist/types/constructor.type.d.ts +12 -0
- package/dist/types/constructor.type.js +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/common.util.d.ts +117 -0
- package/dist/utils/common.util.js +140 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/package.json +42 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Orkhan Karimov <karimovok1@gmail.com> (https://github.com/kerimovok)
|
|
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
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://github.com/honestjs/" target="blank"><img src="https://honestjs.dev/images/honestjs.png" width="120" alt="Honest.js Logo" /></a>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
A modern, TypeScript-first web framework built on top of <a href="https://hono.dev/" target="blank">Hono</a>, designed for building scalable and
|
|
7
|
+
maintainable web applications. Honest combines the elegance and architecture of <a href="https://nestjs.com/" target="blank">Nest</a> with the
|
|
8
|
+
ultra-fast performance of Hono, giving you the best of both worlds.
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://github.com/honestjs/website">
|
|
13
|
+
<u>website</u>
|
|
14
|
+
</a>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
<p align="center">
|
|
18
|
+
<a href="https://github.com/honestjs/examples">
|
|
19
|
+
<u>examples</u>
|
|
20
|
+
</a>
|
|
21
|
+
|
|
|
22
|
+
<a href="https://github.com/honestjs/templates">
|
|
23
|
+
<u>templates</u>
|
|
24
|
+
</a>
|
|
25
|
+
</p>
|
|
26
|
+
|
|
27
|
+
<p align="center">
|
|
28
|
+
<a href="https://github.com/honestjs/middleware">
|
|
29
|
+
<u>middleware</u>
|
|
30
|
+
</a>
|
|
31
|
+
|
|
|
32
|
+
<a href="https://github.com/honestjs/guards">
|
|
33
|
+
<u>guards</u>
|
|
34
|
+
</a>
|
|
35
|
+
|
|
|
36
|
+
<a href="https://github.com/honestjs/pipes">
|
|
37
|
+
<u>pipes</u>
|
|
38
|
+
</a>
|
|
39
|
+
|
|
|
40
|
+
<a href="https://github.com/honestjs/filters">
|
|
41
|
+
<u>filters</u>
|
|
42
|
+
</a>
|
|
43
|
+
</p>
|
|
44
|
+
|
|
45
|
+
<p align="center">
|
|
46
|
+
<a href="https://github.com/honestjs/http-essentials">
|
|
47
|
+
<u>http-essentials</u>
|
|
48
|
+
</a>
|
|
49
|
+
</p>
|
|
50
|
+
|
|
51
|
+
<hr />
|
|
52
|
+
|
|
53
|
+
<div align="center">
|
|
54
|
+
|
|
55
|
+
[](https://github.com/honestjs/honest/blob/main/LICENSE)
|
|
56
|
+
[](https://www.npmjs.com/package/honestjs)
|
|
57
|
+
[](https://www.npmjs.com/package/honestjs)
|
|
58
|
+
[](https://bundlephobia.com/result?p=honestjs)
|
|
59
|
+
[](https://bundlephobia.com/result?p=honestjs)
|
|
60
|
+
[](https://github.com/honestjs/honest/pulse)
|
|
61
|
+
[](https://github.com/honestjs/honest/commits/main)
|
|
62
|
+
[](https://discord.gg/g3TUeXbeq)
|
|
63
|
+
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
## Cursor / Agent skills
|
|
67
|
+
|
|
68
|
+
Install the Honest skill so your editor agent (e.g. Cursor) can use Honest-specific guidance:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
bunx skills add https://github.com/honestjs/skills --skill honest
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
See [honestjs/skills](https://github.com/honestjs/skills) for details.
|
|
75
|
+
|
|
76
|
+
> 🚨 **Early Development Warning** 🚨
|
|
77
|
+
>
|
|
78
|
+
> Honest is currently in early development (pre-v1.0.0). Please be aware that:
|
|
79
|
+
>
|
|
80
|
+
> - The API is not stable and may change frequently
|
|
81
|
+
> - Breaking changes can occur between minor versions
|
|
82
|
+
> - Some features might be incomplete or missing
|
|
83
|
+
> - Documentation may not always be up to date
|
|
84
|
+
>
|
|
85
|
+
> We recommend not using it in production until v1.0.0 is released.
|
|
86
|
+
|
|
87
|
+
> ⚠️ **Documentation is not yet complete** ⚠️
|
|
88
|
+
>
|
|
89
|
+
> If you find any issues or have suggestions for improvements, please open an issue or submit a pull request. See
|
|
90
|
+
> [CONTRIBUTING.md](CONTRIBUTING.md) for how to contribute and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) for community
|
|
91
|
+
> guidelines.
|
|
92
|
+
|
|
93
|
+
## Quick Start
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
bun add -g @honestjs/cli
|
|
97
|
+
honestjs new my-project # alias: honest, hnjs; interactive template picker
|
|
98
|
+
cd my-project
|
|
99
|
+
bun dev
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Templates: **blank** (minimal), **barebone** (modules + services — best for APIs), **mvc** (full-stack with Hono JSX
|
|
103
|
+
views). Use `-t barebone -y` to skip prompts.
|
|
104
|
+
|
|
105
|
+
See **[Getting Started](https://honestjs.dev/docs/getting-started)** on [honestjs.dev](https://honestjs.dev) for the
|
|
106
|
+
full tutorial, and **[FAQ](https://honestjs.dev/docs/faq)** /
|
|
107
|
+
**[Troubleshooting](https://honestjs.dev/docs/troubleshooting)** for common questions and edge cases.
|
|
108
|
+
|
|
109
|
+
## Features
|
|
110
|
+
|
|
111
|
+
- **🚀 High performance** — Built on Hono for maximum speed and minimal overhead.
|
|
112
|
+
- **🏗️ Familiar architecture** — Decorator-based API inspired by NestJS; TypeScript-first.
|
|
113
|
+
- **💉 Dependency injection** — Built-in DI container for clean, testable code and automatic wiring.
|
|
114
|
+
- **🔌 Plugin system** — Extend the app with custom plugins, middleware, pipes, and filters. Plugins run in
|
|
115
|
+
`options.plugins` order; wrapped entries may attach `preProcessors` / `postProcessors` and optional `name` for
|
|
116
|
+
diagnostics.
|
|
117
|
+
- **🛣️ Advanced routing** — Prefixes, API versioning, and nested route organization.
|
|
118
|
+
- **🛡️ Request pipeline** — Middleware, guards, pipes, and filters at app, controller, or handler level.
|
|
119
|
+
- **🧪 Lightweight testing harness** — Helpers for application, controller, and service-level tests.
|
|
120
|
+
- **🧭 Startup guide mode** — Actionable diagnostics hints for startup failures.
|
|
121
|
+
- **📝 TypeScript-first** — Strong typing and great IDE support out of the box.
|
|
122
|
+
- **🖥️ MVC & SSR** — Full-stack apps with Hono JSX views; use the `mvc` template or the docs.
|
|
123
|
+
|
|
124
|
+
### In code
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
import 'reflect-metadata'
|
|
128
|
+
import { Application, Controller, Get, Module, Service } from 'honestjs'
|
|
129
|
+
import { LoggerMiddleware } from '@honestjs/middleware'
|
|
130
|
+
import { AuthGuard } from '@honestjs/guards'
|
|
131
|
+
import { ValidationPipe } from '@honestjs/pipes'
|
|
132
|
+
import { HttpExceptionFilter } from '@honestjs/filters'
|
|
133
|
+
import { ApiDocsPlugin } from '@honestjs/api-docs-plugin'
|
|
134
|
+
|
|
135
|
+
@Service()
|
|
136
|
+
class AppService {
|
|
137
|
+
hello(): string {
|
|
138
|
+
return 'Hello, Honest!'
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
@Controller()
|
|
143
|
+
class AppController {
|
|
144
|
+
constructor(private readonly appService: AppService) {}
|
|
145
|
+
|
|
146
|
+
@Get()
|
|
147
|
+
hello() {
|
|
148
|
+
return this.appService.hello()
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@Module({
|
|
153
|
+
controllers: [AppController],
|
|
154
|
+
services: [AppService]
|
|
155
|
+
})
|
|
156
|
+
class AppModule {}
|
|
157
|
+
|
|
158
|
+
const { app, hono } = await Application.create(AppModule, {
|
|
159
|
+
startupGuide: { verbose: true },
|
|
160
|
+
debug: {
|
|
161
|
+
routes: true,
|
|
162
|
+
plugins: true,
|
|
163
|
+
pipeline: true,
|
|
164
|
+
di: true,
|
|
165
|
+
startup: true
|
|
166
|
+
},
|
|
167
|
+
logger: myLogger,
|
|
168
|
+
strict: { requireRoutes: true },
|
|
169
|
+
deprecations: { printPreV1Warning: true },
|
|
170
|
+
container: myContainer,
|
|
171
|
+
hono: {
|
|
172
|
+
strict: true,
|
|
173
|
+
router: customRouter
|
|
174
|
+
},
|
|
175
|
+
routing: {
|
|
176
|
+
prefix: 'api',
|
|
177
|
+
version: 1
|
|
178
|
+
},
|
|
179
|
+
// Components: use class (e.g. AuthGuard) or instance (e.g. new LoggerMiddleware()) to pass options
|
|
180
|
+
components: {
|
|
181
|
+
middleware: [new LoggerMiddleware()],
|
|
182
|
+
guards: [AuthGuard],
|
|
183
|
+
pipes: [ValidationPipe],
|
|
184
|
+
filters: [HttpExceptionFilter]
|
|
185
|
+
},
|
|
186
|
+
plugins: [
|
|
187
|
+
new RPCPlugin(),
|
|
188
|
+
new ApiDocsPlugin(),
|
|
189
|
+
{
|
|
190
|
+
plugin: MyPlugin,
|
|
191
|
+
name: 'core',
|
|
192
|
+
preProcessors: [pre],
|
|
193
|
+
postProcessors: [post]
|
|
194
|
+
},
|
|
195
|
+
{ plugin: MetricsPlugin, name: 'metrics' }
|
|
196
|
+
],
|
|
197
|
+
onError: (err, c) => c.json({ error: err.message }, 500),
|
|
198
|
+
notFound: (c) => c.json({ error: 'Not found' }, 404)
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
export default hono
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Controllers, services, and modules are wired by decorators; use **guards** for auth, **pipes** for validation, and
|
|
205
|
+
**filters** for error handling. See the [documentation](https://honestjs.dev/docs/overview) for details.
|
|
206
|
+
|
|
207
|
+
## Runtime Metadata Isolation
|
|
208
|
+
|
|
209
|
+
Decorator metadata is still collected globally, but each application instance now runs on an immutable metadata snapshot
|
|
210
|
+
captured during startup. This prevents metadata mutations made after bootstrap from changing behavior in already-running
|
|
211
|
+
applications.
|
|
212
|
+
|
|
213
|
+
## Plugin order
|
|
214
|
+
|
|
215
|
+
Plugins run in the order they appear in `options.plugins`. Put producer plugins (for example RPC) before consumers (for
|
|
216
|
+
example API docs) when one plugin depends on another’s app-context output.
|
|
217
|
+
|
|
218
|
+
## Testing harness
|
|
219
|
+
|
|
220
|
+
Honest exports lightweight helpers for common test setups.
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
import { createControllerTestApplication, createServiceTestContainer, createTestApplication } from 'honestjs'
|
|
224
|
+
|
|
225
|
+
const app = await createTestApplication({
|
|
226
|
+
controllers: [UsersController],
|
|
227
|
+
services: [UsersService]
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
const response = await app.request('/users')
|
|
231
|
+
|
|
232
|
+
const controllerApp = await createControllerTestApplication({
|
|
233
|
+
controller: UsersController
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
const services = createServiceTestContainer({
|
|
237
|
+
preload: [UsersService],
|
|
238
|
+
overrides: [{ provide: UsersService, useValue: mockUsersService }]
|
|
239
|
+
})
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Running tests in this package
|
|
243
|
+
|
|
244
|
+
This repo uses [Bun's test runner](https://bun.sh/docs/cli/test). From the package root:
|
|
245
|
+
|
|
246
|
+
- `bun test` — run all tests once
|
|
247
|
+
- `bun test --watch` — watch mode
|
|
248
|
+
- `bun test <pattern>` — limit to matching file or test names (for example `bun test application.bootstrap`)
|
|
249
|
+
- `bun run test:coverage` — same suite with coverage (summary in the terminal and `coverage/lcov.info`)
|
|
250
|
+
|
|
251
|
+
Co-locate tests as `*.test.ts` next to sources. Import `reflect-metadata` first in any file that loads decorated
|
|
252
|
+
classes, same as in application code.
|
|
253
|
+
|
|
254
|
+
Integration-style cases use `*.integration.test.ts` where the whole `Application` stack is exercised (for example the
|
|
255
|
+
request pipeline). Shared HTTP fixtures for application tests live under `src/testing/fixtures/` as **factory
|
|
256
|
+
functions** so each test gets fresh decorator metadata after `MetadataRegistry.clear()` in `afterEach`.
|
|
257
|
+
|
|
258
|
+
## Startup diagnostics guide mode
|
|
259
|
+
|
|
260
|
+
Enable startup guidance to get actionable remediation hints when initialization fails.
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
await Application.create(AppModule, {
|
|
264
|
+
startupGuide: true
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
await Application.create(AppModule, {
|
|
268
|
+
startupGuide: { verbose: true }
|
|
269
|
+
})
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Guide mode emits startup diagnostics hints for common issues such as missing decorators, strict no-routes startup, and
|
|
273
|
+
metadata issues.
|
|
274
|
+
|
|
275
|
+
## License
|
|
276
|
+
|
|
277
|
+
MIT © [Orkhan Karimov](https://github.com/kerimovok)
|
package/dist/app.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/app.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { Application, Controller, Get, Module, Service } from '.';
|
|
11
|
+
import { serve } from '@hono/node-server';
|
|
12
|
+
let UsersController = class UsersController {
|
|
13
|
+
getUsers() {
|
|
14
|
+
return { users: [] };
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
__decorate([
|
|
18
|
+
Get(),
|
|
19
|
+
__metadata("design:type", Function),
|
|
20
|
+
__metadata("design:paramtypes", []),
|
|
21
|
+
__metadata("design:returntype", void 0)
|
|
22
|
+
], UsersController.prototype, "getUsers", null);
|
|
23
|
+
UsersController = __decorate([
|
|
24
|
+
Controller('users')
|
|
25
|
+
], UsersController);
|
|
26
|
+
let AppModule = class AppModule {
|
|
27
|
+
};
|
|
28
|
+
AppModule = __decorate([
|
|
29
|
+
Module({
|
|
30
|
+
controllers: [UsersController]
|
|
31
|
+
})
|
|
32
|
+
], AppModule);
|
|
33
|
+
const { hono } = await Application.create(AppModule);
|
|
34
|
+
serve(hono, (info) => {
|
|
35
|
+
console.log(`Listening on http://localhost:${info.port}`); // Listening on http://localhost:3000
|
|
36
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IPlugin, PluginEntry, PluginProcessor } from '../interfaces';
|
|
2
|
+
import type { Constructor } from '../types';
|
|
3
|
+
export declare const DEFAULT_PLUGIN_NAME = "AnonymousPlugin";
|
|
4
|
+
export interface NormalizedPluginEntry {
|
|
5
|
+
plugin: IPlugin;
|
|
6
|
+
name: string;
|
|
7
|
+
preProcessors: PluginProcessor[];
|
|
8
|
+
postProcessors: PluginProcessor[];
|
|
9
|
+
}
|
|
10
|
+
export declare function resolvePlugin(pluginType: Constructor<IPlugin> | IPlugin): IPlugin;
|
|
11
|
+
export declare function resolvePluginName(plugin: IPlugin, index: number, override?: string): string;
|
|
12
|
+
export declare function normalizePluginEntry(entry: PluginEntry, index: number): NormalizedPluginEntry;
|
|
13
|
+
export declare function normalizePluginEntries(plugins: PluginEntry[] | undefined): NormalizedPluginEntry[];
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { isConstructor } from '../utils';
|
|
2
|
+
export const DEFAULT_PLUGIN_NAME = 'AnonymousPlugin';
|
|
3
|
+
export function resolvePlugin(pluginType) {
|
|
4
|
+
if (isConstructor(pluginType)) {
|
|
5
|
+
return new pluginType();
|
|
6
|
+
}
|
|
7
|
+
return pluginType;
|
|
8
|
+
}
|
|
9
|
+
export function resolvePluginName(plugin, index, override) {
|
|
10
|
+
const resolved = override || plugin.meta?.name || plugin.constructor?.name;
|
|
11
|
+
if (!resolved || resolved === DEFAULT_PLUGIN_NAME) {
|
|
12
|
+
return `${DEFAULT_PLUGIN_NAME}#${index + 1}`;
|
|
13
|
+
}
|
|
14
|
+
return resolved;
|
|
15
|
+
}
|
|
16
|
+
export function normalizePluginEntry(entry, index) {
|
|
17
|
+
if (entry && typeof entry === 'object' && 'plugin' in entry) {
|
|
18
|
+
const obj = entry;
|
|
19
|
+
const plugin = resolvePlugin(obj.plugin);
|
|
20
|
+
const name = resolvePluginName(plugin, index, obj.name);
|
|
21
|
+
return {
|
|
22
|
+
plugin,
|
|
23
|
+
name,
|
|
24
|
+
preProcessors: obj.preProcessors ?? [],
|
|
25
|
+
postProcessors: obj.postProcessors ?? []
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const plugin = resolvePlugin(entry);
|
|
29
|
+
return {
|
|
30
|
+
plugin,
|
|
31
|
+
name: resolvePluginName(plugin, index),
|
|
32
|
+
preProcessors: [],
|
|
33
|
+
postProcessors: []
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export function normalizePluginEntries(plugins) {
|
|
37
|
+
return (plugins ?? []).map((entry, index) => normalizePluginEntry(entry, index));
|
|
38
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { HonestOptions, ILogger } from '../interfaces';
|
|
2
|
+
import type { Constructor } from '../types';
|
|
3
|
+
export declare function createStartupGuideHints(errorMessage: string): string[];
|
|
4
|
+
export declare function emitStartupGuide(logger: ILogger, startupGuide: HonestOptions['startupGuide'], error: unknown, rootModule: Constructor): void;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export function createStartupGuideHints(errorMessage) {
|
|
2
|
+
const hints = new Set();
|
|
3
|
+
hints.add('Check module wiring: root module imports, controllers, and services should be registered correctly.');
|
|
4
|
+
if (errorMessage.includes('not decorated with @Controller()')) {
|
|
5
|
+
hints.add('Add @Controller() to the class or remove it from module.controllers.');
|
|
6
|
+
}
|
|
7
|
+
if (errorMessage.includes('has no route handlers')) {
|
|
8
|
+
hints.add('Add at least one HTTP method decorator such as @Get() or @Post() in the controller.');
|
|
9
|
+
}
|
|
10
|
+
if (errorMessage.includes('not decorated with @Service()')) {
|
|
11
|
+
hints.add('Add @Service() to injectable classes used in constructor dependencies.');
|
|
12
|
+
}
|
|
13
|
+
if (errorMessage.includes('constructor metadata is missing') || errorMessage.includes('reflect-metadata')) {
|
|
14
|
+
hints.add("Import 'reflect-metadata' in your entry file and enable 'emitDecoratorMetadata' in tsconfig.");
|
|
15
|
+
}
|
|
16
|
+
if (errorMessage.includes('Strict mode: no routes were registered')) {
|
|
17
|
+
hints.add('Disable strict.requireRoutes for empty modules, or add a controller with at least one route.');
|
|
18
|
+
}
|
|
19
|
+
return [...hints];
|
|
20
|
+
}
|
|
21
|
+
export function emitStartupGuide(logger, startupGuide, error, rootModule) {
|
|
22
|
+
if (!startupGuide) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const verbose = typeof startupGuide === 'object' && Boolean(startupGuide.verbose);
|
|
26
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
27
|
+
const hints = createStartupGuideHints(errorMessage);
|
|
28
|
+
logger.emit({
|
|
29
|
+
level: 'warn',
|
|
30
|
+
category: 'startup',
|
|
31
|
+
message: 'Startup guide',
|
|
32
|
+
details: {
|
|
33
|
+
rootModule: rootModule.name,
|
|
34
|
+
errorMessage,
|
|
35
|
+
hints,
|
|
36
|
+
verbose
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
if (verbose) {
|
|
40
|
+
logger.emit({
|
|
41
|
+
level: 'warn',
|
|
42
|
+
category: 'startup',
|
|
43
|
+
message: 'Startup guide (verbose)',
|
|
44
|
+
details: {
|
|
45
|
+
steps: [
|
|
46
|
+
'Verify decorators are present for controllers/services used by DI and routing.',
|
|
47
|
+
"Ensure 'reflect-metadata' is imported once at entry and 'emitDecoratorMetadata' is enabled.",
|
|
48
|
+
'Enable debug.startup for extra startup diagnostics and timing details.'
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IApplicationContext } from './interfaces';
|
|
2
|
+
/**
|
|
3
|
+
* Map-backed implementation of the app-level registry.
|
|
4
|
+
* Used by Application so the app (bootstrap, services, any code with `app`) can share pipeline data by key.
|
|
5
|
+
*/
|
|
6
|
+
export declare class ApplicationContext implements IApplicationContext {
|
|
7
|
+
private readonly store;
|
|
8
|
+
get<T>(key: string): T | undefined;
|
|
9
|
+
set<T>(key: string, value: T): void;
|
|
10
|
+
has(key: string): boolean;
|
|
11
|
+
delete(key: string): boolean;
|
|
12
|
+
keys(): IterableIterator<string>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map-backed implementation of the app-level registry.
|
|
3
|
+
* Used by Application so the app (bootstrap, services, any code with `app`) can share pipeline data by key.
|
|
4
|
+
*/
|
|
5
|
+
export class ApplicationContext {
|
|
6
|
+
store = new Map();
|
|
7
|
+
get(key) {
|
|
8
|
+
return this.store.get(key);
|
|
9
|
+
}
|
|
10
|
+
set(key, value) {
|
|
11
|
+
this.store.set(key, value);
|
|
12
|
+
}
|
|
13
|
+
has(key) {
|
|
14
|
+
return this.store.has(key);
|
|
15
|
+
}
|
|
16
|
+
delete(key) {
|
|
17
|
+
return this.store.delete(key);
|
|
18
|
+
}
|
|
19
|
+
keys() {
|
|
20
|
+
return this.store.keys();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import type { DiContainer, HonestOptions, IApplicationContext, IMetadataRepository, RouteInfo } from './interfaces';
|
|
3
|
+
import type { Constructor } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* Main application class for the Honest framework.
|
|
6
|
+
*
|
|
7
|
+
* All per-app runtime state (routes, global components, DI container) is
|
|
8
|
+
* instance-based. Static decorator metadata lives in MetadataRegistry and
|
|
9
|
+
* is shared across all Application instances in the same process.
|
|
10
|
+
*/
|
|
11
|
+
export declare class Application {
|
|
12
|
+
private readonly hono;
|
|
13
|
+
private readonly container;
|
|
14
|
+
private readonly context;
|
|
15
|
+
private readonly routeRegistry;
|
|
16
|
+
private readonly metadataRepository;
|
|
17
|
+
private readonly componentManager;
|
|
18
|
+
private readonly routeManager;
|
|
19
|
+
private readonly logger;
|
|
20
|
+
private readonly options;
|
|
21
|
+
constructor(options: HonestOptions | undefined, metadataRepository: IMetadataRepository);
|
|
22
|
+
private setupErrorHandlers;
|
|
23
|
+
private shouldEmitRouteDiagnostics;
|
|
24
|
+
private emitStartupGuide;
|
|
25
|
+
register(moduleClass: Constructor): Promise<Application>;
|
|
26
|
+
static create(rootModule: Constructor, options?: HonestOptions): Promise<{
|
|
27
|
+
app: Application;
|
|
28
|
+
hono: Hono;
|
|
29
|
+
}>;
|
|
30
|
+
getApp(): Hono;
|
|
31
|
+
getContainer(): DiContainer;
|
|
32
|
+
getContext(): IApplicationContext;
|
|
33
|
+
getRoutes(): ReadonlyArray<RouteInfo>;
|
|
34
|
+
}
|