@hazeljs/typeorm 0.2.0-beta.53 → 0.2.0-beta.55

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/README.md CHANGED
@@ -85,12 +85,12 @@ export class UserEntity {
85
85
 
86
86
  ### 4. Create a repository
87
87
 
88
+ `@Repository` implies `@Injectable()` — no need to add both decorators.
89
+
88
90
  ```typescript
89
- import { Injectable } from '@hazeljs/core';
90
91
  import { BaseRepository, Repository, TypeOrmService } from '@hazeljs/typeorm';
91
92
  import { UserEntity } from './user.entity';
92
93
 
93
- @Injectable()
94
94
  @Repository({ model: 'User' })
95
95
  export class UserRepository extends BaseRepository<UserEntity> {
96
96
  constructor(typeOrm: TypeOrmService) {
@@ -105,12 +105,14 @@ export class UserRepository extends BaseRepository<UserEntity> {
105
105
 
106
106
  ### 5. Use in a service
107
107
 
108
+ Use `@Service` for service classes — it is the correct decorator for business-logic classes in HazelJS (not `@Injectable`).
109
+
108
110
  ```typescript
109
- import { Injectable } from '@hazeljs/core';
111
+ import { Service } from '@hazeljs/core';
110
112
  import { InjectRepository } from '@hazeljs/typeorm';
111
113
  import { UserRepository } from './user.repository';
112
114
 
113
- @Injectable()
115
+ @Service()
114
116
  export class UserService {
115
117
  constructor(
116
118
  @InjectRepository()
@@ -132,9 +134,10 @@ export class UserService {
132
134
  Use `TypeOrmService.dataSource` for transactions:
133
135
 
134
136
  ```typescript
137
+ import { Service } from '@hazeljs/core';
135
138
  import { TypeOrmService } from '@hazeljs/typeorm';
136
139
 
137
- @Injectable()
140
+ @Service()
138
141
  export class TransferService {
139
142
  constructor(private readonly typeOrm: TypeOrmService) {}
140
143
 
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=base.repository.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.repository.test.d.ts","sourceRoot":"","sources":["../src/base.repository.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const base_repository_1 = require("./base.repository");
4
+ const typeorm_service_1 = require("./typeorm.service");
5
+ class TestEntity {
6
+ }
7
+ class TestRepository extends base_repository_1.BaseRepository {
8
+ constructor(typeOrm) {
9
+ super(typeOrm, TestEntity);
10
+ }
11
+ }
12
+ describe('BaseRepository', () => {
13
+ let typeOrm;
14
+ let repo;
15
+ beforeAll(() => {
16
+ const mockRepository = {
17
+ find: jest.fn().mockResolvedValue([]),
18
+ findOne: jest.fn().mockResolvedValue(null),
19
+ create: jest.fn(),
20
+ save: jest.fn(),
21
+ update: jest.fn().mockResolvedValue(undefined),
22
+ delete: jest.fn().mockResolvedValue(undefined),
23
+ count: jest.fn().mockResolvedValue(0),
24
+ };
25
+ const dataSource = {
26
+ isInitialized: true,
27
+ getRepository: jest.fn(() => mockRepository),
28
+ };
29
+ typeOrm = new typeorm_service_1.TypeOrmService({ dataSource });
30
+ repo = new TestRepository(typeOrm);
31
+ });
32
+ it('should expose repository getter', () => {
33
+ expect(repo['repository']).toBeDefined();
34
+ expect(repo['repository'].find).toBeDefined();
35
+ });
36
+ it('should delegate find to TypeORM repository', async () => {
37
+ const result = await repo.find({});
38
+ expect(Array.isArray(result)).toBe(true);
39
+ });
40
+ it('should delegate count to TypeORM repository', async () => {
41
+ const result = await repo.count({});
42
+ expect(typeof result).toBe('number');
43
+ expect(result).toBe(0);
44
+ });
45
+ describe('handleError', () => {
46
+ it('should throw on generic error', () => {
47
+ expect(() => repo['handleError'](new Error('db error'))).toThrow('Database error: db error');
48
+ });
49
+ it('should map unique constraint code 23505', () => {
50
+ const err = new Error('duplicate');
51
+ err.code = '23505';
52
+ expect(() => repo['handleError'](err)).toThrow(/Unique constraint/);
53
+ });
54
+ it('should map foreign key code 23503', () => {
55
+ const err = new Error('fk');
56
+ err.code = '23503';
57
+ expect(() => repo['handleError'](err)).toThrow('Foreign key constraint violation');
58
+ });
59
+ });
60
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"repository.decorator.d.ts","sourceRoot":"","sources":["../src/repository.decorator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,kBAAkB,CAAC;AAE1B,wBAAgB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,GAAG,cAAc,CAK9E;AAED,wBAAgB,gBAAgB,IAAI,kBAAkB,CAyBrD"}
1
+ {"version":3,"file":"repository.decorator.d.ts","sourceRoot":"","sources":["../src/repository.decorator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,kBAAkB,CAAC;AAE1B,wBAAgB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,GAAG,cAAc,CAW9E;AAED,wBAAgB,gBAAgB,IAAI,kBAAkB,CAyBrD"}
@@ -7,6 +7,12 @@ function Repository(options) {
7
7
  return function (target) {
8
8
  const opts = typeof options === 'string' ? { model: options } : options;
9
9
  Reflect.defineMetadata('hazel:repository', opts, target);
10
+ // Implicitly mark the class as injectable — @Injectable() is not needed separately.
11
+ // Write metadata directly to avoid the ClassDecorator `Function` type constraint.
12
+ Reflect.defineMetadata('hazel:injectable', opts.scope ? { scope: opts.scope } : {}, target);
13
+ if (opts.scope) {
14
+ Reflect.defineMetadata('hazel:scope', opts.scope, target);
15
+ }
10
16
  };
11
17
  }
12
18
  function InjectRepository() {
@@ -0,0 +1,2 @@
1
+ import 'reflect-metadata';
2
+ //# sourceMappingURL=repository.decorator.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository.decorator.test.d.ts","sourceRoot":"","sources":["../src/repository.decorator.test.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC"}
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ require("reflect-metadata");
16
+ const repository_decorator_1 = require("./repository.decorator");
17
+ describe('Repository decorator', () => {
18
+ it('should store options with object', () => {
19
+ let UserRepo = class UserRepo {
20
+ };
21
+ UserRepo = __decorate([
22
+ (0, repository_decorator_1.Repository)({ model: 'User' })
23
+ ], UserRepo);
24
+ const meta = Reflect.getMetadata('hazel:repository', UserRepo);
25
+ expect(meta).toEqual({ model: 'User' });
26
+ });
27
+ it('should store options with string shorthand', () => {
28
+ let PostRepo = class PostRepo {
29
+ };
30
+ PostRepo = __decorate([
31
+ (0, repository_decorator_1.Repository)('Post')
32
+ ], PostRepo);
33
+ const meta = Reflect.getMetadata('hazel:repository', PostRepo);
34
+ expect(meta).toEqual({ model: 'Post' });
35
+ });
36
+ it('should implicitly apply @Injectable so @Injectable() is not needed separately', () => {
37
+ let OrderRepo = class OrderRepo {
38
+ };
39
+ OrderRepo = __decorate([
40
+ (0, repository_decorator_1.Repository)({ model: 'Order' })
41
+ ], OrderRepo);
42
+ const injectable = Reflect.getMetadata('hazel:injectable', OrderRepo);
43
+ expect(injectable).toBeDefined();
44
+ });
45
+ it('should forward scope from RepositoryOptions to injectable metadata', () => {
46
+ let SessionRepo = class SessionRepo {
47
+ };
48
+ SessionRepo = __decorate([
49
+ (0, repository_decorator_1.Repository)({ model: 'Session', scope: 'transient' })
50
+ ], SessionRepo);
51
+ const scope = Reflect.getMetadata('hazel:scope', SessionRepo);
52
+ expect(scope).toBe('transient');
53
+ });
54
+ });
55
+ describe('InjectRepository', () => {
56
+ it('should push repository metadata onto target', () => {
57
+ let UserRepo = class UserRepo {
58
+ };
59
+ UserRepo = __decorate([
60
+ (0, repository_decorator_1.Repository)({ model: 'User' })
61
+ ], UserRepo);
62
+ let Consumer = class Consumer {
63
+ constructor(_repo) { }
64
+ };
65
+ Consumer = __decorate([
66
+ __param(0, (0, repository_decorator_1.InjectRepository)()),
67
+ __metadata("design:paramtypes", [UserRepo])
68
+ ], Consumer);
69
+ const repos = Reflect.getMetadata('hazel:repositories', Consumer);
70
+ expect(repos).toHaveLength(1);
71
+ expect(repos[0]).toEqual({ index: 0, model: 'User' });
72
+ });
73
+ it('should throw when target repository is not decorated', () => {
74
+ class NotARepo {
75
+ }
76
+ expect(() => {
77
+ // Class exists only to trigger InjectRepository(); the decorator throws during definition
78
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
79
+ let Consumer = class Consumer {
80
+ constructor(_repo) { }
81
+ };
82
+ Consumer = __decorate([
83
+ __param(0, (0, repository_decorator_1.InjectRepository)()),
84
+ __metadata("design:paramtypes", [NotARepo])
85
+ ], Consumer);
86
+ }).toThrow(/not decorated with @Repository/);
87
+ });
88
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=typeorm.module.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typeorm.module.test.d.ts","sourceRoot":"","sources":["../src/typeorm.module.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const typeorm_module_1 = require("./typeorm.module");
4
+ const typeorm_service_1 = require("./typeorm.service");
5
+ describe('TypeOrmModule', () => {
6
+ it('should export TypeOrmModule class', () => {
7
+ expect(typeorm_module_1.TypeOrmModule).toBeDefined();
8
+ });
9
+ describe('forRoot', () => {
10
+ it('should return dynamic module with module, providers, exports', () => {
11
+ const result = typeorm_module_1.TypeOrmModule.forRoot({
12
+ type: 'sqlite',
13
+ database: ':memory:',
14
+ synchronize: true,
15
+ });
16
+ expect(result).toHaveProperty('module', typeorm_module_1.TypeOrmModule);
17
+ expect(result).toHaveProperty('providers');
18
+ expect(result).toHaveProperty('exports');
19
+ expect(Array.isArray(result.providers)).toBe(true);
20
+ expect(result.providers).toHaveLength(1);
21
+ expect(result.providers[0]).toMatchObject({
22
+ provide: typeorm_service_1.TypeOrmService,
23
+ useFactory: expect.any(Function),
24
+ });
25
+ expect(result.exports).toContain(typeorm_service_1.TypeOrmService);
26
+ });
27
+ it('should provide TypeOrmService via useFactory returning new DataSource', () => {
28
+ const result = typeorm_module_1.TypeOrmModule.forRoot({
29
+ type: 'postgres',
30
+ host: 'localhost',
31
+ port: 5432,
32
+ username: 'u',
33
+ password: 'p',
34
+ database: 'd',
35
+ });
36
+ const provider = result.providers[0];
37
+ expect(provider.useFactory).toBeDefined();
38
+ // useFactory creates DataSource(options) and TypeOrmService; skip calling it to avoid requiring pg driver
39
+ expect(typeof provider.useFactory).toBe('function');
40
+ });
41
+ });
42
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=typeorm.service.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typeorm.service.test.d.ts","sourceRoot":"","sources":["../src/typeorm.service.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const typeorm_service_1 = require("./typeorm.service");
4
+ describe('TypeOrmService', () => {
5
+ describe('with provided DataSource', () => {
6
+ it('should use provided DataSource and initialize/destroy', async () => {
7
+ const mockDs = {
8
+ isInitialized: false,
9
+ initialize: async () => {
10
+ mockDs.isInitialized = true;
11
+ },
12
+ destroy: async () => {
13
+ mockDs.isInitialized = false;
14
+ },
15
+ getRepository: jest.fn(() => ({ find: jest.fn() })),
16
+ };
17
+ const dataSource = mockDs;
18
+ const service = new typeorm_service_1.TypeOrmService({ dataSource });
19
+ expect(service.dataSource).toBe(dataSource);
20
+ await service.onModuleInit();
21
+ expect(dataSource.isInitialized).toBe(true);
22
+ await service.onModuleDestroy();
23
+ expect(dataSource.isInitialized).toBe(false);
24
+ });
25
+ it('should expose getRepository', () => {
26
+ const mockRepo = { find: jest.fn(), findOne: jest.fn() };
27
+ const dataSource = {
28
+ isInitialized: true,
29
+ getRepository: jest.fn(() => mockRepo),
30
+ };
31
+ const service = new typeorm_service_1.TypeOrmService({ dataSource });
32
+ const repo = service.getRepository('dummy');
33
+ expect(repo).toBe(mockRepo);
34
+ expect(typeof repo.find).toBe('function');
35
+ });
36
+ });
37
+ describe('without options or dataSource', () => {
38
+ const origEnv = process.env.DATABASE_URL;
39
+ afterEach(() => {
40
+ process.env.DATABASE_URL = origEnv;
41
+ });
42
+ it('should throw when DATABASE_URL is not set', () => {
43
+ delete process.env.DATABASE_URL;
44
+ expect(() => new typeorm_service_1.TypeOrmService()).toThrow(/DATABASE_URL|options|dataSource/);
45
+ });
46
+ });
47
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hazeljs/typeorm",
3
- "version": "0.2.0-beta.53",
3
+ "version": "0.2.0-beta.55",
4
4
  "description": "TypeORM integration for HazelJS framework",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -49,5 +49,5 @@
49
49
  "peerDependencies": {
50
50
  "@hazeljs/core": ">=0.2.0-beta.0"
51
51
  },
52
- "gitHead": "84311f65083627fa53bf2a22f96cd212c196873c"
52
+ "gitHead": "f2e54f346eea552595a44607999454a9e388cb9e"
53
53
  }