diff --git a/packages/transactional-adapters/transactional-adapter-pg-promise/README.md b/packages/transactional-adapters/transactional-adapter-pg-promise/README.md new file mode 100644 index 00000000..2dbbb7dd --- /dev/null +++ b/packages/transactional-adapters/transactional-adapter-pg-promise/README.md @@ -0,0 +1,5 @@ +# @nestjs-cls/transactional-adapter-pg-promise + +`pg-promise` adapter for the `@nestjs-cls/transactional` plugin. + +### ➡️ [Go to the documentation website](https://papooch.github.io/nestjs-cls/plugins/available-plugins/transactional) 📖 diff --git a/packages/transactional-adapters/transactional-adapter-pg-promise/jest.config.js b/packages/transactional-adapters/transactional-adapter-pg-promise/jest.config.js new file mode 100644 index 00000000..61cb57ec --- /dev/null +++ b/packages/transactional-adapters/transactional-adapter-pg-promise/jest.config.js @@ -0,0 +1,17 @@ +module.exports = { + moduleFileExtensions: ['js', 'json', 'ts'], + rootDir: '.', + testRegex: '.*\\.spec\\.ts$', + transform: { + '^.+\\.ts$': 'ts-jest', + }, + collectCoverageFrom: ['src/**/*.ts'], + coverageDirectory: '../coverage', + testEnvironment: 'node', + globals: { + 'ts-jest': { + isolatedModules: true, + maxWorkers: 1, + }, + }, +}; diff --git a/packages/transactional-adapters/transactional-adapter-pg-promise/package.json b/packages/transactional-adapters/transactional-adapter-pg-promise/package.json new file mode 100644 index 00000000..da847291 --- /dev/null +++ b/packages/transactional-adapters/transactional-adapter-pg-promise/package.json @@ -0,0 +1,69 @@ +{ + "name": "@nestjs-cls/transactional-adapter-pg-promise", + "version": "1.1.0", + "description": "A pg-promise adapter for @nestjs-cls/transactional", + "author": "Sam Artuso ", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Papooch/nestjs-cls.git" + }, + "homepage": "https://papooch.github.io/nestjs-cls/", + "keywords": [ + "nest", + "nestjs", + "cls", + "continuation-local-storage", + "als", + "AsyncLocalStorage", + "async_hooks", + "request context", + "async context" + ], + "main": "dist/src/index.js", + "types": "dist/src/index.d.ts", + "files": [ + "dist/src/**/!(*.spec).d.ts", + "dist/src/**/!(*.spec).js" + ], + "scripts": { + "prepack": "cp ../../../LICENSE ./LICENSE", + "prebuild": "rimraf dist", + "build": "tsc", + "test": "jest", + "test:watch": "jest --watch", + "test:cov": "jest --coverage" + }, + "peerDependencies": { + "@nestjs-cls/transactional": "workspace:^1.0.1", + "nestjs-cls": "workspace:^4.0.1", + "pg-promise": "^11" + }, + "devDependencies": { + "@nestjs/cli": "^10.0.2", + "@nestjs/common": "^10.0.0", + "@nestjs/core": "^10.0.0", + "@nestjs/testing": "^10.0.0", + "@types/jest": "^28.1.2", + "@types/node": "^18.0.0", + "jest": "^28.1.1", + "pg-promise": "^11", + "reflect-metadata": "^0.1.13", + "rimraf": "^3.0.2", + "rxjs": "^7.5.5", + "ts-jest": "^28.0.5", + "ts-loader": "^9.3.0", + "ts-node": "^10.8.1", + "tsconfig-paths": "^4.0.0", + "typescript": "~4.8.0" + }, + "dependencies": { + "install-peers": "^1.0.4" + } +} diff --git a/packages/transactional-adapters/transactional-adapter-pg-promise/src/index.ts b/packages/transactional-adapters/transactional-adapter-pg-promise/src/index.ts new file mode 100644 index 00000000..4a20e7bb --- /dev/null +++ b/packages/transactional-adapters/transactional-adapter-pg-promise/src/index.ts @@ -0,0 +1 @@ +export * from './lib/transactional-adapter-pg-promise'; diff --git a/packages/transactional-adapters/transactional-adapter-pg-promise/src/lib/transactional-adapter-pg-promise.ts b/packages/transactional-adapters/transactional-adapter-pg-promise/src/lib/transactional-adapter-pg-promise.ts new file mode 100644 index 00000000..d29f01be --- /dev/null +++ b/packages/transactional-adapters/transactional-adapter-pg-promise/src/lib/transactional-adapter-pg-promise.ts @@ -0,0 +1,35 @@ +import { TransactionalAdapter } from '@nestjs-cls/transactional'; +import { IDatabase, ITask } from 'pg-promise'; + +export type PgPromiseDbOrTx = IDatabase | ITask; + +export interface PgPromiseTransactionalAdapterOptions { + /** + * The injection token for the pg-promise instance. + */ + pgPromiseInstanceToken: any; +} + +export class TransactionalAdapterPgPromise + implements TransactionalAdapter +{ + connectionToken: any; + + constructor(options: PgPromiseTransactionalAdapterOptions) { + this.connectionToken = options.pgPromiseInstanceToken; + } + + optionsFactory = (pgPromiseDbOrTxInstance: PgPromiseDbOrTx) => ({ + wrapWithTransaction: async ( + options: any, // FIXME + fn: (...args: any[]) => Promise, + setClient: (client?: PgPromiseDbOrTx) => void, + ) => { + return pgPromiseDbOrTxInstance.tx((tx) => { + setClient(tx); + return fn(); + }); + }, + getFallbackInstance: () => pgPromiseDbOrTxInstance, + }); +} diff --git a/packages/transactional-adapters/transactional-adapter-pg-promise/test/docker-compose.yml b/packages/transactional-adapters/transactional-adapter-pg-promise/test/docker-compose.yml new file mode 100644 index 00000000..499765fb --- /dev/null +++ b/packages/transactional-adapters/transactional-adapter-pg-promise/test/docker-compose.yml @@ -0,0 +1,14 @@ +services: + db: + image: postgres:15 + ports: + - 5432:5432 + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: postgres + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U postgres'] + interval: 1s + timeout: 1s + retries: 5 diff --git a/packages/transactional-adapters/transactional-adapter-pg-promise/test/transactional-adapter-pg-promise.spec.ts b/packages/transactional-adapters/transactional-adapter-pg-promise/test/transactional-adapter-pg-promise.spec.ts new file mode 100644 index 00000000..93525b55 --- /dev/null +++ b/packages/transactional-adapters/transactional-adapter-pg-promise/test/transactional-adapter-pg-promise.spec.ts @@ -0,0 +1,205 @@ +import { + ClsPluginTransactional, + Transactional, + TransactionHost, +} from '@nestjs-cls/transactional'; +import { Inject, Injectable, Module } from '@nestjs/common'; +import { Test, TestingModule } from '@nestjs/testing'; +import { ClsModule } from 'nestjs-cls'; +import { PgPromiseDbOrTx, TransactionalAdapterPgPromise } from '../src'; +import pgPromise from 'pg-promise'; +import { execSync } from 'node:child_process'; + +const PG_PROMISE = 'PG_PROMISE'; + +type UserRecord = { id: number; name: string; email: string }; + +@Injectable() +class UserRepository { + constructor( + private readonly txHost: TransactionHost, + ) {} + + async getUserById(id: number) { + return this.txHost.tx.one( + 'SELECT * FROM public.user WHERE id = $1', + [id], + ); + } + + async createUser(name: string) { + const created = await this.txHost.tx.one( + 'INSERT INTO public.user (name, email) VALUES ($1, $2) RETURNING *', + [name, `${name}@email.com`], + ); + return created; + } +} + +@Injectable() +class UserService { + constructor( + private readonly userRepository: UserRepository, + private readonly txHost: TransactionHost, + @Inject(PG_PROMISE) + private readonly db: PgPromiseDbOrTx, + ) {} + + @Transactional() + async transactionWithDecorator() { + const r1 = await this.userRepository.createUser('John'); + const r2 = await this.userRepository.getUserById(r1.id); + return { r1, r2 }; + } + + @Transactional({ + isolationLevel: 'serializable', // FIXME + }) + async transactionWithDecoratorWithOptions() { + const r1 = await this.userRepository.createUser('James'); + const r2 = await this.db.oneOrNone( + 'SELECT * FROM public.user WHERE id = $1', + [r1.id], + ); + const r3 = await this.userRepository.getUserById(r1.id); + return { r1, r2, r3 }; + } + + async transactionWithFunctionWrapper() { + return this.txHost.withTransaction( + { + isolationLevel: 'serializable', + }, + async () => { + const r1 = await this.userRepository.createUser('Joe'); + const r2 = await this.db.oneOrNone( + 'SELECT * FROM public.user WHERE id = $1', + [r1.id], + ); + const r3 = await this.userRepository.getUserById(r1.id); + return { r1, r2, r3 }; + }, + ); + } + + @Transactional() + async transactionWithDecoratorError() { + await this.userRepository.createUser('Nobody'); + throw new Error('Rollback'); + } +} + +const pgp = pgPromise(); +const db = pgp({ + host: 'localhost', + user: 'postgres', + password: 'postgres', + database: 'postgres', +}); + +@Module({ + providers: [ + { + provide: PG_PROMISE, + useValue: db, + }, + ], + exports: [PG_PROMISE], +}) +class PgPromiseModule {} + +@Module({ + imports: [ + PgPromiseModule, + ClsModule.forRoot({ + plugins: [ + new ClsPluginTransactional({ + imports: [PgPromiseModule], + adapter: new TransactionalAdapterPgPromise({ + pgPromiseInstanceToken: PG_PROMISE, + }), + }), + ], + }), + ], + providers: [UserService, UserRepository], +}) +class AppModule {} + +describe('Transactional', () => { + let module: TestingModule; + let callingService: UserService; + + beforeAll(async () => { + execSync('docker-compose -f test/docker-compose.yml up -d && sleep 2', { + stdio: 'inherit', + }); + await db.query('DROP TABLE IF EXISTS public.user'); + await db.query(`CREATE TABLE public.user ( + id serial NOT NULL, + name varchar NOT NULL, + email varchar NOT NULL, + CONSTRAINT user_pk PRIMARY KEY (id) + );`); + }); + + beforeEach(async () => { + module = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + await module.init(); + callingService = module.get(UserService); + }); + + afterAll(async () => { + pgp.end(); + execSync('docker-compose -f test/docker-compose.yml down', { + stdio: 'inherit', + }); + }); + + describe('TransactionalAdapterPgPromise', () => { + it('should run a transaction with the default options with a decorator', async () => { + const { r1, r2 } = await callingService.transactionWithDecorator(); + expect(r1).toEqual(r2); + const users = await db.many( + 'SELECT * FROM public.user', + ); + expect(users).toEqual(expect.arrayContaining([r1])); + }); + + it('should run a transaction with the specified options with a decorator', async () => { + const { r1, r2, r3 } = + await callingService.transactionWithDecoratorWithOptions(); + expect(r1).toEqual(r3); + expect(r2).toBeNull(); + const users = await db.many( + 'SELECT * FROM public.user', + ); + expect(users).toEqual(expect.arrayContaining([r1])); + }); + + it('should run a transaction with the specified options with a function wrapper', async () => { + const { r1, r2, r3 } = + await callingService.transactionWithFunctionWrapper(); + expect(r1).toEqual(r3); + expect(r2).toBeNull(); + const users = await db.many( + 'SELECT * FROM public.user', + ); + expect(users).toEqual(expect.arrayContaining([r1])); + }); + + it('should rollback a transaction on error', async () => { + await expect( + callingService.transactionWithDecoratorError(), + ).rejects.toThrow(new Error('Rollback')); + const users = await db.many( + 'SELECT * FROM public.user', + ); + expect(users).toEqual( + expect.not.arrayContaining([{ name: 'Nobody' }]), + ); + }); + }); +}); diff --git a/packages/transactional-adapters/transactional-adapter-pg-promise/tsconfig.json b/packages/transactional-adapters/transactional-adapter-pg-promise/tsconfig.json new file mode 100644 index 00000000..bbc28fb3 --- /dev/null +++ b/packages/transactional-adapters/transactional-adapter-pg-promise/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "." + }, + "include": ["src/**/*.ts", "test/**/*.ts"] +} diff --git a/tsconfig.json b/tsconfig.json index d181c782..9a7b64d6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -33,6 +33,9 @@ }, { "path": "packages/transactional-adapters/transactional-adapter-prisma" + }, + { + "path": "packages/transactional-adapters/transactional-adapter-pg-promise" } ] -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index 5fc949f6..ad4bd543 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4680,6 +4680,34 @@ __metadata: languageName: unknown linkType: soft +"@nestjs-cls/transactional-adapter-pg-promise@workspace:packages/transactional-adapters/transactional-adapter-pg-promise": + version: 0.0.0-use.local + resolution: "@nestjs-cls/transactional-adapter-pg-promise@workspace:packages/transactional-adapters/transactional-adapter-pg-promise" + dependencies: + "@nestjs/cli": "npm:^10.0.2" + "@nestjs/common": "npm:^10.0.0" + "@nestjs/core": "npm:^10.0.0" + "@nestjs/testing": "npm:^10.0.0" + "@types/jest": "npm:^28.1.2" + "@types/node": "npm:^18.0.0" + install-peers: "npm:^1.0.4" + jest: "npm:^28.1.1" + pg-promise: "npm:^11" + reflect-metadata: "npm:^0.1.13" + rimraf: "npm:^3.0.2" + rxjs: "npm:^7.5.5" + ts-jest: "npm:^28.0.5" + ts-loader: "npm:^9.3.0" + ts-node: "npm:^10.8.1" + tsconfig-paths: "npm:^4.0.0" + typescript: "npm:~4.8.0" + peerDependencies: + "@nestjs-cls/transactional": "workspace:^1.0.1" + nestjs-cls: "workspace:^4.0.1" + pg-promise: ^11 + languageName: unknown + linkType: soft + "@nestjs-cls/transactional-adapter-prisma@workspace:packages/transactional-adapters/transactional-adapter-prisma": version: 0.0.0-use.local resolution: "@nestjs-cls/transactional-adapter-prisma@workspace:packages/transactional-adapters/transactional-adapter-prisma" @@ -7698,6 +7726,13 @@ __metadata: languageName: node linkType: hard +"assert-options@npm:0.8.1": + version: 0.8.1 + resolution: "assert-options@npm:0.8.1" + checksum: 492c20c75177d96a52fddb462ffb979687b089f91c66da1a46f6b1ced59bb2fd3235e9cd3083c75317ea971b41c292b20c9fb041d8703f8775499115362fbb84 + languageName: node + linkType: hard + "astral-regex@npm:^2.0.0": version: 2.0.0 resolution: "astral-regex@npm:2.0.0" @@ -8246,6 +8281,13 @@ __metadata: languageName: node linkType: hard +"buffer-writer@npm:2.0.0": + version: 2.0.0 + resolution: "buffer-writer@npm:2.0.0" + checksum: c91b2ab09a200cf0862237e5a4dbd5077003b42d26d4f0c596ec7149f82ef83e0751d670bcdf379ed988d1a08c0fac7759a8cb928cf1a4710a1988a7618b1190 + languageName: node + linkType: hard + "buffer@npm:^5.5.0": version: 5.7.1 resolution: "buffer@npm:5.7.1" @@ -10639,6 +10681,15 @@ __metadata: languageName: node linkType: hard +"executioner@npm:^2.0.1": + version: 2.0.1 + resolution: "executioner@npm:2.0.1" + dependencies: + mixly: "npm:^1.0.0" + checksum: 9e20364a6056e9799be37199c4261801583db204aca93c2ab8239f81e3a0daefee775982b6568f291454d08f420c2a5e76dd6515ad6e73a513fb1e7f4dc78569 + languageName: node + linkType: hard + "exit@npm:^0.1.2": version: 0.1.2 resolution: "exit@npm:0.1.2" @@ -11362,6 +11413,13 @@ __metadata: languageName: node linkType: hard +"fulcon@npm:^1.0.1": + version: 1.0.2 + resolution: "fulcon@npm:1.0.2" + checksum: 1d334db53cfa9060d74c2492c8b253db90ceef154044688ca43e443cf784413db4ff5a3704ccf4b7982e677995e5634d7db6f367cfd477adb0097d04581ce3c8 + languageName: node + linkType: hard + "function-bind@npm:^1.1.1": version: 1.1.1 resolution: "function-bind@npm:1.1.1" @@ -12765,6 +12823,15 @@ __metadata: languageName: node linkType: hard +"install-peers@npm:^1.0.4": + version: 1.0.4 + resolution: "install-peers@npm:1.0.4" + dependencies: + executioner: "npm:^2.0.1" + checksum: 4ae7bda8a6e528ab72e5cb7e829f7d7531abfef46d8336b65a858344ee2dfc923560e01c4059d27c68fa3b1e6a3c59dbf596ff8fc48cd00a8bba10b8be3e2ecc + languageName: node + linkType: hard + "interpret@npm:^1.0.0": version: 1.4.0 resolution: "interpret@npm:1.4.0" @@ -15518,6 +15585,15 @@ __metadata: languageName: node linkType: hard +"mixly@npm:^1.0.0": + version: 1.0.0 + resolution: "mixly@npm:1.0.0" + dependencies: + fulcon: "npm:^1.0.1" + checksum: 7b5ba7603b8b6c256993f12bb0ec6d8b73e56fa4a7067b4de3eda5392a6bb0216762b304611973a8f101b566253ea01fec5e689516a495e0e1e16b42ab01bfbe + languageName: node + linkType: hard + "mkdirp-classic@npm:^0.5.2, mkdirp-classic@npm:^0.5.3": version: 0.5.3 resolution: "mkdirp-classic@npm:0.5.3" @@ -16326,6 +16402,13 @@ __metadata: languageName: node linkType: hard +"packet-reader@npm:1.0.0": + version: 1.0.0 + resolution: "packet-reader@npm:1.0.0" + checksum: c86c3321bb07e0f03cc2db59f7701184e0bbfcb914f1fdc963993b03262486deb402292adcef39b64e3530ea66b3b2e2163d6da7b3792a730bdd1c6df3175aaa + languageName: node + linkType: hard + "param-case@npm:^3.0.4": version: 3.0.4 resolution: "param-case@npm:3.0.4" @@ -16562,13 +16645,108 @@ __metadata: languageName: node linkType: hard -"pg-connection-string@npm:2.6.2": +"pg-cloudflare@npm:^1.1.1": + version: 1.1.1 + resolution: "pg-cloudflare@npm:1.1.1" + checksum: a68b957f755be6af813d68ccaf4c906a000fd2ecb362cd281220052cc9e2f6c26da3b88792742387008c30b3bf0d2fa3a0eff04aeb8af4414023c99ae78e07bd + languageName: node + linkType: hard + +"pg-connection-string@npm:2.6.2, pg-connection-string@npm:^2.6.2": version: 2.6.2 resolution: "pg-connection-string@npm:2.6.2" checksum: e8fdea74fcc8bdc3d7c5c6eadd9425fdba7e67fb7fe836f9c0cecad94c8984e435256657d1d8ce0483d1fedef667e7a57e32449a63cb805cb0289fc34b62da35 languageName: node linkType: hard +"pg-int8@npm:1.0.1": + version: 1.0.1 + resolution: "pg-int8@npm:1.0.1" + checksum: be6a02d851fc2a4ae3e9de81710d861de3ba35ac927268973eb3cb618873a05b9424656df464dd43bd7dc3fc5295c3f5b3c8349494f87c7af50ec59ef14e0b98 + languageName: node + linkType: hard + +"pg-minify@npm:1.6.3": + version: 1.6.3 + resolution: "pg-minify@npm:1.6.3" + checksum: 1e8c5a12be032796f87c4bfc5d318035703ac2abebd4848f377925165d3876c05c35dce03d9e942835ff7a51300c5dd1268cd55989b1c18f20aadf700aebfd32 + languageName: node + linkType: hard + +"pg-pool@npm:^3.6.1": + version: 3.6.1 + resolution: "pg-pool@npm:3.6.1" + peerDependencies: + pg: ">=8.0" + checksum: 47837c4e4c2b9e195cec01bd58b6e276acc915537191707ad4d6ed975fd9bc03c73f63cb7fde4cb0e08ed059e35faf60fbd03744dee3af71d4b4631ab40eeb7f + languageName: node + linkType: hard + +"pg-promise@npm:^11": + version: 11.5.4 + resolution: "pg-promise@npm:11.5.4" + dependencies: + assert-options: "npm:0.8.1" + pg: "npm:8.11.3" + pg-minify: "npm:1.6.3" + spex: "npm:3.3.0" + checksum: 07aad44eca10c89c2b6782250f02e33e561168d2f88ffa7a9ad2c1edac287c57e8fa540f17237beb6e9f0d5813236bb3366ca8852b201bda0596a0f38e06ccbe + languageName: node + linkType: hard + +"pg-protocol@npm:^1.6.0": + version: 1.6.0 + resolution: "pg-protocol@npm:1.6.0" + checksum: 318a4d1e9cebd3927b10a8bc412f5017117a1f9a5fafb628d75847da7d1ab81c33250de58596bd0990029e14e92a995a851286d60fc236692299faf509572213 + languageName: node + linkType: hard + +"pg-types@npm:^2.1.0": + version: 2.2.0 + resolution: "pg-types@npm:2.2.0" + dependencies: + pg-int8: "npm:1.0.1" + postgres-array: "npm:~2.0.0" + postgres-bytea: "npm:~1.0.0" + postgres-date: "npm:~1.0.4" + postgres-interval: "npm:^1.1.0" + checksum: ab3f8069a323f601cd2d2279ca8c425447dab3f9b61d933b0601d7ffc00d6200df25e26a4290b2b0783b59278198f7dd2ed03e94c4875797919605116a577c65 + languageName: node + linkType: hard + +"pg@npm:8.11.3": + version: 8.11.3 + resolution: "pg@npm:8.11.3" + dependencies: + buffer-writer: "npm:2.0.0" + packet-reader: "npm:1.0.0" + pg-cloudflare: "npm:^1.1.1" + pg-connection-string: "npm:^2.6.2" + pg-pool: "npm:^3.6.1" + pg-protocol: "npm:^1.6.0" + pg-types: "npm:^2.1.0" + pgpass: "npm:1.x" + peerDependencies: + pg-native: ">=3.0.1" + dependenciesMeta: + pg-cloudflare: + optional: true + peerDependenciesMeta: + pg-native: + optional: true + checksum: 07e6967fc8bd5d72bab9be6620626e8e3ab59128ebf56bf0de83d67f10801a19221d88b3317e90b93339ba48d0498b39967b782ae39686aabda6bc647bceb438 + languageName: node + linkType: hard + +"pgpass@npm:1.x": + version: 1.0.5 + resolution: "pgpass@npm:1.0.5" + dependencies: + split2: "npm:^4.1.0" + checksum: 5ea6c9b2de04c33abb08d33a2dded303c4a3c7162a9264519cbe85c0a9857d712463140ba42fad0c7cd4b21f644dd870b45bb2e02fcbe505b4de0744fd802c1d + languageName: node + linkType: hard + "picocolors@npm:^1.0.0": version: 1.0.0 resolution: "picocolors@npm:1.0.0" @@ -17118,6 +17296,36 @@ __metadata: languageName: node linkType: hard +"postgres-array@npm:~2.0.0": + version: 2.0.0 + resolution: "postgres-array@npm:2.0.0" + checksum: cbd56207e4141d7fbf08c86f2aebf21fa7064943d3f808ec85f442ff94b48d891e7a144cc02665fb2de5dbcb9b8e3183a2ac749959e794b4a4cfd379d7a21d08 + languageName: node + linkType: hard + +"postgres-bytea@npm:~1.0.0": + version: 1.0.0 + resolution: "postgres-bytea@npm:1.0.0" + checksum: febf2364b8a8953695cac159eeb94542ead5886792a9627b97e33f6b5bb6e263bc0706ab47ec221516e79fbd6b2452d668841830fb3b49ec6c0fc29be61892ce + languageName: node + linkType: hard + +"postgres-date@npm:~1.0.4": + version: 1.0.7 + resolution: "postgres-date@npm:1.0.7" + checksum: 0ff91fccc64003e10b767fcfeefb5eaffbc522c93aa65d5051c49b3c4ce6cb93ab091a7d22877a90ad60b8874202c6f1d0f935f38a7235ed3b258efd54b97ca9 + languageName: node + linkType: hard + +"postgres-interval@npm:^1.1.0": + version: 1.2.0 + resolution: "postgres-interval@npm:1.2.0" + dependencies: + xtend: "npm:^4.0.0" + checksum: c1734c3cb79e7f22579af0b268a463b1fa1d084e742a02a7a290c4f041e349456f3bee3b4ee0bb3f226828597f7b76deb615c1b857db9a742c45520100456272 + languageName: node + linkType: hard + "prebuild-install@npm:^7.1.1": version: 7.1.1 resolution: "prebuild-install@npm:7.1.1" @@ -18981,6 +19189,13 @@ __metadata: languageName: node linkType: hard +"spex@npm:3.3.0": + version: 3.3.0 + resolution: "spex@npm:3.3.0" + checksum: e39bad0376df9857b9886dca9c46af31649588895b2fd2b8f38a88ab1ef0f5b6d5cf6dc9002997fb17c9c2601eb899bd911ff004561c823e25ea6729e644a634 + languageName: node + linkType: hard + "split2@npm:^3.0.0": version: 3.2.2 resolution: "split2@npm:3.2.2" @@ -18990,7 +19205,7 @@ __metadata: languageName: node linkType: hard -"split2@npm:^4.0.0": +"split2@npm:^4.0.0, split2@npm:^4.1.0": version: 4.2.0 resolution: "split2@npm:4.2.0" checksum: b292beb8ce9215f8c642bb68be6249c5a4c7f332fc8ecadae7be5cbdf1ea95addc95f0459ef2e7ad9d45fd1064698a097e4eb211c83e772b49bc0ee423e91534