diff --git a/.env.example b/.env.example index 4a74a6b4..6d537708 100644 --- a/.env.example +++ b/.env.example @@ -46,4 +46,10 @@ DRAWIO_URL= DISABLE_TELEMETRY=false # Enable debug logging in production (default: false) -DEBUG_MODE=false \ No newline at end of file +DEBUG_MODE=false + +# Log database queries +DEBUG_DB=false + +# Log http requests +LOG_HTTP=false diff --git a/apps/server/package.json b/apps/server/package.json index 12d801a0..71e68679 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -74,11 +74,13 @@ "jsonwebtoken": "^9.0.3", "kysely": "^0.28.2", "kysely-migration-cli": "^0.4.2", + "kysely-postgres-js": "^3.0.0", "ldapts": "^7.4.0", "mammoth": "^1.11.0", "mime-types": "^2.1.35", "nanoid": "3.3.11", "nestjs-kysely": "^1.2.0", + "nestjs-pino": "^4.5.0", "nodemailer": "^7.0.12", "openid-client": "^5.7.1", "otpauth": "^9.4.1", @@ -86,9 +88,11 @@ "passport-google-oauth20": "^2.0.0", "passport-jwt": "^4.0.1", "pdfjs-dist": "^5.4.394", - "pg": "^8.16.3", "pg-tsquery": "^8.4.2", "pgvector": "^0.2.1", + "postgres": "^3.4.8", + "pino-http": "^11.0.0", + "pino-pretty": "^13.1.3", "postmark": "^4.0.5", "react": "^18.3.1", "reflect-metadata": "^0.2.2", @@ -116,7 +120,6 @@ "@types/nodemailer": "^6.4.17", "@types/passport-google-oauth20": "^2.0.16", "@types/passport-jwt": "^4.0.1", - "@types/pg": "^8.11.11", "@types/supertest": "^6.0.2", "@types/ws": "^8.5.14", "@types/yauzl": "^2.10.3", diff --git a/apps/server/src/app.module.ts b/apps/server/src/app.module.ts index 56691444..8036b849 100644 --- a/apps/server/src/app.module.ts +++ b/apps/server/src/app.module.ts @@ -18,6 +18,7 @@ import { SecurityModule } from './integrations/security/security.module'; import { TelemetryModule } from './integrations/telemetry/telemetry.module'; import { RedisModule } from '@nestjs-labs/nestjs-ioredis'; import { RedisConfigService } from './integrations/redis/redis-config.service'; +import { LoggerModule } from './common/logger/logger.module'; const enterpriseModules = []; try { @@ -35,6 +36,7 @@ try { @Module({ imports: [ + LoggerModule, CoreModule, DatabaseModule, EnvironmentModule, diff --git a/apps/server/src/collaboration/server/collab-app.module.ts b/apps/server/src/collaboration/server/collab-app.module.ts index 08a2f688..eb6b57fa 100644 --- a/apps/server/src/collaboration/server/collab-app.module.ts +++ b/apps/server/src/collaboration/server/collab-app.module.ts @@ -8,9 +8,11 @@ import { QueueModule } from '../../integrations/queue/queue.module'; import { EventEmitterModule } from '@nestjs/event-emitter'; import { HealthModule } from '../../integrations/health/health.module'; import { CollaborationController } from './collaboration.controller'; +import { LoggerModule } from '../../common/logger/logger.module'; @Module({ imports: [ + LoggerModule, DatabaseModule, EnvironmentModule, CollaborationModule, diff --git a/apps/server/src/collaboration/server/collab-main.ts b/apps/server/src/collaboration/server/collab-main.ts index d71da428..1a10167f 100644 --- a/apps/server/src/collaboration/server/collab-main.ts +++ b/apps/server/src/collaboration/server/collab-main.ts @@ -5,8 +5,8 @@ import { NestFastifyApplication, } from '@nestjs/platform-fastify'; import { TransformHttpResponseInterceptor } from '../../common/interceptors/http-response.interceptor'; -import { InternalLogFilter } from '../../common/logger/internal-log-filter'; import { Logger } from '@nestjs/common'; +import { Logger as PinoLogger } from 'nestjs-pino'; async function bootstrap() { const app = await NestFactory.create( @@ -17,10 +17,12 @@ async function bootstrap() { maxParamLength: 500, }), { - logger: new InternalLogFilter(), + bufferLogs: true, }, ); + app.useLogger(app.get(PinoLogger)); + app.setGlobalPrefix('api', { exclude: ['/'] }); app.enableCors(); diff --git a/apps/server/src/common/helpers/utils.ts b/apps/server/src/common/helpers/utils.ts index 738c455b..7c94bb48 100644 --- a/apps/server/src/common/helpers/utils.ts +++ b/apps/server/src/common/helpers/utils.ts @@ -2,6 +2,7 @@ import * as path from 'path'; import * as bcrypt from 'bcrypt'; import { sanitize } from 'sanitize-filename-ts'; import { FastifyRequest } from 'fastify'; +import { Readable, Transform } from 'stream'; export const envPath = path.resolve(process.cwd(), '..', '..', '.env'); @@ -98,3 +99,38 @@ export function hasLicenseOrEE(opts: { const { licenseKey, plan, isCloud } = opts; return Boolean(licenseKey) || (isCloud && plan === 'business'); } + +/** + * Normalizes a database URL for postgres.js compatibility. + * - Removes `sslmode=no-verify` (not supported by postgres.js), keeps other sslmode values + * - Removes `schema` parameter (has no effect via connection string) + * Note: If we don't strip them, the connection will fail + */ +export function normalizePostgresUrl(url: string): string { + const parsed = new URL(url); + const newParams = new URLSearchParams(); + + for (const [key, value] of parsed.searchParams) { + if (key === 'sslmode' && value === 'no-verify') continue; + if (key === 'schema') continue; + newParams.append(key, value); + } + + parsed.search = newParams.toString(); + return parsed.toString(); +} + +export function createByteCountingStream(source: Readable) { + let bytesRead = 0; + const stream = new Transform({ + transform(chunk, encoding, callback) { + bytesRead += chunk.length; + callback(null, chunk); + }, + }); + + source.pipe(stream); + source.on('error', (err) => stream.emit('error', err)); + + return { stream, getBytesRead: () => bytesRead }; +} diff --git a/apps/server/src/common/logger/logger.module.ts b/apps/server/src/common/logger/logger.module.ts new file mode 100644 index 00000000..327605a4 --- /dev/null +++ b/apps/server/src/common/logger/logger.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { LoggerModule as PinoLoggerModule } from 'nestjs-pino'; +import { createPinoConfig } from './pino.config'; + +@Module({ + imports: [PinoLoggerModule.forRoot(createPinoConfig())], + exports: [PinoLoggerModule], +}) +export class LoggerModule {} diff --git a/apps/server/src/common/logger/pino.config.ts b/apps/server/src/common/logger/pino.config.ts new file mode 100644 index 00000000..9d9a14f7 --- /dev/null +++ b/apps/server/src/common/logger/pino.config.ts @@ -0,0 +1,77 @@ +import { Params } from 'nestjs-pino'; +import { stdTimeFunctions } from 'pino'; + +const CONTEXTS_TO_IGNORE = [ + 'InstanceLoader', + 'RoutesResolver', + 'RouterExplorer', + 'WebSocketsController', +]; + +export function createPinoConfig(): Params { + const isProduction = process.env.NODE_ENV === 'production'; + const isDebugMode = process.env.DEBUG_MODE === 'true'; + const logHttp = process.env.LOG_HTTP === 'true'; + + const level = isProduction && !isDebugMode ? 'info' : 'debug'; + + return { + pinoHttp: { + level, + timestamp: stdTimeFunctions.isoTime, + transport: !isProduction + ? { + target: 'pino-pretty', + options: { + colorize: true, + singleLine: true, + translateTime: 'SYS:standard', + ignore: 'pid,hostname', + }, + } + : undefined, + formatters: { + level: (label) => ({ level: label }), + log: (object: Record) => { + if (isProduction && !isDebugMode) { + const context = object['context'] as string | undefined; + if (context && CONTEXTS_TO_IGNORE.includes(context)) { + return { filtered: true }; + } + } + return object; + }, + }, + serializers: { + req: (req) => { + const forwardedFor = req.headers?.['x-forwarded-for']; + const ip = + req.headers?.['cf-connecting-ip'] || + (typeof forwardedFor === 'string' ? forwardedFor.split(',')[0]?.trim() : undefined) || + req.remoteAddress; + + return { + method: req.method, + url: req.url, + ip, + userAgent: req.headers?.['user-agent'], + }; + }, + res: (res) => ({ + statusCode: res.statusCode, + }), + }, + customLogLevel: (_req, res, err) => { + if (res.statusCode >= 500 || err) return 'error'; + if (res.statusCode >= 400) return 'warn'; + return 'info'; + }, + autoLogging: logHttp + ? { + ignore: (req) => + req.url === '/api/health' || req.url === '/api/health/live', + } + : false, + }, + }; +} diff --git a/apps/server/src/core/attachment/attachment.utils.ts b/apps/server/src/core/attachment/attachment.utils.ts index ee72dc9f..23512002 100644 --- a/apps/server/src/core/attachment/attachment.utils.ts +++ b/apps/server/src/core/attachment/attachment.utils.ts @@ -5,15 +5,17 @@ import { sanitizeFileName } from '../../common/helpers'; import * as sharp from 'sharp'; export interface PreparedFile { - buffer: Buffer; + buffer?: Buffer; fileName: string; fileSize: number; fileExtension: string; mimeType: string; + multiPartFile?: MultipartFile; } export async function prepareFile( filePromise: Promise, + options: { skipBuffer?: boolean } = {}, ): Promise { const file = await filePromise; @@ -22,10 +24,16 @@ export async function prepareFile( } try { - const buffer = await file.toBuffer(); + let buffer: Buffer | undefined; + let fileSize = 0; + + if (!options.skipBuffer) { + buffer = await file.toBuffer(); + fileSize = buffer.length; + } + const sanitizedFilename = sanitizeFileName(file.filename); const fileName = sanitizedFilename.slice(0, 255); - const fileSize = buffer.length; const fileExtension = path.extname(file.filename).toLowerCase(); return { @@ -34,6 +42,7 @@ export async function prepareFile( fileSize, fileExtension, mimeType: file.mimetype, + multiPartFile: file, }; } catch (error) { throw error; diff --git a/apps/server/src/core/attachment/services/attachment.service.ts b/apps/server/src/core/attachment/services/attachment.service.ts index 77a044a2..ea94b983 100644 --- a/apps/server/src/core/attachment/services/attachment.service.ts +++ b/apps/server/src/core/attachment/services/attachment.service.ts @@ -4,6 +4,7 @@ import { Logger, NotFoundException, } from '@nestjs/common'; +import { Readable } from 'stream'; import { StorageService } from '../../../integrations/storage/storage.service'; import { MultipartFile } from '@fastify/multipart'; import { @@ -26,6 +27,7 @@ import { SpaceRepo } from '@docmost/db/repos/space/space.repo'; import { InjectQueue } from '@nestjs/bullmq'; import { QueueJob, QueueName } from '../../../integrations/queue/constants'; import { Queue } from 'bullmq'; +import { createByteCountingStream } from '../../../common/helpers/utils'; @Injectable() export class AttachmentService { @@ -49,7 +51,9 @@ export class AttachmentService { attachmentId?: string; }) { const { filePromise, pageId, spaceId, userId, workspaceId } = opts; - const preparedFile: PreparedFile = await prepareFile(filePromise); + const preparedFile: PreparedFile = await prepareFile(filePromise, { + skipBuffer: true, + }); let isUpdate = false; let attachmentId = null; @@ -81,7 +85,14 @@ export class AttachmentService { const filePath = `${getAttachmentFolderPath(AttachmentType.File, workspaceId)}/${attachmentId}/${preparedFile.fileName}`; - await this.uploadToDrive(filePath, preparedFile.buffer); + const { stream, getBytesRead } = createByteCountingStream( + preparedFile.multiPartFile.file, + ); + + await this.uploadToDrive(filePath, stream); + + // Update fileSize from the consumed stream + preparedFile.fileSize = getBytesRead(); let attachment: Attachment = null; try { @@ -142,7 +153,10 @@ export class AttachmentService { const preparedFile: PreparedFile = await prepareFile(filePromise); validateFileType(preparedFile.fileExtension, validImageExtensions); - const processedBuffer = await compressAndResizeIcon(preparedFile.buffer, type); + const processedBuffer = await compressAndResizeIcon( + preparedFile.buffer, + type, + ); preparedFile.buffer = processedBuffer; preparedFile.fileSize = processedBuffer.length; preparedFile.fileName = uuid4() + preparedFile.fileExtension; @@ -232,9 +246,9 @@ export class AttachmentService { } } - async uploadToDrive(filePath: string, fileBuffer: any) { + async uploadToDrive(filePath: string, fileContent: Buffer | Readable) { try { - await this.storageService.upload(filePath, fileBuffer); + await this.storageService.upload(filePath, fileContent); } catch (err) { this.logger.error('Error uploading file to drive:', err); throw new BadRequestException('Error uploading file to drive'); diff --git a/apps/server/src/database/database.module.ts b/apps/server/src/database/database.module.ts index bd331ada..e6cb2904 100644 --- a/apps/server/src/database/database.module.ts +++ b/apps/server/src/database/database.module.ts @@ -7,8 +7,7 @@ import { } from '@nestjs/common'; import { InjectKysely, KyselyModule } from 'nestjs-kysely'; import { EnvironmentService } from '../integrations/environment/environment.service'; -import { CamelCasePlugin, LogEvent, PostgresDialect, sql } from 'kysely'; -import { Pool, types } from 'pg'; +import { CamelCasePlugin, LogEvent, sql } from 'kysely'; import { GroupRepo } from '@docmost/db/repos/group/group.repo'; import { WorkspaceRepo } from '@docmost/db/repos/workspace/workspace.repo'; import { UserRepo } from '@docmost/db/repos/user/user.repo'; @@ -26,9 +25,9 @@ import { UserTokenRepo } from './repos/user-token/user-token.repo'; import { BacklinkRepo } from '@docmost/db/repos/backlink/backlink.repo'; import { ShareRepo } from '@docmost/db/repos/share/share.repo'; import { PageListener } from '@docmost/db/listeners/page.listener'; - -// https://github.com/brianc/node-postgres/issues/811 -types.setTypeParser(types.builtins.INT8, (val) => Number(val)); +import { PostgresJSDialect } from 'kysely-postgres-js'; +import * as postgres from 'postgres'; +import { normalizePostgresUrl } from '../common/helpers'; @Global() @Module({ @@ -37,26 +36,30 @@ types.setTypeParser(types.builtins.INT8, (val) => Number(val)); imports: [], inject: [EnvironmentService], useFactory: (environmentService: EnvironmentService) => ({ - dialect: new PostgresDialect({ - pool: new Pool({ - connectionString: environmentService.getDatabaseURL(), - max: environmentService.getDatabaseMaxPool(), - }).on('error', (err) => { - console.error('Database error:', err.message); - }), + dialect: new PostgresJSDialect({ + postgres: postgres( + normalizePostgresUrl(environmentService.getDatabaseURL()), + { + max: environmentService.getDatabaseMaxPool(), + onnotice: () => {}, + types: { + bigint: { + to: 20, + from: [20, 1700], + serialize: (value: number) => value.toString(), + parse: (value: string) => Number.parseInt(value), + }, + }, + }, + ), }), plugins: [new CamelCasePlugin()], log: (event: LogEvent) => { if (environmentService.getNodeEnv() !== 'development') return; const logger = new Logger(DatabaseModule.name); - if (event.level) { - if (process.env.DEBUG_DB?.toLowerCase() === 'true') { - logger.debug(event.query.sql); - logger.debug('query time: ' + event.queryDurationMillis + ' ms'); - //if (event.query.parameters.length > 0) { - // logger.debug('parameters: ' + event.query.parameters); - //} - } + if (process.env.DEBUG_DB?.toLowerCase() === 'true') { + logger.debug(event.query.sql); + logger.debug('query time: ' + event.queryDurationMillis + ' ms'); } }, }), diff --git a/apps/server/src/database/migrate.ts b/apps/server/src/database/migrate.ts index 22e62491..a5d58766 100644 --- a/apps/server/src/database/migrate.ts +++ b/apps/server/src/database/migrate.ts @@ -1,25 +1,19 @@ import * as path from 'path'; import { promises as fs } from 'fs'; -import pg from 'pg'; -import { - Kysely, - Migrator, - PostgresDialect, - FileMigrationProvider, -} from 'kysely'; +import { Kysely, Migrator, FileMigrationProvider } from 'kysely'; import { run } from 'kysely-migration-cli'; import * as dotenv from 'dotenv'; -import { envPath } from '../common/helpers/utils'; +import { envPath, normalizePostgresUrl } from '../common/helpers'; +import { PostgresJSDialect } from 'kysely-postgres-js'; +import postgres from 'postgres'; dotenv.config({ path: envPath }); const migrationFolder = path.join(__dirname, './migrations'); const db = new Kysely({ - dialect: new PostgresDialect({ - pool: new pg.Pool({ - connectionString: process.env.DATABASE_URL, - }) as any, + dialect: new PostgresJSDialect({ + postgres: postgres(normalizePostgresUrl(process.env.DATABASE_URL)), }), }); diff --git a/apps/server/src/ee b/apps/server/src/ee index fce3e9e9..b6844b01 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit fce3e9e945da114c4f7cdc4de86a6729b072515e +Subproject commit b6844b019c3778d51ff1bb236f30284a0bf8f403 diff --git a/apps/server/src/integrations/import/services/import.service.ts b/apps/server/src/integrations/import/services/import.service.ts index 7901122a..aeeebcee 100644 --- a/apps/server/src/integrations/import/services/import.service.ts +++ b/apps/server/src/integrations/import/services/import.service.ts @@ -10,7 +10,11 @@ import { } from '../../../collaboration/collaboration.util'; import { InjectKysely } from 'nestjs-kysely'; import { KyselyDB } from '@docmost/db/types/kysely.types'; -import { generateSlugId, sanitizeFileName } from '../../../common/helpers'; +import { + generateSlugId, + sanitizeFileName, + createByteCountingStream, +} from '../../../common/helpers'; import { generateJitteredKeyBetween } from 'fractional-indexing-jittered'; import { TiptapTransformer } from '@hocuspocus/transformer'; import * as Y from 'yjs'; @@ -173,15 +177,24 @@ export class ImportService { }; } - async getNewPagePosition(spaceId: string): Promise { - const lastPage = await this.db + async getNewPagePosition( + spaceId: string, + parentPageId?: string, + ): Promise { + let query = this.db .selectFrom('pages') .select(['id', 'position']) .where('spaceId', '=', spaceId) .orderBy('position', (ob) => ob.collate('C').desc()) - .limit(1) - .where('parentPageId', 'is', null) - .executeTakeFirst(); + .limit(1); + + if (parentPageId) { + query = query.where('parentPageId', '=', parentPageId); + } else { + query = query.where('parentPageId', 'is', null); + } + + const lastPage = await query.executeTakeFirst(); if (lastPage) { return generateJitteredKeyBetween(lastPage.position, null); @@ -198,20 +211,21 @@ export class ImportService { workspaceId: string, ) { const file = await filePromise; - const fileBuffer = await file.toBuffer(); const fileExtension = path.extname(file.filename).toLowerCase(); const fileName = sanitizeFileName( path.basename(file.filename, fileExtension), ); - const fileSize = fileBuffer.length; - const fileNameWithExt = fileName + fileExtension; const fileTaskId = uuid7(); const filePath = `${getFileTaskFolderPath(FileTaskType.Import, workspaceId)}/${fileTaskId}/${fileNameWithExt}`; // upload file - await this.storageService.upload(filePath, fileBuffer); + const { stream, getBytesRead } = createByteCountingStream(file.file); + + await this.storageService.upload(filePath, stream); + + const fileSize = getBytesRead(); const fileTask = await this.db .insertInto('fileTasks') diff --git a/apps/server/src/integrations/storage/drivers/local.driver.ts b/apps/server/src/integrations/storage/drivers/local.driver.ts index 5171066c..aada2c05 100644 --- a/apps/server/src/integrations/storage/drivers/local.driver.ts +++ b/apps/server/src/integrations/storage/drivers/local.driver.ts @@ -20,9 +20,15 @@ export class LocalDriver implements StorageDriver { return join(this.config.storagePath, filePath); } - async upload(filePath: string, file: Buffer): Promise { + async upload(filePath: string, file: Buffer | Readable): Promise { try { - await fs.outputFile(this._fullPath(filePath), file); + const fullPath = this._fullPath(filePath); + if (file instanceof Buffer) { + await fs.outputFile(fullPath, file); + } else { + await fs.mkdir(dirname(fullPath), { recursive: true }); + await pipeline(file, createWriteStream(fullPath)); + } } catch (err) { throw new Error(`Failed to upload file: ${(err as Error).message}`); } @@ -42,7 +48,7 @@ export class LocalDriver implements StorageDriver { try { const fromFullPath = this._fullPath(fromFilePath); const toFullPath = this._fullPath(toFilePath); - + if (await this.exists(fromFilePath)) { await fs.copy(fromFullPath, toFullPath); } diff --git a/apps/server/src/integrations/storage/drivers/s3.driver.ts b/apps/server/src/integrations/storage/drivers/s3.driver.ts index f6d48677..ed44fded 100644 --- a/apps/server/src/integrations/storage/drivers/s3.driver.ts +++ b/apps/server/src/integrations/storage/drivers/s3.driver.ts @@ -23,19 +23,21 @@ export class S3Driver implements StorageDriver { this.s3Client = new S3Client(config as any); } - async upload(filePath: string, file: Buffer): Promise { + async upload(filePath: string, file: Buffer | Readable): Promise { try { const contentType = getMimeType(filePath); - const command = new PutObjectCommand({ - Bucket: this.config.bucket, - Key: filePath, - Body: file, - ContentType: contentType, - // ACL: "public-read", + const upload = new Upload({ + client: this.s3Client, + params: { + Bucket: this.config.bucket, + Key: filePath, + Body: file, + ContentType: contentType, + }, }); - await this.s3Client.send(command); + await upload.done(); } catch (err) { throw new Error(`Failed to upload file: ${(err as Error).message}`); } diff --git a/apps/server/src/integrations/storage/interfaces/storage-driver.interface.ts b/apps/server/src/integrations/storage/interfaces/storage-driver.interface.ts index 22a86d2b..f376c56f 100644 --- a/apps/server/src/integrations/storage/interfaces/storage-driver.interface.ts +++ b/apps/server/src/integrations/storage/interfaces/storage-driver.interface.ts @@ -1,7 +1,7 @@ import { Readable } from 'stream'; export interface StorageDriver { - upload(filePath: string, file: Buffer): Promise; + upload(filePath: string, file: Buffer | Readable): Promise; uploadStream(filePath: string, file: Readable, options?: { recreateClient?: boolean }): Promise; diff --git a/apps/server/src/integrations/storage/storage.service.ts b/apps/server/src/integrations/storage/storage.service.ts index d796351b..3ed887af 100644 --- a/apps/server/src/integrations/storage/storage.service.ts +++ b/apps/server/src/integrations/storage/storage.service.ts @@ -8,9 +8,9 @@ export class StorageService { private readonly logger = new Logger(StorageService.name); constructor( @Inject(STORAGE_DRIVER_TOKEN) private storageDriver: StorageDriver, - ) {} + ) { } - async upload(filePath: string, fileContent: Buffer | any) { + async upload(filePath: string, fileContent: Buffer | Readable) { await this.storageDriver.upload(filePath, fileContent); this.logger.debug(`File uploaded successfully. Path: ${filePath}`); } diff --git a/apps/server/src/main.ts b/apps/server/src/main.ts index 79340d6e..406921a0 100644 --- a/apps/server/src/main.ts +++ b/apps/server/src/main.ts @@ -5,9 +5,9 @@ import { NestFastifyApplication, } from '@nestjs/platform-fastify'; import { Logger, NotFoundException, ValidationPipe } from '@nestjs/common'; +import { Logger as PinoLogger } from 'nestjs-pino'; import { TransformHttpResponseInterceptor } from './common/interceptors/http-response.interceptor'; import { WsRedisIoAdapter } from './ws/adapter/ws-redis.adapter'; -import { InternalLogFilter } from './common/logger/internal-log-filter'; import fastifyMultipart from '@fastify/multipart'; import fastifyCookie from '@fastify/cookie'; @@ -24,10 +24,12 @@ async function bootstrap() { }), { rawBody: true, - logger: new InternalLogFilter(), + bufferLogs: true, }, ); + app.useLogger(app.get(PinoLogger)); + app.setGlobalPrefix('api', { exclude: ['robots.txt', 'share/:shareId/p/:pageSlug'], }); @@ -99,9 +101,7 @@ async function bootstrap() { const port = process.env.PORT || 3000; await app.listen(port, '0.0.0.0', () => { - logger.log( - `Listening on http://127.0.0.1:${port} / ${process.env.APP_URL}`, - ); + logger.log(`Listening on http://127.0.0.1:${port} / ${process.env.APP_URL}`); }); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b75c4560..960a1f2a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -545,6 +545,9 @@ importers: kysely-migration-cli: specifier: ^0.4.2 version: 0.4.2 + kysely-postgres-js: + specifier: ^3.0.0 + version: 3.0.0(kysely@0.28.2)(postgres@3.4.8) ldapts: specifier: ^7.4.0 version: 7.4.0 @@ -560,6 +563,9 @@ importers: nestjs-kysely: specifier: ^1.2.0 version: 1.2.0(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.11)(kysely@0.28.2)(reflect-metadata@0.2.2) + nestjs-pino: + specifier: ^4.5.0 + version: 4.5.0(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(pino-http@11.0.0)(pino@10.1.0)(rxjs@7.8.2) nodemailer: specifier: ^7.0.12 version: 7.0.12 @@ -581,15 +587,21 @@ importers: pdfjs-dist: specifier: ^5.4.394 version: 5.4.394 - pg: - specifier: ^8.16.3 - version: 8.16.3 pg-tsquery: specifier: ^8.4.2 version: 8.4.2 pgvector: specifier: ^0.2.1 version: 0.2.1 + postgres: + specifier: ^3.4.8 + version: 3.4.8 + pino-http: + specifier: ^11.0.0 + version: 11.0.0 + pino-pretty: + specifier: ^13.1.3 + version: 13.1.3 postmark: specifier: ^4.0.5 version: 4.0.5 @@ -666,9 +678,6 @@ importers: '@types/passport-jwt': specifier: ^4.0.1 version: 4.0.1 - '@types/pg': - specifier: ^8.11.11 - version: 8.11.11 '@types/supertest': specifier: ^6.0.2 version: 6.0.2 @@ -4764,9 +4773,6 @@ packages: '@types/passport@1.0.17': resolution: {integrity: sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg==} - '@types/pg@8.11.11': - resolution: {integrity: sha512-kGT1qKM8wJQ5qlawUrEkXgvMSXoV213KfMGXcwfDwUIfUHXqXYXOfS1nE1LINRJVVVx5wCm70XnFlMHaIcQAfw==} - '@types/prop-types@15.7.11': resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} @@ -5587,6 +5593,9 @@ packages: resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} engines: {node: '>=12.5.0'} + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + columnify@1.6.0: resolution: {integrity: sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==} engines: {node: '>=8.0.0'} @@ -5947,6 +5956,9 @@ packages: date-fns@4.1.0: resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + dateformat@4.6.3: + resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} + dayjs@1.11.13: resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} @@ -6447,6 +6459,9 @@ packages: resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} engines: {node: '>=4'} + fast-copy@4.0.2: + resolution: {integrity: sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw==} + fast-decode-uri-component@1.0.1: resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} @@ -6800,6 +6815,9 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + help-me@5.0.0: + resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} + hexoid@1.0.0: resolution: {integrity: sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==} engines: {node: '>=8'} @@ -7335,6 +7353,10 @@ packages: react: optional: true + joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} + js-beautify@1.15.1: resolution: {integrity: sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==} engines: {node: '>=14'} @@ -7504,6 +7526,16 @@ packages: resolution: {integrity: sha512-904MSUdzkdxl+k3C67ogvP6ogPOEr0D6ZZDtxAmDeIHEJxZAA+eC+TLAcJt3HQABTPetwsW3pj6y1MPmaveQUg==} hasBin: true + kysely-postgres-js@3.0.0: + resolution: {integrity: sha512-o2t/xNSYJQDW6rVGGFPXKmZ0BEz2dGn66c2B+cO/k9ZNcU2qPWPycQPQ+B+P2MBXbKYq0xV9BZmFIvkUrmFWAQ==} + engines: {bun: '>=1.2', node: '>=20'} + peerDependencies: + kysely: '>= 0.24.0 < 1' + postgres: ^3.4.0 + peerDependenciesMeta: + postgres: + optional: true + kysely@0.28.2: resolution: {integrity: sha512-4YAVLoF0Sf0UTqlhgQMFU9iQECdah7n+13ANkiuVfRvlK+uI0Etbgd7bVP36dKlG+NXWbhGua8vnGt+sdhvT7A==} engines: {node: '>=18.0.0'} @@ -7994,6 +8026,15 @@ packages: kysely: 0.x reflect-metadata: ^0.1.13 || ^0.2.2 + nestjs-pino@4.5.0: + resolution: {integrity: sha512-e54ChJMACSGF8gPYaHsuD07RW7l/OVoV6aI8Hqhpp0ZQ4WA8QY3eewL42JX7Z1U6rV7byNU7bGBV9l6d9V6PDQ==} + engines: {node: '>= 14'} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 + pino: ^7.5.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 + pino-http: ^6.4.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 + rxjs: ^7.1.0 + next@14.2.10: resolution: {integrity: sha512-sDDExXnh33cY3RkS9JuFEKaS4HmlWmDKP1VJioucCG6z5KuA008DPsDZOzi8UfqEk3Ii+2NCQSJrfbEWtZZfww==} engines: {node: '>=18.17.0'} @@ -8133,9 +8174,6 @@ packages: resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} engines: {node: '>= 0.4'} - obuf@1.1.2: - resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} - oidc-token-hash@5.0.3: resolution: {integrity: sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==} engines: {node: ^10.13.0 || >=12.0.0} @@ -8360,10 +8398,6 @@ packages: resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} engines: {node: '>=4.0.0'} - pg-numeric@1.0.2: - resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==} - engines: {node: '>=4'} - pg-pool@3.10.1: resolution: {integrity: sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==} peerDependencies: @@ -8372,9 +8406,6 @@ packages: pg-protocol@1.10.3: resolution: {integrity: sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==} - pg-protocol@1.7.0: - resolution: {integrity: sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==} - pg-tsquery@8.4.2: resolution: {integrity: sha512-waJSlBIKE+shDhuDpuQglTH6dG5zakDhnrnxu8XB8V5c7yoDSuy4pOxY6t2dyoxTjaKMcMmlByJN7n9jx9eqMA==} engines: {node: '>=10'} @@ -8383,10 +8414,6 @@ packages: resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} engines: {node: '>=4'} - pg-types@4.0.2: - resolution: {integrity: sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==} - engines: {node: '>=10'} - pg@8.16.3: resolution: {integrity: sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==} engines: {node: '>= 16.0.0'} @@ -8434,6 +8461,16 @@ packages: pino-abstract-transport@2.0.0: resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} + pino-abstract-transport@3.0.0: + resolution: {integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==} + + pino-http@11.0.0: + resolution: {integrity: sha512-wqg5XIAGRRIWtTk8qPGxkbrfiwEWz1lgedVLvhLALudKXvg1/L2lTFgTGPJ4Z2e3qcRmxoFxDuSdMdMGNM6I1g==} + + pino-pretty@13.1.3: + resolution: {integrity: sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==} + hasBin: true + pino-std-serializers@7.0.0: resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} @@ -8534,37 +8571,22 @@ packages: resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} engines: {node: '>=4'} - postgres-array@3.0.2: - resolution: {integrity: sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==} - engines: {node: '>=12'} - postgres-bytea@1.0.0: resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} engines: {node: '>=0.10.0'} - postgres-bytea@3.0.0: - resolution: {integrity: sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==} - engines: {node: '>= 6'} - postgres-date@1.0.7: resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} engines: {node: '>=0.10.0'} - postgres-date@2.1.0: - resolution: {integrity: sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==} - engines: {node: '>=12'} - postgres-interval@1.2.0: resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} engines: {node: '>=0.10.0'} - postgres-interval@3.0.0: - resolution: {integrity: sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==} + postgres@3.4.8: + resolution: {integrity: sha512-d+JFcLM17njZaOLkv6SCev7uoLaBtfK86vMUXhW1Z4glPWh4jozno9APvW/XKFJ3CCxVoC7OL38BqRydtu5nGg==} engines: {node: '>=12'} - postgres-range@1.1.4: - resolution: {integrity: sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==} - posthog-js@1.255.1: resolution: {integrity: sha512-KMh0o9MhORhEZVjXpktXB5rJ8PfDk+poqBoTSoLzWgNjhJf6D8jcyB9jUMA6vVPfn4YeepVX5NuclDRqOwr5Mw==} peerDependencies: @@ -8691,6 +8713,9 @@ packages: prr@1.0.1: resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + pump@3.0.3: + resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} + punycode.js@2.3.1: resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} engines: {node: '>=6'} @@ -9368,6 +9393,10 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + strip-json-comments@5.0.3: + resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} + engines: {node: '>=14.16'} + stripe@17.5.0: resolution: {integrity: sha512-kcyeAkDFjGsVl17FqnG7q/+xIjt0ZjOo9Dm+q8deAvs2Xe4iAHrhxyoP4etUVFc+/LZJANjIPVR+ZOnt9hr/Ug==} engines: {node: '>=12.*'} @@ -15197,12 +15226,6 @@ snapshots: dependencies: '@types/express': 4.17.23 - '@types/pg@8.11.11': - dependencies: - '@types/node': 22.19.1 - pg-protocol: 1.7.0 - pg-types: 4.0.2 - '@types/prop-types@15.7.11': {} '@types/qrcode@1.5.5': @@ -16229,6 +16252,8 @@ snapshots: color-convert: 2.0.1 color-string: 1.9.1 + colorette@2.0.20: {} + columnify@1.6.0: dependencies: strip-ansi: 6.0.1 @@ -16636,6 +16661,8 @@ snapshots: date-fns@4.1.0: {} + dateformat@4.6.3: {} + dayjs@1.11.13: {} dayjs@1.11.19: {} @@ -17268,6 +17295,8 @@ snapshots: iconv-lite: 0.4.24 tmp: 0.0.33 + fast-copy@4.0.2: {} + fast-decode-uri-component@1.0.1: {} fast-deep-equal@2.0.1: {} @@ -17654,6 +17683,8 @@ snapshots: dependencies: function-bind: 1.1.2 + help-me@5.0.0: {} + hexoid@1.0.0: {} highlight.js@11.11.1: {} @@ -18408,6 +18439,8 @@ snapshots: '@types/react': 18.3.12 react: 18.3.1 + joycon@3.1.1: {} + js-beautify@1.15.1: dependencies: config-chain: 1.1.13 @@ -18579,6 +18612,12 @@ snapshots: '@commander-js/extra-typings': 11.1.0(commander@11.1.0) commander: 11.1.0 + kysely-postgres-js@3.0.0(kysely@0.28.2)(postgres@3.4.8): + dependencies: + kysely: 0.28.2 + optionalDependencies: + postgres: 3.4.8 + kysely@0.28.2: {} langium@3.3.1: @@ -19159,6 +19198,13 @@ snapshots: kysely: 0.28.2 reflect-metadata: 0.2.2 + nestjs-pino@4.5.0(@nestjs/common@11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(pino-http@11.0.0)(pino@10.1.0)(rxjs@7.8.2): + dependencies: + '@nestjs/common': 11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) + pino: 10.1.0 + pino-http: 11.0.0 + rxjs: 7.8.2 + next@14.2.10(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.51.0): dependencies: '@next/env': 14.2.10 @@ -19327,8 +19373,6 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.0.0 - obuf@1.1.2: {} - oidc-token-hash@5.0.3: {} ollama@0.6.3: @@ -19553,19 +19597,19 @@ snapshots: pg-cloudflare@1.2.7: optional: true - pg-connection-string@2.9.1: {} + pg-connection-string@2.9.1: + optional: true - pg-int8@1.0.1: {} - - pg-numeric@1.0.2: {} + pg-int8@1.0.1: + optional: true pg-pool@3.10.1(pg@8.16.3): dependencies: pg: 8.16.3 + optional: true - pg-protocol@1.10.3: {} - - pg-protocol@1.7.0: {} + pg-protocol@1.10.3: + optional: true pg-tsquery@8.4.2: {} @@ -19576,16 +19620,7 @@ snapshots: postgres-bytea: 1.0.0 postgres-date: 1.0.7 postgres-interval: 1.2.0 - - pg-types@4.0.2: - dependencies: - pg-int8: 1.0.1 - pg-numeric: 1.0.2 - postgres-array: 3.0.2 - postgres-bytea: 3.0.0 - postgres-date: 2.1.0 - postgres-interval: 3.0.0 - postgres-range: 1.1.4 + optional: true pg@8.16.3: dependencies: @@ -19596,10 +19631,12 @@ snapshots: pgpass: 1.0.5 optionalDependencies: pg-cloudflare: 1.2.7 + optional: true pgpass@1.0.5: dependencies: split2: 4.2.0 + optional: true pgvector@0.2.1: {} @@ -19630,6 +19667,33 @@ snapshots: dependencies: split2: 4.2.0 + pino-abstract-transport@3.0.0: + dependencies: + split2: 4.2.0 + + pino-http@11.0.0: + dependencies: + get-caller-file: 2.0.5 + pino: 10.1.0 + pino-std-serializers: 7.0.0 + process-warning: 5.0.0 + + pino-pretty@13.1.3: + dependencies: + colorette: 2.0.20 + dateformat: 4.6.3 + fast-copy: 4.0.2 + fast-safe-stringify: 2.1.1 + help-me: 5.0.0 + joycon: 3.1.1 + minimist: 1.2.8 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 3.0.0 + pump: 3.0.3 + secure-json-parse: 4.0.0 + sonic-boom: 4.0.1 + strip-json-comments: 5.0.3 + pino-std-serializers@7.0.0: {} pino@10.1.0: @@ -19741,27 +19805,21 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - postgres-array@2.0.0: {} + postgres-array@2.0.0: + optional: true - postgres-array@3.0.2: {} + postgres-bytea@1.0.0: + optional: true - postgres-bytea@1.0.0: {} - - postgres-bytea@3.0.0: - dependencies: - obuf: 1.1.2 - - postgres-date@1.0.7: {} - - postgres-date@2.1.0: {} + postgres-date@1.0.7: + optional: true postgres-interval@1.2.0: dependencies: xtend: 4.0.2 + optional: true - postgres-interval@3.0.0: {} - - postgres-range@1.1.4: {} + postgres@3.4.8: {} posthog-js@1.255.1: dependencies: @@ -19921,6 +19979,11 @@ snapshots: prr@1.0.1: optional: true + pump@3.0.3: + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + punycode.js@2.3.1: {} punycode@2.3.1: {} @@ -20705,6 +20768,8 @@ snapshots: strip-json-comments@3.1.1: {} + strip-json-comments@5.0.3: {} + stripe@17.5.0: dependencies: '@types/node': 22.19.1 @@ -21441,7 +21506,8 @@ snapshots: xpath@0.0.34: {} - xtend@4.0.2: {} + xtend@4.0.2: + optional: true y-indexeddb@9.0.12(yjs@13.6.29): dependencies: