From 76869f894232dd8f64438cdc15f92a38b454821f Mon Sep 17 00:00:00 2001 From: Bruce Date: Tue, 23 Jul 2024 15:55:20 +0800 Subject: [PATCH] feat(prisma): :sparkles: add `@bit-ocean/prisma` --- packages/prisma/.gitkeep | 0 packages/prisma/README.md | 16 ++ packages/prisma/package.json | 66 +++++++ packages/prisma/prisma/schema.prisma | 13 ++ packages/prisma/src/basic/index.ts | 7 + .../basic/prisma-client-exception.filter.ts | 106 ++++++++++ .../src/basic/prisma-logging.middleware.ts | 68 +++++++ packages/prisma/src/basic/prisma.constants.ts | 1 + .../prisma/src/basic/prisma.interfaces.ts | 50 +++++ packages/prisma/src/basic/prisma.module.ts | 91 +++++++++ packages/prisma/src/basic/prisma.service.ts | 34 ++++ .../prisma/src/basic/soft-delete.extension.ts | 19 ++ .../src/custom/custom-prisma.constants.ts | 1 + .../src/custom/custom-prisma.interfaces.ts | 63 ++++++ .../prisma/src/custom/custom-prisma.module.ts | 82 ++++++++ .../src/custom/custom-prisma.service.ts | 12 ++ packages/prisma/src/custom/index.ts | 4 + packages/prisma/src/index.ts | 2 + packages/prisma/tsconfig.json | 7 + packages/prisma/tsup.config.ts | 15 ++ pnpm-lock.yaml | 181 ++++++++++++++++++ 21 files changed, 838 insertions(+) delete mode 100644 packages/prisma/.gitkeep create mode 100644 packages/prisma/README.md create mode 100644 packages/prisma/package.json create mode 100644 packages/prisma/prisma/schema.prisma create mode 100644 packages/prisma/src/basic/index.ts create mode 100644 packages/prisma/src/basic/prisma-client-exception.filter.ts create mode 100644 packages/prisma/src/basic/prisma-logging.middleware.ts create mode 100644 packages/prisma/src/basic/prisma.constants.ts create mode 100644 packages/prisma/src/basic/prisma.interfaces.ts create mode 100644 packages/prisma/src/basic/prisma.module.ts create mode 100644 packages/prisma/src/basic/prisma.service.ts create mode 100644 packages/prisma/src/basic/soft-delete.extension.ts create mode 100644 packages/prisma/src/custom/custom-prisma.constants.ts create mode 100644 packages/prisma/src/custom/custom-prisma.interfaces.ts create mode 100644 packages/prisma/src/custom/custom-prisma.module.ts create mode 100644 packages/prisma/src/custom/custom-prisma.service.ts create mode 100644 packages/prisma/src/custom/index.ts create mode 100644 packages/prisma/src/index.ts create mode 100644 packages/prisma/tsconfig.json create mode 100644 packages/prisma/tsup.config.ts diff --git a/packages/prisma/.gitkeep b/packages/prisma/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/packages/prisma/README.md b/packages/prisma/README.md new file mode 100644 index 0000000..ec1d257 --- /dev/null +++ b/packages/prisma/README.md @@ -0,0 +1,16 @@ +# @bit-ocean/prisma + +![npm](https://img.shields.io/npm/v/@bit-ocean/prisma?logo=prisma&label=prisma) +[![Made with Prisma](http://made-with.prisma.io/dark.svg)](https://prisma.io) + +> Universal Prisma module. + +## Installation + +```bash +pnpm add @bit-ocean/prisma +``` + +## License + +[MIT](/LICENSE) License © 2024 Bit Ocean diff --git a/packages/prisma/package.json b/packages/prisma/package.json new file mode 100644 index 0000000..b835035 --- /dev/null +++ b/packages/prisma/package.json @@ -0,0 +1,66 @@ +{ + "name": "@bit-ocean/prisma", + "version": "0.0.0", + "description": "Universal Prisma module.", + "author": "Bruce Song (https://github.com/recallwei/)", + "homepage": "https://github.com/bit-ocean-studio/infra#readme", + "bugs": "https://github.com/bit-ocean-studio/infra/issues", + "repository": { + "type": "git", + "url": "https://github.com/bit-ocean-studio/infra.git", + "directory": "packages/prisma" + }, + "keywords": [ + "bit-ocean", + "bit-ocean-infra", + "prisma", + "prisma-module", + "nest-prisma", + "nest-prisma-module" + ], + "files": [ + "dist" + ], + "main": "dist/index.js", + "module": "dist/index.mjs", + "types": "dist/index.d.ts", + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.js", + "types": "./dist/index.d.ts" + } + }, + "scripts": { + "dev": "tsup --watch", + "build": "tsup", + "prebuild": "pnpm prisma generate", + "type:check": "tsc --pretty --noEmit", + "cspell:check": "cspell --no-progress --show-suggestions --show-context --cache **", + "eslint:check": "eslint . --color --cache", + "eslint:fix": "eslint . --color --cache --fix", + "prettier:check": "prettier --check --cache --ignore-unknown --ignore-path=../../.prettierignore .", + "prettier:fix": "prettier --write --cache --ignore-unknown --ignore-path=../../.prettierignore ." + }, + "peerDependencies": { + "@nestjs/common": "^9.0.0 || ^10.0.0", + "@nestjs/core": "^9.0.0 || ^10.0.0", + "@prisma/client": "^5.0.0", + "prisma": "^5.0.0", + "reflect-metadata": "^0.2.2" + }, + "devDependencies": { + "@nestjs/common": "^10.3.10", + "@nestjs/core": "^10.3.10", + "@prisma/client": "^5.17.0", + "prisma": "^5.17.0", + "reflect-metadata": "^0.2.2" + }, + "prisma": { + "schema": "prisma/schema.prisma" + }, + "publishConfig": { + "access": "public" + }, + "license": "MIT" +} diff --git a/packages/prisma/prisma/schema.prisma b/packages/prisma/prisma/schema.prisma new file mode 100644 index 0000000..60fc26a --- /dev/null +++ b/packages/prisma/prisma/schema.prisma @@ -0,0 +1,13 @@ +datasource db { + provider = "sqlite" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} + +model User { + id Int @id @default(autoincrement()) + name String? +} diff --git a/packages/prisma/src/basic/index.ts b/packages/prisma/src/basic/index.ts new file mode 100644 index 0000000..2ec2ec4 --- /dev/null +++ b/packages/prisma/src/basic/index.ts @@ -0,0 +1,7 @@ +export * from './prisma.constants' +export * from './prisma.interfaces' +export * from './prisma.module' +export * from './prisma.service' +export * from './prisma-client-exception.filter' +export * from './prisma-logging.middleware' +export * from './soft-delete.extension' diff --git a/packages/prisma/src/basic/prisma-client-exception.filter.ts b/packages/prisma/src/basic/prisma-client-exception.filter.ts new file mode 100644 index 0000000..8d08c20 --- /dev/null +++ b/packages/prisma/src/basic/prisma-client-exception.filter.ts @@ -0,0 +1,106 @@ +import { ArgumentsHost, Catch, HttpException, HttpServer, HttpStatus } from '@nestjs/common' +import { APP_FILTER, BaseExceptionFilter, HttpAdapterHost } from '@nestjs/core' +import { Prisma } from '@prisma/client' + +export declare type GqlContextType = 'graphql' + +export type ErrorCodesStatusMapping = { + [key: string]: + | number + | { + statusCode?: number + errorMessage?: string + } +} + +@Catch(Prisma?.PrismaClientKnownRequestError) +export class PrismaClientExceptionFilter extends BaseExceptionFilter { + /** + * Default error codes mapping. + * Error codes definition for Prisma Client (Query Engine). + * @see https://www.prisma.io/docs/reference/api-reference/error-reference#prisma-client-query-engine + */ + private readonly defaultMapping = new Map([ + ['P2000', HttpStatus.BAD_REQUEST], + ['P2002', HttpStatus.CONFLICT], + ['P2025', HttpStatus.NOT_FOUND] + ]) + + private readonly userDefinedMapping?: ErrorCodesStatusMapping + + /** + * @param applicationRef + * @param errorCodesStatusMapping + */ + constructor(applicationRef?: HttpServer, errorCodesStatusMapping?: ErrorCodesStatusMapping) { + super(applicationRef) + this.userDefinedMapping = errorCodesStatusMapping + } + + catch(exception: Prisma.PrismaClientKnownRequestError, host: ArgumentsHost) { + return this.catchClientKnownRequestError(exception, host) + } + + private catchClientKnownRequestError( + exception: Prisma.PrismaClientKnownRequestError, + host: ArgumentsHost + ) { + const statusCode = this.userDefinedStatusCode(exception) ?? this.defaultStatusCode(exception) + + const message = + this.userDefinedExceptionMessage(exception) ?? this.defaultExceptionMessage(exception) + + if (host.getType() === 'http') { + if (statusCode === undefined) { + return super.catch(exception, host) + } + + return super.catch(new HttpException({ statusCode, message }, statusCode), host) + } + if (host.getType() === 'graphql') { + if (statusCode === undefined) { + return exception + } + + return new HttpException({ statusCode, message }, statusCode) + } + return super.catch(exception, host) + } + + private userDefinedStatusCode( + exception: Prisma.PrismaClientKnownRequestError + ): number | undefined { + const userDefinedValue = this.userDefinedMapping?.[exception.code] + return typeof userDefinedValue === 'number' ? userDefinedValue : userDefinedValue?.statusCode + } + + private defaultStatusCode(exception: Prisma.PrismaClientKnownRequestError): number | undefined { + return this.defaultMapping.get(exception.code) + } + + private userDefinedExceptionMessage( + exception: Prisma.PrismaClientKnownRequestError + ): string | undefined { + const userDefinedValue = this.userDefinedMapping?.[exception.code] + return typeof userDefinedValue === 'number' ? undefined : userDefinedValue?.errorMessage + } + + private defaultExceptionMessage(exception: Prisma.PrismaClientKnownRequestError): string { + const shortMessage = exception.message.substring(exception.message.indexOf('→')) + return `[${exception.code}]: ${shortMessage + .substring(shortMessage.indexOf('\n')) + .replace(/\n/g, '') + .trim()}` + } +} + +export function providePrismaClientExceptionFilter( + errorCodesStatusMapping?: ErrorCodesStatusMapping +) { + return { + provide: APP_FILTER, + useFactory: ({ httpAdapter }: HttpAdapterHost) => + new PrismaClientExceptionFilter(httpAdapter, errorCodesStatusMapping), + inject: [HttpAdapterHost] + } +} diff --git a/packages/prisma/src/basic/prisma-logging.middleware.ts b/packages/prisma/src/basic/prisma-logging.middleware.ts new file mode 100644 index 0000000..ad8aff1 --- /dev/null +++ b/packages/prisma/src/basic/prisma-logging.middleware.ts @@ -0,0 +1,68 @@ +import type { Logger } from '@nestjs/common' +import type { Prisma } from '@prisma/client' + +export interface LoggingMiddlewareOptions { + logger: Console | Logger + logLevel: 'log' | 'debug' | 'warn' | 'error' + /** + * Create a custom log message. + */ + logMessage?: (query: QueryInfo) => string +} + +export interface QueryInfo { + /** + * The queried prisma model. + */ + model: string + /** + * The performed action on the model e.g. `create`, `findUnique`. + */ + action: string + /** + * Time `Date.now()` before the query execution. + * + */ + before: number + /** + * Time `Date.now()` after the query execution. + */ + after: number + /** + * Execution time of the query in milliseconds. + */ + executionTime: number +} + +export function loggingMiddleware( + { logger, logMessage, logLevel }: LoggingMiddlewareOptions = { + logger: console, + logLevel: 'debug' + } +): Prisma.Middleware { + return async (params, next) => { + const before = Date.now() + + const result = await next(params) + + const after = Date.now() + + const executionTime = after - before + + if (logMessage) { + logger[logLevel]( + logMessage({ + model: params.model!, + action: params.action, + before, + after, + executionTime + }) + ) + } else { + logger[logLevel](`Prisma Query ${params.model}.${params.action} took ${executionTime}ms`) + } + + return result + } +} diff --git a/packages/prisma/src/basic/prisma.constants.ts b/packages/prisma/src/basic/prisma.constants.ts new file mode 100644 index 0000000..8ad6a03 --- /dev/null +++ b/packages/prisma/src/basic/prisma.constants.ts @@ -0,0 +1 @@ +export const PRISMA_SERVICE_OPTIONS = 'PRISMA_SERVICE_OPTIONS' diff --git a/packages/prisma/src/basic/prisma.interfaces.ts b/packages/prisma/src/basic/prisma.interfaces.ts new file mode 100644 index 0000000..b86fa20 --- /dev/null +++ b/packages/prisma/src/basic/prisma.interfaces.ts @@ -0,0 +1,50 @@ +import type { ModuleMetadata, Type } from '@nestjs/common' +import type { Prisma } from '@prisma/client' + +export interface PrismaModuleOptions { + /** + * If `true`, registers `PrismaModule` as a global module. + * @See https://docs.nestjs.com/modules#global-modules + */ + isGlobal?: boolean + /** + * Options for `PrismaService`. + */ + prismaServiceOptions?: PrismaServiceOptions +} + +export interface PrismaServiceOptions { + /** + * Pass options directly to the `PrismaClient`. + * @see https://www.prisma.io/docs/reference/api-reference/prisma-client-reference/#prismaclient + */ + prismaOptions?: Prisma.PrismaClientOptions + /** + * If `true`, the `PrismaClient` explicitly creates a connection pool and your first query will respond instantly. + * For most use cases the lazy connect behavior of `PrismaClient` will do. The first query of `PrismaClient` creates the connection pool. + * @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-prismaclient/connection-management + */ + explicitConnect?: boolean + /** + * Apply Prisma middlewares to perform actions before or after db queries. + * @deprecated + * @see https://www.prisma.io/docs/orm/prisma-client/client-extensions/middleware + */ + middlewares?: Prisma.Middleware[] +} + +export interface PrismaOptionsFactory { + createPrismaOptions: () => PrismaServiceOptions | Promise +} + +export interface PrismaModuleAsyncOptions extends Pick { + /** + * If `true`, registers `PrismaModule` as a global module. + * @See https://docs.nestjs.com/modules#global-modules + */ + isGlobal?: boolean + useExisting?: Type + useClass?: Type + useFactory?: (...args: any[]) => PrismaServiceOptions | Promise + inject?: any[] +} diff --git a/packages/prisma/src/basic/prisma.module.ts b/packages/prisma/src/basic/prisma.module.ts new file mode 100644 index 0000000..a05840d --- /dev/null +++ b/packages/prisma/src/basic/prisma.module.ts @@ -0,0 +1,91 @@ +import { DynamicModule, Module, Provider } from '@nestjs/common' + +import { PRISMA_SERVICE_OPTIONS } from './prisma.constants' +import { + PrismaModuleAsyncOptions, + PrismaModuleOptions, + PrismaOptionsFactory +} from './prisma.interfaces' +import { PrismaService } from './prisma.service' + +@Module({ + providers: [PrismaService], + exports: [PrismaService] +}) +export class PrismaModule { + static forRoot(options: PrismaModuleOptions = {}): DynamicModule { + return { + global: options.isGlobal, + module: PrismaModule, + providers: [ + { + provide: PRISMA_SERVICE_OPTIONS, + useValue: options.prismaServiceOptions + } + ] + } + } + + static forRootAsync(options: PrismaModuleAsyncOptions): DynamicModule { + return { + global: options.isGlobal, + module: PrismaModule, + imports: options.imports ?? [], + providers: this.createAsyncProviders(options) + } + } + + private static createAsyncProviders(options: PrismaModuleAsyncOptions): Provider[] { + if (options.useExisting || options.useFactory) { + return this.createAsyncOptionsProvider(options) + } + + return [ + ...this.createAsyncOptionsProvider(options), + ...(options.useClass + ? [ + { + provide: options.useClass, + useClass: options.useClass + } + ] + : []) + ] + } + + private static createAsyncOptionsProvider(options: PrismaModuleAsyncOptions): Provider[] { + if (options.useFactory) { + return [ + { + provide: PRISMA_SERVICE_OPTIONS, + useFactory: options.useFactory, + inject: options.inject ?? [] + } + ] + } + + if (options.useExisting) { + return [ + { + provide: PRISMA_SERVICE_OPTIONS, + useFactory: async (optionsFactory: PrismaOptionsFactory) => + optionsFactory.createPrismaOptions(), + inject: [options.useExisting] + } + ] + } + + if (options.useClass) { + return [ + { + provide: PRISMA_SERVICE_OPTIONS, + useFactory: async (optionsFactory: PrismaOptionsFactory) => + optionsFactory.createPrismaOptions(), + inject: [options.useClass] + } + ] + } + + return [] + } +} diff --git a/packages/prisma/src/basic/prisma.service.ts b/packages/prisma/src/basic/prisma.service.ts new file mode 100644 index 0000000..5b94665 --- /dev/null +++ b/packages/prisma/src/basic/prisma.service.ts @@ -0,0 +1,34 @@ +import { Inject, Injectable, type OnModuleDestroy, OnModuleInit, Optional } from '@nestjs/common' +import { Prisma, PrismaClient } from '@prisma/client' + +import { PRISMA_SERVICE_OPTIONS } from './prisma.constants' +import { type PrismaServiceOptions } from './prisma.interfaces' + +@Injectable() +export class PrismaService + extends PrismaClient + implements OnModuleInit, OnModuleDestroy +{ + constructor( + @Optional() + @Inject(PRISMA_SERVICE_OPTIONS) + private readonly options: PrismaServiceOptions = {} + ) { + super(options.prismaOptions) + + if (this.options.middlewares) { + this.options.middlewares.forEach((middleware) => this.$use(middleware)) + } + } + + async onModuleInit() { + if (this.options.explicitConnect) { + await this.$connect() + } + } + + // NOTE: Use `onModuleDestroy` to ensure that the database is disconnected when the application is shut down. + async onModuleDestroy() { + await this.$disconnect() + } +} diff --git a/packages/prisma/src/basic/soft-delete.extension.ts b/packages/prisma/src/basic/soft-delete.extension.ts new file mode 100644 index 0000000..7f334eb --- /dev/null +++ b/packages/prisma/src/basic/soft-delete.extension.ts @@ -0,0 +1,19 @@ +import { Prisma } from '@prisma/client' + +export const softDeleteExtension = Prisma.defineExtension({ + name: 'soft-delete', + model: { + $allModels: { + async softDelete(this: T, id: number, _args?: Prisma.Args['where']) { + const context = Prisma.getExtensionContext(this) + const result = await (context as any).update({ + where: { id }, + data: { + deletedAt: new Date() + } + }) + return result + } + } + } +}) diff --git a/packages/prisma/src/custom/custom-prisma.constants.ts b/packages/prisma/src/custom/custom-prisma.constants.ts new file mode 100644 index 0000000..c44f091 --- /dev/null +++ b/packages/prisma/src/custom/custom-prisma.constants.ts @@ -0,0 +1 @@ +export const CUSTOM_PRISMA_CLIENT = 'CUSTOM_PRISMA_CLIENT' diff --git a/packages/prisma/src/custom/custom-prisma.interfaces.ts b/packages/prisma/src/custom/custom-prisma.interfaces.ts new file mode 100644 index 0000000..a2be121 --- /dev/null +++ b/packages/prisma/src/custom/custom-prisma.interfaces.ts @@ -0,0 +1,63 @@ +import type { ModuleMetadata, Type } from '@nestjs/common' + +export type PrismaClientLike = { + /** + * Connect with the database. + */ + $connect(): Promise + /** + * Disconnect from the database. + */ + $disconnect(): Promise +} + +export interface CustomPrismaModuleOptions { + /** + * If `true`, registers `PrismaModule` as a global module. + * @See https://docs.nestjs.com/modules#global-modules + */ + isGlobal?: boolean + /** + * Choose a name to inject the custom prisma service with. + * @example + * name = 'PrismaServiceAuth' + * + * constructor( + * @Inject('PrismaServiceAuth') + * private prismaAuth: CustomPrismaService, + * ){} + */ + name: string + /** + * Pass an instance of your PrismaClient, useful when you specified a custom output path for your PrismaClient. + * @example client = new PrismaClient() + */ + client: Client +} + +export interface CustomPrismaClientFactory { + createPrismaClient(): Client | Promise +} + +export interface CustomPrismaModuleAsyncOptions + extends Pick { + /** + * If `true`, registers `PrismaModule` as a global module. + * @See https://docs.nestjs.com/modules#global-modules + */ + isGlobal?: boolean + /** + * Choose a name to inject the custom prisma service with. + * @example + * name = 'PrismaServiceAuth' + * + * constructor( + * @Inject('PrismaServiceAuth') + * private prismaAuth: CustomPrismaService, + * ){} + */ + name: string + useClass?: Type> + useFactory?: (...args: any[]) => Client | Promise + inject?: any[] +} diff --git a/packages/prisma/src/custom/custom-prisma.module.ts b/packages/prisma/src/custom/custom-prisma.module.ts new file mode 100644 index 0000000..0393b1a --- /dev/null +++ b/packages/prisma/src/custom/custom-prisma.module.ts @@ -0,0 +1,82 @@ +import { DynamicModule, Logger, Module, Provider } from '@nestjs/common' + +import { CUSTOM_PRISMA_CLIENT } from './custom-prisma.constants' +import { + CustomPrismaClientFactory, + CustomPrismaModuleAsyncOptions, + CustomPrismaModuleOptions, + PrismaClientLike +} from './custom-prisma.interfaces' +import { CustomPrismaService } from './custom-prisma.service' + +@Module({}) +export class CustomPrismaModule { + private static readonly logger = new Logger(CustomPrismaModule.name) + + static forRoot( + options: CustomPrismaModuleOptions + ): DynamicModule { + return { + global: options.isGlobal, + module: CustomPrismaModule, + providers: [ + { + provide: CUSTOM_PRISMA_CLIENT, + useValue: options.client + }, + { + provide: options.name, + useClass: CustomPrismaService + } + ], + exports: [options.name] + } + } + + static forRootAsync( + options: CustomPrismaModuleAsyncOptions + ): DynamicModule { + return { + global: options.isGlobal, + module: CustomPrismaModule, + imports: options.imports || [], + providers: [ + ...this.createAsyncProvider(options), + { + provide: options.name, + useClass: CustomPrismaService + } + ], + exports: [options.name] + } + } + + private static createAsyncProvider( + options: CustomPrismaModuleAsyncOptions + ): Provider[] { + if (options.useFactory) { + return [ + { + provide: CUSTOM_PRISMA_CLIENT, + useFactory: options.useFactory, + inject: options.inject || [] + } + ] + } + + if (options.useClass) { + return [ + { provide: options.useClass, useClass: options.useClass }, + { + provide: CUSTOM_PRISMA_CLIENT, + useFactory: async (optionsFactory: CustomPrismaClientFactory) => + optionsFactory.createPrismaClient(), + inject: [options.useClass] + } + ] + } + + this.logger.error('You must at least provide `useFactory` or `useClass`.') + return [] + } +} diff --git a/packages/prisma/src/custom/custom-prisma.service.ts b/packages/prisma/src/custom/custom-prisma.service.ts new file mode 100644 index 0000000..b0866ec --- /dev/null +++ b/packages/prisma/src/custom/custom-prisma.service.ts @@ -0,0 +1,12 @@ +import { Inject, Injectable } from '@nestjs/common' + +import { CUSTOM_PRISMA_CLIENT } from './custom-prisma.constants' +import { PrismaClientLike } from './custom-prisma.interfaces' + +@Injectable() +export class CustomPrismaService { + constructor( + @Inject(CUSTOM_PRISMA_CLIENT) + public client: Client + ) {} +} diff --git a/packages/prisma/src/custom/index.ts b/packages/prisma/src/custom/index.ts new file mode 100644 index 0000000..3fc5096 --- /dev/null +++ b/packages/prisma/src/custom/index.ts @@ -0,0 +1,4 @@ +export * from './custom-prisma.constants' +export * from './custom-prisma.interfaces' +export * from './custom-prisma.module' +export * from './custom-prisma.service' diff --git a/packages/prisma/src/index.ts b/packages/prisma/src/index.ts new file mode 100644 index 0000000..784b99b --- /dev/null +++ b/packages/prisma/src/index.ts @@ -0,0 +1,2 @@ +export * from './basic' +export * from './custom' diff --git a/packages/prisma/tsconfig.json b/packages/prisma/tsconfig.json new file mode 100644 index 0000000..681ad65 --- /dev/null +++ b/packages/prisma/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "@bit-ocean/tsconfig/nest", + "compilerOptions": { + "incremental": false + }, + "include": ["src", "tsup.config.ts"] +} diff --git a/packages/prisma/tsup.config.ts b/packages/prisma/tsup.config.ts new file mode 100644 index 0000000..1df03af --- /dev/null +++ b/packages/prisma/tsup.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from 'tsup' + +export default defineConfig((options) => ({ + entry: ['src/index.ts'], + treeshake: true, + splitting: true, + sourcemap: true, + clean: true, + dts: true, + skipNodeModulesBundle: true, + outDir: 'dist', + format: ['cjs'], + minify: !options.watch, + external: ['@prisma/client', '@nestjs/common', '@nestjs/core'] +})) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 96ccb6a..474d5cc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -456,6 +456,24 @@ importers: specifier: ^0.6.5 version: 0.6.5(prettier-plugin-astro@0.14.1)(prettier@3.3.3) + packages/prisma: + devDependencies: + '@nestjs/common': + specifier: ^10.3.10 + version: 10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1) + '@nestjs/core': + specifier: ^10.3.10 + version: 10.3.10(@nestjs/common@10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)(rxjs@7.8.1) + '@prisma/client': + specifier: ^5.17.0 + version: 5.17.0(prisma@5.17.0) + prisma: + specifier: ^5.17.0 + version: 5.17.0 + reflect-metadata: + specifier: ^0.2.2 + version: 0.2.2 + packages/renovate-config: {} packages/tailwind: @@ -2324,6 +2342,10 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@lukeed/csprng@1.1.0': + resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} + engines: {node: '>=8'} + '@manypkg/find-root@1.1.0': resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} @@ -2344,6 +2366,36 @@ packages: '@napi-rs/wasm-runtime@0.2.4': resolution: {integrity: sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==} + '@nestjs/common@10.3.10': + resolution: {integrity: sha512-H8k0jZtxk1IdtErGDmxFRy0PfcOAUg41Prrqpx76DQusGGJjsaovs1zjXVD1rZWaVYchfT1uczJ6L4Kio10VNg==} + peerDependencies: + class-transformer: '*' + class-validator: '*' + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + class-transformer: + optional: true + class-validator: + optional: true + + '@nestjs/core@10.3.10': + resolution: {integrity: sha512-ZbQ4jovQyzHtCGCrzK5NdtW1SYO2fHSsgSY1+/9WdruYCUra+JDkWEXgZ4M3Hv480Dl3OXehAmY1wCOojeMyMQ==} + peerDependencies: + '@nestjs/common': ^10.0.0 + '@nestjs/microservices': ^10.0.0 + '@nestjs/platform-express': ^10.0.0 + '@nestjs/websockets': ^10.0.0 + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + '@nestjs/microservices': + optional: true + '@nestjs/platform-express': + optional: true + '@nestjs/websockets': + optional: true + '@next/eslint-plugin-next@14.2.5': resolution: {integrity: sha512-LY3btOpPh+OTIpviNojDpUdIbHW9j0JBYBjsIp8IxtDFfYFyORvw3yNq6N231FVqQA7n7lwaf7xHbVJlA1ED7g==} @@ -2378,6 +2430,11 @@ packages: '@nrwl/workspace@19.5.1': resolution: {integrity: sha512-lqX0bgqCv/qc35tqea16uMWbkMN2dxEOCCc81BxKwDf0roSlO0Jnb0vWMCBUrITWbginSe7vovvjfpGbS1QboA==} + '@nuxtjs/opencollective@0.3.2': + resolution: {integrity: sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==} + engines: {node: '>=8.0.0', npm: '>=5.0.0'} + hasBin: true + '@nx/devkit@19.5.1': resolution: {integrity: sha512-Vj8wwzNIR5VIWmuLHhOi4aUVq7eVV5YTbctnEewKT+V/O4LZj+hClGyVNyT8s6b8JIjNWoIO4HXStLnH8rDOlw==} peerDependencies: @@ -2580,6 +2637,30 @@ packages: '@polka/url@1.0.0-next.25': resolution: {integrity: sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==} + '@prisma/client@5.17.0': + resolution: {integrity: sha512-N2tnyKayT0Zf7mHjwEyE8iG7FwTmXDHFZ1GnNhQp0pJUObsuel4ZZ1XwfuAYkq5mRIiC/Kot0kt0tGCfLJ70Jw==} + engines: {node: '>=16.13'} + peerDependencies: + prisma: '*' + peerDependenciesMeta: + prisma: + optional: true + + '@prisma/debug@5.17.0': + resolution: {integrity: sha512-l7+AteR3P8FXiYyo496zkuoiJ5r9jLQEdUuxIxNCN1ud8rdbH3GTxm+f+dCyaSv9l9WY+29L9czaVRXz9mULfg==} + + '@prisma/engines-version@5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053': + resolution: {integrity: sha512-tUuxZZysZDcrk5oaNOdrBnnkoTtmNQPkzINFDjz7eG6vcs9AVDmA/F6K5Plsb2aQc/l5M2EnFqn3htng9FA4hg==} + + '@prisma/engines@5.17.0': + resolution: {integrity: sha512-+r+Nf+JP210Jur+/X8SIPLtz+uW9YA4QO5IXA+KcSOBe/shT47bCcRMTYCbOESw3FFYFTwe7vU6KTWHKPiwvtg==} + + '@prisma/fetch-engine@5.17.0': + resolution: {integrity: sha512-ESxiOaHuC488ilLPnrv/tM2KrPhQB5TRris/IeIV4ZvUuKeaicCl4Xj/JCQeG9IlxqOgf1cCg5h5vAzlewN91Q==} + + '@prisma/get-platform@5.17.0': + resolution: {integrity: sha512-UlDgbRozCP1rfJ5Tlkf3Cnftb6srGrEQ4Nm3og+1Se2gWmCZ0hmPIi+tQikGDUVLlvOWx3Gyi9LzgRP+HTXV9w==} + '@rc-component/async-validator@5.0.4': resolution: {integrity: sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg==} engines: {node: '>=14.x'} @@ -3971,6 +4052,9 @@ packages: confusing-browser-globals@1.0.11: resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} + consola@2.15.3: + resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} + consola@3.2.3: resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} engines: {node: ^14.18.0 || >=16.10.0} @@ -4724,6 +4808,9 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} @@ -5451,6 +5538,10 @@ packages: resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} engines: {node: '>=8'} + iterare@1.2.1: + resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} + engines: {node: '>=6'} + iterator.prototype@1.1.2: resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} @@ -6369,6 +6460,9 @@ packages: path-to-regexp@2.4.0: resolution: {integrity: sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w==} + path-to-regexp@3.2.0: + resolution: {integrity: sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA==} + path-to-regexp@6.2.2: resolution: {integrity: sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==} @@ -6569,6 +6663,11 @@ packages: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + prisma@5.17.0: + resolution: {integrity: sha512-m4UWkN5lBE6yevqeOxEvmepnL5cNPEjzMw2IqDB59AcEV6w7D8vGljDLd1gPFH+W6gUxw9x7/RmN5dCS/WTPxA==} + engines: {node: '>=16.13'} + hasBin: true + prismjs@1.29.0: resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} engines: {node: '>=6'} @@ -6884,6 +6983,9 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + reflect-metadata@0.2.2: + resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + reflect.getprototypeof@1.0.6: resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} engines: {node: '>= 0.4'} @@ -7640,6 +7742,10 @@ packages: engines: {node: '>=0.8.0'} hasBin: true + uid@2.0.2: + resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} + engines: {node: '>=8'} + unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} @@ -10498,6 +10604,8 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 + '@lukeed/csprng@1.1.0': {} + '@manypkg/find-root@1.1.0': dependencies: '@babel/runtime': 7.24.7 @@ -10559,6 +10667,28 @@ snapshots: '@emnapi/runtime': 1.2.0 '@tybys/wasm-util': 0.9.0 + '@nestjs/common@10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1)': + dependencies: + iterare: 1.2.1 + reflect-metadata: 0.2.2 + rxjs: 7.8.1 + tslib: 2.6.3 + uid: 2.0.2 + + '@nestjs/core@10.3.10(@nestjs/common@10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)(rxjs@7.8.1)': + dependencies: + '@nestjs/common': 10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1) + '@nuxtjs/opencollective': 0.3.2 + fast-safe-stringify: 2.1.1 + iterare: 1.2.1 + path-to-regexp: 3.2.0 + reflect-metadata: 0.2.2 + rxjs: 7.8.1 + tslib: 2.6.3 + uid: 2.0.2 + transitivePeerDependencies: + - encoding + '@next/eslint-plugin-next@14.2.5': dependencies: glob: 10.3.10 @@ -10666,6 +10796,14 @@ snapshots: - '@swc/core' - debug + '@nuxtjs/opencollective@0.3.2': + dependencies: + chalk: 4.1.2 + consola: 2.15.3 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + '@nx/devkit@19.5.1(nx@19.5.1(@swc-node/register@1.10.9(@swc/core@1.7.0)(@swc/types@0.1.9)(typescript@5.5.3))(@swc/core@1.7.0))': dependencies: '@nrwl/devkit': 19.5.1(nx@19.5.1(@swc-node/register@1.10.9(@swc/core@1.7.0)(@swc/types@0.1.9)(typescript@5.5.3))(@swc/core@1.7.0)) @@ -10976,6 +11114,31 @@ snapshots: '@polka/url@1.0.0-next.25': {} + '@prisma/client@5.17.0(prisma@5.17.0)': + optionalDependencies: + prisma: 5.17.0 + + '@prisma/debug@5.17.0': {} + + '@prisma/engines-version@5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053': {} + + '@prisma/engines@5.17.0': + dependencies: + '@prisma/debug': 5.17.0 + '@prisma/engines-version': 5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053 + '@prisma/fetch-engine': 5.17.0 + '@prisma/get-platform': 5.17.0 + + '@prisma/fetch-engine@5.17.0': + dependencies: + '@prisma/debug': 5.17.0 + '@prisma/engines-version': 5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053 + '@prisma/get-platform': 5.17.0 + + '@prisma/get-platform@5.17.0': + dependencies: + '@prisma/debug': 5.17.0 + '@rc-component/async-validator@5.0.4': dependencies: '@babel/runtime': 7.24.8 @@ -12589,6 +12752,8 @@ snapshots: confusing-browser-globals@1.0.11: {} + consola@2.15.3: {} + consola@3.2.3: {} conventional-changelog-angular@7.0.0: @@ -13699,6 +13864,8 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-safe-stringify@2.1.1: {} + fastq@1.17.1: dependencies: reusify: 1.0.4 @@ -14521,6 +14688,8 @@ snapshots: html-escaper: 2.0.2 istanbul-lib-report: 3.0.1 + iterare@1.2.1: {} + iterator.prototype@1.1.2: dependencies: define-properties: 1.2.1 @@ -15953,6 +16122,8 @@ snapshots: path-to-regexp@2.4.0: {} + path-to-regexp@3.2.0: {} + path-to-regexp@6.2.2: {} path-type@4.0.0: {} @@ -16086,6 +16257,10 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.3.1 + prisma@5.17.0: + dependencies: + '@prisma/engines': 5.17.0 + prismjs@1.29.0: {} proc-log@3.0.0: {} @@ -16495,6 +16670,8 @@ snapshots: dependencies: picomatch: 2.3.1 + reflect-metadata@0.2.2: {} + reflect.getprototypeof@1.0.6: dependencies: call-bind: 1.0.7 @@ -17449,6 +17626,10 @@ snapshots: uglify-js@3.17.4: optional: true + uid@2.0.2: + dependencies: + '@lukeed/csprng': 1.1.0 + unbox-primitive@1.0.2: dependencies: call-bind: 1.0.7