nestify-js 0.0.0 → 0.1.4

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 kasukabe tsumugi
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,408 @@
1
- # claris
1
+ # Nestify
2
2
 
3
- For kt.js related refactoring.
3
+ [中文版本 README.zh.md](./README.zh.md)
4
+
5
+ > ⚠️ **Warning**: This is not an official release version. APIs may change in the future.
6
+
7
+ **Injecorator** is a portmanteau of "inject" and "decorator" - a dependency injection framework for Fastify that uses modern Stage 3 decorators instead of the legacy decorators used by NestJS.
8
+
9
+ This project was created because NestJS uses the old decorator syntax, but we wanted to leverage the new Stage 3 decorator specification for better type safety and modern JavaScript features.
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ pnpm add nestify-js
15
+ ```
16
+
17
+ ## API Documentation
18
+
19
+ Using of decorators looks basically like they are in NestJS, but with modern Stage 3 syntax.
20
+
21
+ > Note: It is recommended to set "strictPropertyInitialization": false in your tsconfig.json to avoid linting issues when using property injection.
22
+
23
+ ### HTTP Method Decorators
24
+
25
+ These decorators are used to define HTTP routes on controller methods:
26
+
27
+ ```typescript
28
+ import { Get, Post, Put, Patch, Delete, HttpMethod } from 'nestify-js';
29
+
30
+ @Controller('/api')
31
+ class UserController {
32
+ @Get('/users')
33
+ getUsers() {
34
+ return { users: [] };
35
+ }
36
+
37
+ @Post('/users')
38
+ createUser() {
39
+ return { message: 'User created' };
40
+ }
41
+
42
+ @Put('/users/:id')
43
+ updateUser() {
44
+ return { message: 'User updated' };
45
+ }
46
+
47
+ @Patch('/users/:id')
48
+ patchUser() {
49
+ return { message: 'User patched' };
50
+ }
51
+
52
+ @Delete('/users/:id')
53
+ deleteUser() {
54
+ return { message: 'User deleted' };
55
+ }
56
+
57
+ @(HttpMethod('OPTIONS')('/users'))
58
+ optionsUsers() {
59
+ return { methods: ['GET', 'POST'] };
60
+ }
61
+ }
62
+ ```
63
+
64
+ ### Route Configuration
65
+
66
+ #### `@Controller(prefix?: string)`
67
+
68
+ Marks a class as a controller and optionally sets a route prefix:
69
+
70
+ ```typescript
71
+ @Controller('/api/v1')
72
+ class ApiController {
73
+ @Get('/health')
74
+ health() {
75
+ return { status: 'ok' };
76
+ }
77
+ }
78
+ // This creates route: GET /api/v1/health
79
+ ```
80
+
81
+ #### `@ApiSchema(schema)`
82
+
83
+ Sets OpenAPI/Swagger schema information for routes:
84
+
85
+ ```typescript
86
+ @Controller('/users')
87
+ class UserController {
88
+ @Get('/:id')
89
+ @ApiSchema({
90
+ summary: 'Get user by ID',
91
+ description: 'Retrieves a user by their unique identifier',
92
+ tags: ['users'],
93
+ })
94
+ getUser() {
95
+ return { user: {} };
96
+ }
97
+ }
98
+ ```
99
+
100
+ #### `@Opt(options)`
101
+
102
+ Sets additional Fastify route options:
103
+
104
+ ```typescript
105
+ @Controller('/files')
106
+ class FileController {
107
+ @Post('/upload')
108
+ @Opt({
109
+ bodyLimit: 1048576, // 1MB
110
+ attachValidation: true,
111
+ })
112
+ uploadFile() {
113
+ return { uploaded: true };
114
+ }
115
+ }
116
+ ```
117
+
118
+ ### Dependency Injection
119
+
120
+ #### `@Injectable()`
121
+
122
+ Marks a class as a service that can be injected:
123
+
124
+ ```typescript
125
+ @Injectable()
126
+ class UserService {
127
+ getUsers() {
128
+ return [{ id: 1, name: 'John' }];
129
+ }
130
+ }
131
+ ```
132
+
133
+ #### `@Inject(token)`
134
+
135
+ Injects dependencies into class properties:
136
+
137
+ ```typescript
138
+ @Injectable()
139
+ class UserController {
140
+ @Inject(UserService)
141
+ userService: UserService; // here might be linted by typescript, you can set "strictPropertyInitialization": false in tsconfig.json
142
+
143
+ @Inject('DATABASE_URL')
144
+ databaseUrl: string;
145
+
146
+ getUsers() {
147
+ return this.userService.getUsers();
148
+ }
149
+ }
150
+ ```
151
+
152
+ #### `@Module(options)`
153
+
154
+ Defines a module with providers, controllers, imports, and exports:
155
+
156
+ ```typescript
157
+ @Module({
158
+ imports: [DatabaseModule],
159
+ providers: [UserService],
160
+ controllers: [UserController],
161
+ exports: [UserService],
162
+ })
163
+ class UserModule {}
164
+ ```
165
+
166
+ ### Middleware System
167
+
168
+ #### Guards
169
+
170
+ Guards control access to routes:
171
+
172
+ ```typescript
173
+ @Guard()
174
+ class AuthGuard implements InjecoratorGuard {
175
+ canActivate(context: ExecutionContext): boolean {
176
+ const request = context.switchToHttp().getRequest();
177
+ return request.headers.authorization != null;
178
+ }
179
+ }
180
+
181
+ @Controller('/admin')
182
+ @UseGuards(AuthGuard)
183
+ class AdminController {
184
+ @Get('/dashboard')
185
+ getDashboard() {
186
+ return { data: 'sensitive' };
187
+ }
188
+ }
189
+ ```
190
+
191
+ #### Interceptors
192
+
193
+ Interceptors can modify request/response flow:
194
+
195
+ ```typescript
196
+ @Interceptor()
197
+ class LoggingInterceptor implements InjecoratorInterceptor {
198
+ intercept(context: ExecutionContext) {
199
+ const start = Date.now();
200
+ console.log('Request started');
201
+
202
+ return () => {
203
+ console.log(`Request completed in ${Date.now() - start}ms`);
204
+ };
205
+ }
206
+ }
207
+
208
+ @Controller('/api')
209
+ @UseInterceptors(LoggingInterceptor)
210
+ class ApiController {
211
+ @Get('/data')
212
+ getData() {
213
+ return { data: 'example' };
214
+ }
215
+ }
216
+ ```
217
+
218
+ #### Pipes
219
+
220
+ Pipes transform and validate input data:
221
+
222
+ ```typescript
223
+ @Pipe()
224
+ class ValidationPipe implements InjecoratorPipe {
225
+ transform(context: ExecutionContext, input: any[]) {
226
+ // Transform and validate input
227
+ return input;
228
+ }
229
+ }
230
+
231
+ @Controller('/users')
232
+ class UserController {
233
+ @Post('/')
234
+ @Body({ type: 'object', required: ['name', 'email'] })
235
+ createUser(@Body() body: any) {
236
+ return { user: body };
237
+ }
238
+
239
+ @Get('/')
240
+ @Query({ type: 'object' })
241
+ getUsers(@Query() query: any) {
242
+ return { users: [], query };
243
+ }
244
+
245
+ @Get('/:id')
246
+ @Params({ type: 'object', required: ['id'] })
247
+ getUser(@Params() params: any) {
248
+ return { user: { id: params.id } };
249
+ }
250
+
251
+ @Get('/ip')
252
+ getUserIP(@Ip() ip: string) {
253
+ return { ip };
254
+ }
255
+
256
+ @Post('/raw')
257
+ handleRaw(@Raw() raw: any) {
258
+ return { received: true };
259
+ }
260
+ }
261
+ ```
262
+
263
+ #### Filters
264
+
265
+ Filters handle exceptions:
266
+
267
+ ```typescript
268
+ @Filter(HttpException)
269
+ class HttpExceptionFilter implements InjecoratorFilter {
270
+ catch(exception: HttpException, context: ExecutionContext) {
271
+ const response = context.switchToHttp().getReply();
272
+ response.status(exception.status).send({
273
+ error: exception.message,
274
+ timestamp: new Date().toISOString(),
275
+ });
276
+ }
277
+ }
278
+
279
+ @Controller('/api')
280
+ @UseFilters(HttpExceptionFilter)
281
+ class ApiController {
282
+ @Get('/error')
283
+ throwError() {
284
+ throw new HttpException('Something went wrong', 400);
285
+ }
286
+ }
287
+ ```
288
+
289
+ ## Complete Usage Example
290
+
291
+ ```typescript
292
+ import fastify from 'fastify';
293
+ import {
294
+ Module,
295
+ Controller,
296
+ Injectable,
297
+ Inject,
298
+ Get,
299
+ Post,
300
+ Body,
301
+ Params,
302
+ UseGuards,
303
+ Guard,
304
+ apply,
305
+ } from 'nestify-js';
306
+
307
+ // Service
308
+ @Injectable()
309
+ class UserService {
310
+ private users = [
311
+ { id: 1, name: 'Alice' },
312
+ { id: 2, name: 'Bob' },
313
+ ];
314
+
315
+ getUsers() {
316
+ return this.users;
317
+ }
318
+
319
+ getUserById(id: number) {
320
+ return this.users.find((user) => user.id === id);
321
+ }
322
+
323
+ createUser(userData: { name: string }) {
324
+ const user = { id: Date.now(), ...userData };
325
+ this.users.push(user);
326
+ return user;
327
+ }
328
+ }
329
+
330
+ // Guard
331
+ @Guard()
332
+ class AuthGuard {
333
+ canActivate(context) {
334
+ // Simple auth check
335
+ const request = context.switchToHttp().getRequest();
336
+ return request.headers.authorization === 'Bearer valid-token';
337
+ }
338
+ }
339
+
340
+ // Controller
341
+ @Controller('/api/users')
342
+ class UserController {
343
+ @Inject(UserService)
344
+ userService: UserService;
345
+
346
+ @Get('/')
347
+ getUsers() {
348
+ return this.userService.getUsers();
349
+ }
350
+
351
+ @Get('/:id')
352
+ @Params({
353
+ type: 'object',
354
+ properties: { id: { type: 'number' } },
355
+ required: ['id'],
356
+ })
357
+ getUser(@Params() params: { id: number }) {
358
+ return this.userService.getUserById(params.id);
359
+ }
360
+
361
+ @Post('/')
362
+ @UseGuards(AuthGuard)
363
+ @Body({
364
+ type: 'object',
365
+ properties: { name: { type: 'string' } },
366
+ required: ['name'],
367
+ })
368
+ createUser(@Body() body: { name: string }) {
369
+ return this.userService.createUser(body);
370
+ }
371
+ }
372
+
373
+ // Module
374
+ @Module({
375
+ providers: [UserService, AuthGuard],
376
+ controllers: [UserController],
377
+ })
378
+ class AppModule {}
379
+
380
+ // Application setup
381
+ const app = fastify({ logger: true });
382
+
383
+ await apply(app, {
384
+ rootModule: AppModule,
385
+ });
386
+
387
+ await app.listen({ port: 3000 });
388
+ console.log('Server running on http://localhost:3000');
389
+ ```
390
+
391
+ ## Features
392
+
393
+ - ✅ Modern Stage 3 decorators
394
+ - ✅ Dependency injection with circular dependency support
395
+ - ✅ HTTP method decorators (GET, POST, PUT, PATCH, DELETE)
396
+ - ✅ Route parameters, query, and body validation
397
+ - ✅ Guards for authentication/authorization
398
+ - ✅ Interceptors for request/response transformation
399
+ - ✅ Pipes for data transformation and validation
400
+ - ✅ Exception filters
401
+ - ✅ Module system with imports/exports
402
+ - ✅ OpenAPI/Swagger schema support
403
+ - ✅ Built-in HTTP exceptions
404
+ - ✅ Execution context for middleware
405
+
406
+ ## License
407
+
408
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("@nestify-js/core"),l=require("fastify-type-provider-zod"),u=require("@nestify/core"),d=require("@fastify/swagger");d=s(d,1);let f=require("@fastify/swagger-ui");f=s(f,1);let p=require("@nestify-js/shared");function m(e){return function(t,n){(0,u.expectMethodDecorator)(t,n),(0,u.expectObject)(e,`Given opts must be a RouteShorthandOptions of Fastify`),(0,u.metaSetSchema)(n,e)}}async function h(e,t={}){await e.register(d.default,t.swagger),await e.register(f.default,t.ui)}async function g(e,t){return(0,c.fastifyInjecorator)(e,{...t,setup:t.setup??c.setupBasicPipes})}Object.defineProperty(exports,"APP_FILTER",{enumerable:!0,get:function(){return p.APP_FILTER}}),Object.defineProperty(exports,"APP_GUARD",{enumerable:!0,get:function(){return p.APP_GUARD}}),Object.defineProperty(exports,"APP_INTERCEPTOR",{enumerable:!0,get:function(){return p.APP_INTERCEPTOR}}),Object.defineProperty(exports,"APP_LOGGER",{enumerable:!0,get:function(){return p.APP_LOGGER}}),Object.defineProperty(exports,"APP_PIPE",{enumerable:!0,get:function(){return p.APP_PIPE}}),exports.ApiSchema=m,Object.defineProperty(exports,"HttpStatus",{enumerable:!0,get:function(){return p.HttpStatus}}),exports.ZodTypeProvider=l.ZodTypeProvider,exports.apply=g,exports.serializerCompiler=l.serializerCompiler,exports.setupSwagger=h,exports.validatorCompiler=l.validatorCompiler,Object.keys(c).forEach(function(e){e!=="default"&&!Object.prototype.hasOwnProperty.call(exports,e)&&Object.defineProperty(exports,e,{enumerable:!0,get:function(){return c[e]}})});
@@ -0,0 +1,19 @@
1
+ import { FastifyInjecoratorOptions } from "@nestify-js/core";
2
+ import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_LOGGER, APP_PIPE, HttpStatus } from "@nestify-js/shared";
3
+ import { FastifyInstance } from "fastify";
4
+ export * from "@nestify-js/core";
5
+ export * from "@nestify-js/schema";
6
+ export * from "@nestify-js/swagger";
7
+
8
+ //#region src/index.d.ts
9
+ /**
10
+ * Apply Nestify modules to a Fastify instance.
11
+ *
12
+ * Unlike the core `fastifyInjecorator`, this wrapper automatically
13
+ * registers basic pipes (Body, Params, Query, Ip, Raw) via `setupBasicPipes`.
14
+ *
15
+ * Pass `setup` explicitly to override the default behavior.
16
+ */
17
+ declare function apply(app: FastifyInstance, opts: Partial<FastifyInjecoratorOptions>): Promise<void>;
18
+ //#endregion
19
+ export { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_LOGGER, APP_PIPE, HttpStatus, apply };
@@ -0,0 +1,19 @@
1
+ import { FastifyInjecoratorOptions } from "@nestify-js/core";
2
+ import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_LOGGER, APP_PIPE, HttpStatus } from "@nestify-js/shared";
3
+ import { FastifyInstance } from "fastify";
4
+ export * from "@nestify-js/core";
5
+ export * from "@nestify-js/schema";
6
+ export * from "@nestify-js/swagger";
7
+
8
+ //#region src/index.d.ts
9
+ /**
10
+ * Apply Nestify modules to a Fastify instance.
11
+ *
12
+ * Unlike the core `fastifyInjecorator`, this wrapper automatically
13
+ * registers basic pipes (Body, Params, Query, Ip, Raw) via `setupBasicPipes`.
14
+ *
15
+ * Pass `setup` explicitly to override the default behavior.
16
+ */
17
+ declare function apply(app: FastifyInstance, opts: Partial<FastifyInjecoratorOptions>): Promise<void>;
18
+ //#endregion
19
+ export { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_LOGGER, APP_PIPE, HttpStatus, apply };
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ import{fastifyInjecorator as e,setupBasicPipes as t}from"@nestify-js/core";import{ZodTypeProvider as n,serializerCompiler as r,validatorCompiler as i}from"fastify-type-provider-zod";import{expectMethodDecorator as a,expectObject as o,metaSetSchema as s}from"@nestify/core";import c from"@fastify/swagger";import l from"@fastify/swagger-ui";import{APP_FILTER as u,APP_GUARD as d,APP_INTERCEPTOR as f,APP_LOGGER as p,APP_PIPE as m,HttpStatus as h}from"@nestify-js/shared";export*from"@nestify-js/core";function g(e){return function(t,n){a(t,n),o(e,`Given opts must be a RouteShorthandOptions of Fastify`),s(n,e)}}async function _(e,t={}){await e.register(c,t.swagger),await e.register(l,t.ui)}async function v(n,r){return e(n,{...r,setup:r.setup??t})}export{u as APP_FILTER,d as APP_GUARD,f as APP_INTERCEPTOR,p as APP_LOGGER,m as APP_PIPE,g as ApiSchema,h as HttpStatus,n as ZodTypeProvider,v as apply,r as serializerCompiler,_ as setupSwagger,i as validatorCompiler};
package/package.json CHANGED
@@ -1,19 +1,62 @@
1
- {
1
+ {
2
2
  "name": "nestify-js",
3
- "version": "0.0.0",
4
- "description": "For fastify injecorator related refactoring",
5
- "license": "MIT",
3
+ "version": "0.1.4",
4
+ "description": "A NestJS like fastify plugin for dependency injection",
5
+ "description_zh": "一个类似于 NestJS 的 Fastify 依赖注入插件",
6
+ "purpose": "npm",
6
7
  "type": "module",
7
- "private": false,
8
- "keywords": [
9
- "kt.js"
10
- ],
11
- "main": "index.js",
8
+ "main": "./dist/index.cjs",
9
+ "module": "./dist/index.mjs",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.mjs",
15
+ "default": "./dist/index.cjs",
16
+ "require": "./dist/index.cjs"
17
+ }
18
+ },
12
19
  "files": [
13
- "index.js",
14
- "README.md"
20
+ "dist"
15
21
  ],
16
- "publishConfig": {
17
- "access": "public"
22
+ "author": {
23
+ "name": "Kasukabe Tsumugi",
24
+ "email": "futami16237@gmail.com"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/baendlorel/nestify-js"
29
+ },
30
+ "keywords": [
31
+ "nestjs",
32
+ "decorator",
33
+ "fastify",
34
+ "typescript",
35
+ "javascript"
36
+ ],
37
+ "license": "MIT",
38
+ "dependencies": {
39
+ "@fastify/autoload": "^6.3.1",
40
+ "@fastify/one-line-logger": "^2.1.0",
41
+ "@fastify/sensible": "^6.0.4",
42
+ "@fastify/static": "^9.1.3",
43
+ "axios": "^1.18.1",
44
+ "fastify": "^5.9.0",
45
+ "@nestify-js/core": "0.1.4"
46
+ },
47
+ "peerDependencies": {
48
+ "@fastify/multipart": "^9.3.0"
49
+ },
50
+ "peerDependenciesMeta": {
51
+ "@fastify/multipart": {
52
+ "optional": true
53
+ }
54
+ },
55
+ "devDependencies": {
56
+ "@rollup/plugin-replace": "^6.0.3",
57
+ "@types/node": "^26.1.0",
58
+ "rollup-plugin-func-macro": "^1.2.3",
59
+ "tsdown": "^0.22.3",
60
+ "typescript": "^6.0.3"
18
61
  }
19
62
  }
package/index.js DELETED
@@ -1 +0,0 @@
1
- export {};