@hazeljs/prisma 0.2.0-alpha.1
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 +192 -0
- package/README.md +458 -0
- package/dist/__mocks__/@prisma/client/runtime/library.d.ts +10 -0
- package/dist/__mocks__/@prisma/client/runtime/library.d.ts.map +1 -0
- package/dist/__mocks__/@prisma/client/runtime/library.js +13 -0
- package/dist/__mocks__/@prisma/client.d.ts +7 -0
- package/dist/__mocks__/@prisma/client.d.ts.map +1 -0
- package/dist/__mocks__/@prisma/client.js +11 -0
- package/dist/base.repository.d.ts +45 -0
- package/dist/base.repository.d.ts.map +1 -0
- package/dist/base.repository.js +82 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/prisma.module.d.ts +3 -0
- package/dist/prisma.module.d.ts.map +1 -0
- package/dist/prisma.module.js +20 -0
- package/dist/prisma.service.d.ts +7 -0
- package/dist/prisma.service.d.ts.map +1 -0
- package/dist/prisma.service.js +77 -0
- package/dist/prisma.test.d.ts +2 -0
- package/dist/prisma.test.d.ts.map +1 -0
- package/dist/prisma.test.js +271 -0
- package/dist/repository.decorator.d.ts +5 -0
- package/dist/repository.decorator.d.ts.map +1 -0
- package/dist/repository.decorator.js +35 -0
- package/package.json +54 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { PrismaService } from './prisma.service';
|
|
2
|
+
export type PrismaModel = {
|
|
3
|
+
id: number;
|
|
4
|
+
[key: string]: unknown;
|
|
5
|
+
};
|
|
6
|
+
export type WhereUniqueInput = {
|
|
7
|
+
id?: number;
|
|
8
|
+
[key: string]: unknown;
|
|
9
|
+
};
|
|
10
|
+
export type UpdateInput = {
|
|
11
|
+
[key: string]: unknown;
|
|
12
|
+
};
|
|
13
|
+
type PrismaModelDelegate = {
|
|
14
|
+
findMany: () => Promise<unknown[]>;
|
|
15
|
+
findUnique: (args: {
|
|
16
|
+
where: WhereUniqueInput;
|
|
17
|
+
}) => Promise<unknown | null>;
|
|
18
|
+
create: (args: {
|
|
19
|
+
data: unknown;
|
|
20
|
+
}) => Promise<unknown>;
|
|
21
|
+
update: (args: {
|
|
22
|
+
where: WhereUniqueInput;
|
|
23
|
+
data: UpdateInput;
|
|
24
|
+
}) => Promise<unknown>;
|
|
25
|
+
delete: (args: {
|
|
26
|
+
where: WhereUniqueInput;
|
|
27
|
+
}) => Promise<unknown>;
|
|
28
|
+
count: (args?: unknown) => Promise<number>;
|
|
29
|
+
};
|
|
30
|
+
export declare abstract class BaseRepository<T extends PrismaModel> {
|
|
31
|
+
protected readonly prisma: PrismaService;
|
|
32
|
+
protected readonly model: string;
|
|
33
|
+
constructor(prisma: PrismaService, model: string);
|
|
34
|
+
protected get prismaClient(): PrismaService;
|
|
35
|
+
protected get modelDelegate(): PrismaModelDelegate;
|
|
36
|
+
protected handleError(error: unknown): never;
|
|
37
|
+
findMany(): Promise<T[]>;
|
|
38
|
+
findOne(where: WhereUniqueInput): Promise<T | null>;
|
|
39
|
+
create(data: Omit<T, 'id'>): Promise<T>;
|
|
40
|
+
update(where: WhereUniqueInput, data: UpdateInput): Promise<T>;
|
|
41
|
+
delete(where: WhereUniqueInput): Promise<T>;
|
|
42
|
+
count(args?: unknown): Promise<number>;
|
|
43
|
+
}
|
|
44
|
+
export {};
|
|
45
|
+
//# sourceMappingURL=base.repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.repository.d.ts","sourceRoot":"","sources":["../src/base.repository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAKjD,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AAEF,KAAK,mBAAmB,GAAG;IACzB,QAAQ,EAAE,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACnC,UAAU,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,gBAAgB,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IAC3E,MAAM,EAAE,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACtD,MAAM,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,gBAAgB,CAAC;QAAC,IAAI,EAAE,WAAW,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACnF,MAAM,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,gBAAgB,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAChE,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC5C,CAAC;AAEF,8BACsB,cAAc,CAAC,CAAC,SAAS,WAAW;IAItD,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,aAAa;IAH1C,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBAGZ,MAAM,EAAE,aAAa,EACxC,KAAK,EAAE,MAAM;IAKf,SAAS,KAAK,YAAY,IAAI,aAAa,CAE1C;IAED,SAAS,KAAK,aAAa,IAAI,mBAAmB,CAEjD;IAED,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK;IA4BtC,QAAQ,IAAI,OAAO,CAAC,CAAC,EAAE,CAAC;IAKxB,OAAO,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAKnD,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAKvC,MAAM,CAAC,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC;IAK9D,MAAM,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,CAAC,CAAC;IAK3C,KAAK,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;CAG7C"}
|
|
@@ -0,0 +1,82 @@
|
|
|
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 __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.BaseRepository = void 0;
|
|
16
|
+
const core_1 = require("@hazeljs/core");
|
|
17
|
+
const prisma_service_1 = require("./prisma.service");
|
|
18
|
+
const library_1 = require("@prisma/client/runtime/library");
|
|
19
|
+
const core_2 = __importDefault(require("@hazeljs/core"));
|
|
20
|
+
let BaseRepository = class BaseRepository {
|
|
21
|
+
constructor(prisma, model) {
|
|
22
|
+
this.prisma = prisma;
|
|
23
|
+
this.model = model;
|
|
24
|
+
}
|
|
25
|
+
get prismaClient() {
|
|
26
|
+
return this.prisma;
|
|
27
|
+
}
|
|
28
|
+
get modelDelegate() {
|
|
29
|
+
return this.prismaClient[this.model];
|
|
30
|
+
}
|
|
31
|
+
handleError(error) {
|
|
32
|
+
core_2.default.error('Database error:', error);
|
|
33
|
+
if (error instanceof library_1.PrismaClientKnownRequestError) {
|
|
34
|
+
let errorMessage;
|
|
35
|
+
let target;
|
|
36
|
+
switch (error.code) {
|
|
37
|
+
case 'P2002':
|
|
38
|
+
target = error.meta?.target;
|
|
39
|
+
errorMessage = `Unique constraint violation on field${target ? `s: ${target.join(', ')}` : ''}`;
|
|
40
|
+
break;
|
|
41
|
+
case 'P2025':
|
|
42
|
+
errorMessage = 'Record not found';
|
|
43
|
+
break;
|
|
44
|
+
case 'P2003':
|
|
45
|
+
errorMessage = 'Foreign key constraint violation';
|
|
46
|
+
break;
|
|
47
|
+
default:
|
|
48
|
+
errorMessage = `Database error: ${error.message}`;
|
|
49
|
+
}
|
|
50
|
+
throw new Error(errorMessage);
|
|
51
|
+
}
|
|
52
|
+
throw new Error(`Database error: ${error.message}`);
|
|
53
|
+
}
|
|
54
|
+
async findMany() {
|
|
55
|
+
const result = await this.modelDelegate.findMany();
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
async findOne(where) {
|
|
59
|
+
const result = await this.modelDelegate.findUnique({ where });
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
async create(data) {
|
|
63
|
+
const result = await this.modelDelegate.create({ data });
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
async update(where, data) {
|
|
67
|
+
const result = await this.modelDelegate.update({ where, data });
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
async delete(where) {
|
|
71
|
+
const result = await this.modelDelegate.delete({ where });
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
async count(args) {
|
|
75
|
+
return this.modelDelegate.count(args);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
exports.BaseRepository = BaseRepository;
|
|
79
|
+
exports.BaseRepository = BaseRepository = __decorate([
|
|
80
|
+
(0, core_1.Injectable)(),
|
|
81
|
+
__metadata("design:paramtypes", [prisma_service_1.PrismaService, String])
|
|
82
|
+
], BaseRepository);
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hazeljs/prisma - Prisma integration for HazelJS
|
|
3
|
+
*/
|
|
4
|
+
export { PrismaModule } from './prisma.module';
|
|
5
|
+
export { PrismaService } from './prisma.service';
|
|
6
|
+
export { BaseRepository, type PrismaModel, type WhereUniqueInput, type UpdateInput, } from './base.repository';
|
|
7
|
+
export { Repository } from './repository.decorator';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EACL,cAAc,EACd,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,WAAW,GACjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @hazeljs/prisma - Prisma integration for HazelJS
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Repository = exports.BaseRepository = exports.PrismaService = exports.PrismaModule = void 0;
|
|
7
|
+
var prisma_module_1 = require("./prisma.module");
|
|
8
|
+
Object.defineProperty(exports, "PrismaModule", { enumerable: true, get: function () { return prisma_module_1.PrismaModule; } });
|
|
9
|
+
var prisma_service_1 = require("./prisma.service");
|
|
10
|
+
Object.defineProperty(exports, "PrismaService", { enumerable: true, get: function () { return prisma_service_1.PrismaService; } });
|
|
11
|
+
var base_repository_1 = require("./base.repository");
|
|
12
|
+
Object.defineProperty(exports, "BaseRepository", { enumerable: true, get: function () { return base_repository_1.BaseRepository; } });
|
|
13
|
+
var repository_decorator_1 = require("./repository.decorator");
|
|
14
|
+
Object.defineProperty(exports, "Repository", { enumerable: true, get: function () { return repository_decorator_1.Repository; } });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma.module.d.ts","sourceRoot":"","sources":["../src/prisma.module.ts"],"names":[],"mappings":"AAGA,qBAIa,YAAY;CAAG"}
|
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.PrismaModule = void 0;
|
|
10
|
+
const core_1 = require("@hazeljs/core");
|
|
11
|
+
const prisma_service_1 = require("./prisma.service");
|
|
12
|
+
let PrismaModule = class PrismaModule {
|
|
13
|
+
};
|
|
14
|
+
exports.PrismaModule = PrismaModule;
|
|
15
|
+
exports.PrismaModule = PrismaModule = __decorate([
|
|
16
|
+
(0, core_1.Module)({
|
|
17
|
+
providers: [prisma_service_1.PrismaService],
|
|
18
|
+
exports: [prisma_service_1.PrismaService],
|
|
19
|
+
})
|
|
20
|
+
], PrismaModule);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma.service.d.ts","sourceRoot":"","sources":["../src/prisma.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAwB9C,qBACa,aAAc,SAAQ,YAAY;;IAoCvC,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAU7B,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;CASvC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
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 __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.PrismaService = void 0;
|
|
16
|
+
const core_1 = require("@hazeljs/core");
|
|
17
|
+
const client_1 = require("@prisma/client");
|
|
18
|
+
const core_2 = __importDefault(require("@hazeljs/core"));
|
|
19
|
+
function isQueryEvent(event) {
|
|
20
|
+
return 'query' in event && 'params' in event && 'duration' in event;
|
|
21
|
+
}
|
|
22
|
+
function isErrorEvent(event) {
|
|
23
|
+
return 'message' in event;
|
|
24
|
+
}
|
|
25
|
+
let PrismaService = class PrismaService extends client_1.PrismaClient {
|
|
26
|
+
constructor() {
|
|
27
|
+
super({
|
|
28
|
+
log: [
|
|
29
|
+
{
|
|
30
|
+
emit: 'event',
|
|
31
|
+
level: 'query',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
emit: 'event',
|
|
35
|
+
level: 'error',
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
});
|
|
39
|
+
this.$on('query', (e) => {
|
|
40
|
+
if (isQueryEvent(e)) {
|
|
41
|
+
core_2.default.debug(`Query: ${e.query}`);
|
|
42
|
+
core_2.default.debug(`Params: ${e.params}`);
|
|
43
|
+
core_2.default.debug(`Duration: ${e.duration}ms`);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
this.$on('error', (e) => {
|
|
47
|
+
if (isErrorEvent(e)) {
|
|
48
|
+
core_2.default.error('Prisma Error:', e);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
async onModuleInit() {
|
|
53
|
+
try {
|
|
54
|
+
await this.$connect();
|
|
55
|
+
core_2.default.info('Connected to database');
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
core_2.default.error('Failed to connect to database:', error);
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async onModuleDestroy() {
|
|
63
|
+
try {
|
|
64
|
+
await this.$disconnect();
|
|
65
|
+
core_2.default.info('Disconnected from database');
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
core_2.default.error('Error disconnecting from database:', error);
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
exports.PrismaService = PrismaService;
|
|
74
|
+
exports.PrismaService = PrismaService = __decorate([
|
|
75
|
+
(0, core_1.Injectable)(),
|
|
76
|
+
__metadata("design:paramtypes", [])
|
|
77
|
+
], PrismaService);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma.test.d.ts","sourceRoot":"","sources":["../src/prisma.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/// <reference types="jest" />
|
|
3
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
4
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
5
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
6
|
+
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;
|
|
7
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
8
|
+
};
|
|
9
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
10
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
jest.mock('@hazeljs/core', () => ({
|
|
14
|
+
__esModule: true,
|
|
15
|
+
Injectable: () => () => undefined,
|
|
16
|
+
HazelModule: () => () => undefined,
|
|
17
|
+
Module: () => () => undefined,
|
|
18
|
+
RepositoryOptions: class {
|
|
19
|
+
},
|
|
20
|
+
logger: { info: jest.fn(), debug: jest.fn(), warn: jest.fn(), error: jest.fn() },
|
|
21
|
+
default: { info: jest.fn(), debug: jest.fn(), warn: jest.fn(), error: jest.fn() },
|
|
22
|
+
}));
|
|
23
|
+
const core_1 = __importDefault(require("@hazeljs/core"));
|
|
24
|
+
const prisma_service_1 = require("./prisma.service");
|
|
25
|
+
const base_repository_1 = require("./base.repository");
|
|
26
|
+
const repository_decorator_1 = require("./repository.decorator");
|
|
27
|
+
const library_1 = require("./__mocks__/@prisma/client/runtime/library");
|
|
28
|
+
// ─── PrismaService ────────────────────────────────────────────────────────────
|
|
29
|
+
describe('PrismaService', () => {
|
|
30
|
+
let service;
|
|
31
|
+
let $onMock;
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
service = new prisma_service_1.PrismaService();
|
|
34
|
+
$onMock = service.$on;
|
|
35
|
+
});
|
|
36
|
+
it('instantiates without error', () => {
|
|
37
|
+
expect(service).toBeDefined();
|
|
38
|
+
});
|
|
39
|
+
it('registers $on handler for query events', () => {
|
|
40
|
+
expect($onMock).toHaveBeenCalledWith('query', expect.any(Function));
|
|
41
|
+
});
|
|
42
|
+
it('registers $on handler for error events', () => {
|
|
43
|
+
expect($onMock).toHaveBeenCalledWith('error', expect.any(Function));
|
|
44
|
+
});
|
|
45
|
+
it('query event callback logs valid query events', () => {
|
|
46
|
+
const queryCallback = $onMock.mock.calls.find(([evt]) => evt === 'query')?.[1];
|
|
47
|
+
expect(queryCallback).toBeDefined();
|
|
48
|
+
queryCallback({ query: 'SELECT 1', params: '[]', duration: 5 });
|
|
49
|
+
expect(core_1.default.debug).toHaveBeenCalled();
|
|
50
|
+
});
|
|
51
|
+
it('query event callback ignores non-query events', () => {
|
|
52
|
+
const queryCallback = $onMock.mock.calls.find(([evt]) => evt === 'query')?.[1];
|
|
53
|
+
jest.clearAllMocks();
|
|
54
|
+
queryCallback({ message: 'not a query event' });
|
|
55
|
+
expect(core_1.default.debug).not.toHaveBeenCalled();
|
|
56
|
+
});
|
|
57
|
+
it('error event callback logs prisma error events', () => {
|
|
58
|
+
const errorCallback = $onMock.mock.calls.find(([evt]) => evt === 'error')?.[1];
|
|
59
|
+
expect(errorCallback).toBeDefined();
|
|
60
|
+
errorCallback({ message: 'some error' });
|
|
61
|
+
expect(core_1.default.error).toHaveBeenCalled();
|
|
62
|
+
});
|
|
63
|
+
it('error event callback ignores events without message', () => {
|
|
64
|
+
const errorCallback = $onMock.mock.calls.find(([evt]) => evt === 'error')?.[1];
|
|
65
|
+
jest.clearAllMocks();
|
|
66
|
+
errorCallback({ query: 'SELECT 1', params: '[]', duration: 2 });
|
|
67
|
+
expect(core_1.default.error).not.toHaveBeenCalled();
|
|
68
|
+
});
|
|
69
|
+
it('onModuleInit calls $connect and resolves', async () => {
|
|
70
|
+
await expect(service.onModuleInit()).resolves.toBeUndefined();
|
|
71
|
+
expect(service.$connect).toHaveBeenCalled();
|
|
72
|
+
});
|
|
73
|
+
it('onModuleInit throws and logs when $connect rejects', async () => {
|
|
74
|
+
service.$connect.mockRejectedValueOnce(new Error('connection refused'));
|
|
75
|
+
await expect(service.onModuleInit()).rejects.toThrow('connection refused');
|
|
76
|
+
});
|
|
77
|
+
it('onModuleDestroy calls $disconnect and resolves', async () => {
|
|
78
|
+
await expect(service.onModuleDestroy()).resolves.toBeUndefined();
|
|
79
|
+
expect(service.$disconnect).toHaveBeenCalled();
|
|
80
|
+
});
|
|
81
|
+
it('onModuleDestroy throws and logs when $disconnect rejects', async () => {
|
|
82
|
+
service.$disconnect.mockRejectedValueOnce(new Error('disconnect failed'));
|
|
83
|
+
await expect(service.onModuleDestroy()).rejects.toThrow('disconnect failed');
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
function buildMockDelegate() {
|
|
87
|
+
return {
|
|
88
|
+
findMany: jest.fn(),
|
|
89
|
+
findUnique: jest.fn(),
|
|
90
|
+
create: jest.fn(),
|
|
91
|
+
update: jest.fn(),
|
|
92
|
+
delete: jest.fn(),
|
|
93
|
+
count: jest.fn(),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
class TestRepository extends base_repository_1.BaseRepository {
|
|
97
|
+
constructor(prisma) {
|
|
98
|
+
super(prisma, 'testModel');
|
|
99
|
+
}
|
|
100
|
+
// Expose protected handleError for testing
|
|
101
|
+
testHandleError(error) {
|
|
102
|
+
return this.handleError(error);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function buildRepo() {
|
|
106
|
+
const delegate = buildMockDelegate();
|
|
107
|
+
const mockPrisma = {
|
|
108
|
+
$on: jest.fn(),
|
|
109
|
+
$connect: jest.fn().mockResolvedValue(undefined),
|
|
110
|
+
$disconnect: jest.fn().mockResolvedValue(undefined),
|
|
111
|
+
testModel: delegate,
|
|
112
|
+
};
|
|
113
|
+
const repo = new TestRepository(mockPrisma);
|
|
114
|
+
return { repo, delegate };
|
|
115
|
+
}
|
|
116
|
+
describe('BaseRepository', () => {
|
|
117
|
+
describe('findMany()', () => {
|
|
118
|
+
it('returns all records', async () => {
|
|
119
|
+
const { repo, delegate } = buildRepo();
|
|
120
|
+
const records = [
|
|
121
|
+
{ id: 1, name: 'Alice' },
|
|
122
|
+
{ id: 2, name: 'Bob' },
|
|
123
|
+
];
|
|
124
|
+
delegate.findMany.mockResolvedValue(records);
|
|
125
|
+
await expect(repo.findMany()).resolves.toEqual(records);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
describe('findOne()', () => {
|
|
129
|
+
it('returns matching record', async () => {
|
|
130
|
+
const { repo, delegate } = buildRepo();
|
|
131
|
+
const record = { id: 1, name: 'Alice' };
|
|
132
|
+
delegate.findUnique.mockResolvedValue(record);
|
|
133
|
+
await expect(repo.findOne({ id: 1 })).resolves.toEqual(record);
|
|
134
|
+
expect(delegate.findUnique).toHaveBeenCalledWith({ where: { id: 1 } });
|
|
135
|
+
});
|
|
136
|
+
it('returns null when not found', async () => {
|
|
137
|
+
const { repo, delegate } = buildRepo();
|
|
138
|
+
delegate.findUnique.mockResolvedValue(null);
|
|
139
|
+
await expect(repo.findOne({ id: 99 })).resolves.toBeNull();
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
describe('create()', () => {
|
|
143
|
+
it('creates and returns a record', async () => {
|
|
144
|
+
const { repo, delegate } = buildRepo();
|
|
145
|
+
const created = { id: 3, name: 'Charlie' };
|
|
146
|
+
delegate.create.mockResolvedValue(created);
|
|
147
|
+
await expect(repo.create({ name: 'Charlie' })).resolves.toEqual(created);
|
|
148
|
+
expect(delegate.create).toHaveBeenCalledWith({ data: { name: 'Charlie' } });
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
describe('update()', () => {
|
|
152
|
+
it('updates and returns the updated record', async () => {
|
|
153
|
+
const { repo, delegate } = buildRepo();
|
|
154
|
+
const updated = { id: 1, name: 'Updated' };
|
|
155
|
+
delegate.update.mockResolvedValue(updated);
|
|
156
|
+
await expect(repo.update({ id: 1 }, { name: 'Updated' })).resolves.toEqual(updated);
|
|
157
|
+
expect(delegate.update).toHaveBeenCalledWith({ where: { id: 1 }, data: { name: 'Updated' } });
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
describe('delete()', () => {
|
|
161
|
+
it('deletes and returns the deleted record', async () => {
|
|
162
|
+
const { repo, delegate } = buildRepo();
|
|
163
|
+
const deleted = { id: 1, name: 'Alice' };
|
|
164
|
+
delegate.delete.mockResolvedValue(deleted);
|
|
165
|
+
await expect(repo.delete({ id: 1 })).resolves.toEqual(deleted);
|
|
166
|
+
expect(delegate.delete).toHaveBeenCalledWith({ where: { id: 1 } });
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
describe('count()', () => {
|
|
170
|
+
it('returns record count', async () => {
|
|
171
|
+
const { repo, delegate } = buildRepo();
|
|
172
|
+
delegate.count.mockResolvedValue(5);
|
|
173
|
+
await expect(repo.count()).resolves.toBe(5);
|
|
174
|
+
});
|
|
175
|
+
it('passes args to count', async () => {
|
|
176
|
+
const { repo, delegate } = buildRepo();
|
|
177
|
+
delegate.count.mockResolvedValue(2);
|
|
178
|
+
const args = { where: { name: 'Alice' } };
|
|
179
|
+
await expect(repo.count(args)).resolves.toBe(2);
|
|
180
|
+
expect(delegate.count).toHaveBeenCalledWith(args);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
describe('prismaClient / modelDelegate getters', () => {
|
|
184
|
+
it('prismaClient returns the prisma instance', () => {
|
|
185
|
+
const { repo } = buildRepo();
|
|
186
|
+
expect(repo.prismaClient).toBeDefined();
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
describe('handleError()', () => {
|
|
190
|
+
it('throws unique constraint error for P2002 with target', () => {
|
|
191
|
+
const { repo } = buildRepo();
|
|
192
|
+
const err = new library_1.PrismaClientKnownRequestError('unique', {
|
|
193
|
+
code: 'P2002',
|
|
194
|
+
meta: { target: ['email', 'username'] },
|
|
195
|
+
});
|
|
196
|
+
expect(() => repo.testHandleError(err)).toThrow('Unique constraint violation on fields: email, username');
|
|
197
|
+
});
|
|
198
|
+
it('throws unique constraint error for P2002 without target', () => {
|
|
199
|
+
const { repo } = buildRepo();
|
|
200
|
+
const err = new library_1.PrismaClientKnownRequestError('unique', { code: 'P2002' });
|
|
201
|
+
expect(() => repo.testHandleError(err)).toThrow('Unique constraint violation on field');
|
|
202
|
+
});
|
|
203
|
+
it('throws not found error for P2025', () => {
|
|
204
|
+
const { repo } = buildRepo();
|
|
205
|
+
const err = new library_1.PrismaClientKnownRequestError('not found', { code: 'P2025' });
|
|
206
|
+
expect(() => repo.testHandleError(err)).toThrow('Record not found');
|
|
207
|
+
});
|
|
208
|
+
it('throws foreign key error for P2003', () => {
|
|
209
|
+
const { repo } = buildRepo();
|
|
210
|
+
const err = new library_1.PrismaClientKnownRequestError('fk', { code: 'P2003' });
|
|
211
|
+
expect(() => repo.testHandleError(err)).toThrow('Foreign key constraint violation');
|
|
212
|
+
});
|
|
213
|
+
it('throws generic db error for unknown prisma code', () => {
|
|
214
|
+
const { repo } = buildRepo();
|
|
215
|
+
const err = new library_1.PrismaClientKnownRequestError('unknown', { code: 'P9999' });
|
|
216
|
+
expect(() => repo.testHandleError(err)).toThrow('Database error:');
|
|
217
|
+
});
|
|
218
|
+
it('throws wrapped error for non-prisma errors', () => {
|
|
219
|
+
const { repo } = buildRepo();
|
|
220
|
+
expect(() => repo.testHandleError(new Error('generic error'))).toThrow('Database error: generic error');
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
// ─── Repository decorator ─────────────────────────────────────────────────────
|
|
225
|
+
describe('@Repository decorator', () => {
|
|
226
|
+
it('sets hazel:repository metadata with string shorthand', () => {
|
|
227
|
+
let UserRepo = class UserRepo {
|
|
228
|
+
};
|
|
229
|
+
UserRepo = __decorate([
|
|
230
|
+
(0, repository_decorator_1.Repository)('user')
|
|
231
|
+
], UserRepo);
|
|
232
|
+
const meta = Reflect.getMetadata('hazel:repository', UserRepo);
|
|
233
|
+
expect(meta).toEqual({ model: 'user' });
|
|
234
|
+
});
|
|
235
|
+
it('sets hazel:repository metadata with options object', () => {
|
|
236
|
+
let PostRepo = class PostRepo {
|
|
237
|
+
};
|
|
238
|
+
PostRepo = __decorate([
|
|
239
|
+
(0, repository_decorator_1.Repository)({ model: 'post' })
|
|
240
|
+
], PostRepo);
|
|
241
|
+
const meta = Reflect.getMetadata('hazel:repository', PostRepo);
|
|
242
|
+
expect(meta).toEqual({ model: 'post' });
|
|
243
|
+
});
|
|
244
|
+
it('sets hazel:injectable metadata', () => {
|
|
245
|
+
let CommentRepo = class CommentRepo {
|
|
246
|
+
};
|
|
247
|
+
CommentRepo = __decorate([
|
|
248
|
+
(0, repository_decorator_1.Repository)('comment')
|
|
249
|
+
], CommentRepo);
|
|
250
|
+
const injectable = Reflect.getMetadata('hazel:injectable', CommentRepo);
|
|
251
|
+
expect(injectable).toEqual({});
|
|
252
|
+
});
|
|
253
|
+
it('sets hazel:scope metadata when scope is provided', () => {
|
|
254
|
+
let SessionRepo = class SessionRepo {
|
|
255
|
+
};
|
|
256
|
+
SessionRepo = __decorate([
|
|
257
|
+
(0, repository_decorator_1.Repository)({ model: 'session', scope: 'REQUEST' })
|
|
258
|
+
], SessionRepo);
|
|
259
|
+
const scope = Reflect.getMetadata('hazel:scope', SessionRepo);
|
|
260
|
+
expect(scope).toBe('REQUEST');
|
|
261
|
+
});
|
|
262
|
+
it('sets hazel:injectable metadata with scope when scope is provided', () => {
|
|
263
|
+
let LogRepo = class LogRepo {
|
|
264
|
+
};
|
|
265
|
+
LogRepo = __decorate([
|
|
266
|
+
(0, repository_decorator_1.Repository)({ model: 'log', scope: 'TRANSIENT' })
|
|
267
|
+
], LogRepo);
|
|
268
|
+
const injectable = Reflect.getMetadata('hazel:injectable', LogRepo);
|
|
269
|
+
expect(injectable).toEqual({ scope: 'TRANSIENT' });
|
|
270
|
+
});
|
|
271
|
+
});
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { RepositoryOptions } from '@hazeljs/core';
|
|
2
|
+
import 'reflect-metadata';
|
|
3
|
+
export declare function Repository(options: RepositoryOptions | string): ClassDecorator;
|
|
4
|
+
export declare function InjectRepository(): ParameterDecorator;
|
|
5
|
+
//# sourceMappingURL=repository.decorator.d.ts.map
|
|
@@ -0,0 +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,CAW9E;AAED,wBAAgB,gBAAgB,IAAI,kBAAkB,CA0BrD"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Repository = Repository;
|
|
4
|
+
exports.InjectRepository = InjectRepository;
|
|
5
|
+
require("reflect-metadata");
|
|
6
|
+
function Repository(options) {
|
|
7
|
+
return function (target) {
|
|
8
|
+
const opts = typeof options === 'string' ? { model: options } : options;
|
|
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
|
+
}
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function InjectRepository() {
|
|
19
|
+
return function (target, propertyKey, parameterIndex) {
|
|
20
|
+
if (!propertyKey) {
|
|
21
|
+
throw new Error('InjectRepository decorator must be used on a method parameter');
|
|
22
|
+
}
|
|
23
|
+
const repositoryType = Reflect.getMetadata('design:paramtypes', target, propertyKey)[parameterIndex];
|
|
24
|
+
const model = Reflect.getMetadata('hazel:repository:model', repositoryType);
|
|
25
|
+
if (!model) {
|
|
26
|
+
throw new Error(`Repository ${repositoryType.name} is not decorated with @Repository`);
|
|
27
|
+
}
|
|
28
|
+
const repositories = Reflect.getMetadata('hazel:repositories', target) || [];
|
|
29
|
+
repositories.push({
|
|
30
|
+
index: parameterIndex,
|
|
31
|
+
model,
|
|
32
|
+
});
|
|
33
|
+
Reflect.defineMetadata('hazel:repositories', repositories, target);
|
|
34
|
+
};
|
|
35
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hazeljs/prisma",
|
|
3
|
+
"version": "0.2.0-alpha.1",
|
|
4
|
+
"description": "Prisma ORM integration for HazelJS framework",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"test": "jest --coverage --passWithNoTests",
|
|
13
|
+
"lint": "eslint \"src/**/*.ts\"",
|
|
14
|
+
"lint:fix": "eslint \"src/**/*.ts\" --fix",
|
|
15
|
+
"clean": "rm -rf dist"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@prisma/client": "^6.8.2"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/node": "^20.17.50",
|
|
22
|
+
"@typescript-eslint/eslint-plugin": "^8.18.2",
|
|
23
|
+
"@typescript-eslint/parser": "^8.18.2",
|
|
24
|
+
"eslint": "^8.56.0",
|
|
25
|
+
"jest": "^29.7.0",
|
|
26
|
+
"prisma": "^6.8.2",
|
|
27
|
+
"ts-jest": "^29.1.2",
|
|
28
|
+
"typescript": "^5.3.3"
|
|
29
|
+
},
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/hazel-js/hazeljs.git",
|
|
36
|
+
"directory": "packages/prisma"
|
|
37
|
+
},
|
|
38
|
+
"keywords": [
|
|
39
|
+
"hazeljs",
|
|
40
|
+
"prisma",
|
|
41
|
+
"orm",
|
|
42
|
+
"database"
|
|
43
|
+
],
|
|
44
|
+
"author": "Muhammad Arslan <muhammad.arslan@hazeljs.com>",
|
|
45
|
+
"license": "Apache-2.0",
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/hazeljs/hazel-js/issues"
|
|
48
|
+
},
|
|
49
|
+
"homepage": "https://hazeljs.com",
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"@hazeljs/core": ">=0.2.0-beta.0"
|
|
52
|
+
},
|
|
53
|
+
"gitHead": "cbc5ee2c12ced28fd0576faf13c5f078c1e8421e"
|
|
54
|
+
}
|